6 #include "tm_core_cmds.h"
8 /* The goal target of the current execution */
11 /* All the rules defined by the TMakefile */
12 tm_rule_list
*tm_rules
= NULL
;
15 /* Create a new explicit rule.
16 * Makes copies of all the arguments to store in the rule.
18 tm_rule
*new_rule(const char *target
, target_list
*deps
, const char *recipe
)
20 tm_rule
*rule
= malloc(sizeof(tm_rule
));
21 rule
->target
= malloc(strlen(target
) + 1);
23 rule
->recipe
= malloc(strlen(recipe
) + 1);
25 strcpy(rule
->target
, target
);
26 rule
->deps
= target_list_copy(deps
);
28 strcpy(rule
->recipe
, recipe
);
31 rule
->type
= TM_EXPLICIT
;
32 rule
->mark
= TM_UNMARKED
;
33 rule
->always_oodate
= 0;
38 /* Create a new filename rule.
39 * Makes a copy of its argument.
41 tm_rule
*new_filename(const char *target
)
43 tm_rule
*rule
= new_rule(target
, NULL
, NULL
);
45 rule
->type
= TM_FILENAME
;
50 /* Make a copy of a rule.
52 tm_rule
*rule_copy(tm_rule
*rule
)
60 copy
= malloc(sizeof(tm_rule
));
63 copy
->target
= malloc(strlen(rule
->target
) + 1);
64 strcpy(copy
->target
, rule
->target
);
69 copy
->deps
= target_list_copy(rule
->deps
);
74 copy
->recipe
= malloc(strlen(rule
->recipe
) + 1);
75 strcpy(copy
->recipe
, rule
->recipe
);
79 copy
->type
= rule
->type
;
80 copy
->mark
= rule
->mark
;
81 copy
->always_oodate
= rule
->always_oodate
;
87 void free_rule(tm_rule
*rule
)
91 free_target_list(rule
->deps
);
98 /* Takes the name of a target and another target list and
99 * returns a target list with the target at the head.
100 * Target lists are NULL-terminated.
102 * e.g., to make a one-element list:
103 * target_cons("foo", NULL);
105 target_list
*target_cons(const char *name
, target_list
*next
)
107 target_list
*targets
= malloc(sizeof(target_list
));
109 targets
->name
= malloc(strlen(name
) + 1);
111 strcpy(targets
->name
, name
);
112 targets
->next
= next
;
117 /* Frees a target list.
119 void free_target_list(target_list
*targets
)
122 target_list
*node
= targets
->next
;
129 /* Copy a target list.
131 target_list
*target_list_copy(target_list
*targets
)
136 if (!targets
) return NULL
;
138 rev
= target_list_reverse(targets
);
139 copy
= target_list_reverse(rev
);
140 free_target_list(rev
);
145 /* Take a rule and another rule list and return
146 * a rule list with the rule at the head.
147 * Rule lists are NULL-terminated.
149 tm_rule_list
*rule_cons(tm_rule
*rule
, tm_rule_list
*next
)
151 tm_rule_list
*rules
= malloc(sizeof(tm_rule_list
));
153 rules
->rule
= rule_copy(rule
);
161 void free_rule_list(tm_rule_list
*rules
)
164 tm_rule_list
*node
= rules
->next
;
166 free_rule(rules
->rule
);
173 /* Return 1 if the target is in targets, else return 0.
175 int target_exists(const char *target
, target_list
*targets
)
177 for (; targets
; targets
= targets
->next
) {
178 if (strcmp(targets
->name
, target
) == 0) {
187 /* Take a target and a rule list and return the rule associated
188 * with that target if it exists, otherwise return NULL.
190 tm_rule
*find_rule(const char *target
, tm_rule_list
*rules
)
192 tm_rule_list
*node
= rules
;
193 tm_rule
*rule
= NULL
;
196 if (strcmp(node
->rule
->target
, target
) == 0) {
208 /* Take a list of targets and a pointer to a rule list.
209 * If no rule for a target is found in the rule list, add
210 * a new TM_FILENAME rule to the rule list.
212 static void find_files_from_deps(target_list
*targets
, tm_rule_list
**rules
)
214 for (; targets
; targets
= targets
->next
) {
215 tm_rule
*rule
= find_rule(targets
->name
, *rules
);
218 rule
= new_filename(targets
->name
);
219 *rules
= rule_cons(rule
, *rules
);
225 /* Scour through all the dependencies in a rule list and allocate new
226 * filename rules for things deemed to be files.
227 * Updates the rule list with the new rules.
229 void find_files(tm_rule_list
**rules
)
233 for (node
= *rules
; node
; node
= node
->next
) {
234 find_files_from_deps(node
->rule
->deps
, rules
);
239 /* Take a list of targets and a list of rules and return
240 * a list of the rules associated with those targets.
242 tm_rule_list
*find_rules(target_list
*targets
, tm_rule_list
*rules
)
244 tm_rule_list
*mapped
= NULL
;
245 target_list
*node
= targets
;
248 tm_rule
*rule
= find_rule(node
->name
, rules
);
250 mapped
= rule_cons(rule
, mapped
);
258 /* Return a new rule list representing the reverse of another rule list
260 tm_rule_list
*rule_list_reverse(tm_rule_list
*rules
)
262 tm_rule_list
*rev
= NULL
;
263 tm_rule_list
*node
= rules
;
265 for (; node
; node
= node
->next
) {
266 rev
= rule_cons(node
->rule
, rev
);
272 /* Return a new target list representing the reverse of another target list
274 target_list
*target_list_reverse(target_list
*targets
)
276 target_list
*rev
= NULL
;
277 target_list
*node
= targets
;
279 for (; node
; node
= node
->next
) {
280 rev
= target_cons(node
->name
, rev
);
287 /* Tarjan's algorithm for topological sorting */
288 static int topsort_visit(tm_rule
*rule
, tm_rule_list
*rules
, tm_rule_list
**sorted
)
290 if (rule
->mark
== TM_TEMPORARY
) {
291 fprintf(stderr
, "ERROR: Cycle detected in dependency graph\n");
295 if (rule
->mark
== TM_UNMARKED
) {
296 tm_rule_list
*deps
= NULL
;
297 tm_rule_list
*node
= NULL
;
299 rule
->mark
= TM_TEMPORARY
;
300 deps
= find_rules(rule
->deps
, rules
);
302 for (node
= deps
; node
; node
= node
->next
) {
306 n
= topsort_visit(node
->rule
, rules
, sorted
);
309 free_rule_list(deps
);
313 free_rule_list(deps
);
315 rule
->mark
= TM_PERMANENT
;
316 *sorted
= rule_cons(rule
, *sorted
);
322 /* Perform a topological sort of the dependency graph to reach target.
323 * Also checks for cycles in the dependency graph.
325 tm_rule_list
*topsort(const char *target
, tm_rule_list
*rules
)
327 tm_rule_list
*sorted
= NULL
;
328 tm_rule_list
*rev
= NULL
;
329 tm_rule
*rule
= find_rule(target
, rules
);
334 if (topsort_visit(rule
, rules
, &sorted
) < 0) {
338 rev
= rule_list_reverse(sorted
);
339 free_rule_list(sorted
);
344 /* Return a string representation of a target list.
345 * Consists of all the targets in the list separated by spaces.
347 char *target_list_to_string(target_list
*targets
)
349 target_list
*node
= targets
;
350 int len
= 1; /* for NUL terminator */
360 for (node
= targets
; node
; node
= node
->next
) {
361 len
+= strlen(node
->name
) + 1;
366 for (node
= targets
; node
; node
= node
->next
) {
367 p
+= sprintf(p
, "%s ", node
->name
);
369 *(p
-1) = '\0'; /* get rid of the trailing space */
375 /* Print a rule list to stdout.
377 void print_rule_list(tm_rule_list
*rules
)
382 for (node
= rules
; node
; node
= node
->next
) {
385 printf("\t%s: ", node
->rule
->target
);
386 for (deps
= node
->rule
->deps
; deps
; deps
= deps
->next
) {
387 printf("%s ", deps
->name
);
389 switch (node
->rule
->type
) {
391 printf("(explicit)");
394 printf("(implicit)");
397 printf("(filename)");
403 if (node
->rule
->recipe
) {
406 if (node
->rule
->always_oodate
) {