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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * headers.c - handle #includes in source files
20 * Using regular expressions provided as the variable $(HDRSCAN),
21 * headers() searches a file for #include files and phonies up a
24 * $(HDRRULE) <target> : <include files> ;
27 * headers() - scan a target for include files and call HDRRULE
30 * headers1() - using regexp, scan a file and build include LIST
48 #ifndef OPT_HEADER_CACHE_EXT
49 static LIST *headers1 (const char *file, LIST *hdrscan);
55 * headers() - scan a target for include files and call HDRRULE
59 void headers (TARGET
*t
) {
63 if (!(hdrscan
= var_get("HDRSCAN")) || !(hdrrule
= var_get("HDRRULE"))) return;
64 /* doctor up call to HDRRULE rule */
65 /* call headers1() to get LIST of included files */
66 if (DEBUG_HEADER
) printf("header scan %s\n", t
->name
);
68 lol_add(&lol
, list_new(L0
, t
->name
, 1));
70 #ifdef OPT_HEADER_CACHE_EXT
71 lol_add(&lol, hcache(t, hdrscan));
73 lol_add(&lol, headers1(t->boundname, hdrscan));
76 lol_add(&lol
, hcache(t
, hdrscan
));
77 if (lol_get(&lol
, 1)) list_free(evaluate_rule(hdrrule
->string
, &lol
, L0
));
83 struct lcicacheentry_s
{
86 struct lcicacheentry_s
*children
;
87 struct lcicacheentry_s
*next
;
89 typedef struct lcicacheentry_s lcicacheentry_t
;
91 static lcicacheentry_t
*lcicache
= NULL
;
94 const char *lci_cache_find (const char *basefile
, int bflen
, dstring_t
*fname
) {
95 if (basefile
== NULL
|| basefile
[0] == 0 || bflen
< 1) return NULL
;
96 if (fname
== NULL
|| dstr_cstr(fname
)[0] == 0) return NULL
;
97 for (lcicacheentry_t
*e0
= lcicache
; e0
!= NULL
; e0
= e0
->next
) {
98 int slen
= strlen(e0
->str
);
99 if (slen
== bflen
&& memcmp(e0
->str
, basefile
, slen
) == 0) {
101 const char *fnc
= dstr_cstr(fname
);
102 for (lcicacheentry_t
*e1
= e0
->children
; e1
!= NULL
; e1
= e1
->next
) {
103 if (strcmp(e1
->str
, fnc
) == 0) return e1
->val
;
111 void lci_cache_put (const char *basefile
, int bflen
, dstring_t
*fname
, dstring_t
*xname
) {
112 if (basefile
== NULL
|| basefile
[0] == 0 || bflen
< 1) return;
113 if (fname
== NULL
|| dstr_cstr(fname
)[0] == 0) return;
114 if (xname
== NULL
) return;
115 // create final entry
116 lcicacheentry_t
*ne
= calloc(1, sizeof(lcicacheentry_t
));
117 ne
->str
= strdup(dstr_cstr(fname
));
118 ne
->val
= strdup(dstr_cstr(xname
));
120 for (lcicacheentry_t
*e0
= lcicache
; e0
!= NULL
; e0
= e0
->next
) {
121 int slen
= strlen(e0
->str
);
122 if (slen
== bflen
&& memcmp(e0
->str
, basefile
, slen
) == 0) {
124 ne
->next
= e0
->children
;
130 lcicacheentry_t
*bk
= calloc(1, sizeof(lcicacheentry_t
));
131 bk
->str
= strdup(basefile
);
138 static void resolve_local_include (dstring_t
*dest
, const char *basefile
, dstring_t
*fname
) {
140 if (dstr_cstr(fname
)[0] == '/') { dstr_set_cstr(dest
, dstr_cstr(fname
)); return; }
141 int bflen
= strlen(basefile
);
142 while (bflen
> 0 && basefile
[bflen
-1] != '/') --bflen
;
143 if (bflen
== 0) { dstr_set_cstr(dest
, dstr_cstr(fname
)); return; }
145 const char *riname
= lci_cache_find(basefile
, bflen
, fname
);
146 if (riname
!= NULL
) {
148 dstr_set_cstr(dest
, riname
);
153 if (basefile[0] != '/') {
154 static char buf[8192];
155 char *dir = getcwd(buf, sizeof(buf));
157 dstr_push_cstr(dest, dir);
158 if (dir[strlen(dir)-1] != '/') dstr_push_char(dest, '/');
162 dstr_push_buf(dest
, basefile
, bflen
);
163 dstr_push_cstr(dest
, dstr_cstr(fname
));
164 if (access(dstr_cstr(dest
), R_OK
) != 0) dstr_set_cstr(dest
, dstr_cstr(fname
));
166 lci_cache_put(basefile
, bflen
, fname
, dest
);
171 * headers1() - using regexp, scan a file and build include LIST
174 #ifndef OPT_HEADER_CACHE_EXT
178 LIST
*headers1 (const char *file
, LIST
*hdrscan
) {
183 regexp_t
*re
[MAXINC
];
184 regexp_t
*re_macros
, *re_dlinc
= NULL
;
186 int line_size
= 16384;
187 char *buf
; /* line_size size */
188 dstring_t buf2
, buf3
;
190 for (const LIST
*t
= var_get("HDRPATH_IGNORE"); t
!= NULL
; t
= t
->next
) {
191 if (t
->string
&& t
->string
[0]) {
192 if (strncmp(file
, t
->string
, strlen(t
->string
)) == 0) {
193 if (DEBUG_HEADER
) printf("DBG: ignoring header [%s]\n", file
);
199 char *t
= strrchr(file
, '.');
200 dlang
= (t
!= NULL
&& (strcmp(t
, ".d") == 0 || strcmp(t
, ".di") == 0));
202 if (DEBUG_HEADER
) printf("DBG: trying header [%s] (D=%d)\n", file
, dlang
);
203 //fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
208 LIST *gdc = var_get("GDC");
209 fprintf(stderr, "GDC: <"); list_print_ex(stderr, gdc, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
210 LIST *gdcfa = var_get("GDCFLAGS.all");
211 fprintf(stderr, "GDCFLAGS.all: <"); list_print_ex(stderr, gdcfa, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
212 LIST *gdcf = var_get("GDCFLAGS");
213 fprintf(stderr, "GDCFLAGS: <"); list_print_ex(stderr, gdcf, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
216 fprintf(stderr, "TOP: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
217 t = var_get("SUBDIR_TOKENS");
218 fprintf(stderr, "SUBDIR_TOKENS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
220 fprintf(stderr, "HDRS: <"); list_print_ex(stderr, t, LPFLAG_NO_TRSPACE); fprintf(stderr, ">\n");
222 fprintf(stderr, "DBG: trying header [%s] (D=%d)\n", file, dlang);
224 if (DEBUG_HEADER
) printf("trying GDC deps for [%s]...\n", file
);
225 result
= gdcDeps(file
);
226 if (result
!= NULL
) {
228 printf("GDC deps for [%s]\n", file
);
229 for (const LIST
*l
= result
; l
!= NULL
; l
= l
->next
) printf(" <%s>\n", l
->string
);
234 if (!(f
= fopen(file
, "r"))) return result
;
235 //fprintf(stderr, "DBG: processing header [%s] (D=%d)\n", file, dlang);
238 LIST *t = var_get("DLANG");
239 dlang = (t != NULL && t->string[0]);
240 //fprintf(stderr, "DLANG: %d\n", dlang);
244 re
[rec
++] = regexp_compile("\\bimport\\s+([^;]+)\\s*;", 0); // don't use ".+n?;", it's buggy
245 re
[rec
++] = regexp_compile("\\bimport\\s*\\(([^)]+)\\)", 0);
246 re_dlinc
= regexp_compile("\\s*([^,;]+)\\s*", 0);
249 while (rec
< MAXINC
&& hdrscan
) {
250 //printf("header re %d: [%s]\n", rec, hdrscan->string);
251 re
[rec
] = regexp_compile(hdrscan
->string
, 0);
253 hdrscan
= list_next(hdrscan
);
256 /* the following regexp is used to detect cases where a */
257 /* file is included through a line line "#include MACRO" */
258 re_macros
= regexp_compile("^\\s*#\\s*include\\s+([A-Za-z_][A-Za-z0-9_]*).*$", 0);
259 if (DEBUG_HEADER
) printf("header processing: [%s] (%d)\n", file
, rec
);
260 if ((buf
= malloc(line_size
)) == NULL
) { fprintf(stderr
, "FATAL: out of memory!\n"); abort(); }
263 while (fgets(buf
, line_size
, f
)) {
264 for (i
= 0; i
< rec
; ++i
) {
265 mt
[0].sp
= mt
[0].ep
= NULL
;
266 if (regexp_execute(re
[i
], buf
, mt
, 2) > 0 && mt
[1].sp
!= NULL
) {
267 /* copy and terminate extracted string */
268 int l
= mt
[1].ep
-mt
[1].sp
;
269 dstr_set_buf(&buf2
, mt
[1].sp
, l
);
270 if (dlang
&& i
<= 1) {
271 //fprintf(stderr, "dlang: [%s]\n", dstr_cstr(&buf2));
273 /* this is DLang 'import' directive */
274 const char *s
= dstr_cstr(&buf2
);
275 //fprintf(stderr, "=== %d; [%s]\n", l, dstr_cstr(&buf2));
279 if (!regexp_execute(re_dlinc
, s
, mt
, 2) || mt
[1].sp
== NULL
) break;
280 dstr_set_buf(&buf3
, mt
[1].sp
, mt
[1].ep
-mt
[1].sp
);
281 dstr_push_cstr(&buf3
, ".d");
282 p
= dstr_cstr(&buf3
);
283 //fprintf(stderr, "*** [%s]\n", dstr_cstr(&buf3));
284 //fprintf(stderr, "+++000 [%s]\n", dstr_cstr(&buf3));
285 if ((t
= strchr(p
, ':')) != NULL
) { againD
= 1; *t
= 0; }
286 if ((t
= strchr(p
, '=')) != NULL
) p
= t
+1;
287 //fprintf(stderr, "+++001 [%s]\n", dstr_cstr(&buf3));
288 while (*p
&& isspace(*p
)) ++p
;
291 while (t
> p
&& isspace(t
[-1])) --t
;
294 // add ".d" again if it was cut
295 // note that we have always place for it
296 if (*p
&& againD
) strcat(p
, ".d");
297 //fprintf(stderr, "+++002 [%s]\n", dstr_cstr(&buf3));
299 for (t
= p
; *t
; ++t
) if (*t
== '.') *t
= '/';
300 //fprintf(stderr, "+++ [%s]\n", dstr_cstr(&buf3));
301 *(strrchr(p
, '/')) = '.'; // restore '.d'
302 //fprintf(stderr, "dlang include: [%s]\n", p);
303 result
= list_new(result
, p
, 0);
304 // add name/package.d too
305 char *pd
= malloc(strlen(p
)+32);
307 strcpy(strrchr(pd
, '.'), "/package.d");
308 //fprintf(stderr, "dlang include pkg: [%s]\n", pd);
309 result
= list_new(result
, pd
, 0);
315 /* this is DLang import() operator */
316 /* TODO: process string escapes */
317 //fprintf(stderr, "II: [%s]\n", dstr_cstr(&buf2));
318 if (dstr_cstr(&buf2
)[0] == '"' || dstr_cstr(&buf2
)[0] == '`') {
319 char *fn
= strdup(dstr_cstr(&buf2
)+1);
320 if (fn
[0] && fn
[1] && fn
[strlen(fn
)-1] == dstr_cstr(&buf2
)[dstr_len(&buf2
)-1]) {
321 fn
[strlen(fn
)-1] = 0;
322 //fprintf(stderr, "import directive: [%s]\n", fn);
323 result
= list_new(result
, fn
, 0);
329 // check if this is local include
331 resolve_local_include(&xname
, file
, &buf2
);
332 //fprintf(stderr, "FILE: <%s>; include: <%s>\n", file, dstr_cstr(&xname));
333 result
= list_new(result
, dstr_cstr(&xname
), 0);
336 if (DEBUG_HEADER
) printf("header found: %s\n", dstr_cstr(&buf2
));
339 /* special treatment for #include MACRO */
340 mt
[0].sp
= mt
[0].ep
= NULL
;
341 if (regexp_execute(re_macros
, buf
, mt
, 2) > 0 && mt
[1].sp
!= NULL
) {
342 const char *header_filename
;
343 int l
= mt
[1].ep
-mt
[1].sp
;
344 dstr_set_buf(&buf2
, mt
[1].sp
, l
);
345 if (DEBUG_HEADER
) printf("macro header found: %s", dstr_cstr(&buf2
));
346 header_filename
= macro_header_get(dstr_cstr(&buf2
));
347 if (header_filename
) {
348 if (DEBUG_HEADER
) printf(" resolved to '%s'\n", header_filename
);
349 result
= list_new(result
, header_filename
, 0);
351 if (DEBUG_HEADER
) printf(" ignored !!\n");
359 if (re_dlinc
!= NULL
) regexp_free(re_dlinc
);
360 regexp_free(re_macros
);
361 while (--rec
>= 0) regexp_free(re
[rec
]);