1 /*
2 * cook - file construction tool
3 * Copyright (C) 1994-1999, 2001, 2002, 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 * the kitchen
21 *
22 * This file contains the part of cook that actually decides which
23 * recipes will be cooked.
24 *
25 * When cook has a target, cook performs the following actions in the order
26 * given:
27 *
28 * 1. Cook scans through the instantiated prerequisite recipes. All
29 * prerequisite recipes with the target in their target list are used.
30 *
31 * If the recipe is used, any prerequisite files are recursively
32 * cooked, then if any of the prerequisite files were out of date, then
33 * all other explicit or implicit recipes with the same target will be
34 * deemed to be out of date.
35 *
36 * 2. Cook then scans through the instantiated explicit recipes. All
37 * explicit recipes with the target in their target list are used.
38 *
39 * If the recipe is used, any prerequisite files are recursively
40 * cooked, then if any prerequisites were out of date (including those
41 * of prerequisite recipes) then the actions bound to this recipe will
42 * be evaluated.
43 *
44 * If there are no ingredients, then it is not out-of-date. The
45 * body will be performed if (a) the target does not yet exist, or
46 * (b) the "force" flag is set, usually in the "set" clause of
47 * the recipe.
48 *
49 * 3. If the target was not the subject of any explicit recipe, cook then
50 * scans the instantiated implicit recipes. Only first implicit recipe
51 * for which cook knows how to cook will be used.
52 *
53 * Implicit recipe targets and prerequisites may contain a wilcard
54 * character (%), which is why they are implicit. If more than one
55 * wildcard character appears, only the last is considered the wilcard
56 * charcater.
57 *
58 * If an implicit recipe is used, when expressions are evaluaded into
59 * word lists, any word containing the wildcard charcater (%) will be
60 * expanded out by the current wildcard expansion.
61 *
62 * 4. If the target is not the subject of any prerequisite or explicit
63 * recipe, and no implicit recipes can be applied, then two things can
64 * happen.
65 * a. If the file exists, then it is up to date, or
66 * b. If the file does not exist then cook doesn't know how.
67 *
68 * If a command in the actions bound to any recipe fail, cook will not
69 * evaluate those actions any further, and will not evaluate the actions
70 * of any recipe for which the target of the failed actions was a
71 * prerequisite.
72 *
73 * Cook will trap recursive looping of targets. If a recursion loop is
74 * detected, then
75 * 1. If the file exists, then it is up to date, or
76 * 2. If the file does not exist then cook doesn't know how.
77 */
78
79 #include <common/ac/stddef.h>
80 #include <common/ac/stdio.h>
81 #include <common/ac/time.h>
82
83 #include <common/error.h>
84 #include <common/error_intl.h>
85 #include <common/mem.h>
86 #include <common/os_path_cat.h>
87 #include <common/star.h>
88 #include <common/symtab.h>
89 #include <common/trace.h>
90 #include <cook/cascade.h>
91 #include <cook/cook.h>
92 #include <cook/desist.h>
93 #include <cook/expr.h>
94 #include <cook/fingerprint.h>
95 #include <cook/fingerprint/value.h>
96 #include <cook/flag.h>
97 #include <cook/graph.h>
98 #include <cook/graph/build.h>
99 #include <cook/graph/file_pair.h>
100 #include <cook/graph/leaf.h>
101 #include <cook/graph/stats.h>
102 #include <cook/graph/walk.h>
103 #include <cook/graph/web.h>
104 #include <cook/id.h>
105 #include <cook/id/variable.h>
106 #include <cook/match/new_by_recip.h>
107 #include <cook/opcode/context.h>
108 #include <cook/option.h>
109 #include <cook/os_interface.h>
110 #include <cook/recipe.h>
111 #include <cook/recipe/list.h>
112 #include <cook/stmt.h>
113
114
115 static recipe_list_ty explicit; /* the explicit recipes */
116 static symtab_ty *explicit_stp; /* the explicit recipes, indexed */
117 static recipe_list_ty implicit; /* the implicit recipes */
118 static symtab_ty *implicit_stp; /* the explicit recipes, indexed */
119 static string_list_ty cook_auto_list;
120 static string_list_ty cook_auto_list_nonleaf;
121
122
123 /*
124 * NAME
125 * cook_search_list
126 *
127 * SYNOPSIS
128 * cook_search_list(string_list_ty *slp);
129 *
130 * DESCRIPTION
131 * The cook_search_list function is used to get the search list
132 * from the "search_list" variable.
133 *
134 * Defaulting and clean-up are done here, also.
135 * If absent, defaults to ".".
136 * If the first element is not "." then it is inserted.
137 *
138 * ARGUMENTS
139 * slp - where to put the result
140 */
141
142 void
cook_search_list(const opcode_context_ty * ocp,string_list_ty * slp)143 cook_search_list(const opcode_context_ty *ocp, string_list_ty *slp)
144 {
145 string_ty *s;
146 id_ty *idp;
147
148 /*
149 * make sure the variable exists
150 */
151 trace(("cook_search_list()\n{\n"));
152 idp = opcode_context_id_search(ocp, id_search_list);
153 if (!idp)
154 {
155 string_list_constructor(slp);
156 s = str_from_c(".");
157 string_list_append(slp, s);
158 str_free(s);
159 opcode_context_id_assign
160 (
161 (opcode_context_ty *)ocp,
162 id_search_list,
163 id_variable_new(slp),
164 0
165 );
166 string_list_destructor(slp);
167 }
168
169 /*
170 * extract its string value
171 */
172 id_variable_query(idp, slp);
173
174 /*
175 * make sure the search list isn't empty
176 * make sure the search list has "." as the first element
177 */
178 if
179 (
180 !slp->nstrings
181 ||
182 slp->string[0]->str_length != 1
183 ||
184 slp->string[0]->str_text[0] != '.'
185 )
186 {
187 s = str_from_c(".");
188 string_list_prepend(slp, s);
189 str_free(s);
190 opcode_context_id_assign
191 (
192 (opcode_context_ty *)ocp,
193 id_search_list,
194 id_variable_new(slp),
195 0
196 );
197 }
198 trace(("}\n"));
199 }
200
201
202 /*
203 * NAME
204 * cook_mtime_oldest
205 *
206 * SYNOPSIS
207 * time_t cook_mtime_oldest(string_ty *path, long *depth_p);
208 *
209 * DESCRIPTION
210 * The cook_mtime_oldest function is used to scan the search path
211 * for a file to determine the last-modified time of the file.
212 *
213 * Look for the copies of the file which are identical to the
214 * shallowest copy; use the oldest time of all. Return the deepest
215 * level found.
216 *
217 * ARGUMENTS
218 * path - file to get the mtime for
219 * depth_p - where to put the depth
220 *
221 * RETURNS
222 * long; -1 on error, 0 if no such file, >0 for time
223 *
224 * CAVEAT
225 * The user must design recipes using the [resolve] function.
226 */
227
228 time_t
cook_mtime_oldest(const opcode_context_ty * ocp,string_ty * path,long * depth_p,long max_fp_depth)229 cook_mtime_oldest(const opcode_context_ty *ocp, string_ty *path,
230 long *depth_p, long max_fp_depth)
231 {
232 time_t result;
233 long bogus;
234
235 trace(("cook_mtime_oldest(path = \"%s\", max = %ld)\n{\n", path->str_text,
236 max_fp_depth));
237 if (!depth_p)
238 depth_p = &bogus;
239 if (path->str_text[0] == '/')
240 {
241 result = os_mtime_oldest(path);
242 *depth_p = 32767;
243 }
244 else
245 {
246 fp_value_ty *prv_fp;
247 string_list_ty sl;
248 size_t j;
249
250 prv_fp = 0;
251 result = 0;
252 cook_search_list(ocp, &sl);
253 *depth_p = sl.nstrings;
254 for (j = 0; j < sl.nstrings; ++j)
255 {
256 fp_value_ty *fp;
257 string_ty *s1;
258 string_ty *s2;
259 time_t t;
260
261 s1 = sl.string[j];
262 s2 = os_path_cat(s1, path);
263
264 /*
265 * This allows the safe use of fp_search below,
266 * since os_mtime_oldest updates the fingerprint
267 * cache for the file.
268 */
269 t = os_mtime_oldest(s2);
270
271 if (!t)
272 {
273 /* File not found */
274 str_free(s2);
275 continue;
276 }
277
278 trace(("mtime(\"%s\") was %ld\n", s2->str_text, (long)t));
279
280 /* File found */
281 if (!prv_fp)
282 {
283 /* Shallowest file found */
284 result = t;
285 *depth_p = j;
286 trace(("is the first\n"));
287 if (option_test(OPTION_FINGERPRINT))
288 {
289 prv_fp = fp_search(s2);
290 if (prv_fp)
291 {
292 /* Look for deeper copies */
293 str_free(s2);
294 continue;
295 }
296 }
297 str_free(s2);
298 break;
299 }
300
301 /* Found a deeper version */
302 assert(option_test(OPTION_FINGERPRINT));
303 if ((long)j >= max_fp_depth)
304 {
305 str_free(s2);
306 break;
307 }
308 fp = fp_search(s2);
309 str_free(s2);
310
311 if
312 (
313 fp
314 &&
315 str_equal
316 (
317 fp->contents_fingerprint,
318 prv_fp->contents_fingerprint
319 )
320 )
321 {
322 /* Deeper version is same as shallow one */
323 if (t < result)
324 result = t;
325 *depth_p = j;
326 }
327 else
328 {
329 trace(("was different\n"));
330 break;
331 }
332 }
333 string_list_destructor(&sl);
334 }
335 trace(("return %ld (%d);\n", (long)result, *depth_p));
336 trace(("}\n"));
337 return result;
338 }
339
340
341 /*
342 * NAME
343 * cook_mtime_newest
344 *
345 * SYNOPSIS
346 * time_t cook_mtime_newest(string_ty *path, long *depth_p);
347 *
348 * DESCRIPTION
349 * The cook_mtime_newest function is used to scan the search path
350 * for a file to determine the last-modified time of the file.
351 *
352 * Look for the copies of the file which are identical to the
353 * shallowest copy; use the newest time of all. Return the
354 * shallowest level found.
355 *
356 * ARGUMENTS
357 * path - file to get the mtime for
358 * depth_p - where to put the depth
359 *
360 * RETURNS
361 * long; -1 on error, 0 if no such file, >0 for time
362 *
363 * CAVEAT
364 * The user must design recipes using the [resolve] function.
365 */
366
367 time_t
cook_mtime_newest(const opcode_context_ty * ocp,string_ty * path,long * depth_p,long max_fp_depth)368 cook_mtime_newest(const opcode_context_ty *ocp, string_ty *path,
369 long *depth_p, long max_fp_depth)
370 {
371 time_t result;
372
373 trace(("cook_mtime_newest(path = \"%s\")\n{\n", path->str_text));
374 if (path->str_text[0] == '/')
375 {
376 result = os_mtime_newest(path);
377 *depth_p = 0;
378 }
379 else
380 {
381 fp_value_ty *prv_fp;
382 string_list_ty sl;
383 size_t j;
384
385 prv_fp = 0;
386 result = 0;
387 cook_search_list(ocp, &sl);
388 *depth_p = sl.nstrings;
389 for (j = 0; j < sl.nstrings; ++j)
390 {
391 fp_value_ty *fp;
392 string_ty *s1;
393 string_ty *s2;
394 time_t t;
395
396 s1 = sl.string[j];
397 s2 = os_path_cat(s1, path);
398
399 /*
400 * This allows the safe use of fp_search below,
401 * since os_mtime_newest updates the fingerprint
402 * cache for the file.
403 */
404 t = os_mtime_newest(s2);
405
406 if (!t)
407 {
408 /* File was not found */
409 str_free(s2);
410 continue;
411 }
412
413 trace(("mtime(\"%s\") was %ld\n", s2->str_text, (long)t));
414
415 /* File found */
416 if (!prv_fp)
417 {
418 /* Shallowest file found */
419 result = t;
420 *depth_p = j;
421 trace(("is the first\n"));
422
423 if (option_test(OPTION_FINGERPRINT))
424 {
425 prv_fp = fp_search(s2);
426 if (prv_fp)
427 {
428 /* Look for deeper copies */
429 str_free(s2);
430 continue;
431 }
432 }
433 str_free(s2);
434 break;
435 }
436
437 /* Found a deeper version */
438 assert(option_test(OPTION_FINGERPRINT));
439 if ((long)j >= max_fp_depth)
440 {
441 str_free(s2);
442 break;
443 }
444 fp = fp_search(s2);
445 str_free(s2);
446
447 if
448 (
449 fp
450 &&
451 str_equal
452 (
453 fp->contents_fingerprint,
454 prv_fp->contents_fingerprint
455 )
456 )
457 {
458 /* Deeper version is same as shallow one */
459 if (t > result)
460 result = t;
461 /* do not alter depth */
462 }
463 else
464 {
465 trace(("was different\n"));
466 break;
467 }
468 }
469 string_list_destructor(&sl);
470 }
471 trace(("return %ld (%d);\n", (long)result, *depth_p));
472 trace(("}\n"));
473 return result;
474 }
475
476
477 /*
478 * NAME
479 * cook_mtime_resolve
480 *
481 * SYNOPSIS
482 * int cook_mtime_resolve(string_list_ty *output, string_list_ty *input);
483 *
484 * DESCRIPTION
485 * The cook_mtime_resolve function is used to
486 * resolve the name used for a file in the search list.
487 *
488 * It implements the "resolve" built-in function.
489 *
490 * ARGUMENTS
491 * input - the function arguments (0 is the func name)
492 * output - where to put the results
493 *
494 * RETURNS
495 * int; 0 on success, -1 on error
496 *
497 * CAVEAT
498 * The user must design rules using the [resolve] function.
499 */
500
501 int
cook_mtime_resolve(const opcode_context_ty * ocp,string_list_ty * output,const string_list_ty * input,int start)502 cook_mtime_resolve(const opcode_context_ty *ocp, string_list_ty *output,
503 const string_list_ty *input, int start)
504 {
505 int result;
506 size_t j;
507
508 trace(("cook_mtime_resolve(input = %08lX, output = %08lX)\n{\n", input,
509 output));
510 result = 0;
511 for (j = start; j < input->nstrings; ++j)
512 {
513 string_ty *arg;
514
515 arg = input->string[j];
516 arg = cook_mtime_resolve1(ocp, arg);
517 if (!arg)
518 {
519 result = -1;
520 break;
521 }
522 string_list_append(output, arg);
523 str_free(arg);
524 }
525 trace(("return %d;\n", result));
526 trace(("}\n"));
527 return result;
528 }
529
530
531 string_ty *
cook_mtime_resolve1(const opcode_context_ty * ocp,string_ty * arg)532 cook_mtime_resolve1(const opcode_context_ty *ocp, string_ty *arg)
533 {
534 string_list_ty sl;
535 int done;
536 size_t k;
537
538 if (arg->str_text[0] == '/')
539 return str_copy(arg);
540
541 cook_search_list(ocp, &sl);
542 done = 0;
543 for (k = 0; k < sl.nstrings; ++k)
544 {
545 string_ty *s1;
546 string_ty *s2;
547 time_t t;
548
549 s1 = sl.string[k];
550 s2 = os_path_cat(s1, arg);
551 t = os_mtime_newest(s2);
552 if (t < 0)
553 {
554 str_free(s2);
555 string_list_destructor(&sl);
556 return 0;
557 }
558 if (t > 0)
559 {
560 string_list_destructor(&sl);
561 return s2;
562 }
563 str_free(s2);
564 }
565 string_list_destructor(&sl);
566 return str_copy(arg);
567 }
568
569
570 /*
571 * NAME
572 * cook - construct files
573 *
574 * SYNOPSIS
575 * int cook(string_list_ty *targets);
576 *
577 * DESCRIPTION
578 * The cook function is used to cook the given set of targets.
579 *
580 * RETURNS
581 * The cook function returns 0 if all of the targets cooked sucessfully,
582 * or 1 if there was any problem (exit statii).
583 *
584 * CAVEAT
585 * This function must be called after evrything has been initialized,
586 * and the cookbook read in.
587 */
588
589 int
cook(string_list_ty * wlp)590 cook(string_list_ty *wlp)
591 {
592 int retval;
593 graph_ty *gp;
594 graph_build_status_ty gb_status;
595 graph_walk_status_ty gw_status;
596
597 /*
598 * set interrupts to catch
599 *
600 * Note that tee(1) [see listing.c] must ignore them
601 * for the generated messages to appear in the log file.
602 */
603 trace(("cook(wlp = %08lX)\n{\n", wlp));
604 desist_enable();
605
606 /*
607 * Build the dependency graph.
608 */
609 retval = 0;
610 gp = graph_new();
611 if
612 (
613 cook_auto_list_nonleaf.nstrings > 0
614 &&
615 cascade_used()
616 &&
617 option_test(OPTION_CASCADE)
618 &&
619 !option_test(OPTION_SILENT)
620 &&
621 option_test(OPTION_INCLUDE_COOKED_WARNING)
622 )
623 {
624 gp->file_pair = graph_file_pair_new((string_list_ty *) 0);
625 graph_file_pair_foreign_derived(gp->file_pair, &cook_auto_list_nonleaf);
626 }
627 gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 1);
628 if (option_test(OPTION_REASON))
629 graph_print_statistics(gp);
630 switch (gb_status)
631 {
632 case graph_build_status_error:
633 retval = 1;
634 break;
635
636 case graph_build_status_backtrack:
637 /* assert(0); */
638 retval = 1;
639 break;
640
641 case graph_build_status_success:
642 break;
643 }
644
645 /*
646 * Walk the dependency graph.
647 */
648 if (retval == 0)
649 {
650 gw_status = graph_walk(gp);
651 switch (gw_status)
652 {
653 case graph_walk_status_uptodate:
654 case graph_walk_status_uptodate_done:
655 case graph_walk_status_done:
656 break;
657
658 case graph_walk_status_done_stop:
659 case graph_walk_status_wait:
660 assert(0);
661 /* fall through... */
662
663 case graph_walk_status_error:
664 retval = 1;
665 break;
666 }
667 }
668
669 /*
670 * Release any resources held by the graph.
671 */
672 graph_delete(gp);
673
674 /*
675 * Return the result to the caller.
676 */
677 trace(("return %d;\n", retval));
678 trace(("}\n"));
679 return retval;
680 }
681
682
683 /*
684 * NAME
685 * cook_pairs
686 *
687 * SYNOPSIS
688 * int cook_pairs(string_list_ty *);
689 *
690 * DESCRIPTION
691 * The cook_pairs function is used to print generate pair-wise file
692 * dependencies for the ancestors of the given targets.
693 *
694 * RETURNS
695 * int; 0 on success, 1 on failure (exit statii)
696 */
697
698 int
cook_pairs(string_list_ty * wlp)699 cook_pairs(string_list_ty *wlp)
700 {
701 int retval;
702 graph_ty *gp;
703 graph_build_status_ty gb_status;
704 graph_walk_status_ty gw_status;
705
706 /*
707 * set interrupts to catch
708 *
709 * Note that tee(1) [see listing.c] must ignore them
710 * for the generated messages to appear in the log file.
711 */
712 trace(("cook(wlp = %08lX)\n{\n", wlp));
713 desist_enable();
714
715 /*
716 * Build the dependency graph.
717 */
718 retval = 0;
719 gp = graph_new();
720 gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 0);
721 if (option_test(OPTION_REASON))
722 graph_print_statistics(gp);
723 switch (gb_status)
724 {
725 case graph_build_status_error:
726 retval = 1;
727 break;
728
729 case graph_build_status_backtrack:
730 /* assert(0); */
731 retval = 1;
732 break;
733
734 case graph_build_status_success:
735 break;
736 }
737
738 /*
739 * Walk the dependency graph.
740 */
741 if (retval == 0)
742 {
743 gw_status = graph_walk_pairs(gp);
744 switch (gw_status)
745 {
746 case graph_walk_status_uptodate:
747 case graph_walk_status_uptodate_done:
748 case graph_walk_status_done:
749 break;
750
751 case graph_walk_status_done_stop:
752 case graph_walk_status_wait:
753 assert(0);
754 /* fall through... */
755
756 case graph_walk_status_error:
757 retval = 1;
758 break;
759 }
760 }
761
762 /*
763 * Release any resources held by the graph.
764 */
765 graph_delete(gp);
766
767 /*
768 * Return the result to the caller.
769 */
770 trace(("return %d;\n", retval));
771 trace(("}\n"));
772 return retval;
773 }
774
775
776 /*
777 * NAME
778 * cook_script
779 *
780 * SYNOPSIS
781 * void cook_script(string_list_ty *);
782 *
783 * DESCRIPTION
784 * The cook_script function is used to print a shell script to
785 * build the the given targets. It's only an approximation of the
786 * full cook semantics.
787 *
788 * RETURNS
789 * int; 0 on success, 1 on failure (exit statii)
790 */
791
792 int
cook_script(string_list_ty * wlp)793 cook_script(string_list_ty *wlp)
794 {
795 int retval;
796 graph_ty *gp;
797 graph_build_status_ty gb_status;
798 graph_walk_status_ty gw_status;
799
800 /*
801 * set interrupts to catch
802 *
803 * Note that tee(1) [see listing.c] must ignore them
804 * for the generated messages to appear in the log file.
805 */
806 trace(("cook(wlp = %08lX)\n{\n", wlp));
807 desist_enable();
808
809 /*
810 * Build the dependency graph.
811 */
812 retval = 0;
813 gp = graph_new();
814 gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 0);
815 if (option_test(OPTION_REASON))
816 graph_print_statistics(gp);
817 switch (gb_status)
818 {
819 case graph_build_status_error:
820 retval = 1;
821 break;
822
823 case graph_build_status_backtrack:
824 /* assert(0); */
825 retval = 1;
826 break;
827
828 case graph_build_status_success:
829 break;
830 }
831
832 /*
833 * Walk the dependency graph.
834 */
835 if (retval == 0)
836 {
837 gw_status = graph_walk_script(gp);
838 switch (gw_status)
839 {
840 case graph_walk_status_uptodate:
841 case graph_walk_status_uptodate_done:
842 case graph_walk_status_done:
843 break;
844
845 case graph_walk_status_done_stop:
846 case graph_walk_status_wait:
847 assert(0);
848 /* fall through... */
849
850 case graph_walk_status_error:
851 retval = 1;
852 break;
853 }
854 }
855
856 /*
857 * Release any resources held by the graph.
858 */
859 graph_delete(gp);
860
861 /*
862 * Return the result to the caller.
863 */
864 trace(("return %d;\n", retval));
865 trace(("}\n"));
866 return retval;
867 }
868
869
870 /*
871 * NAME
872 * cook_web
873 *
874 * SYNOPSIS
875 * void cook_web(string_list_ty *);
876 *
877 * DESCRIPTION
878 * The cook_web function is used to print a shell web to
879 * build the the given targets. It's only an approximation of the
880 * full cook semantics.
881 *
882 * RETURNS
883 * int; 0 on success, 1 on failure (exit statii)
884 */
885
886 int
cook_web(string_list_ty * wlp)887 cook_web(string_list_ty *wlp)
888 {
889 int retval;
890 graph_ty *gp;
891 graph_build_status_ty gb_status;
892
893 /*
894 * set interrupts to catch
895 *
896 * Note that tee(1) [see listing.c] must ignore them
897 * for the generated messages to appear in the log file.
898 */
899 trace(("cook(wlp = %08lX)\n{\n", wlp));
900 desist_enable();
901
902 /*
903 * Build the dependency graph.
904 */
905 retval = 0;
906 gp = graph_new();
907 gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 0);
908 if (option_test(OPTION_REASON))
909 graph_print_statistics(gp);
910 switch (gb_status)
911 {
912 case graph_build_status_error:
913 retval = 1;
914 break;
915
916 case graph_build_status_backtrack:
917 /* assert(0); */
918 retval = 1;
919 break;
920
921 case graph_build_status_success:
922 break;
923 }
924
925 /*
926 * Walk the dependency graph.
927 */
928 if (retval == 0)
929 graph_walk_web(gp);
930
931 /*
932 * Release any resources held by the graph.
933 */
934 graph_delete(gp);
935
936 /*
937 * Return the result to the caller.
938 */
939 trace(("return %d;\n", retval));
940 trace(("}\n"));
941 return retval;
942 }
943
944
945 /*
946 * NAME
947 * cook_auto
948 *
949 * SYNOPSIS
950 * void cook_auto(string_ty *);
951 *
952 * DESCRIPTION
953 * The cook_auto function is used to that this file needs to be
954 * automaticaly cooked. This is done for files inccluded using the
955 * #include-cooked mechanism.
956 */
957
958 void
cook_auto(string_list_ty * wlp)959 cook_auto(string_list_ty *wlp)
960 {
961 string_list_append_list_unique(&cook_auto_list, wlp);
962 }
963
964
965 /*
966 * NAME
967 * cook_auto_required
968 *
969 * SYNOPSIS
970 * int cook_auto_required(void);
971 *
972 * DESCRIPTION
973 * The cook_auto_required function is used to automaticly re-build
974 * any files included by the #include-cooked mechanism which may
975 * have been out of date.
976 *
977 * RETURNS
978 * int; -1 on error
979 * 0 if everything was up-to-date, the cookbook does not
980 * need to be read in again
981 * 1 if one or more include files were re-build, and the
982 * cookbook must be read again.
983 */
984
985 int
cook_auto_required(void)986 cook_auto_required(void)
987 {
988 int retval;
989 graph_ty *gp;
990 graph_build_status_ty gb_status;
991 graph_walk_status_ty gw_status;
992 size_t j;
993
994 /*
995 * This may have been explicitly forbidden on the command line.
996 */
997 if (!option_test(OPTION_INCLUDE_COOKED))
998 return 0;
999
1000 retval = 0;
1001
1002 option_set(OPTION_ACTION, OPTION_LEVEL_AUTO, 1);
1003 option_set(OPTION_TOUCH, OPTION_LEVEL_AUTO, 0);
1004 option_set(OPTION_REASON, OPTION_LEVEL_COOKBOOK, 0);
1005
1006 /*
1007 * Build the dependency graph.
1008 */
1009 gp = graph_new();
1010 if
1011 (
1012 !option_test(OPTION_SILENT)
1013 &&
1014 option_test(OPTION_INCLUDE_COOKED_WARNING)
1015 )
1016 gp->file_pair = graph_file_pair_new(&cook_auto_list);
1017 gb_status =
1018 graph_build_list
1019 (
1020 gp,
1021 &cook_auto_list,
1022 graph_build_preference_error,
1023 0 /* not primary, no up-to-date commentary */
1024 );
1025 if (option_test(OPTION_REASON))
1026 graph_print_statistics(gp);
1027 switch (gb_status)
1028 {
1029 case graph_build_status_error:
1030 retval = -1;
1031 break;
1032
1033 case graph_build_status_backtrack:
1034 /* assert(0); */
1035 retval = -1;
1036 break;
1037
1038 case graph_build_status_success:
1039 break;
1040 }
1041
1042 /*
1043 * Build a list of non-leaf cook-auto files.
1044 */
1045 string_list_destructor(&cook_auto_list_nonleaf);
1046 for (j = 0; j < cook_auto_list.nstrings; ++j)
1047 {
1048 string_ty *fn;
1049
1050 fn = cook_auto_list.string[j];
1051 if (!graph_file_leaf_p(gp, fn))
1052 string_list_append(&cook_auto_list_nonleaf, fn);
1053 }
1054
1055 /*
1056 * Walk the dependency graph.
1057 */
1058 if (retval == 0)
1059 {
1060 gw_status = graph_walk(gp);
1061 switch (gw_status)
1062 {
1063 case graph_walk_status_uptodate:
1064 case graph_walk_status_uptodate_done:
1065 break;
1066
1067 case graph_walk_status_done:
1068 retval = 1;
1069 break;
1070
1071 case graph_walk_status_done_stop:
1072 case graph_walk_status_wait:
1073 assert(0);
1074 /* fall through... */
1075
1076 case graph_walk_status_error:
1077 retval = -1;
1078 break;
1079 }
1080 }
1081
1082 /*
1083 * Release any resources held by the graph.
1084 */
1085 graph_delete(gp);
1086
1087 option_undo(OPTION_REASON, OPTION_LEVEL_COOKBOOK);
1088 option_undo(OPTION_ACTION, OPTION_LEVEL_AUTO);
1089 option_undo(OPTION_TOUCH, OPTION_LEVEL_AUTO);
1090
1091 return retval;
1092 }
1093
1094
1095 /*
1096 * NAME
1097 * cook_reset
1098 *
1099 * SYNOPSIS
1100 * void cook_reset(void);
1101 *
1102 * DESCRIPTION
1103 * The cook_reset function is used to reset the recipe lists in
1104 * preparation for re-reading a cookbook. Usually the result of a
1105 * #include-cooked file being re-build.
1106 */
1107
1108 void
cook_reset(void)1109 cook_reset(void)
1110 {
1111 leaf_reset();
1112 string_list_destructor(&cook_auto_list);
1113 /* Don't nuke cook_auto_list_nonleaf, we need it for later */
1114 cook_implicit_nth_by_name(0, 0);
1115 if (explicit_stp)
1116 {
1117 symtab_free(explicit_stp);
1118 explicit_stp = 0;
1119 }
1120 if (implicit_stp)
1121 {
1122 symtab_free(implicit_stp);
1123 implicit_stp = 0;
1124 }
1125 recipe_list_destructor(&explicit);
1126 recipe_list_destructor(&implicit);
1127 cascade_reset();
1128 }
1129
1130
1131 /*
1132 * NAME
1133 * cook_find_default
1134 *
1135 * SYNOPSIS
1136 * void cook_find_default(string_list_ty *);
1137 *
1138 * DESCRIPTION
1139 * The cook_find_default function is used to find the default
1140 * target(s). The explicit recipes are examined - the targets of
1141 * the first one (that doesn't have nodefault set) are the default
1142 * targets.
1143 */
1144
1145 void
cook_find_default(string_list_ty * wlp)1146 cook_find_default(string_list_ty *wlp)
1147 {
1148 size_t j;
1149 recipe_ty *rp;
1150
1151 /*
1152 * use the forst one
1153 * explicitly flagged default
1154 */
1155 for (j = 0; j < explicit.nrecipes; ++j)
1156 {
1157 rp = explicit.recipe[j];
1158 if (flag_query(rp->flags, RF_DEFAULT))
1159 {
1160 string_list_copy_constructor(wlp, rp->target);
1161 return;
1162 }
1163 }
1164
1165 /*
1166 * use the first one
1167 * not flagged nodefault
1168 */
1169 for (j = 0; j < explicit.nrecipes; ++j)
1170 {
1171 rp = explicit.recipe[j];
1172 if (!flag_query(rp->flags, RF_DEFAULT_OFF))
1173 {
1174 string_list_copy_constructor(wlp, rp->target);
1175 return;
1176 }
1177 }
1178
1179 /*
1180 * fatal error otherwise
1181 */
1182 fatal_intl(0, i18n("no default target"));
1183 }
1184
1185
1186 static void
explicit_reap(void * p)1187 explicit_reap(void *p)
1188 {
1189 recipe_list_ty *rlp;
1190
1191 rlp = p;
1192 recipe_list_delete(rlp);
1193 }
1194
1195
1196 static recipe_list_ty *
cook_explicit_find(string_ty * filename)1197 cook_explicit_find(string_ty *filename)
1198 {
1199 recipe_list_ty *rlp;
1200
1201 if (!explicit_stp)
1202 {
1203 explicit_stp = symtab_alloc(200);
1204 explicit_stp->reap = explicit_reap;
1205 }
1206 rlp = symtab_query(explicit_stp, filename);
1207 if (!rlp)
1208 {
1209 rlp = recipe_list_new();
1210 symtab_assign(explicit_stp, filename, rlp);
1211 }
1212 return rlp;
1213 }
1214
1215
1216 static recipe_list_ty *
cook_implicit_find(string_ty * filename)1217 cook_implicit_find(string_ty *filename)
1218 {
1219 recipe_list_ty *rlp;
1220
1221 if (!implicit_stp)
1222 {
1223 implicit_stp = symtab_alloc(200);
1224 implicit_stp->reap = explicit_reap;
1225 }
1226 rlp = symtab_query(implicit_stp, filename);
1227 if (!rlp)
1228 {
1229 rlp = recipe_list_new();
1230 symtab_assign(implicit_stp, filename, rlp);
1231 }
1232 return rlp;
1233 }
1234
1235
1236 /*
1237 * NAME
1238 * cook_explicit_append
1239 *
1240 * SYNOPSIS
1241 * void cook_explicit_append(recipe_ty *);
1242 *
1243 * DESCRIPTION
1244 * The cook_explicit_append function is used to append a recipe to
1245 * the explicit recipe list. Used by the cookbook evaluation
1246 * functions.
1247 */
1248
1249 void
cook_explicit_append(recipe_ty * rp)1250 cook_explicit_append(recipe_ty *rp)
1251 {
1252 size_t j;
1253
1254 trace(("cook_explicit_append(rp = %08lX)\n{\n", (long)rp));
1255 recipe_list_append(&explicit, rp);
1256 for (j = 0; j < rp->target->nstrings; ++j)
1257 {
1258 string_ty *filename;
1259 recipe_list_ty *rlp;
1260
1261 filename = rp->target->string[j];
1262 rlp = cook_explicit_find(filename);
1263 recipe_list_append(rlp, rp);
1264 }
1265 trace(("}\n"));
1266 }
1267
1268
1269 /*
1270 * NAME
1271 * cook_implicit_append
1272 *
1273 * SYNOPSIS
1274 * void cook_implicit_append(recipe_ty *);
1275 *
1276 * DESCRIPTION
1277 * The cook_implicit_append function is used to append a recipe to
1278 * the explicit recipe list. Used by the cookbook evaluation
1279 * functions.
1280 */
1281
1282 void
cook_implicit_append(recipe_ty * rp)1283 cook_implicit_append( recipe_ty *rp)
1284 {
1285 string_ty *base;
1286 string_list_ty base_list;
1287 recipe_list_ty *rlp;
1288 size_t j;
1289 match_ty *mp;
1290
1291 /*
1292 * Create a suitable matching object. We need to set the recipe
1293 * flags, to know which matching flavour.
1294 */
1295 mp = match_new_by_recipe(rp);
1296
1297 string_list_constructor(&base_list);
1298 for (j = 0; j < rp->target->nstrings; ++j)
1299 {
1300 base = os_entryname(rp->target->string[j]);
1301 if (match_usage_mask(mp, base, &rp->pos) != 0)
1302 {
1303 match_delete(mp);
1304 str_free(base);
1305 string_list_destructor(&base_list);
1306 recipe_list_append(&implicit, rp);
1307 return;
1308 }
1309 string_list_append_unique(&base_list, base);
1310 str_free(base);
1311 }
1312 match_delete(mp);
1313 for (j = 0; j < base_list.nstrings; ++j)
1314 {
1315 base = base_list.string[j];
1316 rlp = cook_implicit_find(base);
1317 recipe_list_append(rlp, rp);
1318 }
1319 string_list_destructor(&base_list);
1320 }
1321
1322
1323 /*
1324 * NAME
1325 * cook_explicit_nth
1326 *
1327 * SYNOPSIS
1328 * recipe_ty *cook_explicit_nth(long);
1329 *
1330 * DESCRIPTION
1331 * The cook_explicit_nth function is used to get the n'th recipe
1332 * from the explicit recipe list.
1333 *
1334 * RETURNS
1335 * recipe_ty *; the recipe you asked for, or NULL if you went off
1336 * the end.
1337 */
1338
1339 const recipe_list_ty *
cook_explicit_by_name(string_ty * filename)1340 cook_explicit_by_name(string_ty *filename)
1341 {
1342 return cook_explicit_find(filename);
1343 }
1344
1345
1346 /*
1347 * NAME
1348 * cook_implicit_nth
1349 *
1350 * SYNOPSIS
1351 * recipe_ty *cook_implicit_nth(long);
1352 *
1353 * DESCRIPTION
1354 * The cook_implicit_nth function is used to get the n'th recipe
1355 * from the implicit recipe list.
1356 *
1357 * RETURNS
1358 * recipe_ty *; the recipe you asked for, or NULL if you went off
1359 * the end.
1360 */
1361
1362 recipe_ty *
cook_implicit_nth(long n)1363 cook_implicit_nth(long n)
1364 {
1365 if (n < 0 || (size_t) n >= implicit.nrecipes)
1366 return 0;
1367 return implicit.recipe[n];
1368 }
1369
1370
1371 /*
1372 * NAME
1373 * cook_implicit_nth_by_name
1374 *
1375 * SYNOPSIS
1376 * recipe_ty *cook_implicit_nth_by_name(long, string_ty *);
1377 *
1378 * DESCRIPTION
1379 * The cook_implicit_nth_by_name function is used to get the n'th
1380 * recipe from the implicit recipe list; indexed by the last filename
1381 * element if non-pattern.
1382 *
1383 * RETURNS
1384 * recipe_ty *; the recipe you asked for, or NULL if you went off
1385 * the end.
1386 */
1387
1388 recipe_ty *
cook_implicit_nth_by_name(long n,string_ty * name)1389 cook_implicit_nth_by_name(long n, string_ty *name)
1390 {
1391 static string_ty *prev;
1392 static recipe_list_ty *rlp;
1393
1394 if (!name)
1395 {
1396 /* used to clear the state between passes */
1397 if (prev)
1398 str_free(prev);
1399 prev = 0;
1400 rlp = 0;
1401 return 0;
1402 }
1403 if (!prev || !str_equal(prev, name))
1404 {
1405 if (prev)
1406 str_free(prev);
1407 prev = str_copy(name);
1408 rlp = cook_implicit_find(name);
1409 }
1410 if (n < 0 || (size_t) n >= rlp->nrecipes)
1411 return 0;
1412 return rlp->recipe[n];
1413 }
1414