# depend.awk -- awk script used to construct makefile dependencies # for nethack's source files (`make depend' support for Makefile.src). # $NHDT-Date: 1709577497 2024/03/04 18:38:17 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ # # usage: # awk -f depend.awk ../include/*.h list-of-.c/.cpp-files # (might need nawk or gawk instead of plain awk if it is really old) # meta usage: # ( cd src ; make all ; cp ../sys/unix/Makefile.src ./Makefile ; \ # make depend ; cp ./Makefile ../sys/unix/Makefile.src ; \ # cd .. ; sh sys/unix/setup.sh [sys/unix/hints/FOO] ) # newer usage: # cd sys/unix ; make -f Makefile.src updatedepend # # This awk program scans each file in sequence, looking for lines beginning # with `#include "' and recording the name inside the quotes. For .h files, # that's all it does. For each .c file, it writes out a make rule for the # corresponding .o file; dependencies in nested header files are propagated # to the .o target. # # Variables that can be set on the command line: # -v dontsortdeps=1 do not sort the dependencies # -v dontsortfules=1 do not sort the rules # # config.h and hack.h get special handling because of their heavy use; # timestamps for them allow make to avoid rechecking dates on # subsidiary headers for every source file; # extern.h gets special handling to avoid excessive recompilation # during development; # patchlev.h gets special handling because it only exists on systems # which consider filename patchlevel.h to be too long; # amiconf.h moved from ../include/ to ../outdated/include/ so skip it # interp.c gets special handling because it usually doesn't exist; it's # assumed to be the last #include in the file where it occurs. # win32api.h gets special handling because it only exists for some ports; # it's assumed to be the last #include in the file where it occurs # zlib.h ditto # BEGIN { FS = "\"" #for `#include "X"', $2 is X special[++sp_cnt] = "../include/config.h" special[++sp_cnt] = "../include/hack.h" alt_deps["../include/extern.h"] = "" alt_deps["../include/patchlev.h"] = "" alt_deps["../include/amiconf.h"] = "" alt_deps["interp.c"] = " #interp.c" #comment it out alt_deps["../include/win32api.h"] = " #../include/win32api.h" alt_deps["../include/zlib.h"] = " #zlib.h" #comment it out } FNR == 1 { output_dep() #finish previous file file = FILENAME #setup for current file } /^[#][ \t]*include[ \t]+["]/ { #find `#include "X"' incl = $2 #[3.4.0: gnomehack headers currently aren't in include] #[3.6.2: Qt4 headers aren't in include either] #[3.6.2: curses headers likewise] #[3.7.0: Qt headers have moved; process 'moc' files] if (incl ~ /[.]h$/) { if (incl ~ "curses[.]h") incl = "" # skip "curses.h"; it should be else if (incl ~ /^..\/lib\/lua-.*\/src\/l/) incl = "" # skip lua headers else if (incl ~ /^curs/) # curses special case incl = "../win/curses/" incl else if (incl ~ /(.*\/)*qt_/) { # Qt special cases # Qt v3 headers are in ../win/Qt3 # Qt v4/v5/v6 headers are in ../win/Qt # *.moc files have path in their #include if (file ~ /[.]moc$/) ; # keep 'incl' as-is else if (file ~ /^[.][.]\/win\/Qt3\/.*/) incl = "../win/Qt3/" incl else # Qt v4/v5/v6 incl = "../win/Qt/" incl } else if (incl ~ /^gn/) # gnomehack special case incl = "../win/gnome/" incl else incl = "../include/" incl } deps[file] = deps[file] " " incl } END { output_dep() #finish the last file output_final() #write output } # # `file' has been fully scanned, so process it now; for .h files, # don't do anything (we've just been collecting their dependencies); # for .c files, output the `make' rule for corresponding .o file # function output_dep(){ if(!dontsortrules){ worklist[++worklistctr] = file } else { output_final2() } } function output_final( x) { if(!dontsortrules){ nhsort(worklist, 1, worklistctr, 1) for(x=1;x<=worklistctr;x++){ file = worklist[x] output_final2() } } else { return } } function output_final2( base, targ, moc) { #get the file's base name (including suffix) base = file; sub("^.+/", "", base) #for qt source files, add qt timestamp file as extra dependency moc = (base ~ /[.]moc$/) if (moc || base ~ /(.+\/)*qt_.*[.]cpp$/) { deps[file] = deps[file] " $(QTn_H)" } if ((base ~ /[.]cp*$/ || moc) && !(file in filedone)) { #prior to very first .c|.cpp file, handle some special header file cases if (!c_count++) output_specials() #construct object filename from source filename targ = base; sub("[.]cp*$", ".o", targ) #format and write the collected dependencies format_dep(targ, file) #generated file tile.c can appear more than once in the list of files #so track which files have already been handled; can't reuse done[] here filedone[file]++; } } # # handle some targets (config.h, hack.h) via special timestamping rules # function output_specials( i, sp, alt_sp) { for (i = 1; i <= sp_cnt; i++) { sp = special[i] #change "../include/foo.h" first to "foo.h", then ultimately to "$(FOO_H)" alt_sp = sp; sub("^.+/", "", alt_sp) print "#", alt_sp, "timestamp" #output a `make' comment #- sub("[.]", "_", alt_sp); alt_sp = "$(" toupper(alt_sp) ")" #+ Some nawks don't have toupper(), so hardwire these instead. sub("config.h", "$(CONFIG_H)", alt_sp); sub("hack.h", "$(HACK_H)", alt_sp) format_dep(alt_sp, sp) #output the target print "\ttouch " alt_sp #output a build command alt_deps[sp] = alt_sp #alternate dependency for depend() } print "#" } # # write a target and its dependency list in pretty-printed format; # if target's primary source file has a path prefix, also write build command # function format_dep(target, source, col, n, i, list, prefix, moc) { split("", done) #``for (x in done) delete done[x]'' moc = (target ~ /[.]moc$/) prefix = (moc || substr(target,1,1) == "$") ? "" : "$(TARGETPFX)" printf("%s%s:", prefix, target); col = length(target) + 1 + length(prefix) #- printf("\t"); col += 8 - (col % 8); #- if (col == 8) { printf("\t"); col += 8 } source = depend("", source, 0) sub(" +$", "", source) #strip trailing spaces, if any n = split(source, list, " +") #first: leading whitespace yields empty 1st element; not sure why moc #files duplicate the target as next element but we need to skip that too first = moc ? 3 : 2 source = list[first] if (!dontsortdeps){ nhsort(list, first, n, 0) } for (i = first; i <= n; i++) { if (col + length(list[i]) >= (i < n ? 78 : 80) - 1) { printf(" \\\n\t\t"); col = 16 #make a backslash+newline split } else { printf(" "); col++; } printf("%s", list[i]); col += length(list[i]) } printf("\n") #terminate #write build command if first source entry has non-include path prefix if (moc) { print "\t$(MOCPATH) -o $@ " source } else if (source ~ /\// && substr(source, 1, 11) != "../include/") { if (source ~ /[.]cpp$/ ) print "\t$(TARGET_CXX) $(TARGET_CXXFLAGS) -c -o $@ " source else if (source ~ /\/X11\//) # "../win/X11/foo.c" print "\t$(TARGET_CC) $(TARGET_CFLAGS) $(X11CFLAGS) -c -o $@ " source else if (source ~ /\/gnome\//) # "../win/gnome/foo.c" print "\t$(TARGET_CC) $(TARGET_CFLAGS) $(GNOMEINC) -c -o $@ " source else print "\t$(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ " source } } # # recursively add the dependencies for file `name' to string `inout' # (unless `skip', in which case we're only marking files as already done) # function depend(inout, name, skip, n, i, list) { if (!done[name]++) { if (name in alt_deps) { #some names have non-conventional dependencies if (!skip) inout = inout " " alt_deps[name] skip = 1 } else { #ordinary name if (!skip) inout = inout " " name } if (name in deps) { #- n = split(deps[name], list, " +") #- for (i = 2; i <= n; i++) #(leading whitespace yields empty 1st element) #- inout = depend(inout, list[i], skip) #+ At least one implementation of nawk handles the local array `list' wrong, #+ so the clumsier substitute code below is used as a workaround. list = deps[name]; sub("^ +", "", list) while (list) { match((list " "), " +"); i = RSTART; n = RLENGTH inout = depend(inout, substr(list, 1, i-1), skip) list = substr(list, i+n) } } } return inout } # # sort list[first]..list[last] # Derived from: https://www.baeldung.com/linux/awk-begin-and-end-rules # function nhsort(list, first, last, cmpid, i,j,temp) { for (i = first; i <= last-1; i++) { for (j = i+1; j <= last; j++) { if (nhcmp(list[i], list[j], cmpid)) { temp = list[i] list[i] = list[j] list[j] = temp } } } } function nhcmp(a,b,cmpid) { if(cmpid == 0){ # sort dependencies # commented out entry (there can be only one) MUST be last if (a ~ /^#/){ return 1} if (b ~ /^#/){ return 0} # 2 .c or .cpp files if (a ~ /\.c(pp)?$/ && b ~ /\.c(pp)?$/ ){ return a > b } # a .c or .cpp file and anything else if (a ~ /\.c(pp)?$/){ return 0 } if (b ~ /\.c(pp)?$/){ return 1 } # default return a > b } else if(cmpid == 1){ # sort rules # 2 .h files if (a ~ /\.h$/ && b ~ /\/.h$/){ return a > b } # a .h and anything else if (a ~ /\.h$/){ return 0 } if (b ~ /\.h$/){ return 1 } # 2 .c or .cpp files if (a ~ /\.c(pp)?$/ && b ~ /\.c(pp)?$/ ){ return a > b } # a .c or .cpp file and anything else if (a ~ /\.c(pp)?$/){ return 0 } if (b ~ /\.c(pp)?$/){ return 1 } # 2 .moc files if (a ~ /\.moc$/ && b ~ /\.moc$/){ return a > b } # a .moc and anything else if (a ~ /\.moc$/){ return 0 } if (b ~ /\.moc$/){ return 1 } # default return a > b } else { print "internal error cmpid=" cmpid exit 1 } } #depend.awk#