1 /*
2 * cook - file construction tool
3 * Copyright (C) 1997, 1998, 2001, 2003, 2006, 2007 Peter Miller;
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 * If the relationship between a target and a derived ingredient appears
21 * only in a derived cookbook, it is likely that a clean build (solely
22 * from primary source files) will fail. It is recommended that
23 * relationships such as this be placed in a primary source cookbook.
24 *
25 * The functions in this file are used to detect such situations, and
26 * issue warnings about them when found.
27 */
28
29 #include <common/error_intl.h>
30 #include <cook/expr/position.h>
31 #include <cook/graph.h>
32 #include <cook/graph/file_pair.h>
33 #include <common/mem.h>
34 #include <common/str_list.h>
35 #include <common/symtab.h>
36 #include <common/trace.h>
37
38
39 typedef struct target_ty target_ty;
40 struct target_ty
41 {
42 string_ty *filename;
43 string_list_ty pos;
44 };
45
46 typedef struct ingredient_ty ingredient_ty;
47 struct ingredient_ty
48 {
49 string_ty *filename;
50 int include_cooked;
51 symtab_ty *target;
52 };
53
54
55 /*
56 * NAME
57 * target_new
58 *
59 * SYNOPSIS
60 * target_ty *target_new(string_ty *);
61 *
62 * DESCRIPTION
63 * The target_new function is used to allocate a new instance of a
64 * target. This is used to remember the location of a relationship
65 * between a target and an ingredient.
66 *
67 * CAVEAT
68 * Use target_delete when you are done with it.
69 */
70
71 static target_ty *
target_new(string_ty * filename)72 target_new(string_ty *filename)
73 {
74 target_ty *tp;
75
76 trace(("target_new()\n{\n"));
77 tp = mem_alloc(sizeof(target_ty));
78 tp->filename = str_copy(filename);
79 string_list_constructor(&tp->pos);
80 trace(("}\n"));
81 return tp;
82 }
83
84
85 /*
86 * NAME
87 * target_delete
88 *
89 * SYNOPSIS
90 * void target_delete(target_ty *);
91 *
92 * DESCRIPTION
93 * The target_delete function is used to release the resources held
94 * by a target instance.
95 */
96
97 static void
target_delete(target_ty * tp)98 target_delete(target_ty *tp)
99 {
100 trace(("target_delete()\n{\n"));
101 str_free(tp->filename);
102 string_list_destructor(&tp->pos);
103 mem_free(tp);
104 trace(("}\n"));
105 }
106
107
108 /*
109 * NAME
110 * target_reap
111 *
112 * SYNOPSIS
113 * void target_reap(void);
114 *
115 * DESCRIPTION
116 * The target_reap function is used to delete target values in a
117 * target symbol table.
118 */
119
120 static void
target_reap(void * p)121 target_reap(void *p)
122 {
123 target_ty *tp;
124
125 tp = p;
126 target_delete(tp);
127 }
128
129
130 /*
131 * NAME
132 * target_append
133 *
134 * SYNOPSIS
135 * void target_append(target_ty *, const expr_position_ty *);
136 *
137 * DESCRIPTION
138 * The target_append function is used to append recipe locations to
139 * a target relationship. This will be checked later.
140 */
141
142 static void
target_append(target_ty * tp,const expr_position_ty * pp)143 target_append(target_ty *tp, const expr_position_ty *pp)
144 {
145 trace(("target_append()\n{\n"));
146 string_list_append_unique(&tp->pos, pp->pos_name);
147 trace(("}\n"));
148 }
149
150
151 /*
152 * NAME
153 * foreign_derived
154 *
155 * SYNOPSIS
156 * int foreign_derived(graph_file_pair_ty *, string_ty *);
157 *
158 * DESCRIPTION
159 * The foreign_derived function is used to test if the named file
160 * is a foreign derived file. This is used in generating warnings
161 * for cascaded ingredients in dependency include files.
162 */
163
164 static int
foreign_derived(graph_file_pair_ty * gfpp,string_ty * filename)165 foreign_derived(graph_file_pair_ty *gfpp, string_ty *filename)
166 {
167 void *p;
168
169 if (!gfpp->foreign_derived)
170 return 0;
171 p = symtab_query(gfpp->foreign_derived, filename);
172 return (p != 0);
173 }
174
175
176 /*
177 * NAME
178 * target_check
179 *
180 * SYNOPSIS
181 * void target_check(graph_file_pair_ty *, target_ty *, string_ty *,
182 * graph_ty *);
183 *
184 * DESCRIPTION
185 * The target_check function is used to check that at least one of
186 * the files describing the relationship between this target and
187 * this ingredient is a leaf file. The ingredient is known to be
188 * derived (non-leaf).
189 *
190 * A warning will be issued if this problem is found. The long
191 * warning will be issued, also, but only once per Cook command.
192 */
193
194 static void
target_check(graph_file_pair_ty * gfpp,target_ty * tp,string_ty * ingredient,graph_ty * gp)195 target_check(graph_file_pair_ty *gfpp, target_ty *tp, string_ty *ingredient,
196 graph_ty *gp)
197 {
198 size_t j;
199 static int the_long_version;
200 sub_context_ty *scp;
201 string_ty *fn;
202
203 /*
204 * If any of the cookbooks which describe this relationship are
205 * leaf files, there is no problem. Leave quietly.
206 */
207 trace(("ingredient_new()\n{\n"));
208 assert(tp->pos.nstrings);
209 if (!tp->pos.nstrings)
210 {
211 trace(("}\n"));
212 return;
213 }
214 for (j = 0; j < tp->pos.nstrings; ++j)
215 {
216 fn = tp->pos.string[j];
217 if (!foreign_derived(gfpp, fn) && graph_file_leaf_p(gp, fn))
218 {
219 trace(("}\n"));
220 return;
221 }
222 }
223
224 /*
225 * Houston, we have a problem.
226 */
227 scp = sub_context_new();
228 sub_var_set
229 (
230 scp,
231 "RELATionship", "%s: %s;",
232 tp->filename->str_text,
233 ingredient->str_text
234 );
235 sub_var_set_string(scp, "File_Name", tp->pos.string[0]);
236 error_intl
237 (
238 scp,
239 i18n("warning: the ``$relationship'' recipe is only in $filename")
240 );
241 sub_context_delete(scp);
242
243 /*
244 * also issue a longer warning, but only once
245 */
246 if (!the_long_version)
247 {
248 the_long_version = 1;
249 error_intl(0, i18n("this means that a clean build will fail"));
250 }
251 trace(("}\n"));
252 }
253
254
255 /*
256 * NAME
257 * ingredient_new
258 *
259 * SYNOPSIS
260 * ingredient_ty *ingredient_new(string_ty *, int);
261 *
262 * DESCRIPTION
263 * The ingredient_new function is used to allocate a new instance
264 * of an ingredient. Targets which lead to this ingredient, and
265 * the position of the recipes within in the cookbooks, are
266 * remembered within this data structure.
267 *
268 * CAVEAT
269 * Release with ingredient_delete when you are done with it.
270 */
271
272 static ingredient_ty *
ingredient_new(string_ty * filename,int include_cooked)273 ingredient_new(string_ty *filename, int include_cooked)
274 {
275 ingredient_ty *ip;
276
277 trace(("ingredient_new()\n{\n"));
278 ip = mem_alloc(sizeof(ingredient_ty));
279 ip->filename = str_copy(filename);
280 ip->include_cooked = include_cooked;
281 ip->target = 0;
282 trace(("}\n"));
283 return ip;
284 }
285
286
287 /*
288 * NAME
289 * ingredient_delete
290 *
291 * SYNOPSIS
292 * void ingredient_delete(ingredient_ty *);
293 *
294 * DESCRIPTION
295 * The ingredient_delete function is used to release the resources
296 * held by an ingredient instance.
297 */
298
299 static void
ingredient_delete(ingredient_ty * ip)300 ingredient_delete(ingredient_ty *ip)
301 {
302 trace(("ingredient_delete()\n{\n"));
303 str_free(ip->filename);
304 if (ip->target)
305 symtab_free(ip->target);
306 mem_free(ip);
307 trace(("}\n"));
308 }
309
310
311 /*
312 * NAME
313 * ingredient_reap
314 *
315 * SYNOPSIS
316 * void ingredient_reap(void *);
317 *
318 * DESCRIPTION
319 * The ingredient_reap function is used to delete ingredient values
320 * in an ingredient symbol table.
321 */
322
323 static void
ingredient_reap(void * p)324 ingredient_reap(void *p)
325 {
326 ingredient_ty *ip;
327
328 ip = p;
329 ingredient_delete(ip);
330 }
331
332
333 /*
334 * NAME
335 * ingredient_append
336 *
337 * SYNOPSIS
338 * void ingredient_append(ingredient_ty *, string_ty *,
339 * const expr_position_ty *);
340 *
341 * DESCRIPTION
342 * The ingredient_append function is used to remember the location
343 * of a relationship between the given target and the given
344 * ingredient.
345 */
346
347 static void
ingredient_append(ingredient_ty * ip,string_ty * target,const expr_position_ty * pp)348 ingredient_append(ingredient_ty *ip, string_ty *target,
349 const expr_position_ty *pp)
350 {
351 target_ty *tp;
352
353 trace(("ingredient_append()\n{\n"));
354 if (!ip->target)
355 {
356 ip->target = symtab_alloc(1);
357 ip->target->reap = target_reap;
358 }
359 tp = symtab_query(ip->target, target);
360 if (!tp)
361 {
362 tp = target_new(target);
363 symtab_assign(ip->target, target, tp);
364 }
365 target_append(tp, pp);
366 trace(("}\n"));
367 }
368
369
370 /*
371 * NAME
372 * ingredient_check
373 *
374 * SYNOPSIS
375 * void ingredient_check(graph_file_pair_ty *, ingredient_ty *,
376 * string_ty *, graph_ty *);
377 *
378 * DESCRIPTION
379 * The ingredient_check function is used to check that at least one
380 * of the files describing the relationship between this target and
381 * this ingredient is a leaf file. The ingredient is known to be
382 * derived (non-leaf).
383 */
384
385 static void
ingredient_check(graph_file_pair_ty * gfpp,ingredient_ty * ip,string_ty * target,graph_ty * gp)386 ingredient_check(graph_file_pair_ty *gfpp, ingredient_ty *ip, string_ty *target,
387 graph_ty *gp)
388 {
389 target_ty *tp;
390
391 /*
392 * Find the target. It should always be there, but if it is
393 * not, leave quietly.
394 */
395 trace(("ingredient_check()\n{\n"));
396 assert(gfpp);
397 assert(ip);
398 assert(ip->target);
399 assert(target);
400 tp = symtab_query(ip->target, target);
401 assert(tp);
402 if (!tp)
403 {
404 trace(("}\n"));
405 return;
406 }
407
408 /*
409 * Check the target. This is actually a simple list of files
410 * which describe this relationship.
411 */
412 target_check(gfpp, tp, ip->filename, gp);
413 trace(("}\n"));
414 }
415
416
417 /*
418 * NAME
419 * graph_file_pair_new
420 *
421 * SYNOPSIS
422 * void graph_file_pair_new(void);
423 *
424 * DESCRIPTION
425 * The graph_file_pair_new function is used to allocate a new
426 * instance of the file pair location checking information.
427 *
428 * CAVEAT
429 * Release using graph_file_pair_delete when you are done with it.
430 */
431
432 graph_file_pair_ty *
graph_file_pair_new(string_list_ty * slp)433 graph_file_pair_new(string_list_ty *slp)
434 {
435 graph_file_pair_ty *gfpp;
436 size_t j;
437
438 trace(("graph_file_pair_new()\n{\n"));
439 gfpp = mem_alloc(sizeof(graph_file_pair_ty));
440 gfpp->stp = symtab_alloc(slp ? slp->nstrings : 5);
441 gfpp->stp->reap = ingredient_reap;
442 gfpp->foreign_derived = 0;
443 if (slp)
444 {
445 for (j = 0; j < slp->nstrings; ++j)
446 {
447 ingredient_ty *ip;
448
449 ip = ingredient_new(slp->string[j], 1);
450 symtab_assign(gfpp->stp, slp->string[j], ip);
451 }
452 }
453 trace(("}\n"));
454 return gfpp;
455 }
456
457
458 /*
459 * NAME
460 * graph_file_pair_delete
461 *
462 * SYNOPSIS
463 * void graph_file_pair_delete(graph_file_pair_ty *);
464 *
465 * DESCRIPTION
466 * The graph_file_pair_delete function is used to relesae the
467 * resources used by a graph_file_pair instance.
468 */
469
470 void
graph_file_pair_delete(graph_file_pair_ty * gfpp)471 graph_file_pair_delete(graph_file_pair_ty *gfpp)
472 {
473 trace(("graph_file_pair_delete()\n{\n"));
474 symtab_free(gfpp->stp);
475 if (gfpp->foreign_derived)
476 symtab_free(gfpp->foreign_derived);
477 mem_free(gfpp);
478 trace(("}\n"));
479 }
480
481
482 /*
483 * NAME
484 * graph_file_pair_remember
485 *
486 * SYNOPSIS
487 * void graph_file_pair_remember(graph_file_pair_ty *,
488 * string_ty *target, string_ty *ingredient,
489 * const expr_position_ty *);
490 *
491 * DESCRIPTION
492 * The graph_file_pair_remember function is used to remember the
493 * position of a relationship between a target and an ingredient.
494 * These will be checked for ``clean'' derivability, later.
495 */
496
497 void
graph_file_pair_remember(graph_file_pair_ty * gfpp,string_ty * target,string_ty * ingredient,const expr_position_ty * pp)498 graph_file_pair_remember(graph_file_pair_ty *gfpp, string_ty *target,
499 string_ty *ingredient, const expr_position_ty *pp)
500 {
501 ingredient_ty *ip;
502
503 trace(("graph_file_pair_remember(target = \"%s\", "
504 "ingredient = \"%s\")\n{\n", target->str_text, ingredient->str_text));
505 ip = symtab_query(gfpp->stp, ingredient);
506 if (!ip)
507 {
508 ip = ingredient_new(ingredient, 0);
509 symtab_assign(gfpp->stp, ingredient, ip);
510 }
511 ingredient_append(ip, target, pp);
512 trace(("}\n"));
513 }
514
515
516 /*
517 * NAME
518 * graph_file_pair_remember_lists
519 *
520 * SYNOPSIS
521 * void graph_file_pair_remember_lists(graph_file_pair_ty *,
522 * string_list_ty *targets, string_list_ty *ingredients,
523 * const expr_position_ty *);
524 *
525 * DESCRIPTION
526 * The graph_file_pair_remember_lists function is used to remember
527 * the position of a relationship between a list of targets and a
528 * list of ingredients. These will be checked for ``clean''
529 * derivability, later.
530 */
531
532 void
graph_file_pair_remember_tlist(graph_file_pair_ty * gfpp,string_list_ty * target,string_ty * ingredient,const expr_position_ty * pp)533 graph_file_pair_remember_tlist(graph_file_pair_ty *gfpp, string_list_ty *target,
534 string_ty *ingredient, const expr_position_ty *pp)
535 {
536 size_t j;
537
538 trace(("graph_file_pair_remember_tlist()\n{\n"));
539 for (j = 0; j < target->nstrings; ++j)
540 {
541 graph_file_pair_remember(gfpp, target->string[j], ingredient, pp);
542 }
543 trace(("}\n"));
544 }
545
546
547 void
graph_file_pair_remember_lists(graph_file_pair_ty * gfpp,string_list_ty * target,string_list_ty * ingredient,const expr_position_ty * pp)548 graph_file_pair_remember_lists(graph_file_pair_ty *gfpp, string_list_ty *target,
549 string_list_ty *ingredient, const expr_position_ty *pp)
550 {
551 size_t k;
552
553 trace(("graph_file_pair_remember_lists()\n{\n"));
554 for (k = 0; k < ingredient->nstrings; ++k)
555 {
556 graph_file_pair_remember_tlist(gfpp, target, ingredient->string[k], pp);
557 }
558 trace(("}\n"));
559 }
560
561
562 /*
563 * NAME
564 * graph_file_pair_check
565 *
566 * SYNOPSIS
567 * void graph_file_pair_check(void);
568 *
569 * DESCRIPTION
570 * The graph_file_pair_check function is used to check that the
571 * relationship between the target and the ingredient either (a)
572 * does not need deriving, or (b) is derivable. Uses information
573 * accumulated earlier using graph_file_pair_remember function.
574 */
575
576 void
graph_file_pair_check(graph_file_pair_ty * gfpp,string_ty * target,string_ty * ingredient,graph_ty * gp)577 graph_file_pair_check(graph_file_pair_ty *gfpp, string_ty *target,
578 string_ty *ingredient, graph_ty *gp)
579 {
580 ingredient_ty *ip;
581
582 /*
583 * We are looking for non-leaf ingredients.
584 * If this ingredient is a leaf, no further processing is equired.
585 */
586 trace(("graph_file_pair_check()\n{\n"));
587 if (graph_file_leaf_p(gp, ingredient))
588 {
589 trace(("}\n"));
590 return;
591 }
592
593 /*
594 * Find the list of files in which this relationship is
595 * expressed. It should not happen, but if there is no such
596 * dependency, just return as if there was no problem.
597 */
598 ip = symtab_query(gfpp->stp, ingredient);
599 assert(ip);
600 if (!ip)
601 {
602 trace(("}\n"));
603 return;
604 }
605 assert(ip->target);
606 if (!ip->target)
607 {
608 trace(("}\n"));
609 return;
610 }
611
612 /*
613 * check back to the target via the ingredient
614 */
615 ingredient_check(gfpp, ip, target, gp);
616 trace(("}\n"));
617 }
618
619
620 /*
621 * NAME
622 * graph_file_pair_foreign_derived
623 *
624 * SYNOPSIS
625 * void graph_file_pair_foreign_derived(graph_file_pair_ty *,
626 * const string_list_ty *);
627 *
628 * DESCRIPTION
629 * The graph_file_pair_foreign_derived function is used to add
630 * a list of foreigh derived files. These files are foreign to
631 * the graph being walked. This is used to generate warnings for
632 * cascaded ingredients.
633 */
634
635 void
graph_file_pair_foreign_derived(graph_file_pair_ty * gfpp,const string_list_ty * slp)636 graph_file_pair_foreign_derived(graph_file_pair_ty *gfpp,
637 const string_list_ty *slp)
638 {
639 size_t j;
640 string_ty *fn;
641
642 if (!gfpp->foreign_derived)
643 gfpp->foreign_derived = symtab_alloc(slp->nstrings);
644 for (j = 0; j < slp->nstrings; ++j)
645 {
646 fn = slp->string[j];
647 symtab_assign(gfpp->foreign_derived, fn, fn);
648 }
649 }
650
651
652 #ifdef DEBUG
653
654
655 /*
656 * NAME
657 * graph_file_pair_check
658 *
659 * SYNOPSIS
660 * int graph_file_pair_exists(graph_file_pair_ty *gfpp,
661 * string_ty *target, string_ty *ingredient);
662 *
663 * DESCRIPTION
664 * The graph_file_pair_exists function is used at debug time to
665 * check that the graph file pairs have been constructed correctly.
666 */
667
668 int
graph_file_pair_exists(graph_file_pair_ty * gfpp,string_ty * target,string_ty * ingredient)669 graph_file_pair_exists(graph_file_pair_ty *gfpp, string_ty *target,
670 string_ty *ingredient)
671 {
672 ingredient_ty *ip;
673 int result;
674
675 /*
676 * We are looking for non-leaf ingredients.
677 * If this ingredient is a leaf, no further processing is equired.
678 */
679 trace(("graph_file_pair_exists()\n{\n"));
680 if (!gfpp)
681 {
682 trace(("return 0;\n}\n"));
683 return 0;
684 }
685
686 /*
687 * Make sure the ingredient is known.
688 */
689 ip = symtab_query(gfpp->stp, ingredient);
690 if (!ip)
691 {
692 trace(("return 0;\n}\n"));
693 return 0;
694 }
695
696 /*
697 * Make sure the ingredient has heard of the target.
698 */
699 if (!ip->target)
700 {
701 trace(("return 0;\n}\n"));
702 return 0;
703 }
704 result = !!symtab_query(ip->target, target);
705 trace(("return %d;\n}\n", result));
706 return result;
707 }
708
709 #endif
710