1# depend.awk -- awk script used to construct makefile dependencies 2# for nethack's source files (`make depend' support for Makefile.src). 3# 4# usage: 5# cd src ; nawk -f depend.awk ../include/*.h list-of-.c/.cpp-files 6# 7# This awk program scans each file in sequence, looking for lines beginning 8# with `#include "' and recording the name inside the quotes. For .h files, 9# that's all it does. For each .c file, it writes out a make rule for the 10# corresponding .o file; dependencies in nested header files are propagated 11# to the .o target. 12# 13# config.h and hack.h get special handling because of their heavy use; 14# timestamps for them allow make to avoid rechecking dates on 15# subsidiary headers for every source file; 16# extern.h gets special handling to avoid excessive recompilation 17# during development; 18# patchlev.h gets special handling because it only exists on systems 19# which consider filename patchlevel.h to be too long; 20# interp.c gets special handling because it usually doesn't exist; it's 21# assumed to be the last #include in the file where it occurs. 22# win32api.h gets special handling because it only exists for some ports; 23# it's assumed to be the last #include in the file where it occurs 24# 25BEGIN { FS = "\"" #for `#include "X"', $2 is X 26 special[++sp_cnt] = "../include/config.h" 27 special[++sp_cnt] = "../include/hack.h" 28 alt_deps["../include/extern.h"] = "" 29 alt_deps["../include/patchlev.h"] = "" 30 alt_deps["interp.c"] = " #interp.c" #comment it out 31 alt_deps["../include/win32api.h"] = " #../include/win32api.h" 32 } 33FNR == 1 { output_dep() #finish previous file 34 file = FILENAME #setup for current file 35 } 36/^\#[ \t]*include[ \t]+\"/ { #find `#include "X"' 37 incl = $2; 38 #[3.4.0: gnomehack headers currently aren't in include] 39 if (incl ~ /\.h$/) { 40 if (incl ~ /^gn/) # gnomehack special case 41 incl = "../win/gnome/" incl 42 else 43 incl = "../include/" incl 44 } 45 deps[file] = deps[file] " " incl 46 } 47END { output_dep() } #finish the last file 48 49 50# 51# `file' has been fully scanned, so process it now; for .h files, 52# don't do anything (we've just been collecting their dependencies); 53# for .c files, output the `make' rule for corresponding .o file 54# 55function output_dep( targ) 56{ 57 if (file ~ /\.cp*$/) { 58 #prior to very first .c|.cpp file, handle some special header file cases 59 if (!c_count++) 60 output_specials() 61 #construct object filename from source filename 62 targ = file; sub("^.+/", "", targ); sub("\\.cp*$", ".o", targ) 63 #format and write the collected dependencies 64 format_dep(targ, file) 65 } 66} 67 68# 69# handle some targets (config.h, hack.h) via special timestamping rules 70# 71function output_specials( i, sp, alt_sp) 72{ 73 for (i = 1; i <= sp_cnt; i++) { 74 sp = special[i] 75 #change "../include/foo.h" first to "foo.h", then ultimately to "$(FOO_H)" 76 alt_sp = sp; sub("^.+/", "", alt_sp) 77 print "#", alt_sp, "timestamp" #output a `make' comment 78 #- sub("\\.", "_", alt_sp); alt_sp = "$(" toupper(alt_sp) ")" 79 #+ Some nawks don't have toupper(), so hardwire these instead. 80 sub("config.h", "$(CONFIG_H)", alt_sp); sub("hack.h", "$(HACK_H)", alt_sp); 81 format_dep(alt_sp, sp) #output the target 82 print "\ttouch " alt_sp #output a build command 83 alt_deps[sp] = alt_sp #alternate dependency for depend() 84 } 85 print "#" 86} 87 88# 89# write a target and its dependency list in pretty-printed format; 90# if target's primary source file has a path prefix, also write build command 91# 92function format_dep(target, source, n, i, list) 93{ 94 split("", done) #``for (x in done) delete done[x]'' 95 printf("%s:", target); col = length(target) + 1 96 #- printf("\t"); col += 8 - (col % 8); 97 #- if (col == 8) { printf("\t"); col += 8 } 98 source = depend("", source, 0) 99 n = split(source, list, " +") 100 for (i = 2; i <= n; i++) { #(leading whitespace yields empty 1st element) 101 if (col + length(list[i]) >= (i < n ? 78 : 80)) { 102 printf(" \\\n\t\t"); col = 16 #make a backslash+newline split 103 } else { 104 printf(" "); col++; 105 } 106 printf("%s", list[i]); col += length(list[i]) 107 } 108 printf("\n") #terminate 109 #write build command if first source entry has non-include path prefix 110 source = list[2] 111 if (source ~ /\// && substr(source, 1, 11) != "../include/") { 112 if (source ~ /\.cpp$/ ) 113 print "\t$(CXX) $(CXXFLAGS) -c " source 114 else if (source ~ /\/gnome\//) # "../win/gnome/foo.c" 115 print "\t$(CC) $(CFLAGS) $(GNOMEINC) -c " source 116 else 117 print "\t$(CC) $(CFLAGS) -c " source 118 } 119} 120 121# 122# recursively add the dependencies for file `name' to string `inout' 123# (unless `skip', in which case we're only marking files as already done) 124# 125function depend(inout, name, skip, n, i, list) 126{ 127 if (!done[name]++) { 128 if (name in alt_deps) { #some names have non-conventional dependencies 129 if (!skip) inout = inout " " alt_deps[name] 130 skip = 1 131 } else { #ordinary name 132 if (!skip) inout = inout " " name 133 } 134 if (name in deps) { 135 #- n = split(deps[name], list, " +") 136 #- for (i = 2; i <= n; i++) #(leading whitespace yields empty 1st element) 137 #- inout = depend(inout, list[i], skip) 138 #+ At least one implementation of nawk handles the local array `list' wrong, 139 #+ so the clumsier substitute code below is used as a workaround. 140 list = deps[name]; sub("^ +", "", list) 141 while (list) { 142 match((list " "), " +"); i = RSTART; n = RLENGTH 143 inout = depend(inout, substr(list, 1, i-1), skip) 144 list = substr(list, i+n) 145 } 146 } 147 } 148 return inout 149} 150 151#depend.awk# 152