GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: parse.c,v 1.118 2016/10/23 14:54:14 espie Exp $ */ |
||
2 |
/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */ |
||
3 |
|||
4 |
/* |
||
5 |
* Copyright (c) 1999 Marc Espie. |
||
6 |
* |
||
7 |
* Extensive code changes for the OpenBSD project. |
||
8 |
* |
||
9 |
* Redistribution and use in source and binary forms, with or without |
||
10 |
* modification, are permitted provided that the following conditions |
||
11 |
* are met: |
||
12 |
* 1. Redistributions of source code must retain the above copyright |
||
13 |
* notice, this list of conditions and the following disclaimer. |
||
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
15 |
* notice, this list of conditions and the following disclaimer in the |
||
16 |
* documentation and/or other materials provided with the distribution. |
||
17 |
* |
||
18 |
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
||
19 |
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
20 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
21 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
||
22 |
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
23 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
24 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
25 |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
26 |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
27 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
28 |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
29 |
*/ |
||
30 |
/* |
||
31 |
* Copyright (c) 1988, 1989, 1990, 1993 |
||
32 |
* The Regents of the University of California. All rights reserved. |
||
33 |
* Copyright (c) 1989 by Berkeley Softworks |
||
34 |
* All rights reserved. |
||
35 |
* |
||
36 |
* This code is derived from software contributed to Berkeley by |
||
37 |
* Adam de Boor. |
||
38 |
* |
||
39 |
* Redistribution and use in source and binary forms, with or without |
||
40 |
* modification, are permitted provided that the following conditions |
||
41 |
* are met: |
||
42 |
* 1. Redistributions of source code must retain the above copyright |
||
43 |
* notice, this list of conditions and the following disclaimer. |
||
44 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
45 |
* notice, this list of conditions and the following disclaimer in the |
||
46 |
* documentation and/or other materials provided with the distribution. |
||
47 |
* 3. Neither the name of the University nor the names of its contributors |
||
48 |
* may be used to endorse or promote products derived from this software |
||
49 |
* without specific prior written permission. |
||
50 |
* |
||
51 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
52 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
53 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
54 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
55 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
56 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
57 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
58 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
59 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
60 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
61 |
* SUCH DAMAGE. |
||
62 |
*/ |
||
63 |
|||
64 |
#include <assert.h> |
||
65 |
#include <ctype.h> |
||
66 |
#include <stddef.h> |
||
67 |
#include <stdio.h> |
||
68 |
#include <stdlib.h> |
||
69 |
#include <string.h> |
||
70 |
#include <ohash.h> |
||
71 |
#include "config.h" |
||
72 |
#include "defines.h" |
||
73 |
#include "dir.h" |
||
74 |
#include "direxpand.h" |
||
75 |
#include "job.h" |
||
76 |
#include "buf.h" |
||
77 |
#include "for.h" |
||
78 |
#include "lowparse.h" |
||
79 |
#include "arch.h" |
||
80 |
#include "cond.h" |
||
81 |
#include "suff.h" |
||
82 |
#include "parse.h" |
||
83 |
#include "var.h" |
||
84 |
#include "targ.h" |
||
85 |
#include "error.h" |
||
86 |
#include "str.h" |
||
87 |
#include "main.h" |
||
88 |
#include "gnode.h" |
||
89 |
#include "memory.h" |
||
90 |
#include "extern.h" |
||
91 |
#include "lst.h" |
||
92 |
#include "parsevar.h" |
||
93 |
#include "stats.h" |
||
94 |
#include "garray.h" |
||
95 |
#include "node_int.h" |
||
96 |
#include "nodehashconsts.h" |
||
97 |
|||
98 |
|||
99 |
/* gsources and gtargets should be local to some functions, but they're |
||
100 |
* set as persistent arrays for performance reasons. |
||
101 |
*/ |
||
102 |
static struct growableArray gsources, gtargets; |
||
103 |
static struct ohash htargets; |
||
104 |
static bool htargets_setup = false; |
||
105 |
#define SOURCES_SIZE 128 |
||
106 |
#define TARGETS_SIZE 32 |
||
107 |
|||
108 |
static LIST theUserIncPath;/* list of directories for "..." includes */ |
||
109 |
static LIST theSysIncPath; /* list of directories for <...> includes */ |
||
110 |
Lst systemIncludePath = &theSysIncPath; |
||
111 |
Lst userIncludePath = &theUserIncPath; |
||
112 |
|||
113 |
static GNode *mainNode; /* The main target to create. This is the |
||
114 |
* first target on the first dependency |
||
115 |
* line in the first makefile */ |
||
116 |
/*- |
||
117 |
* specType contains the special TYPE of the current target. It is |
||
118 |
* SPECIAL_NONE if the target is unspecial. If it *is* special, however, |
||
119 |
* the children are linked as children of the parent but not vice versa. |
||
120 |
* This variable is set in ParseDoDependency |
||
121 |
*/ |
||
122 |
|||
123 |
static unsigned int specType; |
||
124 |
static int waiting; |
||
125 |
|||
126 |
/* |
||
127 |
* Predecessor node for handling .ORDER. Initialized to NULL when .ORDER |
||
128 |
* seen, then set to each successive source on the line. |
||
129 |
*/ |
||
130 |
static GNode *predecessor; |
||
131 |
|||
132 |
static void ParseLinkSrc(GNode *, GNode *); |
||
133 |
static int ParseDoOp(GNode **, unsigned int); |
||
134 |
static int ParseAddDep(GNode *, GNode *); |
||
135 |
static void ParseDoSrc(struct growableArray *, struct growableArray *, int, |
||
136 |
const char *, const char *); |
||
137 |
static int ParseFindMain(void *, void *); |
||
138 |
static void ParseClearPath(void *); |
||
139 |
|||
140 |
static void add_target_node(const char *, const char *); |
||
141 |
static void add_target_nodes(const char *, const char *); |
||
142 |
static void apply_op(struct growableArray *, unsigned int, GNode *); |
||
143 |
static void ParseDoDependency(const char *); |
||
144 |
static void ParseAddCmd(void *, void *); |
||
145 |
static void ParseHasCommands(void *); |
||
146 |
static bool handle_poison(const char *); |
||
147 |
static bool handle_for_loop(Buffer, const char *); |
||
148 |
static bool handle_undef(const char *); |
||
149 |
#define ParseReadLoopLine(linebuf) Parse_ReadUnparsedLine(linebuf, "for loop") |
||
150 |
static bool handle_bsd_command(Buffer, Buffer, const char *); |
||
151 |
static bool register_target(GNode *, struct ohash *); |
||
152 |
static char *strip_comments(Buffer, const char *); |
||
153 |
static char *resolve_include_filename(const char *, const char *, bool); |
||
154 |
static void handle_include_file(const char *, const char *, bool, bool); |
||
155 |
static bool lookup_bsd_include(const char *); |
||
156 |
static void lookup_sysv_style_include(const char *, const char *, bool); |
||
157 |
static void lookup_sysv_include(const char *, const char *); |
||
158 |
static void lookup_conditional_include(const char *, const char *); |
||
159 |
static bool parse_as_special_line(Buffer, Buffer, const char *); |
||
160 |
static unsigned int parse_operator(const char **); |
||
161 |
|||
162 |
static const char *parse_do_targets(Lst, unsigned int *, const char *); |
||
163 |
static void parse_target_line(struct growableArray *, const char *, |
||
164 |
const char *, bool *); |
||
165 |
|||
166 |
static void finish_commands(struct growableArray *); |
||
167 |
static void parse_commands(struct growableArray *, const char *); |
||
168 |
static void create_special_nodes(void); |
||
169 |
static bool found_delimiter(const char *); |
||
170 |
static unsigned int handle_special_targets(Lst); |
||
171 |
static void dump_targets(void); |
||
172 |
static void dedup_targets(struct growableArray *); |
||
173 |
static void build_target_group(struct growableArray *, struct ohash *t); |
||
174 |
static void reset_target_hash(void); |
||
175 |
|||
176 |
|||
177 |
#define P(k) k, sizeof(k), K_##k |
||
178 |
|||
179 |
static struct { |
||
180 |
const char *keyword; |
||
181 |
size_t sz; |
||
182 |
uint32_t hv; |
||
183 |
unsigned int type; |
||
184 |
unsigned int special_op; |
||
185 |
} specials[] = { |
||
186 |
{ P(NODE_EXEC), SPECIAL_EXEC | SPECIAL_TARGETSOURCE, OP_EXEC, }, |
||
187 |
{ P(NODE_IGNORE), SPECIAL_IGNORE | SPECIAL_TARGETSOURCE, OP_IGNORE, }, |
||
188 |
{ P(NODE_INCLUDES), SPECIAL_NOTHING | SPECIAL_TARGET, 0, }, |
||
189 |
{ P(NODE_INVISIBLE),SPECIAL_INVISIBLE | SPECIAL_TARGETSOURCE,OP_INVISIBLE, }, |
||
190 |
{ P(NODE_JOIN), SPECIAL_JOIN | SPECIAL_TARGETSOURCE, OP_JOIN, }, |
||
191 |
{ P(NODE_LIBS), SPECIAL_NOTHING | SPECIAL_TARGET, 0, }, |
||
192 |
{ P(NODE_MADE), SPECIAL_MADE | SPECIAL_TARGETSOURCE, OP_MADE, }, |
||
193 |
{ P(NODE_MAIN), SPECIAL_MAIN | SPECIAL_TARGET, 0, }, |
||
194 |
{ P(NODE_MAKE), SPECIAL_MAKE | SPECIAL_TARGETSOURCE, OP_MAKE, }, |
||
195 |
{ P(NODE_MAKEFLAGS), SPECIAL_MFLAGS | SPECIAL_TARGET, 0, }, |
||
196 |
{ P(NODE_MFLAGS), SPECIAL_MFLAGS | SPECIAL_TARGET, 0, }, |
||
197 |
{ P(NODE_NOTMAIN), SPECIAL_NOTMAIN | SPECIAL_TARGETSOURCE, OP_NOTMAIN, }, |
||
198 |
{ P(NODE_NOTPARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, }, |
||
199 |
{ P(NODE_NO_PARALLEL),SPECIAL_NOTPARALLEL | SPECIAL_TARGET, 0, }, |
||
200 |
{ P(NODE_NULL), SPECIAL_NOTHING | SPECIAL_TARGET, 0, }, |
||
201 |
{ P(NODE_OPTIONAL), SPECIAL_OPTIONAL | SPECIAL_TARGETSOURCE,OP_OPTIONAL, }, |
||
202 |
{ P(NODE_ORDER), SPECIAL_ORDER | SPECIAL_TARGET, 0, }, |
||
203 |
{ P(NODE_PARALLEL), SPECIAL_PARALLEL | SPECIAL_TARGET, 0, }, |
||
204 |
{ P(NODE_PATH), SPECIAL_PATH | SPECIAL_TARGET, 0, }, |
||
205 |
{ P(NODE_PHONY), SPECIAL_PHONY | SPECIAL_TARGETSOURCE, OP_PHONY, }, |
||
206 |
{ P(NODE_PRECIOUS), SPECIAL_PRECIOUS | SPECIAL_TARGETSOURCE,OP_PRECIOUS, }, |
||
207 |
{ P(NODE_RECURSIVE),SPECIAL_MAKE | SPECIAL_TARGETSOURCE, OP_MAKE, }, |
||
208 |
{ P(NODE_SILENT), SPECIAL_SILENT | SPECIAL_TARGETSOURCE, OP_SILENT, }, |
||
209 |
{ P(NODE_SINGLESHELL),SPECIAL_NOTHING | SPECIAL_TARGET, 0, }, |
||
210 |
{ P(NODE_SUFFIXES), SPECIAL_SUFFIXES | SPECIAL_TARGET, 0, }, |
||
211 |
{ P(NODE_USE), SPECIAL_USE | SPECIAL_TARGETSOURCE, OP_USE, }, |
||
212 |
{ P(NODE_WAIT), SPECIAL_WAIT | SPECIAL_TARGETSOURCE, 0 }, |
||
213 |
{ P(NODE_CHEAP), SPECIAL_CHEAP | SPECIAL_TARGETSOURCE, OP_CHEAP, }, |
||
214 |
{ P(NODE_EXPENSIVE),SPECIAL_EXPENSIVE | SPECIAL_TARGETSOURCE,OP_EXPENSIVE, }, |
||
215 |
{ P(NODE_POSIX), SPECIAL_NOTHING | SPECIAL_TARGET, 0 }, |
||
216 |
{ P(NODE_SCCS_GET), SPECIAL_NOTHING | SPECIAL_TARGET, 0 }, |
||
217 |
}; |
||
218 |
|||
219 |
#undef P |
||
220 |
|||
221 |
static void |
||
222 |
create_special_nodes() |
||
223 |
{ |
||
224 |
unsigned int i; |
||
225 |
|||
226 |
✓✓ | 1095445 |
for (i = 0; i < sizeof(specials)/sizeof(specials[0]); i++) { |
227 |
1044886 |
GNode *gn = Targ_FindNodeh(specials[i].keyword, |
|
228 |
522443 |
specials[i].sz, specials[i].hv, TARG_CREATE); |
|
229 |
522443 |
gn->special = specials[i].type; |
|
230 |
522443 |
gn->special_op = specials[i].special_op; |
|
231 |
} |
||
232 |
16853 |
} |
|
233 |
|||
234 |
/*- |
||
235 |
*--------------------------------------------------------------------- |
||
236 |
* ParseLinkSrc -- |
||
237 |
* Link the parent node to its new child. Used by |
||
238 |
* ParseDoDependency. If the specType isn't 'Not', the parent |
||
239 |
* isn't linked as a parent of the child. |
||
240 |
* |
||
241 |
* Side Effects: |
||
242 |
* New elements are added to the parents list of cgn and the |
||
243 |
* children list of cgn. the unmade field of pgn is updated |
||
244 |
* to reflect the additional child. |
||
245 |
*--------------------------------------------------------------------- |
||
246 |
*/ |
||
247 |
static void |
||
248 |
ParseLinkSrc(GNode *pgn, GNode *cgn) |
||
249 |
{ |
||
250 |
✓✓ | 6646892 |
if (Lst_AddNew(&pgn->children, cgn)) { |
251 |
✓✗ | 3011503 |
if (specType == SPECIAL_NONE) |
252 |
3011503 |
Lst_AtEnd(&cgn->parents, pgn); |
|
253 |
3011503 |
pgn->unmade++; |
|
254 |
3011503 |
} |
|
255 |
3323446 |
} |
|
256 |
|||
257 |
static char * |
||
258 |
operator_string(int op) |
||
259 |
{ |
||
260 |
/* XXX we don't bother freeing this, it's used for a fatal error |
||
261 |
* anyways |
||
262 |
*/ |
||
263 |
char *result = emalloc(5); |
||
264 |
char *t = result; |
||
265 |
if (op & OP_DEPENDS) { |
||
266 |
*t++ = ':'; |
||
267 |
} |
||
268 |
if (op & OP_FORCE) { |
||
269 |
*t++ = '!'; |
||
270 |
} |
||
271 |
if (op & OP_DOUBLEDEP) { |
||
272 |
*t++ = ':'; |
||
273 |
*t++ = ':'; |
||
274 |
} |
||
275 |
*t = 0; |
||
276 |
return result; |
||
277 |
} |
||
278 |
|||
279 |
/*- |
||
280 |
*--------------------------------------------------------------------- |
||
281 |
* ParseDoOp -- |
||
282 |
* Apply the parsed operator to the given target node. Used in a |
||
283 |
* Array_Find call by ParseDoDependency once all targets have |
||
284 |
* been found and their operator parsed. If the previous and new |
||
285 |
* operators are incompatible, a major error is taken. |
||
286 |
* |
||
287 |
* Side Effects: |
||
288 |
* The type field of the node is altered to reflect any new bits in |
||
289 |
* the op. |
||
290 |
*--------------------------------------------------------------------- |
||
291 |
*/ |
||
292 |
static int |
||
293 |
ParseDoOp(GNode **gnp, unsigned int op) |
||
294 |
{ |
||
295 |
11832514 |
GNode *gn = *gnp; |
|
296 |
/* |
||
297 |
* If the dependency mask of the operator and the node don't match and |
||
298 |
* the node has actually had an operator applied to it before, and the |
||
299 |
* operator actually has some dependency information in it, complain. |
||
300 |
*/ |
||
301 |
✓✓✗✓ |
5947446 |
if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && |
302 |
✓✓ | 4677558 |
!OP_NOP(gn->type) && !OP_NOP(op)) { |
303 |
Parse_Error(PARSE_FATAL, |
||
304 |
"Inconsistent dependency operator for target %s\n" |
||
305 |
"\t(was %s%s, now %s%s)", |
||
306 |
gn->name, gn->name, operator_string(gn->type), |
||
307 |
gn->name, operator_string(op)); |
||
308 |
return 0; |
||
309 |
} |
||
310 |
|||
311 |
✓✓✓✓ |
5922693 |
if (op == OP_DOUBLEDEP && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { |
312 |
/* If the node was the object of a :: operator, we need to |
||
313 |
* create a new instance of it for the children and commands on |
||
314 |
* this dependency line. The new instance is placed on the |
||
315 |
* 'cohorts' list of the initial one (note the initial one is |
||
316 |
* not on its own cohorts list) and the new instance is linked |
||
317 |
* to all parents of the initial instance. */ |
||
318 |
GNode *cohort; |
||
319 |
LstNode ln; |
||
320 |
|||
321 |
20 |
cohort = Targ_NewGN(gn->name); |
|
322 |
/* Duplicate links to parents so graph traversal is simple. |
||
323 |
* Perhaps some type bits should be duplicated? |
||
324 |
* |
||
325 |
* Make the cohort invisible as well to avoid duplicating it |
||
326 |
* into other variables. True, parents of this target won't |
||
327 |
* tend to do anything with their local variables, but better |
||
328 |
* safe than sorry. */ |
||
329 |
✗✓ | 40 |
for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) |
330 |
ParseLinkSrc(Lst_Datum(ln), cohort); |
||
331 |
20 |
cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; |
|
332 |
20 |
Lst_AtEnd(&gn->cohorts, cohort); |
|
333 |
|||
334 |
/* Replace the node in the targets list with the new copy */ |
||
335 |
20 |
*gnp = cohort; |
|
336 |
gn = cohort; |
||
337 |
20 |
} |
|
338 |
/* We don't want to nuke any previous flags (whatever they were) so we |
||
339 |
* just OR the new operator into the old. */ |
||
340 |
5916257 |
gn->type |= op; |
|
341 |
5916257 |
return 1; |
|
342 |
5916257 |
} |
|
343 |
|||
344 |
/*- |
||
345 |
*--------------------------------------------------------------------- |
||
346 |
* ParseAddDep -- |
||
347 |
* Check if the pair of GNodes given needs to be synchronized. |
||
348 |
* This has to be when two nodes are on different sides of a |
||
349 |
* .WAIT directive. |
||
350 |
* |
||
351 |
* Results: |
||
352 |
* Returns 0 if the two targets need to be ordered, 1 otherwise. |
||
353 |
* If it returns 0, the search can stop. |
||
354 |
* |
||
355 |
* Side Effects: |
||
356 |
* A dependency can be added between the two nodes. |
||
357 |
* |
||
358 |
*--------------------------------------------------------------------- |
||
359 |
*/ |
||
360 |
static int |
||
361 |
ParseAddDep(GNode *p, GNode *s) |
||
362 |
{ |
||
363 |
if (p->order < s->order) { |
||
364 |
/* XXX: This can cause loops, and loops can cause unmade |
||
365 |
* targets, but checking is tedious, and the debugging output |
||
366 |
* can show the problem. */ |
||
367 |
Lst_AtEnd(&p->successors, s); |
||
368 |
Lst_AtEnd(&s->preds, p); |
||
369 |
return 1; |
||
370 |
} else |
||
371 |
return 0; |
||
372 |
} |
||
373 |
|||
374 |
static void |
||
375 |
apply_op(struct growableArray *targets, unsigned int op, GNode *gn) |
||
376 |
{ |
||
377 |
✓✓ | 11375596 |
if (op) |
378 |
3628380 |
gn->type |= op; |
|
379 |
else |
||
380 |
✓✓ | 10765728 |
Array_ForEach(targets, ParseLinkSrc, gn); |
381 |
5687798 |
} |
|
382 |
|||
383 |
/*- |
||
384 |
*--------------------------------------------------------------------- |
||
385 |
* ParseDoSrc -- |
||
386 |
* Given the name of a source, figure out if it is an attribute |
||
387 |
* and apply it to the targets if it is. Else decide if there is |
||
388 |
* some attribute which should be applied *to* the source because |
||
389 |
* of some special target and apply it if so. Otherwise, make the |
||
390 |
* source be a child of the targets in the list 'targets' |
||
391 |
* |
||
392 |
* Side Effects: |
||
393 |
* Operator bits may be added to the list of targets or to the source. |
||
394 |
* The targets may have a new source added to their lists of children. |
||
395 |
*--------------------------------------------------------------------- |
||
396 |
*/ |
||
397 |
static void |
||
398 |
ParseDoSrc( |
||
399 |
struct growableArray *targets, |
||
400 |
struct growableArray *sources, |
||
401 |
int tOp, /* operator (if any) from special targets */ |
||
402 |
const char *src, /* name of the source to handle */ |
||
403 |
const char *esrc) |
||
404 |
{ |
||
405 |
11438096 |
GNode *gn = Targ_FindNodei(src, esrc, TARG_CREATE); |
|
406 |
✓✓ | 5719048 |
if ((gn->special & SPECIAL_SOURCE) != 0) { |
407 |
✓✗ | 31189 |
if (gn->special_op) { |
408 |
✓✓✓✗ |
155945 |
Array_FindP(targets, ParseDoOp, gn->special_op); |
409 |
31189 |
return; |
|
410 |
} else { |
||
411 |
assert((gn->special & SPECIAL_MASK) == SPECIAL_WAIT); |
||
412 |
waiting++; |
||
413 |
return; |
||
414 |
} |
||
415 |
} |
||
416 |
|||
417 |
✓✗✓ | 5687859 |
switch (specType) { |
418 |
case SPECIAL_MAIN: |
||
419 |
/* |
||
420 |
* If we have noted the existence of a .MAIN, it means we need |
||
421 |
* to add the sources of said target to the list of things |
||
422 |
* to create. Note that this will only be invoked if the user |
||
423 |
* didn't specify a target on the command line. This is to |
||
424 |
* allow #ifmake's to succeed, or something... |
||
425 |
*/ |
||
426 |
61 |
Lst_AtEnd(create, gn->name); |
|
427 |
/* |
||
428 |
* Add the name to the .TARGETS variable as well, so the user |
||
429 |
* can employ that, if desired. |
||
430 |
*/ |
||
431 |
61 |
Var_Append(".TARGETS", gn->name); |
|
432 |
61 |
return; |
|
433 |
|||
434 |
case SPECIAL_ORDER: |
||
435 |
/* |
||
436 |
* Create proper predecessor/successor links between the |
||
437 |
* previous source and the current one. |
||
438 |
*/ |
||
439 |
if (predecessor != NULL) { |
||
440 |
Lst_AtEnd(&predecessor->successors, gn); |
||
441 |
Lst_AtEnd(&gn->preds, predecessor); |
||
442 |
} |
||
443 |
predecessor = gn; |
||
444 |
break; |
||
445 |
|||
446 |
default: |
||
447 |
/* |
||
448 |
* In the case of a source that was the object of a :: operator, |
||
449 |
* the attribute is applied to all of its instances (as kept in |
||
450 |
* the 'cohorts' list of the node) or all the cohorts are linked |
||
451 |
* to all the targets. |
||
452 |
*/ |
||
453 |
5687798 |
apply_op(targets, tOp, gn); |
|
454 |
✗✓ | 5687798 |
if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { |
455 |
LstNode ln; |
||
456 |
|||
457 |
for (ln=Lst_First(&gn->cohorts); ln != NULL; |
||
458 |
ln = Lst_Adv(ln)){ |
||
459 |
apply_op(targets, tOp, Lst_Datum(ln)); |
||
460 |
} |
||
461 |
} |
||
462 |
break; |
||
463 |
} |
||
464 |
|||
465 |
5687798 |
gn->order = waiting; |
|
466 |
✓✓ | 11387560 |
Array_AtEnd(sources, gn); |
467 |
✗✓ | 5687798 |
if (waiting) |
468 |
Array_Find(sources, ParseAddDep, gn); |
||
469 |
11406846 |
} |
|
470 |
|||
471 |
/*- |
||
472 |
*----------------------------------------------------------------------- |
||
473 |
* ParseFindMain -- |
||
474 |
* Find a real target in the list and set it to be the main one. |
||
475 |
* Called by ParseDoDependency when a main target hasn't been found |
||
476 |
* yet. |
||
477 |
* |
||
478 |
* Results: |
||
479 |
* 1 if main not found yet, 0 if it is. |
||
480 |
* |
||
481 |
* Side Effects: |
||
482 |
* mainNode is changed and. |
||
483 |
*----------------------------------------------------------------------- |
||
484 |
*/ |
||
485 |
static int |
||
486 |
ParseFindMain(void *gnp, void *dummy UNUSED) |
||
487 |
{ |
||
488 |
1432346 |
GNode *gn = gnp; |
|
489 |
|||
490 |
✓✓✓✓ |
755184 |
if ((gn->type & OP_NOTARGET) == 0 && gn->special == SPECIAL_NONE) { |
491 |
16853 |
mainNode = gn; |
|
492 |
16853 |
return 0; |
|
493 |
} else { |
||
494 |
699320 |
return 1; |
|
495 |
} |
||
496 |
716173 |
} |
|
497 |
|||
498 |
/*- |
||
499 |
*----------------------------------------------------------------------- |
||
500 |
* ParseClearPath -- |
||
501 |
* Reinit path to an empty path |
||
502 |
*----------------------------------------------------------------------- |
||
503 |
*/ |
||
504 |
static void |
||
505 |
ParseClearPath(void *p) |
||
506 |
{ |
||
507 |
Lst path = p; |
||
508 |
|||
509 |
Lst_Destroy(path, Dir_Destroy); |
||
510 |
Lst_Init(path); |
||
511 |
} |
||
512 |
|||
513 |
static void |
||
514 |
add_target_node(const char *line, const char *end) |
||
515 |
{ |
||
516 |
GNode *gn; |
||
517 |
|||
518 |
11770136 |
gn = Suff_ParseAsTransform(line, end); |
|
519 |
|||
520 |
✓✓ | 5885068 |
if (gn == NULL) { |
521 |
5109104 |
gn = Targ_FindNodei(line, end, TARG_CREATE); |
|
522 |
5109104 |
gn->type &= ~OP_DUMMY; |
|
523 |
5109104 |
} |
|
524 |
|||
525 |
✓✓ | 11772467 |
Array_AtEnd(>argets, gn); |
526 |
5885068 |
} |
|
527 |
|||
528 |
static void |
||
529 |
add_target_nodes(const char *line, const char *end) |
||
530 |
{ |
||
531 |
|||
532 |
✓✓ | 11770134 |
if (Dir_HasWildcardsi(line, end)) { |
533 |
/* |
||
534 |
* Targets are to be sought only in the current directory, |
||
535 |
* so create an empty path for the thing. Note we need to |
||
536 |
* use Dir_Destroy in the destruction of the path as the |
||
537 |
* Dir module could have added a directory to the path... |
||
538 |
*/ |
||
539 |
char *targName; |
||
540 |
1 |
LIST emptyPath; |
|
541 |
1 |
LIST curTargs; |
|
542 |
|||
543 |
1 |
Lst_Init(&emptyPath); |
|
544 |
1 |
Lst_Init(&curTargs); |
|
545 |
1 |
Dir_Expandi(line, end, &emptyPath, &curTargs); |
|
546 |
1 |
Lst_Destroy(&emptyPath, Dir_Destroy); |
|
547 |
✓✓ | 6 |
while ((targName = Lst_DeQueue(&curTargs)) != NULL) { |
548 |
2 |
add_target_node(targName, targName + strlen(targName)); |
|
549 |
} |
||
550 |
1 |
Lst_Destroy(&curTargs, NOFREE); |
|
551 |
1 |
} else { |
|
552 |
5885066 |
add_target_node(line, end); |
|
553 |
} |
||
554 |
5885067 |
} |
|
555 |
|||
556 |
/* special target line check: a proper delimiter is a ':' or '!', but |
||
557 |
* we don't want to end a target on such a character if there is a better |
||
558 |
* match later on. |
||
559 |
* By "better" I mean one that is followed by whitespace. This allows the |
||
560 |
* user to have targets like: |
||
561 |
* fie::fi:fo: fum |
||
562 |
* where "fie::fi:fo" is the target. In real life this is used for perl5 |
||
563 |
* library man pages where "::" separates an object from its class. Ie: |
||
564 |
* "File::Spec::Unix". |
||
565 |
* This behaviour is also consistent with other versions of make. |
||
566 |
*/ |
||
567 |
static bool |
||
568 |
found_delimiter(const char *s) |
||
569 |
{ |
||
570 |
✓✓✓✓ |
251949266 |
if (*s == '!' || *s == ':') { |
571 |
5249239 |
const char *p = s + 1; |
|
572 |
|||
573 |
✓✓✓✓ |
10482205 |
if (*s == ':' && *p == ':') |
574 |
1130 |
p++; |
|
575 |
|||
576 |
/* Found the best match already. */ |
||
577 |
✓✓✓✓ |
9520673 |
if (ISSPACE(*p) || *p == '\0') |
578 |
5248969 |
return true; |
|
579 |
|||
580 |
270 |
do { |
|
581 |
270 |
p += strcspn(p, "!:"); |
|
582 |
✓✗ | 270 |
if (*p == '\0') |
583 |
break; |
||
584 |
270 |
p++; |
|
585 |
✗✓ | 270 |
} while (!ISSPACE(*p)); |
586 |
|||
587 |
/* No better match later on... */ |
||
588 |
✗✓ | 270 |
if (*p == '\0') |
589 |
return true; |
||
590 |
✓✓ | 270 |
} |
591 |
78739544 |
return false; |
|
592 |
83988513 |
} |
|
593 |
|||
594 |
static const char * |
||
595 |
parse_do_targets(Lst paths, unsigned int *op, const char *line) |
||
596 |
{ |
||
597 |
5249083 |
const char *cp; |
|
598 |
|||
599 |
5249083 |
do { |
|
600 |
✓✗✓✓ ✓✗ |
337862346 |
for (cp = line; *cp && !ISSPACE(*cp) && *cp != '(';) { |
601 |
✗✓ | 83988513 |
if (*cp == '$') |
602 |
/* Must be a dynamic source (would have been |
||
603 |
* expanded otherwise), so call the Var module |
||
604 |
* to parse the puppy so we can safely advance |
||
605 |
* beyond it...There should be no errors in |
||
606 |
* this, as they would have been discovered in |
||
607 |
* the initial Var_Subst and we wouldn't be |
||
608 |
* here. */ |
||
609 |
Var_ParseSkip(&cp, NULL); |
||
610 |
else { |
||
611 |
✓✓ | 83988513 |
if (found_delimiter(cp)) |
612 |
break; |
||
613 |
78739544 |
cp++; |
|
614 |
} |
||
615 |
} |
||
616 |
|||
617 |
✗✓ | 5885067 |
if (*cp == '(') { |
618 |
LIST temp; |
||
619 |
Lst_Init(&temp); |
||
620 |
/* Archives must be handled specially to make sure the |
||
621 |
* OP_ARCHV flag is set in their 'type' field, for one |
||
622 |
* thing, and because things like "archive(file1.o |
||
623 |
* file2.o file3.o)" are permissible. |
||
624 |
* Arch_ParseArchive will set 'line' to be the first |
||
625 |
* non-blank after the archive-spec. It creates/finds |
||
626 |
* nodes for the members and places them on the given |
||
627 |
* list, returning true if all went well and false if |
||
628 |
* there was an error in the specification. On error, |
||
629 |
* line should remain untouched. */ |
||
630 |
if (!Arch_ParseArchive(&line, &temp, NULL)) { |
||
631 |
Parse_Error(PARSE_FATAL, |
||
632 |
"Error in archive specification: \"%s\"", |
||
633 |
line); |
||
634 |
return NULL; |
||
635 |
} else { |
||
636 |
AppendList2Array(&temp, >argets); |
||
637 |
Lst_Destroy(&temp, NOFREE); |
||
638 |
cp = line; |
||
639 |
continue; |
||
640 |
} |
||
641 |
} |
||
642 |
✗✓ | 5885067 |
if (*cp == '\0') { |
643 |
/* Ending a dependency line without an operator is a |
||
644 |
* Bozo no-no */ |
||
645 |
/* Deeper check for cvs conflicts */ |
||
646 |
if (gtargets.n > 0 && |
||
647 |
(strcmp(gtargets.a[0]->name, "<<<<<<<") == 0 || |
||
648 |
strcmp(gtargets.a[0]->name, ">>>>>>>") == 0)) { |
||
649 |
Parse_Error(PARSE_FATAL, |
||
650 |
"Need an operator (likely from a cvs update conflict)"); |
||
651 |
} else { |
||
652 |
Parse_Error(PARSE_FATAL, |
||
653 |
"Need an operator in '%s'", line); |
||
654 |
} |
||
655 |
return NULL; |
||
656 |
} |
||
657 |
/* |
||
658 |
* Have word in line. Get or create its nodes and stick it at |
||
659 |
* the end of the targets list |
||
660 |
*/ |
||
661 |
✓✗ | 5885067 |
if (*line != '\0') |
662 |
5885067 |
add_target_nodes(line, cp); |
|
663 |
|||
664 |
✓✓ | 13071478 |
while (ISSPACE(*cp)) |
665 |
650672 |
cp++; |
|
666 |
5885067 |
line = cp; |
|
667 |
✓✓✓✓ ✓✗ |
18274912 |
} while (*line != '!' && *line != ':' && *line); |
668 |
5249083 |
*op = handle_special_targets(paths); |
|
669 |
5249083 |
return cp; |
|
670 |
5249083 |
} |
|
671 |
|||
672 |
static void |
||
673 |
dump_targets() |
||
674 |
{ |
||
675 |
size_t i; |
||
676 |
for (i = 0; i < gtargets.n; i++) |
||
677 |
fprintf(stderr, "%s", gtargets.a[i]->name); |
||
678 |
fprintf(stderr, "\n"); |
||
679 |
} |
||
680 |
|||
681 |
static unsigned int |
||
682 |
handle_special_targets(Lst paths) |
||
683 |
{ |
||
684 |
size_t i; |
||
685 |
int seen_path = 0; |
||
686 |
int seen_special = 0; |
||
687 |
int seen_normal = 0; |
||
688 |
int type; |
||
689 |
|||
690 |
✓✓ | 27517385 |
for (i = 0; i < gtargets.n; i++) { |
691 |
5885068 |
type = gtargets.a[i]->special; |
|
692 |
✓✓ | 5885068 |
if ((type & SPECIAL_MASK) == SPECIAL_PATH) { |
693 |
910 |
seen_path++; |
|
694 |
910 |
Lst_AtEnd(paths, find_suffix_path(gtargets.a[i])); |
|
695 |
✓✓ | 5885068 |
} else if ((type & SPECIAL_TARGET) != 0) |
696 |
166682 |
seen_special++; |
|
697 |
else |
||
698 |
5717476 |
seen_normal++; |
|
699 |
} |
||
700 |
✗✓ | 5249083 |
if ((seen_path != 0) + (seen_special != 0) + (seen_normal != 0) > 1) { |
701 |
Parse_Error(PARSE_FATAL, "Wrong mix of special targets"); |
||
702 |
dump_targets(); |
||
703 |
specType = SPECIAL_ERROR; |
||
704 |
return 0; |
||
705 |
} |
||
706 |
✓✓ | 5249083 |
if (seen_normal != 0) { |
707 |
5081491 |
specType = SPECIAL_NONE; |
|
708 |
5081491 |
return 0; |
|
709 |
✓✓ | 167592 |
} else if (seen_path != 0) { |
710 |
910 |
specType = SPECIAL_PATH; |
|
711 |
910 |
return 0; |
|
712 |
✗✓ | 166682 |
} else if (seen_special == 0) { |
713 |
specType = SPECIAL_NONE; |
||
714 |
return 0; |
||
715 |
✗✓ | 166682 |
} else if (seen_special != 1) { |
716 |
Parse_Error(PARSE_FATAL, |
||
717 |
"Mixing special targets is not allowed"); |
||
718 |
dump_targets(); |
||
719 |
return 0; |
||
720 |
✓✗ | 166682 |
} else if (seen_special == 1) { |
721 |
198851 |
specType = gtargets.a[0]->special & SPECIAL_MASK; |
|
722 |
✓✗✗✓ |
198851 |
switch (specType) { |
723 |
case SPECIAL_MAIN: |
||
724 |
✓✓ | 32169 |
if (!Lst_IsEmpty(create)) { |
725 |
32108 |
specType = SPECIAL_NONE; |
|
726 |
32108 |
} |
|
727 |
break; |
||
728 |
case SPECIAL_NOTPARALLEL: |
||
729 |
{ |
||
730 |
extern int maxJobs; |
||
731 |
|||
732 |
maxJobs = 1; |
||
733 |
compatMake = 1; |
||
734 |
break; |
||
735 |
} |
||
736 |
case SPECIAL_ORDER: |
||
737 |
predecessor = NULL; |
||
738 |
break; |
||
739 |
default: |
||
740 |
break; |
||
741 |
} |
||
742 |
166682 |
return gtargets.a[0]->special_op; |
|
743 |
} else { |
||
744 |
/* we're allowed to have 0 target */ |
||
745 |
specType = SPECIAL_NONE; |
||
746 |
return 0; |
||
747 |
} |
||
748 |
5249083 |
} |
|
749 |
|||
750 |
static unsigned int |
||
751 |
parse_operator(const char **pos) |
||
752 |
{ |
||
753 |
10498166 |
const char *cp = *pos; |
|
754 |
unsigned int op = OP_ERROR; |
||
755 |
|||
756 |
✓✓ | 5249083 |
if (*cp == '!') { |
757 |
op = OP_FORCE; |
||
758 |
✓✗ | 5249083 |
} else if (*cp == ':') { |
759 |
✓✓ | 5232810 |
if (cp[1] == ':') { |
760 |
op = OP_DOUBLEDEP; |
||
761 |
997 |
cp++; |
|
762 |
997 |
} else { |
|
763 |
op = OP_DEPENDS; |
||
764 |
} |
||
765 |
} else { |
||
766 |
Parse_Error(PARSE_FATAL, "Missing dependency operator"); |
||
767 |
return OP_ERROR; |
||
768 |
} |
||
769 |
|||
770 |
5249083 |
cp++; /* Advance beyond operator */ |
|
771 |
|||
772 |
/* Get to the first source */ |
||
773 |
✓✓ | 12518674 |
while (ISSPACE(*cp)) |
774 |
1010254 |
cp++; |
|
775 |
5249083 |
*pos = cp; |
|
776 |
5249083 |
return op; |
|
777 |
5249083 |
} |
|
778 |
|||
779 |
/*- |
||
780 |
*--------------------------------------------------------------------- |
||
781 |
* ParseDoDependency -- |
||
782 |
* Parse the dependency line in line. |
||
783 |
* |
||
784 |
* Side Effects: |
||
785 |
* The nodes of the sources are linked as children to the nodes of the |
||
786 |
* targets. Some nodes may be created. |
||
787 |
* |
||
788 |
* We parse a dependency line by first extracting words from the line and |
||
789 |
* finding nodes in the list of all targets with that name. This is done |
||
790 |
* until a character is encountered which is an operator character. Currently |
||
791 |
* these are only ! and :. At this point the operator is parsed and the |
||
792 |
* pointer into the line advanced until the first source is encountered. |
||
793 |
* The parsed operator is applied to each node in the 'targets' list, |
||
794 |
* which is where the nodes found for the targets are kept, by means of |
||
795 |
* the ParseDoOp function. |
||
796 |
* The sources are read in much the same way as the targets were except |
||
797 |
* that now they are expanded using the wildcarding scheme of the C-Shell |
||
798 |
* and all instances of the resulting words in the list of all targets |
||
799 |
* are found. Each of the resulting nodes is then linked to each of the |
||
800 |
* targets as one of its children. |
||
801 |
* Certain targets are handled specially. These are the ones detailed |
||
802 |
* by the specType variable. |
||
803 |
* The storing of transformation rules is also taken care of here. |
||
804 |
* A target is recognized as a transformation rule by calling |
||
805 |
* Suff_IsTransform. If it is a transformation rule, its node is gotten |
||
806 |
* from the suffix module via Suff_AddTransform rather than the standard |
||
807 |
* Targ_FindNode in the target module. |
||
808 |
*--------------------------------------------------------------------- |
||
809 |
*/ |
||
810 |
static void |
||
811 |
ParseDoDependency(const char *line) /* the line to parse */ |
||
812 |
{ |
||
813 |
5249083 |
const char *cp; /* our current position */ |
|
814 |
unsigned int op; /* the operator on the line */ |
||
815 |
5249083 |
LIST paths; /* List of search paths to alter when parsing |
|
816 |
* a list of .PATH targets */ |
||
817 |
5249083 |
unsigned int tOp; /* operator from special target */ |
|
818 |
|||
819 |
5249083 |
waiting = 0; |
|
820 |
5249083 |
Lst_Init(&paths); |
|
821 |
|||
822 |
5249083 |
Array_Reset(&gsources); |
|
823 |
|||
824 |
5249083 |
cp = parse_do_targets(&paths, &tOp, line); |
|
825 |
✗✓ | 5249083 |
if (cp == NULL || specType == SPECIAL_ERROR) { |
826 |
/* invalidate targets for further processing */ |
||
827 |
Array_Reset(>argets); |
||
828 |
return; |
||
829 |
} |
||
830 |
|||
831 |
5249083 |
op = parse_operator(&cp); |
|
832 |
✗✓ | 5249083 |
if (op == OP_ERROR) { |
833 |
/* invalidate targets for further processing */ |
||
834 |
Array_Reset(>argets); |
||
835 |
return; |
||
836 |
} |
||
837 |
|||
838 |
✓✓✓✗ |
28153370 |
Array_FindP(>argets, ParseDoOp, op); |
839 |
5249083 |
dedup_targets(>argets); |
|
840 |
|||
841 |
5249083 |
line = cp; |
|
842 |
|||
843 |
/* |
||
844 |
* Several special targets take different actions if present with no |
||
845 |
* sources: |
||
846 |
* a .SUFFIXES line with no sources clears out all old suffixes |
||
847 |
* a .PRECIOUS line makes all targets precious |
||
848 |
* a .IGNORE line ignores errors for all targets |
||
849 |
* a .SILENT line creates silence when making all targets |
||
850 |
* a .PATH removes all directories from the search path(s). |
||
851 |
*/ |
||
852 |
✓✓ | 5249083 |
if (!*line) { |
853 |
✓✗✗✗ ✗✓ |
5166689 |
switch (specType) { |
854 |
case SPECIAL_SUFFIXES: |
||
855 |
317 |
Suff_ClearSuffixes(); |
|
856 |
317 |
break; |
|
857 |
case SPECIAL_PRECIOUS: |
||
858 |
allPrecious = true; |
||
859 |
break; |
||
860 |
case SPECIAL_IGNORE: |
||
861 |
ignoreErrors = true; |
||
862 |
break; |
||
863 |
case SPECIAL_SILENT: |
||
864 |
beSilent = true; |
||
865 |
break; |
||
866 |
case SPECIAL_PATH: |
||
867 |
Lst_Every(&paths, ParseClearPath); |
||
868 |
break; |
||
869 |
default: |
||
870 |
break; |
||
871 |
} |
||
872 |
✗✓ | 977183 |
} else if (specType == SPECIAL_MFLAGS) { |
873 |
/* Call on functions in main.c to deal with these arguments */ |
||
874 |
Main_ParseArgLine(line); |
||
875 |
return; |
||
876 |
✗✓ | 977183 |
} else if (specType == SPECIAL_NOTPARALLEL) { |
877 |
return; |
||
878 |
} |
||
879 |
|||
880 |
/* |
||
881 |
* NOW GO FOR THE SOURCES |
||
882 |
*/ |
||
883 |
✓✓ | 10498166 |
if (specType == SPECIAL_SUFFIXES || specType == SPECIAL_PATH || |
884 |
5249083 |
specType == SPECIAL_NOTHING) { |
|
885 |
✓✓ | 1209126 |
while (*line) { |
886 |
/* |
||
887 |
* If the target was one that doesn't take files as its |
||
888 |
* sources but takes something like suffixes, we take each |
||
889 |
* space-separated word on the line as a something and deal |
||
890 |
* with it accordingly. |
||
891 |
* |
||
892 |
* If the target was .SUFFIXES, we take each source as a |
||
893 |
* suffix and add it to the list of suffixes maintained by |
||
894 |
* the Suff module. |
||
895 |
* |
||
896 |
* If the target was a .PATH, we add the source as a |
||
897 |
* directory to search on the search path. |
||
898 |
* |
||
899 |
* If it was .INCLUDES, the source is taken to be the |
||
900 |
* suffix of files which will be #included and whose search |
||
901 |
* path should be present in the .INCLUDES variable. |
||
902 |
* |
||
903 |
* If it was .LIBS, the source is taken to be the suffix of |
||
904 |
* files which are considered libraries and whose search |
||
905 |
* path should be present in the .LIBS variable. |
||
906 |
* |
||
907 |
* If it was .NULL, the source is the suffix to use when a |
||
908 |
* file has no valid suffix. |
||
909 |
*/ |
||
910 |
✓✓✓✓ |
7623431 |
while (*cp && !ISSPACE(*cp)) |
911 |
1988057 |
cp++; |
|
912 |
✓✓✓ | 1131750 |
switch (specType) { |
913 |
case SPECIAL_SUFFIXES: |
||
914 |
564448 |
Suff_AddSuffixi(line, cp); |
|
915 |
564448 |
break; |
|
916 |
case SPECIAL_PATH: |
||
917 |
{ |
||
918 |
LstNode ln; |
||
919 |
|||
920 |
✓✓ | 5708 |
for (ln = Lst_First(&paths); ln != NULL; |
921 |
1427 |
ln = Lst_Adv(ln)) |
|
922 |
1427 |
Dir_AddDiri(Lst_Datum(ln), line, cp); |
|
923 |
break; |
||
924 |
} |
||
925 |
default: |
||
926 |
break; |
||
927 |
} |
||
928 |
✓✓ | 565875 |
if (*cp != '\0') |
929 |
527510 |
cp++; |
|
930 |
✓✓ | 1168260 |
while (ISSPACE(*cp)) |
931 |
18255 |
cp++; |
|
932 |
565875 |
line = cp; |
|
933 |
} |
||
934 |
38688 |
Lst_Destroy(&paths, NOFREE); |
|
935 |
38688 |
} else { |
|
936 |
✓✓ | 21858886 |
while (*line) { |
937 |
/* |
||
938 |
* The targets take real sources, so we must beware of |
||
939 |
* archive specifications (i.e. things with left |
||
940 |
* parentheses in them) and handle them accordingly. |
||
941 |
*/ |
||
942 |
✓✓✓✓ |
249253402 |
while (*cp && !ISSPACE(*cp)) { |
943 |
✗✓✗✗ ✗✗ |
77671754 |
if (*cp == '(' && cp > line && cp[-1] != '$') { |
944 |
/* |
||
945 |
* Only stop for a left parenthesis if |
||
946 |
* it isn't at the start of a word |
||
947 |
* (that'll be for variable changes |
||
948 |
* later) and isn't preceded by a |
||
949 |
* dollar sign (a dynamic source). |
||
950 |
*/ |
||
951 |
break; |
||
952 |
} else { |
||
953 |
77671754 |
cp++; |
|
954 |
} |
||
955 |
} |
||
956 |
|||
957 |
✗✓ | 5719048 |
if (*cp == '(') { |
958 |
GNode *gn; |
||
959 |
LIST sources; /* list of archive source |
||
960 |
* names after expansion */ |
||
961 |
|||
962 |
Lst_Init(&sources); |
||
963 |
if (!Arch_ParseArchive(&line, &sources, NULL)) { |
||
964 |
Parse_Error(PARSE_FATAL, |
||
965 |
"Error in source archive spec \"%s\"", |
||
966 |
line); |
||
967 |
return; |
||
968 |
} |
||
969 |
|||
970 |
while ((gn = Lst_DeQueue(&sources)) != NULL) |
||
971 |
ParseDoSrc(>argets, &gsources, tOp, |
||
972 |
gn->name, NULL); |
||
973 |
cp = line; |
||
974 |
} else { |
||
975 |
const char *endSrc = cp; |
||
976 |
|||
977 |
5719048 |
ParseDoSrc(>argets, &gsources, tOp, line, |
|
978 |
endSrc); |
||
979 |
✓✓ | 5719048 |
if (*cp) |
980 |
4800044 |
cp++; |
|
981 |
} |
||
982 |
✓✓ | 12380082 |
while (ISSPACE(*cp)) |
983 |
470993 |
cp++; |
|
984 |
5719048 |
line = cp; |
|
985 |
} |
||
986 |
} |
||
987 |
|||
988 |
✓✓ | 5249083 |
if (mainNode == NULL) { |
989 |
/* If we have yet to decide on a main target to make, in the |
||
990 |
* absence of any user input, we want the first target on |
||
991 |
* the first dependency line that is actually a real target |
||
992 |
* (i.e. isn't a .USE or .EXEC rule) to be made. */ |
||
993 |
✓✓✓✓ |
3545989 |
Array_Find(>argets, ParseFindMain, NULL); |
994 |
715588 |
} |
|
995 |
10498166 |
} |
|
996 |
|||
997 |
/*- |
||
998 |
* ParseAddCmd -- |
||
999 |
* Lst_ForEach function to add a command line to all targets |
||
1000 |
* |
||
1001 |
* The new command may be added to the commands list of the node. |
||
1002 |
* |
||
1003 |
* If the target already had commands, we ignore the new ones, but |
||
1004 |
* we note that we got double commands (in case we actually get to run |
||
1005 |
* that ambiguous target). |
||
1006 |
* |
||
1007 |
* Note this does not apply to :: dependency lines, since those |
||
1008 |
* will generate fresh cloned nodes and add them to the cohorts |
||
1009 |
* field of the main node. |
||
1010 |
*/ |
||
1011 |
static void |
||
1012 |
ParseAddCmd(void *gnp, void *cmd) |
||
1013 |
{ |
||
1014 |
27837226 |
GNode *gn = gnp; |
|
1015 |
|||
1016 |
✓✓ | 13918613 |
if (!(gn->type & OP_HAS_COMMANDS)) |
1017 |
13914878 |
Lst_AtEnd(&gn->commands, cmd); |
|
1018 |
else |
||
1019 |
3735 |
gn->type |= OP_DOUBLE; |
|
1020 |
13918613 |
} |
|
1021 |
|||
1022 |
/*- |
||
1023 |
*----------------------------------------------------------------------- |
||
1024 |
* ParseHasCommands -- |
||
1025 |
* Record that the target gained commands through OP_HAS_COMMANDS, |
||
1026 |
* so that double command lists may be ignored. |
||
1027 |
*----------------------------------------------------------------------- |
||
1028 |
*/ |
||
1029 |
static void |
||
1030 |
ParseHasCommands(void *gnp) |
||
1031 |
{ |
||
1032 |
8167456 |
GNode *gn = gnp; |
|
1033 |
4083728 |
gn->type |= OP_HAS_COMMANDS; |
|
1034 |
|||
1035 |
4083728 |
} |
|
1036 |
|||
1037 |
|||
1038 |
/* Strip comments from line. Build a copy in buffer if necessary, */ |
||
1039 |
static char * |
||
1040 |
strip_comments(Buffer copy, const char *line) |
||
1041 |
{ |
||
1042 |
const char *comment; |
||
1043 |
const char *p; |
||
1044 |
|||
1045 |
72229858 |
comment = strchr(line, '#'); |
|
1046 |
✗✓ | 36114929 |
assert(comment != line); |
1047 |
✓✓ | 36114929 |
if (comment == NULL) |
1048 |
35827870 |
return (char *)line; |
|
1049 |
else { |
||
1050 |
287059 |
Buf_Reset(copy); |
|
1051 |
|||
1052 |
✓✓ | 20171750 |
for (p = line; *p != '\0'; p++) { |
1053 |
✓✓ | 10085870 |
if (*p == '\\') { |
1054 |
✓✓ | 15 |
if (p[1] == '#') { |
1055 |
5 |
Buf_Addi(copy, line, p); |
|
1056 |
✗✓ | 10 |
Buf_AddChar(copy, '#'); |
1057 |
5 |
line = p+2; |
|
1058 |
5 |
} |
|
1059 |
✓✗ | 15 |
if (p[1] != '\0') |
1060 |
15 |
p++; |
|
1061 |
✓✓ | 10085855 |
} else if (*p == '#') |
1062 |
break; |
||
1063 |
} |
||
1064 |
287059 |
Buf_Addi(copy, line, p); |
|
1065 |
287059 |
return Buf_Retrieve(copy); |
|
1066 |
} |
||
1067 |
36114929 |
} |
|
1068 |
|||
1069 |
|||
1070 |
|||
1071 |
/*** |
||
1072 |
*** Support for various include constructs |
||
1073 |
***/ |
||
1074 |
|||
1075 |
|||
1076 |
void |
||
1077 |
Parse_AddIncludeDir(const char *dir) |
||
1078 |
{ |
||
1079 |
Dir_AddDir(userIncludePath, dir); |
||
1080 |
} |
||
1081 |
|||
1082 |
static char * |
||
1083 |
resolve_include_filename(const char *file, const char *efile, bool isSystem) |
||
1084 |
{ |
||
1085 |
char *fullname; |
||
1086 |
|||
1087 |
/* Look up system files on the system path first */ |
||
1088 |
✓✓ | 207118 |
if (isSystem) { |
1089 |
158563 |
fullname = Dir_FindFileNoDoti(file, efile, systemIncludePath); |
|
1090 |
✓✓ | 158563 |
if (fullname) |
1091 |
132421 |
return fullname; |
|
1092 |
} |
||
1093 |
|||
1094 |
/* Handle non-system non-absolute files... */ |
||
1095 |
✓✓✓✓ |
123252 |
if (!isSystem && file[0] != '/') { |
1096 |
/* ... by looking first under the same directory as the |
||
1097 |
* current file */ |
||
1098 |
char *slash = NULL; |
||
1099 |
const char *fname; |
||
1100 |
|||
1101 |
7307 |
fname = Parse_Getfilename(); |
|
1102 |
|||
1103 |
✓✗ | 7307 |
if (fname != NULL) |
1104 |
7307 |
slash = strrchr(fname, '/'); |
|
1105 |
|||
1106 |
✓✓ | 7307 |
if (slash != NULL) { |
1107 |
char *newName; |
||
1108 |
|||
1109 |
7192 |
newName = Str_concati(fname, slash, file, efile, '/'); |
|
1110 |
7192 |
fullname = Dir_FindFile(newName, userIncludePath); |
|
1111 |
✗✓ | 7192 |
if (fullname == NULL) |
1112 |
fullname = Dir_FindFile(newName, defaultPath); |
||
1113 |
7192 |
free(newName); |
|
1114 |
✓✗ | 7192 |
if (fullname) |
1115 |
7192 |
return fullname; |
|
1116 |
} |
||
1117 |
✓✓ | 115 |
} |
1118 |
|||
1119 |
/* Now look first on the -I search path, then on the .PATH |
||
1120 |
* search path, if not found in a -I directory. |
||
1121 |
* XXX: Suffix specific? */ |
||
1122 |
67505 |
fullname = Dir_FindFilei(file, efile, userIncludePath); |
|
1123 |
✓✓ | 67505 |
if (fullname) |
1124 |
60365 |
return fullname; |
|
1125 |
7140 |
fullname = Dir_FindFilei(file, efile, defaultPath); |
|
1126 |
✓✓ | 7140 |
if (fullname) |
1127 |
153 |
return fullname; |
|
1128 |
|||
1129 |
/* Still haven't found the makefile. Look for it on the system |
||
1130 |
* path as a last resort (if we haven't already). */ |
||
1131 |
✓✗ | 6987 |
if (isSystem) |
1132 |
6987 |
return NULL; |
|
1133 |
else |
||
1134 |
return Dir_FindFilei(file, efile, systemIncludePath); |
||
1135 |
207118 |
} |
|
1136 |
|||
1137 |
static void |
||
1138 |
handle_include_file(const char *file, const char *efile, bool isSystem, |
||
1139 |
bool errIfNotFound) |
||
1140 |
{ |
||
1141 |
char *fullname; |
||
1142 |
|||
1143 |
207118 |
fullname = resolve_include_filename(file, efile, isSystem); |
|
1144 |
✓✓✗✓ |
214105 |
if (fullname == NULL && errIfNotFound) |
1145 |
Parse_Error(PARSE_FATAL, "Could not find %.*s", |
||
1146 |
(int)(efile - file), file); |
||
1147 |
|||
1148 |
✓✓ | 207118 |
if (fullname != NULL) { |
1149 |
FILE *f; |
||
1150 |
|||
1151 |
200131 |
f = fopen(fullname, "r"); |
|
1152 |
✗✓✗✗ |
200131 |
if (f == NULL && errIfNotFound) |
1153 |
Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); |
||
1154 |
else |
||
1155 |
200131 |
Parse_FromFile(fullname, f); |
|
1156 |
200131 |
} |
|
1157 |
207118 |
} |
|
1158 |
|||
1159 |
/* .include <file> (system) or .include "file" (normal) */ |
||
1160 |
static bool |
||
1161 |
lookup_bsd_include(const char *file) |
||
1162 |
{ |
||
1163 |
char endc; |
||
1164 |
const char *efile; |
||
1165 |
char *file2; |
||
1166 |
bool isSystem; |
||
1167 |
|||
1168 |
/* find starting delimiter */ |
||
1169 |
✓✓ | 904645 |
while (ISSPACE(*file)) |
1170 |
180929 |
file++; |
|
1171 |
|||
1172 |
/* determine type of file */ |
||
1173 |
✓✓ | 180929 |
if (*file == '<') { |
1174 |
isSystem = true; |
||
1175 |
endc = '>'; |
||
1176 |
✓✗ | 180929 |
} else if (*file == '"') { |
1177 |
isSystem = false; |
||
1178 |
endc = '"'; |
||
1179 |
} else { |
||
1180 |
Parse_Error(PARSE_WARNING, |
||
1181 |
".include filename must be delimited by '\"' or '<'"); |
||
1182 |
return false; |
||
1183 |
} |
||
1184 |
|||
1185 |
/* delimit file name between file and efile */ |
||
1186 |
✓✓ | 4658100 |
for (efile = ++file; *efile != endc; efile++) { |
1187 |
✗✓ | 2148121 |
if (*efile == '\0') { |
1188 |
Parse_Error(PARSE_WARNING, |
||
1189 |
"Unclosed .include filename. '%c' expected", endc); |
||
1190 |
return false; |
||
1191 |
} |
||
1192 |
} |
||
1193 |
/* Substitute for any variables in the file name before trying to |
||
1194 |
* find the thing. */ |
||
1195 |
180929 |
file2 = Var_Substi(file, efile, NULL, false); |
|
1196 |
180929 |
handle_include_file(file2, strchr(file2, '\0'), isSystem, true); |
|
1197 |
180929 |
free(file2); |
|
1198 |
180929 |
return true; |
|
1199 |
180929 |
} |
|
1200 |
|||
1201 |
|||
1202 |
static void |
||
1203 |
lookup_sysv_style_include(const char *line, const char *directive, |
||
1204 |
bool errIfMissing) |
||
1205 |
{ |
||
1206 |
char *file; |
||
1207 |
char *name; |
||
1208 |
char *ename; |
||
1209 |
bool okay = false; |
||
1210 |
|||
1211 |
/* Substitute for any variables in the file name before trying to |
||
1212 |
* find the thing. */ |
||
1213 |
26189 |
file = Var_Subst(line, NULL, false); |
|
1214 |
|||
1215 |
/* sys5 allows for list of files separated by spaces */ |
||
1216 |
name = file; |
||
1217 |
52378 |
while (1) { |
|
1218 |
/* find beginning of name */ |
||
1219 |
✓✓ | 157134 |
while (ISSPACE(*name)) |
1220 |
26189 |
name++; |
|
1221 |
✓✓ | 52378 |
if (*name == '\0') |
1222 |
break; |
||
1223 |
✓✓✓✗ |
783847 |
for (ename = name; *ename != '\0' && !ISSPACE(*ename);) |
1224 |
243823 |
ename++; |
|
1225 |
26189 |
handle_include_file(name, ename, true, errIfMissing); |
|
1226 |
okay = true; |
||
1227 |
name = ename; |
||
1228 |
} |
||
1229 |
|||
1230 |
26189 |
free(file); |
|
1231 |
✗✓ | 26189 |
if (!okay) { |
1232 |
Parse_Error(PARSE_FATAL, "Filename missing from \"%s\"", |
||
1233 |
directive); |
||
1234 |
} |
||
1235 |
26189 |
} |
|
1236 |
|||
1237 |
|||
1238 |
/* system V construct: include file */ |
||
1239 |
static void |
||
1240 |
lookup_sysv_include(const char *file, const char *directive) |
||
1241 |
{ |
||
1242 |
lookup_sysv_style_include(file, directive, true); |
||
1243 |
} |
||
1244 |
|||
1245 |
|||
1246 |
/* sinclude file and -include file */ |
||
1247 |
static void |
||
1248 |
lookup_conditional_include(const char *file, const char *directive) |
||
1249 |
{ |
||
1250 |
52378 |
lookup_sysv_style_include(file, directive, false); |
|
1251 |
26189 |
} |
|
1252 |
|||
1253 |
|||
1254 |
/*** |
||
1255 |
*** BSD-specific . constructs |
||
1256 |
*** They all follow the same pattern: |
||
1257 |
*** if the syntax matches BSD stuff, then we're committed to handle |
||
1258 |
*** them and report fatal errors (like, include file not existing) |
||
1259 |
*** otherwise, we return false, and hope somebody else will handle it. |
||
1260 |
***/ |
||
1261 |
|||
1262 |
static bool |
||
1263 |
handle_poison(const char *line) |
||
1264 |
{ |
||
1265 |
const char *p = line; |
||
1266 |
int type = POISON_NORMAL; |
||
1267 |
bool not = false; |
||
1268 |
bool paren_to_match = false; |
||
1269 |
const char *name, *ename; |
||
1270 |
|||
1271 |
while (ISSPACE(*p)) |
||
1272 |
p++; |
||
1273 |
if (*p == '!') { |
||
1274 |
not = true; |
||
1275 |
p++; |
||
1276 |
} |
||
1277 |
while (ISSPACE(*p)) |
||
1278 |
p++; |
||
1279 |
if (strncmp(p, "defined", 7) == 0) { |
||
1280 |
type = POISON_DEFINED; |
||
1281 |
p += 7; |
||
1282 |
} else if (strncmp(p, "empty", 5) == 0) { |
||
1283 |
type = POISON_EMPTY; |
||
1284 |
p += 5; |
||
1285 |
} |
||
1286 |
while (ISSPACE(*p)) |
||
1287 |
p++; |
||
1288 |
if (*p == '(') { |
||
1289 |
paren_to_match = true; |
||
1290 |
p++; |
||
1291 |
} |
||
1292 |
while (ISSPACE(*p)) |
||
1293 |
p++; |
||
1294 |
name = ename = p; |
||
1295 |
while (*p != '\0' && !ISSPACE(*p)) { |
||
1296 |
if (*p == ')' && paren_to_match) { |
||
1297 |
paren_to_match = false; |
||
1298 |
p++; |
||
1299 |
break; |
||
1300 |
} |
||
1301 |
p++; |
||
1302 |
ename = p; |
||
1303 |
} |
||
1304 |
while (ISSPACE(*p)) |
||
1305 |
p++; |
||
1306 |
switch(type) { |
||
1307 |
case POISON_NORMAL: |
||
1308 |
case POISON_EMPTY: |
||
1309 |
if (not) |
||
1310 |
type = POISON_INVALID; |
||
1311 |
break; |
||
1312 |
case POISON_DEFINED: |
||
1313 |
if (not) |
||
1314 |
type = POISON_NOT_DEFINED; |
||
1315 |
else |
||
1316 |
type = POISON_INVALID; |
||
1317 |
break; |
||
1318 |
} |
||
1319 |
if ((*p != '\0' && *p != '#') || type == POISON_INVALID) { |
||
1320 |
Parse_Error(PARSE_WARNING, "Invalid syntax for .poison: %s", |
||
1321 |
line); |
||
1322 |
return false; |
||
1323 |
} else { |
||
1324 |
Var_Mark(name, ename, type); |
||
1325 |
return true; |
||
1326 |
} |
||
1327 |
} |
||
1328 |
|||
1329 |
|||
1330 |
static bool |
||
1331 |
handle_for_loop(Buffer linebuf, const char *line) |
||
1332 |
{ |
||
1333 |
For *loop; |
||
1334 |
|||
1335 |
423368 |
loop = For_Eval(line); |
|
1336 |
✓✗ | 211684 |
if (loop != NULL) { |
1337 |
bool ok; |
||
1338 |
211684 |
do { |
|
1339 |
/* Find the matching endfor. */ |
||
1340 |
2273896 |
line = ParseReadLoopLine(linebuf); |
|
1341 |
✗✓ | 2273896 |
if (line == NULL) { |
1342 |
Parse_Error(PARSE_FATAL, |
||
1343 |
"Unexpected end of file in for loop.\n"); |
||
1344 |
return false; |
||
1345 |
} |
||
1346 |
2273896 |
ok = For_Accumulate(loop, line); |
|
1347 |
✓✓ | 2273896 |
} while (ok); |
1348 |
211684 |
For_Run(loop); |
|
1349 |
211684 |
return true; |
|
1350 |
} else |
||
1351 |
return false; |
||
1352 |
211684 |
} |
|
1353 |
|||
1354 |
static bool |
||
1355 |
handle_undef(const char *line) |
||
1356 |
{ |
||
1357 |
const char *eline; |
||
1358 |
|||
1359 |
while (ISSPACE(*line)) |
||
1360 |
line++; |
||
1361 |
for (eline = line; !ISSPACE(*eline) && *eline != '\0';) |
||
1362 |
eline++; |
||
1363 |
Var_Deletei(line, eline); |
||
1364 |
return true; |
||
1365 |
} |
||
1366 |
|||
1367 |
/* global hub for the construct */ |
||
1368 |
static bool |
||
1369 |
handle_bsd_command(Buffer linebuf, Buffer copy, const char *line) |
||
1370 |
{ |
||
1371 |
char *stripped; |
||
1372 |
|||
1373 |
✓✓ | 111323249 |
while (ISSPACE(*line)) |
1374 |
31875289 |
line++; |
|
1375 |
|||
1376 |
/* delegate basic classification to the conditional module */ |
||
1377 |
✓✓✓✓ ✗✗✓ |
23012989 |
switch (Cond_Eval(line)) { |
1378 |
case COND_SKIP: |
||
1379 |
/* Skip to next conditional that evaluates to COND_PARSE. */ |
||
1380 |
7155432 |
do { |
|
1381 |
8362451 |
line = Parse_ReadNextConditionalLine(linebuf); |
|
1382 |
✓✗ | 8362451 |
if (line != NULL) { |
1383 |
✓✓ | 53516018 |
while (ISSPACE(*line)) |
1384 |
18395558 |
line++; |
|
1385 |
8362451 |
stripped = strip_comments(copy, line); |
|
1386 |
8362451 |
} |
|
1387 |
✓✗✓✓ |
16724902 |
} while (line != NULL && Cond_Eval(stripped) != COND_PARSE); |
1388 |
/* FALLTHROUGH */ |
||
1389 |
case COND_PARSE: |
||
1390 |
14525798 |
return true; |
|
1391 |
case COND_ISFOR: |
||
1392 |
211684 |
return handle_for_loop(linebuf, line + 3); |
|
1393 |
case COND_ISINCLUDE: |
||
1394 |
180929 |
return lookup_bsd_include(line + 7); |
|
1395 |
case COND_ISPOISON: |
||
1396 |
return handle_poison(line + 6); |
||
1397 |
case COND_ISUNDEF: |
||
1398 |
return handle_undef(line + 5); |
||
1399 |
default: |
||
1400 |
break; |
||
1401 |
} |
||
1402 |
|||
1403 |
939146 |
return false; |
|
1404 |
15857557 |
} |
|
1405 |
|||
1406 |
/* postprocess group of targets prior to linking stuff with them */ |
||
1407 |
static bool |
||
1408 |
register_target(GNode *gn, struct ohash *t) |
||
1409 |
{ |
||
1410 |
unsigned int slot; |
||
1411 |
uint32_t hv; |
||
1412 |
1404428 |
const char *ename = NULL; |
|
1413 |
GNode *gn2; |
||
1414 |
|||
1415 |
702214 |
hv = ohash_interval(gn->name, &ename); |
|
1416 |
|||
1417 |
702214 |
slot = ohash_lookup_interval(t, gn->name, ename, hv); |
|
1418 |
702214 |
gn2 = ohash_find(t, slot); |
|
1419 |
|||
1420 |
✓✓ | 702214 |
if (gn2 == NULL) { |
1421 |
669906 |
ohash_insert(t, slot, gn); |
|
1422 |
669906 |
return true; |
|
1423 |
} else |
||
1424 |
32308 |
return false; |
|
1425 |
702214 |
} |
|
1426 |
|||
1427 |
static void |
||
1428 |
build_target_group(struct growableArray *targets, struct ohash *t) |
||
1429 |
{ |
||
1430 |
LstNode ln; |
||
1431 |
bool seen_target = false; |
||
1432 |
8102882 |
unsigned int i; |
|
1433 |
|||
1434 |
/* may be 0 if wildcard expansion resulted in zero match */ |
||
1435 |
✓✓ | 4051441 |
if (targets->n <= 1) |
1436 |
4030215 |
return; |
|
1437 |
|||
1438 |
/* Perform checks to see if we must tie targets together */ |
||
1439 |
/* XXX */ |
||
1440 |
✓✓ | 21226 |
if (targets->a[0]->type & OP_TRANSFORM) |
1441 |
2219 |
return; |
|
1442 |
|||
1443 |
✓✓ | 143954 |
for (ln = Lst_First(&targets->a[0]->commands); ln != NULL; |
1444 |
52970 |
ln = Lst_Adv(ln)) { |
|
1445 |
56010 |
struct command *cmd = Lst_Datum(ln); |
|
1446 |
✓✓ | 56010 |
if (Var_Check_for_target(cmd->string)) { |
1447 |
seen_target = true; |
||
1448 |
3040 |
break; |
|
1449 |
} |
||
1450 |
✓✓ | 52970 |
} |
1451 |
✗✓ | 19007 |
if (DEBUG(TARGGROUP)) { |
1452 |
fprintf(stderr, |
||
1453 |
seen_target ? "No target group at %lu: ": |
||
1454 |
"Target group at %lu:", Parse_Getlineno()); |
||
1455 |
for (i = 0; i < targets->n; i++) |
||
1456 |
fprintf(stderr, " %s", targets->a[i]->name); |
||
1457 |
fprintf(stderr, "\n"); |
||
1458 |
} |
||
1459 |
✓✓ | 19007 |
if (seen_target) |
1460 |
3040 |
return; |
|
1461 |
|||
1462 |
GNode *gn, *gn2; |
||
1463 |
/* targets may already participate in groupling lists, |
||
1464 |
* so rebuild the circular list "from scratch" |
||
1465 |
*/ |
||
1466 |
|||
1467 |
✓✓ | 103482 |
for (i = 0; i < targets->n; i++) { |
1468 |
35774 |
gn = targets->a[i]; |
|
1469 |
✓✓ | 107970 |
for (gn2 = gn->groupling; gn2 != gn; gn2 = gn2->groupling) { |
1470 |
35990 |
if (!gn2) |
|
1471 |
break; |
||
1472 |
216 |
register_target(gn2, t); |
|
1473 |
} |
||
1474 |
} |
||
1475 |
|||
1476 |
✓✓ | 103898 |
for (gn = ohash_first(t, &i); gn != NULL; gn = ohash_next(t, &i)) { |
1477 |
35982 |
gn->groupling = gn2; |
|
1478 |
gn2 = gn; |
||
1479 |
} |
||
1480 |
15967 |
gn = ohash_first(t, &i); |
|
1481 |
15967 |
gn->groupling = gn2; |
|
1482 |
4067408 |
} |
|
1483 |
|||
1484 |
static void |
||
1485 |
reset_target_hash() |
||
1486 |
{ |
||
1487 |
✓✓ | 132026 |
if (htargets_setup) |
1488 |
55878 |
ohash_delete(&htargets); |
|
1489 |
66013 |
ohash_init(&htargets, 5, &gnode_info); |
|
1490 |
66013 |
htargets_setup = true; |
|
1491 |
66013 |
} |
|
1492 |
|||
1493 |
void |
||
1494 |
Parse_End() |
||
1495 |
{ |
||
1496 |
✓✓ | 33706 |
if (htargets_setup) |
1497 |
10135 |
ohash_delete(&htargets); |
|
1498 |
16853 |
} |
|
1499 |
|||
1500 |
static void |
||
1501 |
dedup_targets(struct growableArray *targets) |
||
1502 |
{ |
||
1503 |
unsigned int i, j; |
||
1504 |
|||
1505 |
✓✓ | 10498166 |
if (targets->n <= 1) |
1506 |
5183070 |
return; |
|
1507 |
|||
1508 |
66013 |
reset_target_hash(); |
|
1509 |
/* first let's de-dup the list */ |
||
1510 |
✓✓ | 1536022 |
for (i = 0, j = 0; i < targets->n; i++) { |
1511 |
701998 |
GNode *gn = targets->a[i]; |
|
1512 |
✓✓ | 701998 |
if (register_target(gn, &htargets)) |
1513 |
669698 |
targets->a[j++] = targets->a[i]; |
|
1514 |
} |
||
1515 |
66013 |
targets->n = j; |
|
1516 |
5315096 |
} |
|
1517 |
|||
1518 |
|||
1519 |
/*** |
||
1520 |
*** handle a group of commands |
||
1521 |
***/ |
||
1522 |
|||
1523 |
static void |
||
1524 |
finish_commands(struct growableArray *targets) |
||
1525 |
{ |
||
1526 |
8102882 |
build_target_group(targets, &htargets); |
|
1527 |
✓✓ | 16270338 |
Array_Every(targets, ParseHasCommands); |
1528 |
4051441 |
} |
|
1529 |
|||
1530 |
static void |
||
1531 |
parse_commands(struct growableArray *targets, const char *line) |
||
1532 |
{ |
||
1533 |
/* add the command to the list of |
||
1534 |
* commands of all targets in the dependency spec */ |
||
1535 |
|||
1536 |
struct command *cmd; |
||
1537 |
27664170 |
size_t len = strlen(line); |
|
1538 |
|||
1539 |
13832085 |
cmd = emalloc(sizeof(struct command) + len); |
|
1540 |
13832085 |
memcpy(&cmd->string, line, len+1); |
|
1541 |
13832085 |
Parse_FillLocation(&cmd->location); |
|
1542 |
|||
1543 |
✓✓ | 55501396 |
Array_ForEach(targets, ParseAddCmd, cmd); |
1544 |
13832085 |
} |
|
1545 |
|||
1546 |
static bool |
||
1547 |
parse_as_special_line(Buffer buf, Buffer copy, const char *line) |
||
1548 |
{ |
||
1549 |
✓✓✓✓ |
71362513 |
if (*line == '.' && handle_bsd_command(buf, copy, line+1)) |
1550 |
14918411 |
return true; |
|
1551 |
if (FEATURES(FEATURE_SYSVINCLUDE) && |
||
1552 |
✓✓ | 12834067 |
strncmp(line, "include", 7) == 0 && |
1553 |
✗✓ | 16843 |
ISSPACE(line[7]) && |
1554 |
strchr(line, ':') == NULL) { |
||
1555 |
/* It's an S3/S5-style "include". */ |
||
1556 |
lookup_sysv_include(line + 7, "include"); |
||
1557 |
return true; |
||
1558 |
} |
||
1559 |
✓✗ | 26189 |
if (FEATURES(FEATURE_CONDINCLUDE) && |
1560 |
✓✓ | 12834067 |
strncmp(line, "sinclude", 8) == 0 && |
1561 |
✓✗ | 26189 |
ISSPACE(line[8]) && |
1562 |
26189 |
strchr(line, ':') == NULL) { |
|
1563 |
26189 |
lookup_conditional_include(line+8, "sinclude"); |
|
1564 |
26189 |
return true; |
|
1565 |
} |
||
1566 |
if (FEATURES(FEATURE_CONDINCLUDE) && |
||
1567 |
✗✓ | 12807878 |
strncmp(line, "-include", 8) == 0 && |
1568 |
ISSPACE(line[8]) && |
||
1569 |
strchr(line, ':') == NULL) { |
||
1570 |
lookup_conditional_include(line+8, "-include"); |
||
1571 |
return true; |
||
1572 |
} |
||
1573 |
12807878 |
return false; |
|
1574 |
27752478 |
} |
|
1575 |
|||
1576 |
static void |
||
1577 |
parse_target_line(struct growableArray *targets, const char *line, |
||
1578 |
const char *stripped, bool *pcommands_seen) |
||
1579 |
{ |
||
1580 |
size_t pos; |
||
1581 |
char *end; |
||
1582 |
char *cp; |
||
1583 |
char *cmd; |
||
1584 |
|||
1585 |
/* let's start a new set of commands */ |
||
1586 |
10498166 |
Array_Reset(targets); |
|
1587 |
|||
1588 |
/* XXX this is a dirty heuristic to handle target: dep ; commands */ |
||
1589 |
cmd = NULL; |
||
1590 |
/* First we need to find eventual dependencies */ |
||
1591 |
5249083 |
pos = strcspn(stripped, ":!"); |
|
1592 |
/* go over :!, and find ; */ |
||
1593 |
✓✗✓✓ |
10498166 |
if (stripped[pos] != '\0' && |
1594 |
5249083 |
(end = strchr(stripped+pos+1, ';')) != NULL) { |
|
1595 |
✗✓ | 100 |
if (line != stripped) |
1596 |
/* find matching ; in original... The |
||
1597 |
* original might be slightly longer. */ |
||
1598 |
cmd = strchr(line+(end-stripped), ';'); |
||
1599 |
else |
||
1600 |
cmd = end; |
||
1601 |
/* kill end of line. */ |
||
1602 |
100 |
*end = '\0'; |
|
1603 |
100 |
} |
|
1604 |
/* We now know it's a dependency line so it needs to |
||
1605 |
* have all variables expanded before being parsed. |
||
1606 |
*/ |
||
1607 |
5249083 |
cp = Var_Subst(stripped, NULL, false); |
|
1608 |
5249083 |
ParseDoDependency(cp); |
|
1609 |
5249083 |
free(cp); |
|
1610 |
|||
1611 |
/* Parse command if it's not empty. */ |
||
1612 |
✓✓ | 5249083 |
if (cmd != NULL) { |
1613 |
100 |
do { |
|
1614 |
200 |
cmd++; |
|
1615 |
✓✓ | 200 |
} while (ISSPACE(*cmd)); |
1616 |
✓✗ | 100 |
if (*cmd != '\0') { |
1617 |
100 |
parse_commands(targets, cmd); |
|
1618 |
100 |
*pcommands_seen = true; |
|
1619 |
100 |
} |
|
1620 |
} |
||
1621 |
5249083 |
} |
|
1622 |
|||
1623 |
void |
||
1624 |
Parse_File(const char *filename, FILE *stream) |
||
1625 |
{ |
||
1626 |
char *line; |
||
1627 |
bool expectingCommands = false; |
||
1628 |
67416 |
bool commands_seen = false; |
|
1629 |
|||
1630 |
/* somewhat permanent spaces to shave time */ |
||
1631 |
33708 |
BUFFER buf; |
|
1632 |
33708 |
BUFFER copy; |
|
1633 |
|||
1634 |
33708 |
Buf_Init(&buf, MAKE_BSIZE); |
|
1635 |
33708 |
Buf_Init(©, MAKE_BSIZE); |
|
1636 |
|||
1637 |
33708 |
Parse_FromFile(filename, stream); |
|
1638 |
33708 |
do { |
|
1639 |
✓✓ | 102174876 |
while ((line = Parse_ReadNormalLine(&buf)) != NULL) { |
1640 |
✓✓ | 41584463 |
if (*line == '\t') { |
1641 |
✓✗ | 13831985 |
if (expectingCommands) { |
1642 |
13831985 |
commands_seen = true; |
|
1643 |
13831985 |
parse_commands(>argets, line+1); |
|
1644 |
13831985 |
} else |
|
1645 |
Parse_Error(PARSE_FATAL, |
||
1646 |
"Unassociated shell command \"%s\"", |
||
1647 |
line); |
||
1648 |
} else { |
||
1649 |
27752478 |
const char *stripped = strip_comments(©, |
|
1650 |
line); |
||
1651 |
✓✓ | 27752478 |
if (!parse_as_special_line(&buf, ©, |
1652 |
stripped)) { |
||
1653 |
✓✓ | 12807878 |
if (commands_seen) |
1654 |
4032610 |
finish_commands(>argets); |
|
1655 |
12807878 |
commands_seen = false; |
|
1656 |
✓✓ | 12807878 |
if (Parse_As_Var_Assignment(stripped)) |
1657 |
7558795 |
expectingCommands = false; |
|
1658 |
else { |
||
1659 |
5249083 |
parse_target_line(>argets, |
|
1660 |
line, stripped, |
||
1661 |
&commands_seen); |
||
1662 |
expectingCommands = true; |
||
1663 |
} |
||
1664 |
} |
||
1665 |
} |
||
1666 |
} |
||
1667 |
✓✓ | 9502975 |
} while (Parse_NextFile()); |
1668 |
|||
1669 |
✓✓ | 33708 |
if (commands_seen) |
1670 |
18831 |
finish_commands(>argets); |
|
1671 |
/* Make sure conditionals are clean. */ |
||
1672 |
33708 |
Cond_End(); |
|
1673 |
|||
1674 |
33708 |
Parse_ReportErrors(); |
|
1675 |
33708 |
Buf_Destroy(&buf); |
|
1676 |
33708 |
Buf_Destroy(©); |
|
1677 |
33708 |
} |
|
1678 |
|||
1679 |
void |
||
1680 |
Parse_Init(void) |
||
1681 |
{ |
||
1682 |
33706 |
mainNode = NULL; |
|
1683 |
Static_Lst_Init(userIncludePath); |
||
1684 |
Static_Lst_Init(systemIncludePath); |
||
1685 |
16853 |
Array_Init(>argets, TARGETS_SIZE); |
|
1686 |
16853 |
Array_Init(&gsources, SOURCES_SIZE); |
|
1687 |
16853 |
create_special_nodes(); |
|
1688 |
16853 |
} |
|
1689 |
|||
1690 |
void |
||
1691 |
Parse_MainName(Lst listmain) /* result list */ |
||
1692 |
{ |
||
1693 |
✗✓ | 38 |
if (mainNode == NULL) { |
1694 |
Punt("no target to make."); |
||
1695 |
/*NOTREACHED*/ |
||
1696 |
✗✓ | 38 |
} else if (mainNode->type & OP_DOUBLEDEP) { |
1697 |
19 |
Lst_AtEnd(listmain, mainNode); |
|
1698 |
Lst_Concat(listmain, &mainNode->cohorts); |
||
1699 |
} |
||
1700 |
else |
||
1701 |
Lst_AtEnd(listmain, mainNode); |
||
1702 |
19 |
} |
Generated by: GCOVR (Version 3.3) |