Add install target
[tmk.git] / tm_target.c
blob9351d16db7e4e1fe8b1279822c6b705a8765da81
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;
34 return rule;
37 /* Create a new filename rule.
38 * Makes a copy of its argument.
40 tm_rule *new_filename(const char *target)
42 tm_rule *rule = new_rule(target, NULL, NULL);
44 rule->type = TM_FILENAME;
46 return rule;
49 /* Make a copy of a rule.
51 tm_rule *rule_copy(tm_rule *rule)
53 tm_rule *copy;
55 if (!rule) {
56 return NULL;
59 copy = malloc(sizeof(tm_rule));
61 if (rule->target) {
62 copy->target = malloc(strlen(rule->target) + 1);
63 strcpy(copy->target, rule->target);
64 } else {
65 copy->target = NULL;
67 if (rule->deps) {
68 copy->deps = target_list_copy(rule->deps);
69 } else {
70 copy->deps = NULL;
72 if (rule->recipe) {
73 copy->recipe = malloc(strlen(rule->recipe) + 1);
74 strcpy(copy->recipe, rule->recipe);
75 } else {
76 copy->recipe = NULL;
78 copy->type = rule->type;
79 copy->mark = rule->mark;
81 return copy;
84 /* Free a rule */
85 void free_rule(tm_rule *rule)
87 if (!rule) return;
88 free(rule->target);
89 free_target_list(rule->deps);
90 free(rule->recipe);
91 free(rule);
96 /* Takes the name of a target and another target list and
97 * returns a target list with the target at the head.
98 * Target lists are NULL-terminated.
100 * e.g., to make a one-element list:
101 * target_cons("foo", NULL);
103 target_list *target_cons(const char *name, target_list *next)
105 target_list *targets = malloc(sizeof(target_list));
107 targets->name = malloc(strlen(name) + 1);
109 strcpy(targets->name, name);
110 targets->next = next;
112 return targets;
115 /* Frees a target list.
117 void free_target_list(target_list *targets)
119 while (targets) {
120 target_list *node = targets->next;
121 free(targets->name);
122 free(targets);
123 targets = node;
127 /* Copy a target list.
129 target_list *target_list_copy(target_list *targets)
131 target_list *rev;
132 target_list *copy;
134 if (!targets) return NULL;
136 rev = target_list_reverse(targets);
137 copy = target_list_reverse(rev);
138 free_target_list(rev);
139 return copy;
143 /* Take a rule and another rule list and return
144 * a rule list with the rule at the head.
145 * Rule lists are NULL-terminated.
147 tm_rule_list *rule_cons(tm_rule *rule, tm_rule_list *next)
149 tm_rule_list *rules = malloc(sizeof(tm_rule_list));
151 rules->rule = rule_copy(rule);
152 rules->next = next;
154 return rules;
157 /* Frees a rule list
159 void free_rule_list(tm_rule_list *rules)
161 while (rules) {
162 tm_rule_list *node = rules->next;
163 if (rules->rule)
164 free_rule(rules->rule);
165 free(rules);
166 rules = node;
171 /* Return 1 if the target is in targets, else return 0.
173 int target_exists(const char *target, target_list *targets)
175 for (; targets; targets = targets->next) {
176 if (strcmp(targets->name, target) == 0) {
177 return 1;
181 return 0;
185 /* Take a target and a rule list and return the rule associated
186 * with that target if it exists, otherwise return NULL.
188 tm_rule *find_rule(const char *target, tm_rule_list *rules)
190 tm_rule_list *node = rules;
191 tm_rule *rule = NULL;
193 while (node) {
194 if (strcmp(node->rule->target, target) == 0) {
195 rule = node->rule;
196 break;
199 node = node->next;
202 return rule;
206 /* Take a list of targets and a pointer to a rule list.
207 * If no rule for a target is found in the rule list, add
208 * a new TM_FILENAME rule to the rule list.
210 static void find_files_from_deps(target_list *targets, tm_rule_list **rules)
212 for (; targets; targets = targets->next) {
213 tm_rule *rule = find_rule(targets->name, *rules);
215 if (!rule) {
216 rule = new_filename(targets->name);
217 *rules = rule_cons(rule, *rules);
218 free_rule(rule);
223 /* Scour through all the dependencies in a rule list and allocate new
224 * filename rules for things deemed to be files.
225 * Updates the rule list with the new rules.
227 void find_files(tm_rule_list **rules)
229 tm_rule_list *node;
231 for (node = *rules; node; node = node->next) {
232 find_files_from_deps(node->rule->deps, rules);
237 /* Take a list of targets and a list of rules and return
238 * a list of the rules associated with those targets.
240 tm_rule_list *find_rules(target_list *targets, tm_rule_list *rules)
242 tm_rule_list *mapped = NULL;
243 target_list *node = targets;
245 while (node) {
246 tm_rule *rule = find_rule(node->name, rules);
247 if (rule)
248 mapped = rule_cons(rule, mapped);
249 node = node->next;
252 return mapped;
256 /* Return a new rule list representing the reverse of another rule list
258 tm_rule_list *rule_list_reverse(tm_rule_list *rules)
260 tm_rule_list *rev = NULL;
261 tm_rule_list *node = rules;
263 for (; node; node = node->next) {
264 rev = rule_cons(node->rule, rev);
267 return rev;
270 /* Return a new target list representing the reverse of another target list
272 target_list *target_list_reverse(target_list *targets)
274 target_list *rev = NULL;
275 target_list *node = targets;
277 for (; node; node = node->next) {
278 rev = target_cons(node->name, rev);
281 return rev;
285 /* Tarjan's algorithm for topological sorting */
286 static int topsort_visit(tm_rule *rule, tm_rule_list *rules, tm_rule_list **sorted)
288 if (rule->mark == TM_TEMPORARY) {
289 fprintf(stderr, "ERROR: Cycle detected in dependency graph\n");
290 return -1;
293 if (rule->mark == TM_UNMARKED) {
294 tm_rule_list *deps = NULL;
295 tm_rule_list *node = NULL;
297 rule->mark = TM_TEMPORARY;
298 deps = find_rules(rule->deps, rules);
300 for (node = deps; node; node = node->next) {
301 int n = 0;
303 if (node->rule)
304 n = topsort_visit(node->rule, rules, sorted);
306 if (n < 0) {
307 free_rule_list(deps);
308 return n;
311 free_rule_list(deps);
313 rule->mark = TM_PERMANENT;
314 *sorted = rule_cons(rule, *sorted);
317 return 0;
320 /* Perform a topological sort of the dependency graph to reach target.
321 * Also checks for cycles in the dependency graph.
323 tm_rule_list *topsort(const char *target, tm_rule_list *rules)
325 tm_rule_list *sorted = NULL;
326 tm_rule_list *rev = NULL;
327 tm_rule *rule = find_rule(target, rules);
329 if (rule == NULL)
330 return NULL;
332 if (topsort_visit(rule, rules, &sorted) < 0) {
333 return NULL;
336 rev = rule_list_reverse(sorted);
337 free_rule_list(sorted);
338 return rev;
342 /* Return a string representation of a target list.
343 * Consists of all the targets in the list separated by spaces.
345 char *target_list_to_string(target_list *targets)
347 target_list *node = targets;
348 int len = 1; /* for NUL terminator */
349 char *str = NULL;
350 char *p = NULL;
352 if (!targets) {
353 str = malloc(1);
354 str[0] = '\0';
355 return str;
358 for (node = targets; node; node = node->next) {
359 len += strlen(node->name) + 1;
362 str = malloc(len);
363 p = str;
364 for (node = targets; node; node = node->next) {
365 p += sprintf(p, "%s ", node->name);
367 *(p-1) = '\0'; /* get rid of the trailing space */
369 return str;
373 /* Print a rule list to stdout.
375 void print_rule_list(tm_rule_list *rules)
377 tm_rule_list *node;
379 printf("{\n");
380 for (node = rules; node; node = node->next) {
381 target_list *deps;
383 printf("\t%s: ", node->rule->target);
384 for (deps = node->rule->deps; deps; deps = deps->next) {
385 printf("%s ", deps->name);
387 switch (node->rule->type) {
388 case TM_EXPLICIT:
389 printf("(explicit)");
390 break;
391 case TM_IMPLICIT:
392 printf("(implicit)");
393 break;
394 case TM_FILENAME:
395 printf("(filename)");
396 break;
397 default:
398 printf("(UNKNOWN)");
399 break;
401 if (node->rule->recipe) {
402 printf("(recipe)");
404 printf("\n");
406 printf("}\n");