mirror of https://github.com/zrafa/xinu-avr.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
16 KiB
710 lines
16 KiB
#!/bin/sh |
|
######################################################################### |
|
# # |
|
# build-make - build entries for a makefile # |
|
# # |
|
# use: build-make dir_spec... # |
|
# # |
|
# where each dir_spec, which specifies one of the source # |
|
# directories to include has the form: # |
|
# # |
|
# -s source_dir_path [pattern...] # |
|
# # |
|
# Build-make generates rules for a Makefile that will compile # |
|
# the source files from the specified set of source directories. # |
|
# Build-make is run in the directory that will hold the Makefile, # |
|
# and assumes .o files will be placed in the ./binaries # |
|
# directory in the same file as the Makefile. # |
|
# # |
|
# # |
|
# A directory specification begins with a -s srgument that is # |
|
# followed by a source directory name (a relative path is # |
|
# recommended, but not required), and a list of zero or more # |
|
# patterns. The patterns are used to specify which files # |
|
# from source directory should be omitted from consideration. # |
|
# Build-make starts by making a list of all C and assembly # |
|
# language files in the directory (i.e. files that match *.c and # |
|
# *.[Ss]). It then uses the set of patterns (if any are present) # |
|
# to decide which of the files to exclude from further # |
|
# processing. A pattern can contain literal characters plus one # |
|
# asterisk, which indicates a wild card match. For example, # |
|
# 'xxx', 'xxx*', '*xxx', 'xxx*yyy' are legal forms of patterns. # |
|
# Note: any pattern argument that contains an asterisk must be # |
|
# enclosed in quotes on the command line to prevent the shell # |
|
# from interpreting it. For example, to omit files that start # |
|
# with 'debug', one would specify: 'debug*' in quotes. Omitting # |
|
# files has two purposes. First, it allows files to be left in # |
|
# the soruce directory that are not normally needed (e.g., # |
|
# debug.c). Second, a programmer can specify a set of files # |
|
# for which standard compilation rules are insufficient. Of # |
|
# course, if a programmer omits a file, the programmer must # |
|
# insert a compilation rule into the Makefile. # |
|
# # |
|
# Build-make does not create a complete Makefile. Instead, # |
|
# build-make writes text output to stdout and allows the # |
|
# programmer to use the output to construct a Makefile. # |
|
# Typically, a programmer runs: # |
|
# # |
|
# build-make > Makedefs # |
|
# # |
|
# and then inserts the line # |
|
# # |
|
# include Makedefs # |
|
# # |
|
# into a Makefile. Doing so allows the Makedefs to be recreated # |
|
# easily if the contents of the source directories change. # |
|
# # |
|
# Build-make generates two variables: SOURCE_FILES that contains # |
|
# a list of all source file names, and OBJECT_FILES that lists # |
|
# all the object files. it also generates a list of rules # |
|
# for generating object files. For example, a rule might # |
|
# be: # |
|
# # |
|
# binaries/aaa.o: ../../system/aaa.c # |
|
# $(CC) $(CFLAGS) -o binaries/aaa.o ../../system/aaa.c # |
|
# # |
|
######################################################################### |
|
|
|
MSG1='use is: build-make dir_spec...' |
|
MSG2=' where dir_spec is -s source_dir_path [pattern...]' |
|
|
|
# |
|
# Define prefixes shared by this script and the awk program |
|
# |
|
PRE_ERR='+ERROR:' |
|
PRE_DIR='+DIR:' |
|
PRE_OMIT='+OMIT:' |
|
PRE_CFILE='+CFILE:' |
|
PRE_SFILE='+SFILE:' |
|
|
|
export PRE_ERR PRE_DIR PRE_OMIT PRE_CFILE PRE_SFILE |
|
|
|
TMP1=".,BUILDM1-$$" |
|
TMP2=".,BUILDM2-$$" |
|
trap "rm -f $TMP1 $TMP2; exit" 1 2 3 |
|
|
|
# |
|
# Parse arguments and generate input to the script below |
|
# |
|
|
|
if test $# -lt 2; then |
|
echo "$MSG1" >&2 |
|
echo "$MSG2" >&2 |
|
exit 1 |
|
fi |
|
|
|
# iterate through dirspecs |
|
|
|
( |
|
> $TMP1 |
|
> $TMP2 |
|
|
|
while test $# -gt 0; do |
|
|
|
# parse -s and get directory name |
|
|
|
if test $# -lt 2; then |
|
echo "$PRE_ERR" |
|
echo "$MSG1" >&2 |
|
echo "$MSG2" >&2 |
|
exit 1 |
|
fi |
|
|
|
case "x$1" in |
|
x-s) DIR="$2" |
|
# verify that directory exists |
|
if test -d $DIR; then |
|
echo "$PRE_DIR$DIR" |
|
else |
|
echo "$PRE_ERR" |
|
echo "cannot find a source directory named $DIR" >&2 |
|
exit 1 |
|
fi |
|
shift; shift |
|
;; |
|
*) echo 'expecting -s at start of directory specification' >&2 |
|
echo "$PRE_ERR" |
|
echo "$MSG1" >&2 |
|
echo "$MSG2" >&2 |
|
;; |
|
esac |
|
|
|
# |
|
# See if DIR contains any .c or .[sS] files |
|
# |
|
if test "x$DIR" = "x."; then |
|
CFILES=`ls *.c 2>$TMP1` |
|
SFILES=`ls *.[sS] 2>$TMP2` |
|
else |
|
CFILES=`ls $DIR/*.c 2>$TMP1` |
|
SFILES=`ls $DIR/*.[sS] 2>$TMP2` |
|
fi |
|
|
|
if test -s $TMP1 -a -s $TMP2; then |
|
(echo;echo "Warning: directory $DIR does not contain any .c or .[sS] files to compile";echo) >&2 |
|
fi |
|
rm -f $TMP1 $TMP2 |
|
|
|
# arguments up to the next -s as patterns that specify omitted files |
|
|
|
LOOKING=true |
|
while $LOOKING; do |
|
if test $# -eq 0; then |
|
LOOKING=false |
|
elif test "x$1" = "x-s"; then |
|
LOOKING=false |
|
else |
|
echo "$PRE_OMIT$1" |
|
shift |
|
fi |
|
done |
|
|
|
# output .s and .S files for this directory |
|
|
|
if test "x$SFILES" != "x"; then |
|
for i in $SFILES; do |
|
echo "$PRE_SFILE$i" |
|
done |
|
fi |
|
|
|
# output .c files for this directory |
|
|
|
if test "x$CFILES" != "x"; then |
|
for i in $CFILES; do |
|
echo "$PRE_CFILE$i" |
|
done |
|
fi |
|
|
|
done |
|
) | |
|
|
|
gawk ' |
|
|
|
######################################################################### |
|
# # |
|
# # |
|
# Start of awk script # |
|
# # |
|
# # |
|
######################################################################### |
|
|
|
#################### |
|
# |
|
# function ftmlist - format a list of file names |
|
# |
|
# args are: number of names, array of names, maximum name length, |
|
# directory name, prefix to use |
|
# |
|
#################### |
|
|
|
function fmtlist(n,items,maxl,dname, pfix) { |
|
|
|
if (n <= 0) { |
|
return; |
|
} |
|
|
|
if (doneheading == 0) { |
|
printf("\n\n%s\n%s\n%sDirectory %s\n%s\n%s\n",hdr4, hdr5, hdr6, dname, hdr5, hdr4); |
|
doneheading = 1; |
|
} |
|
fmt2 = sprintf("\n%%-%ds\\\n",maxwid-1); |
|
printf(fmt2, pfix " ="); |
|
|
|
namwid = maxl + 2; |
|
# |
|
# compute number of names per line allowing 2 blanks between them |
|
# and not taking more than actwid characters |
|
# |
|
nperline = int( (actwid+2)/(maxl+2) ); |
|
extrabl = actwid-(nperline*maxl + 2*(nperline-1))+1; |
|
cnt = 1; |
|
fmt = sprintf("%%-%ds",maxl); |
|
for (i=1; i<=n ; i++) { |
|
if (cnt > nperline) { |
|
for (b=1; b<=extrabl; b++) { |
|
printf(" "); |
|
} |
|
printf("\\\n"); |
|
cnt = 1; |
|
} |
|
if (cnt == 1) { |
|
printf("%s", tb); |
|
} else { |
|
printf(" "); |
|
} |
|
printf(fmt, items[i]); |
|
cnt++; |
|
} |
|
printf("\n"); |
|
return; |
|
} |
|
|
|
#################### |
|
# |
|
# function isomitted - check a file name against the list of omitted patters |
|
# for the current directory and report 1 if the file |
|
# name is omitted |
|
# |
|
# arg: file name to check |
|
# |
|
# globals used: nomits, omits[] list of omitted patterns (1 through nomits) |
|
# |
|
#################### |
|
|
|
function isomitted(io_fname) { |
|
|
|
io_flen = length(io_fname); |
|
|
|
# |
|
# Iterate through all omit patterns for the current directory |
|
# |
|
|
|
for (io_j=1; io_j <= nomits; io_j++) { |
|
|
|
# Get pattern to check |
|
io_omit = omits[io_j]; |
|
|
|
# length of omit pattern |
|
io_olen = length(io_omit); |
|
|
|
# Find where "*" occurs in the pattern and set prefix length |
|
|
|
io_plen = index(io_omit, "*") - 1; |
|
|
|
if (io_plen < 0) { # No * in the pattern, so check exact match |
|
|
|
if (io_fname == io_omit) { |
|
return 1; |
|
} else { |
|
continue; |
|
} |
|
|
|
} |
|
|
|
# At least one * occurs in the pattern |
|
|
|
if (io_olen == 1) { # Pattern is just a *, so omit everything |
|
return 1; |
|
} |
|
|
|
if (io_plen > 1) { # if fixed prefix exists, check for match |
|
|
|
# A file name shorter than the prefix is not omitted |
|
|
|
if (io_flen < io_plen) { |
|
continue; |
|
} |
|
|
|
# If prefix does not match, name is not omitted |
|
|
|
if ( substr(io_omit, 1, io_plen) != substr(io_fname, 1, io_plen) ) { |
|
continue; |
|
} |
|
} |
|
|
|
# Prefix has matched; check suffix |
|
|
|
io_slen = io_olen - io_plen -1; |
|
|
|
if (io_slen <= 0) { # If no suffix, pattern matches |
|
return 1; |
|
} |
|
|
|
# A file name shorter than the suffix is not omitted |
|
|
|
if (io_flen < io_slen) { |
|
continue; |
|
} |
|
|
|
# If suffix in omit pattern exactly matches the suffix of the |
|
# file name, this file name is omitted |
|
|
|
if ( substr(io_omit, io_olen - (io_slen - 1)) == \ |
|
substr(io_fname, length(io_fname) - (io_slen - 1)) ) { |
|
return 1; |
|
} |
|
|
|
} |
|
|
|
# The file name does not match any omitted pattern, so keep it |
|
|
|
return 0; |
|
} |
|
|
|
#################### |
|
# |
|
# getbasename - extract the basename from a path name and return it |
|
# |
|
# arg: path name |
|
# |
|
#################### |
|
|
|
function getbasename(gb_path) { |
|
|
|
for (gb_pos = length(gb_path); gb_pos > 0; gb_pos--) { |
|
if (substr(gb_path, gb_pos, 1) == "/") { |
|
break; |
|
} |
|
} |
|
if (gb_pos > 0) { |
|
return substr(gb_path, gb_pos+1); |
|
} else { |
|
return gb_path; |
|
} |
|
} |
|
|
|
|
|
#################### |
|
# |
|
# Initialize variables for the awk script |
|
# |
|
#################### |
|
|
|
BEGIN { |
|
ndirs = 0; # Number of directories found so far |
|
maxwid = 81; # Maximum width of formatted line |
|
actwid = maxwid - 10; # Actual width for names (line width minus 8 for tab indent and |
|
# 2 for " \" at the end of the line) |
|
|
|
tb = " "; # Tab character |
|
plen_err = length("'$PRE_ERR'"); |
|
plen_dir = length("'$PRE_DIR'"); |
|
plen_omt = length("'$PRE_OMIT'"); |
|
plen_cfl = length("'$PRE_CFILE'"); |
|
plen_sfl = length("'$PRE_SFILE'"); |
|
|
|
|
|
hdr1 = "#-------------------------------------------------------------------------------#"; |
|
hdr2 = "# #"; |
|
hdr3 = "# Definitions generated by build-make on '"`date`"' #"; |
|
hdr4 = "#------------------------------------------------------------------"; |
|
hdr5 = "#"; |
|
hdr6 = "# "; |
|
hdr7 = "# Rules For Generating Object Files #"; |
|
hdr8 = "# Rules for files in directory "; |
|
|
|
msg1 = "SRC_CFILES ="; |
|
msg2 = "SRC_SFILES ="; |
|
msg3 = "OBJ_FILES ="; |
|
} |
|
|
|
# |
|
# Skip blank lines in input |
|
# |
|
/^ *$/ { |
|
next; |
|
} |
|
|
|
# |
|
# Handle errors |
|
# |
|
/^\'${PRE_ERR}'/ { |
|
exit(1); |
|
} |
|
|
|
# |
|
# Start a new directory |
|
# |
|
|
|
/^\'$PRE_DIR'/ { |
|
|
|
ndirs++; |
|
dirs[ndirs] = substr($0,plen_dir+1); |
|
|
|
# Initialize counts for this directory |
|
|
|
ncfiles[ndirs] = 0; |
|
nsfiles[ndirs] = 0; |
|
nomits = 0; |
|
next; |
|
} |
|
|
|
# |
|
# Collect list of omitted files for this directory (must occur in the input before |
|
# any C or S files for the directory) |
|
# |
|
|
|
/^\'$PRE_OMIT'/ { |
|
|
|
nomits++; |
|
omits[nomits] = substr($0,plen_omt+1); |
|
next; |
|
} |
|
|
|
# |
|
# Handle a C file from the current directory |
|
# |
|
|
|
/^\'$PRE_CFILE'/ { |
|
|
|
fullname = substr($0,plen_cfl+1); |
|
basename = getbasename(fullname); |
|
|
|
# Skip omitted files |
|
|
|
if (isomitted(basename) > 0) { |
|
next; |
|
} |
|
|
|
ncfiles[ndirs]++; |
|
cpath[ndirs "%" ncfiles[ndirs]] = fullname; |
|
cfile[ndirs "%" ncfiles[ndirs]] = basename; |
|
|
|
next; |
|
} |
|
|
|
# |
|
# Handle as S file from the current directory |
|
# |
|
|
|
/^\'$PRE_SFILE'/ { |
|
|
|
fullname = substr($0,plen_sfl+1); |
|
basename = getbasename(fullname); |
|
|
|
# Skip omitted files |
|
|
|
if (isomitted(basename) > 0) { |
|
next; |
|
} |
|
|
|
nsfiles[ndirs]++; |
|
spath[ndirs "%" nsfiles[ndirs]] = fullname; |
|
sfile[ndirs "%" nsfiles[ndirs]] = basename; |
|
|
|
next; |
|
} |
|
|
|
|
|
# |
|
# Any other input line is unexpected |
|
# |
|
|
|
{ |
|
print "Error: unexpected input line ->", $0; |
|
exit 1; |
|
} |
|
|
|
END { |
|
################################################################# |
|
# # |
|
# Do all the work of formatting and printing the makefile # |
|
# rules. Start by printing an initial comment block and the # |
|
# variable initializtion statements. # |
|
# # |
|
################################################################# |
|
|
|
printf("%s\n%s\n%s\n%s\n%s\n\n",hdr1, hdr2, hdr3, hdr2, hdr1); |
|
printf("%s\n%s\n\n",msg1, msg2, msg3); |
|
|
|
totlen = 0; # max over valid source file names in all directories |
|
|
|
################################################################# |
|
# # |
|
# Iterate over all directories and generate a unique prefix # |
|
# to be used on make variables for each directory # |
|
# # |
|
################################################################# |
|
|
|
totlen = 0; # max file name length across all directories |
|
|
|
for (d=1; d<=ndirs; d++) { |
|
|
|
# Generate a unique variable name prefix for this directory |
|
|
|
dirpath = dirs[d]; |
|
strt = 1; |
|
str = toupper(dirpath); |
|
finish = length(str); |
|
ch = substr(str,strt,1); |
|
while ( (ch == ".") || (ch == "/") ) { |
|
strt++; |
|
if (strt > finish) { |
|
break; |
|
} |
|
ch = substr(str,strt,1); |
|
} |
|
if (strt > finish) { |
|
if (substr(str,1,1) == ".") { |
|
prefix = "DOT"; |
|
} else { |
|
prefix = "SLASH"; |
|
} |
|
for (i=2; i<=finish; i++) { |
|
if (substr(str,i,1) == ".") { |
|
prefix = prefix "_DOT"; |
|
} else { |
|
prefix = prefix "_SLASH"; |
|
} |
|
} |
|
} else { |
|
prefix = ""; |
|
for (i=strt; i<=finish; i++) { |
|
ch = substr(str,i,1); |
|
if (ch == "/") { |
|
prefix = prefix "_"; |
|
} else { |
|
prefix = prefix ch; |
|
} |
|
} |
|
} |
|
pref[d] = prefix; |
|
|
|
doneheading = 0; |
|
|
|
# |
|
# Compute the maximum length of a file name in this directory |
|
# and update the max length across all directories |
|
# |
|
|
|
maxlen = 0; # max file name length for this directory |
|
|
|
# Check S files first |
|
|
|
for (i = 1; i <= nsfiles[d]; i++) { |
|
srcsfiles[i] = sfile[d "%" i] |
|
l = length(srcsfiles[i]); |
|
if (l > maxlen) { |
|
maxlen = l; |
|
} |
|
} |
|
if (maxlen > totlen) { # update global max across all directories |
|
totlen = maxlen; |
|
} |
|
|
|
# Check C files |
|
|
|
for (i = 1; i <= ncfiles[d]; i++) { |
|
srccfiles[i] = cfile[d "%" i] |
|
l = length(srccfiles[i]); |
|
if (l > maxlen) { |
|
maxlen = l; |
|
} |
|
} |
|
if (maxlen > totlen) { # update global max across all directories |
|
totlen = maxlen; |
|
} |
|
|
|
# Format the list of S files for this directory |
|
|
|
fmtlist(nsfiles[d], srcsfiles, maxlen, dirs[d], pref[d] "_SFILES"); |
|
|
|
# Format the list of C files for this directory |
|
|
|
fmtlist(ncfiles[d], srccfiles, maxlen, dirs[d], pref[d] "_CFILES"); |
|
|
|
printf("\n"); |
|
|
|
# |
|
# Generate assignments for Makefile variables |
|
# |
|
|
|
nasn = 0; |
|
|
|
# |
|
# Generate assignments for variables SRC_CFILES and SRC_SFILES |
|
# |
|
if (ncfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = "SRC_CFILES"; |
|
asnr[nasn] = sprintf("+= ${%s_CFILES}", pref[d]); |
|
} |
|
if (nsfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = "SRC_SFILES"; |
|
asnr[nasn] = sprintf("+= ${%s_SFILES}", pref[d]); |
|
} |
|
|
|
# |
|
# Generate assignments for variables prefix_CFULL and prefix_SFULL |
|
# |
|
|
|
if (ncfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = sprintf("%s_CFULL", prefix); |
|
asnr[nasn] = sprintf("+= ${%s_CFILES:%%=%s/%%}", pref[d], dirs[d]); |
|
} |
|
if (nsfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = sprintf("%s_SFULL", prefix); |
|
asnr[nasn] = sprintf("+= ${%s_SFILES:%%=%s/%%}", pref[d], dirs[d]); |
|
} |
|
|
|
# |
|
# Generate assignments for variables SRC_CFULL and SRC_SFULL |
|
# |
|
|
|
if (ncfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = "SRC_CFULL"; |
|
asnr[nasn] = sprintf("+= ${%s_CFULL}", pref[d]); |
|
} |
|
if (nsfiles[d] > 0) { |
|
nasn++; |
|
asnl[nasn] = "SRC_SFULL"; |
|
asnr[nasn] = sprintf("+= ${%s_SFULL}", pref[d]); |
|
} |
|
wid = 0; |
|
for (i=1; i<=nasn; i++) { |
|
n = length(asnl[i]); |
|
if (n > wid) { |
|
wid = n; |
|
} |
|
} |
|
fmt3 = sprintf("%%-%ds %%s\n", wid); |
|
for (i=1; i<=nasn; i++) { |
|
printf(fmt3, asnl[i], asnr[i]); |
|
} |
|
} |
|
|
|
printf("\n\n%s\n%s\n%s\n%s\n%s\n\n", hdr1, hdr2, hdr7, hdr2, hdr1); |
|
|
|
printf("OBJ_TMP = ${patsubst %%.s,%%.o,$(SRC_SFILES)} # substitute .s => .o\n"); |
|
printf("OBJ_SFILES = ${patsubst %%.S,%%.o,$(OBJ_TMP)} # substitute .S => .o\n"); |
|
printf("OBJ_CFILES = ${patsubst %%.c,%%.o,$(SRC_CFILES)} # substitute .c => .o\n"); |
|
printf("OBJ_LIST = ${OBJ_CFILES} ${OBJ_SFILES}\n"); |
|
printf("OBJ_FILES = ${OBJ_LIST:%%=binaries/%%}\n"); |
|
printf("SRC_FULL = ${SRC_CFULL} ${SRC_SFULL}\n"); |
|
|
|
fmt4c = sprintf("%%-%ds%%s\n\t${CC} ${CFLAGS} -o %%s %%s\n",9+totlen+3); |
|
fmt4s = sprintf("%%-%ds%%s\n\t${CC} ${CFLAGS} -o %%s %%s\n",9+totlen+3); |
|
for (d=1; d<=ndirs; d++) { |
|
needheadings = 1; |
|
|
|
# print dependencies for .s files |
|
|
|
for (i = 1; i <= nsfiles[d]; i++) { |
|
if (needheadings > 0) { |
|
printf("\n%s\n%s%s\n%s\n\n", hdr4, hdr8, dirs[d], hdr4); |
|
needheadings = 0; |
|
} |
|
fname = sfile[d "%" i]; |
|
tmpobj = "binaries/" substr(fname, 1, length(fname)-2) ".o"; |
|
tmpsrc = dirs[d] "/" fname; |
|
printf(fmt4s, tmpobj ":", tmpsrc, tmpobj, tmpsrc); |
|
} |
|
|
|
# print dependencies for .c files |
|
|
|
for (i = 1; i <= ncfiles[d]; i++) { |
|
if (needheadings > 0) { |
|
printf("\n%s\n%s%s\n%s\n\n", hdr4, hdr8, dirs[d], hdr4); |
|
needheadings = 0; |
|
} |
|
fname = cfile[d "%" i]; |
|
tmpobj = "binaries/" substr(fname, 1, length(fname)-2) ".o"; |
|
tmpsrc = dirs[d] "/" fname; |
|
printf(fmt4c, tmpobj ":", tmpsrc, tmpobj, tmpsrc); |
|
} |
|
|
|
} |
|
printf("\nobjs: ${OBJ_FILES}\n"); |
|
|
|
printf("\nlist_obj:\n\t@echo ${OBJ_FILES}\n"); |
|
|
|
printf("\nlist_csrc:\n\t@echo ${SRC_CFILES}\n"); |
|
|
|
printf("\nlist_ssrc:\n\t@echo ${SRC_SFILES}\n"); |
|
|
|
printf("\n# Export variables for recursive make calls (such as the library)\n\nexport\n\n"); |
|
} '
|
|
|