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