Fix formatting
[tmk.git] / tm_target.c
blobe20064f9150019db8da7c0adfa86be52b5bbfcf4
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include "tm_target.h"
6 #include "tm_core_cmds.h"
8 /* The goal target of the current execution */
9 char *tm_goal = NULL;
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);
22 if (recipe)
23 rule->recipe = malloc(strlen(recipe) + 1);
25 strcpy(rule->target, target);
26 rule->deps = target_list_copy(deps);
27 if (recipe)
28 strcpy(rule->recipe, recipe);
29 else
30 rule->recipe = NULL;
31 rule->type = TM_EXPLICIT;
32 rule->mark = TM_UNMARKED;
33 rule->always_oodate = 0;
35 return rule;
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;
47 return rule;
50 /* Make a copy of a rule.
52 tm_rule *rule_copy(tm_rule *rule)
54 tm_rule *copy;
56 if (!rule) {
57 return NULL;
60 copy = malloc(sizeof(tm_rule));
62 if (rule->target) {
63 copy->target = malloc(strlen(rule->target) + 1);
64 strcpy(copy->target, rule->target);
65 } else {
66 copy->target = NULL;
68 if (rule->deps) {
69 copy->deps = target_list_copy(rule->deps);
70 } else {
71 copy->deps = NULL;
73 if (rule->recipe) {
74 copy->recipe = malloc(strlen(rule->recipe) + 1);
75 strcpy(copy->recipe, rule->recipe);
76 } else {
77 copy->recipe = NULL;
79 copy->type = rule->type;
80 copy->mark = rule->mark;
81 copy->always_oodate = rule->always_oodate;
83 return copy;
86 /* Free a rule */
87 void free_rule(tm_rule *rule)
89 if (!rule) return;
90 free(rule->target);
91 free_target_list(rule->deps);
92 free(rule->recipe);
93 free(rule);
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;
114 return targets;
117 /* Frees a target list.
119 void free_target_list(target_list *targets)
121 while (targets) {
122 target_list *node = targets->next;
123 free(targets->name);
124 free(targets);
125 targets = node;
129 /* Copy a target list.
131 target_list *target_list_copy(target_list *targets)
133 target_list *rev;
134 target_list *copy;
136 if (!targets) return NULL;
138 rev = target_list_reverse(targets);
139 copy = target_list_reverse(rev);
140 free_target_list(rev);
141 return copy;
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);
154 rules->next = next;
156 return rules;
159 /* Frees a rule list
161 void free_rule_list(tm_rule_list *rules)
163 while (rules) {
164 tm_rule_list *node = rules->next;
165 if (rules->rule)
166 free_rule(rules->rule);
167 free(rules);
168 rules = node;
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) {
179 return 1;
183 return 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;
195 while (node) {
196 if (strcmp(node->rule->target, target) == 0) {
197 rule = node->rule;
198 break;
201 node = node->next;
204 return rule;
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);
217 if (!rule) {
218 rule = new_filename(targets->name);
219 *rules = rule_cons(rule, *rules);
220 free_rule(rule);
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)
231 tm_rule_list *node;
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;
247 while (node) {
248 tm_rule *rule = find_rule(node->name, rules);
249 if (rule)
250 mapped = rule_cons(rule, mapped);
251 node = node->next;
254 return 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);
269 return 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);
283 return 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");
292 return -1;
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) {
303 int n = 0;
305 if (node->rule)
306 n = topsort_visit(node->rule, rules, sorted);
308 if (n < 0) {
309 free_rule_list(deps);
310 return n;
313 free_rule_list(deps);
315 rule->mark = TM_PERMANENT;
316 *sorted = rule_cons(rule, *sorted);
319 return 0;
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);
331 if (rule == NULL)
332 return NULL;
334 if (topsort_visit(rule, rules, &sorted) < 0) {
335 return NULL;
338 rev = rule_list_reverse(sorted);
339 free_rule_list(sorted);
340 return rev;
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 */
351 char *str = NULL;
352 char *p = NULL;
354 if (!targets) {
355 str = malloc(1);
356 str[0] = '\0';
357 return str;
360 for (node = targets; node; node = node->next) {
361 len += strlen(node->name) + 1;
364 str = malloc(len);
365 p = str;
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 */
371 return str;
375 /* Print a rule list to stdout.
377 void print_rule_list(tm_rule_list *rules)
379 tm_rule_list *node;
381 printf("{\n");
382 for (node = rules; node; node = node->next) {
383 target_list *deps;
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) {
390 case TM_EXPLICIT:
391 printf("(explicit)");
392 break;
393 case TM_IMPLICIT:
394 printf("(implicit)");
395 break;
396 case TM_FILENAME:
397 printf("(filename)");
398 break;
399 default:
400 printf("(UNKNOWN)");
401 break;
403 if (node->rule->recipe) {
404 printf("(recipe)");
406 if (node->rule->always_oodate) {
407 printf("(always)");
409 printf("\n");
411 printf("}\n");