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
25 * $(HDRRULE) <target> : <include files> ;
28 * headers() - scan a target for include files and call HDRRULE
31 * headers1() - using regexp, scan a file and build include LIST
49 #ifndef OPT_HEADER_CACHE_EXT
50 static LIST *headers1 (const char *file, LIST *hdrscan);
56 * headers() - scan a target for include files and call HDRRULE
60 void headers (TARGET
*t
) {
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
);
69 lol_add(&lol
, list_new(L0
, t
->name
, 1));
71 #ifdef OPT_HEADER_CACHE_EXT
72 lol_add(&lol, hcache(t, hdrscan));
74 lol_add(&lol, headers1(t->boundname, hdrscan));
77 lol_add(&lol
, hcache(t
, hdrscan
));
78 if (lol_get(&lol
, 1)) list_free(evaluate_rule(hdrrule
->string
, &lol
, L0
));
84 struct lcicacheentry_s
{
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) {
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
;
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
));
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) {
125 ne
->next
= e0
->children
;
131 lcicacheentry_t
*bk
= calloc(1, sizeof(lcicacheentry_t
));
132 bk
->str
= strdup(basefile
);
139 static void resolve_local_include (dstring_t
*dest
, const char *basefile
, dstring_t
*fname
) {
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; }
146 const char *riname
= lci_cache_find(basefile
, bflen
, fname
);
147 if (riname
!= NULL
) {
149 dstr_set_cstr(dest
, riname
);
154 if (basefile[0] != '/') {
155 static char buf[8192];
156 char *dir = getcwd(buf, sizeof(buf));
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
));
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
179 LIST
*headers1 (const char *file
, LIST
*hdrscan
) {
184 regexp_t
*re
[MAXINC
];
185 regexp_t
*re_macros
, *re_dlinc
= NULL
;
187 int line_size
= 16384;
188 char *buf
; /* line_size size */
189 dstring_t buf2
, buf3
;
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
);
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);
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");
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");
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
) {
229 printf("GDC deps for [%s]\n", file
);
230 for (const LIST
*l
= result
; l
!= NULL
; l
= l
->next
) printf(" <%s>\n", l
->string
);
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);
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);
250 while (rec
< MAXINC
&& hdrscan
) {
251 //printf("header re %d: [%s]\n", rec, hdrscan->string);
252 re
[rec
] = regexp_compile(hdrscan
->string
, 0);
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(); }
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));
274 /* this is DLang 'import' directive */
275 const char *s
= dstr_cstr(&buf2
);
276 //fprintf(stderr, "=== %d; [%s]\n", l, dstr_cstr(&buf2));
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
;
292 while (t
> p
&& isspace(t
[-1])) --t
;
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));
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);
308 strcpy(strrchr(pd
, '.'), "/package.d");
309 //fprintf(stderr, "dlang include pkg: [%s]\n", pd);
310 result
= list_new(result
, pd
, 0);
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);
330 // check if this is local include
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);
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);
352 if (DEBUG_HEADER
) printf(" ignored !!\n");
360 if (re_dlinc
!= NULL
) regexp_free(re_dlinc
);
361 regexp_free(re_macros
);
362 while (--rec
>= 0) regexp_free(re
[rec
]);