fixed include scanner, so K8 I.V.A.N. is properly recompiled now
[k8jam.git] / src / headers.c
blob6f9a22254e433c73e4e62fbdcad1ac76ac9856a9
1 /*
2 * Copyright 1993, 2000 Christopher Seiwald.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * headers.c - handle #includes in source files
21 * Using regular expressions provided as the variable $(HDRSCAN),
22 * headers() searches a file for #include files and phonies up a
23 * rule invocation:
25 * $(HDRRULE) <target> : <include files> ;
27 * External routines:
28 * headers() - scan a target for include files and call HDRRULE
30 * Internal routines:
31 * headers1() - using regexp, scan a file and build include LIST
33 #include "jam.h"
34 #include "lists.h"
35 #include "parse.h"
36 #include "compile.h"
37 #include "rules.h"
38 #include "variable.h"
39 #include "re9.h"
40 #include "headers.h"
41 #include "newstr.h"
42 #include "hdrmacro.h"
43 #include "hcache.h"
44 #include "dstrings.h"
45 #include "gdcdeps.h"
49 #ifndef OPT_HEADER_CACHE_EXT
50 static LIST *headers1 (const char *file, LIST *hdrscan);
51 #endif
56 * headers() - scan a target for include files and call HDRRULE
58 #define MAXINC (32)
60 void headers (TARGET *t) {
61 LIST *hdrscan;
62 LIST *hdrrule;
63 LOL lol;
64 if (!(hdrscan = var_get("HDRSCAN")) || !(hdrrule = var_get("HDRRULE"))) return;
65 /* doctor up call to HDRRULE rule */
66 /* call headers1() to get LIST of included files */
67 if (DEBUG_HEADER) printf("header scan %s\n", t->name);
68 lol_init(&lol);
69 lol_add(&lol, list_new(L0, t->name, 1));
71 #ifdef OPT_HEADER_CACHE_EXT
72 lol_add(&lol, hcache(t, hdrscan));
73 #else
74 lol_add(&lol, headers1(t->boundname, hdrscan));
75 #endif
77 lol_add(&lol, hcache(t, hdrscan));
78 if (lol_get(&lol, 1)) list_free(evaluate_rule(hdrrule->string, &lol, L0));
79 /* clean up */
80 lol_free(&lol);
84 struct lcicacheentry_s {
85 const char *str;
86 const char *val;
87 struct lcicacheentry_s *children;
88 struct lcicacheentry_s *next;
90 typedef struct lcicacheentry_s lcicacheentry_t;
92 static lcicacheentry_t *lcicache = NULL;
95 const char *lci_cache_find (const char *basefile, int bflen, dstring_t *fname) {
96 if (basefile == NULL || basefile[0] == 0 || bflen < 1) return NULL;
97 if (fname == NULL || dstr_cstr(fname)[0] == 0) return NULL;
98 for (lcicacheentry_t *e0 = lcicache; e0 != NULL; e0 = e0->next) {
99 int slen = strlen(e0->str);
100 if (slen == bflen && memcmp(e0->str, basefile, slen) == 0) {
101 // found bucket
102 const char *fnc = dstr_cstr(fname);
103 for (lcicacheentry_t *e1 = e0->children; e1 != NULL; e1 = e1->next) {
104 if (strcmp(e1->str, fnc) == 0) return e1->val;
108 return NULL;
112 void lci_cache_put (const char *basefile, int bflen, dstring_t *fname, dstring_t *xname) {
113 if (basefile == NULL || basefile[0] == 0 || bflen < 1) return;
114 if (fname == NULL || dstr_cstr(fname)[0] == 0) return;
115 if (xname == NULL) return;
116 // create final entry
117 lcicacheentry_t *ne = calloc(1, sizeof(lcicacheentry_t));
118 ne->str = strdup(dstr_cstr(fname));
119 ne->val = strdup(dstr_cstr(xname));
120 // find bucket
121 for (lcicacheentry_t *e0 = lcicache; e0 != NULL; e0 = e0->next) {
122 int slen = strlen(e0->str);
123 if (slen == bflen && memcmp(e0->str, basefile, slen) == 0) {
124 // found bucket
125 ne->next = e0->children;
126 e0->children = ne;
127 return;
130 // create new bucket
131 lcicacheentry_t *bk = calloc(1, sizeof(lcicacheentry_t));
132 bk->str = strdup(basefile);
133 bk->children = ne;
134 bk->next = lcicache;
135 lcicache = bk;
139 static void resolve_local_include (dstring_t *dest, const char *basefile, dstring_t *fname) {
140 dstr_init(dest);
141 if (dstr_cstr(fname)[0] == '/') { dstr_set_cstr(dest, dstr_cstr(fname)); return; }
142 int bflen = strlen(basefile);
143 while (bflen > 0 && basefile[bflen-1] != '/') --bflen;
144 if (bflen == 0) { dstr_set_cstr(dest, dstr_cstr(fname)); return; }
145 // check cache
146 const char *riname = lci_cache_find(basefile, bflen, fname);
147 if (riname != NULL) {
148 // i found her!
149 dstr_set_cstr(dest, riname);
150 return;
152 // build name
154 if (basefile[0] != '/') {
155 static char buf[8192];
156 char *dir = getcwd(buf, sizeof(buf));
157 if (dir && dir[0]) {
158 dstr_push_cstr(dest, dir);
159 if (dir[strlen(dir)-1] != '/') dstr_push_char(dest, '/');
163 dstr_push_buf(dest, basefile, bflen);
164 dstr_push_cstr(dest, dstr_cstr(fname));
165 if (access(dstr_cstr(dest), R_OK) != 0) dstr_set_cstr(dest, dstr_cstr(fname));
166 // put it in cache
167 lci_cache_put(basefile, bflen, fname, dest);
172 * headers1() - using regexp, scan a file and build include LIST
175 #ifndef OPT_HEADER_CACHE_EXT
176 static
177 #endif
179 LIST *headers1 (const char *file, LIST *hdrscan) {
180 FILE *f;
181 int i;
182 int rec = 0;
183 LIST *result = NULL;
184 regexp_t *re[MAXINC];
185 regexp_t *re_macros, *re_dlinc = NULL;
186 re9_sub_t mt[4];
187 int line_size = 16384;
188 char *buf; /* line_size size */
189 dstring_t buf2, buf3;
190 int dlang = 0;
191 for (const LIST *t = var_get("HDRPATH_IGNORE"); t != NULL; t = t->next) {
192 if (t->string && t->string[0]) {
193 if (strncmp(file, t->string, strlen(t->string)) == 0) {
194 if (DEBUG_HEADER) printf("DBG: ignoring header [%s]\n", file);
195 return result;
200 char *t = strrchr(file, '.');
201 dlang = (t != NULL && (strcmp(t, ".d") == 0 || strcmp(t, ".di") == 0));
203 if (DEBUG_HEADER) printf("DBG: trying header [%s] (D=%d)\n", file, dlang);
204 //fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
205 if (dlang) {
206 // try GDC
209 LIST *gdc = var_get("GDC");
210 fprintf(stderr, "GDC: <"); list_print_ex(stderr, gdc, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
211 LIST *gdcfa = var_get("GDCFLAGS.all");
212 fprintf(stderr, "GDCFLAGS.all: <"); list_print_ex(stderr, gdcfa, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
213 LIST *gdcf = var_get("GDCFLAGS");
214 fprintf(stderr, "GDCFLAGS: <"); list_print_ex(stderr, gdcf, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
215 LIST *t;
216 t = var_get("TOP");
217 fprintf(stderr, "TOP: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
218 t = var_get("SUBDIR_TOKENS");
219 fprintf(stderr, "SUBDIR_TOKENS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
220 t = var_get("HDRS");
221 fprintf(stderr, "HDRS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
223 fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
225 if (DEBUG_HEADER) printf("trying GDC deps for [%s]...\n", file);
226 result = gdcDeps(file);
227 if (result != NULL) {
228 if (DEBUG_HEADER) {
229 printf("GDC deps for [%s]\n", file);
230 for (const LIST *l = result; l != NULL; l = l->next) printf(" <%s>\n", l->string);
232 return result;
235 if (!(f = fopen(file, "r"))) return result;
236 //fprintf(stderr, "DBG: processing header [%s] (D=%d)\n", file, dlang);
239 LIST *t = var_get("DLANG");
240 dlang = (t != NULL && t->string[0]);
241 //fprintf(stderr, "DLANG: %d\n", dlang);
244 if (dlang) {
245 re[rec++] = regexp_compile("\\bimport\\s+([^;]+)\\s*;", 0); // don't use ".+n?;", it's buggy
246 re[rec++] = regexp_compile("\\bimport\\s*\\(([^)]+)\\)", 0);
247 re_dlinc = regexp_compile("\\s*([^,;]+)\\s*", 0);
249 if (!dlang) {
250 while (rec < MAXINC && hdrscan) {
251 //printf("header re %d: [%s]\n", rec, hdrscan->string);
252 re[rec] = regexp_compile(hdrscan->string, 0);
253 ++rec;
254 hdrscan = list_next(hdrscan);
257 /* the following regexp is used to detect cases where a */
258 /* file is included through a line line "#include MACRO" */
259 re_macros = regexp_compile("^\\s*#\\s*include\\s+([A-Za-z_][A-Za-z0-9_]*).*$", 0);
260 if (DEBUG_HEADER) printf("header processing: [%s] (%d)\n", file, rec);
261 if ((buf = malloc(line_size)) == NULL) { fprintf(stderr, "FATAL: out of memory!\n"); abort(); }
262 dstr_init(&buf2);
263 dstr_init(&buf3);
264 while (fgets(buf, line_size, f)) {
265 for (i = 0; i < rec; ++i) {
266 mt[0].sp = mt[0].ep = NULL;
267 if (regexp_execute(re[i], buf, mt, 2) > 0 && mt[1].sp != NULL) {
268 /* copy and terminate extracted string */
269 int l = mt[1].ep-mt[1].sp;
270 dstr_set_buf(&buf2, mt[1].sp, l);
271 if (dlang && i <= 1) {
272 //fprintf(stderr, "dlang: [%s]\n", dstr_cstr(&buf2));
273 if (i == 0) {
274 /* this is DLang 'import' directive */
275 const char *s = dstr_cstr(&buf2);
276 //fprintf(stderr, "=== %d; [%s]\n", l, dstr_cstr(&buf2));
277 while (*s) {
278 char *p, *t;
279 int againD = 0;
280 if (!regexp_execute(re_dlinc, s, mt, 2) || mt[1].sp == NULL) break;
281 dstr_set_buf(&buf3, mt[1].sp, mt[1].ep-mt[1].sp);
282 dstr_push_cstr(&buf3, ".d");
283 p = dstr_cstr(&buf3);
284 //fprintf(stderr, "*** [%s]\n", dstr_cstr(&buf3));
285 //fprintf(stderr, "+++000 [%s]\n", dstr_cstr(&buf3));
286 if ((t = strchr(p, ':')) != NULL) { againD = 1; *t = 0; }
287 if ((t = strchr(p, '=')) != NULL) p = t+1;
288 //fprintf(stderr, "+++001 [%s]\n", dstr_cstr(&buf3));
289 while (*p && isspace(*p)) ++p;
290 if (*p) {
291 t = p+strlen(p);
292 while (t > p && isspace(t[-1])) --t;
293 *t = 0;
295 // add ".d" again if it was cut
296 // note that we have always place for it
297 if (*p && againD) strcat(p, ".d");
298 //fprintf(stderr, "+++002 [%s]\n", dstr_cstr(&buf3));
299 if (*p) {
300 for (t = p; *t; ++t) if (*t == '.') *t = '/';
301 //fprintf(stderr, "+++ [%s]\n", dstr_cstr(&buf3));
302 *(strrchr(p, '/')) = '.'; // restore '.d'
303 //fprintf(stderr, "dlang include: [%s]\n", p);
304 result = list_new(result, p, 0);
305 // add name/package.d too
306 char *pd = malloc(strlen(p)+32);
307 strcpy(pd, p);
308 strcpy(strrchr(pd, '.'), "/package.d");
309 //fprintf(stderr, "dlang include pkg: [%s]\n", pd);
310 result = list_new(result, pd, 0);
311 free(pd);
313 s = mt[1].ep;
315 } else {
316 /* this is DLang import() operator */
317 /* TODO: process string escapes */
318 //fprintf(stderr, "II: [%s]\n", dstr_cstr(&buf2));
319 if (dstr_cstr(&buf2)[0] == '"' || dstr_cstr(&buf2)[0] == '`') {
320 char *fn = strdup(dstr_cstr(&buf2)+1);
321 if (fn[0] && fn[1] && fn[strlen(fn)-1] == dstr_cstr(&buf2)[dstr_len(&buf2)-1]) {
322 fn[strlen(fn)-1] = 0;
323 //fprintf(stderr, "import directive: [%s]\n", fn);
324 result = list_new(result, fn, 0);
326 free(fn);
329 } else {
330 // check if this is local include
331 dstring_t xname;
332 resolve_local_include(&xname, file, &buf2);
333 //fprintf(stderr, "FILE: <%s>; include: <%s>\n", file, dstr_cstr(&xname));
334 result = list_new(result, dstr_cstr(&xname), 0);
335 dstr_done(&xname);
337 if (DEBUG_HEADER) printf("header found: %s\n", dstr_cstr(&buf2));
340 /* special treatment for #include MACRO */
341 mt[0].sp = mt[0].ep = NULL;
342 if (regexp_execute(re_macros, buf, mt, 2) > 0 && mt[1].sp != NULL) {
343 const char *header_filename;
344 int l = mt[1].ep-mt[1].sp;
345 dstr_set_buf(&buf2, mt[1].sp, l);
346 if (DEBUG_HEADER) printf("macro header found: %s", dstr_cstr(&buf2));
347 header_filename = macro_header_get(dstr_cstr(&buf2));
348 if (header_filename) {
349 if (DEBUG_HEADER) printf(" resolved to '%s'\n", header_filename);
350 result = list_new(result, header_filename, 0);
351 } else {
352 if (DEBUG_HEADER) printf(" ignored !!\n");
356 dstr_done(&buf3);
357 dstr_done(&buf2);
358 free(buf);
359 fclose(f);
360 if (re_dlinc != NULL) regexp_free(re_dlinc);
361 regexp_free(re_macros);
362 while (--rec >= 0) regexp_free(re[rec]);
363 return result;