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