Update HTML doc template.
[tmk.git] / tm_update.c
blob7efd811fe941be2ad04a5c0b3604d057c4c525f0
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include <sqlite3.h>
7 #define JIM_EMBEDDED
8 #include <jim.h>
10 #include "tmake.h"
11 #include "tm_target.h"
12 #include "tm_crypto.h"
13 #include "tm_update.h"
16 /* A list of targets that were updated during the current run */
17 target_list *updated_targets = NULL;
20 /* Returns true if a file exists and is readable.
22 int file_exists(const char *filename)
24 FILE *fp;
26 if ((fp = fopen(filename, "r"))) {
27 fclose(fp);
28 return 1;
31 return 0;
35 /* Used takes a Jim interpreter and a Jim error code.
36 * If there was an error, display an error message and exit.
37 * Otherwise, do nothing.
39 void wrap(Jim_Interp *interp, int error)
41 if (error == JIM_ERR) {
42 Jim_MakeErrorMessage(interp);
43 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
44 Jim_FreeInterp(interp);
45 exit(EXIT_FAILURE);
50 /* Update a target using the associated rule from tm_rules.
51 * Takes a SQLite database handle representing the sha1sum cache
52 * and the name of the TMakefile being evaluated, along with
53 * the name of the target to update.
55 int update(sqlite3 *db, const char *tmfile, const char *target)
57 unsigned char digest[CRYPTO_HASH_SIZE];
58 char newhash[CRYPTO_HASH_STRING_LENGTH];
59 tm_rule *rule = NULL;
60 const char *fmt = "INSERT OR REPLACE INTO TMCache (TMakefile, Target, Hash) VALUES (?, ?, ?)";
61 sqlite3_stmt *stm = NULL;
62 const char *stmtail;
63 int sqlrc;
65 rule = find_rule(target, tm_rules);
67 if (!rule) {
68 return (JIM_ERR);
71 updated_targets = target_cons(rule->target, updated_targets);
73 if (rule->type == TM_EXPLICIT) {
74 TM_CRYPTO_HASH_DATA(rule->recipe, digest);
75 } else if (rule->type == TM_FILENAME) {
76 TM_CRYPTO_HASH_FILE(target, digest);
77 } else {
78 fprintf(stderr, "WARNING: Unknown rule type %d\n"
79 " Don't know how to update cache for %s\n", rule->type, target);
80 return (JIM_ERR);
83 TM_CRYPTO_HASH_TO_STRING(digest, newhash);
85 sqlrc = sqlite3_prepare(db, fmt, -1, &stm, &stmtail);
86 if (sqlrc != SQLITE_OK) {
87 fprintf(stderr, "WARNING: Unable to update cache for target %s\n", target);
88 return (JIM_ERR);
91 sqlite3_bind_text(stm, 1, tmfile, -1, SQLITE_TRANSIENT);
92 sqlite3_bind_text(stm, 2, target, -1, SQLITE_TRANSIENT);
93 sqlite3_bind_text(stm, 3, newhash, -1, SQLITE_TRANSIENT);
94 sqlrc = sqlite3_step(stm);
95 if (sqlrc != SQLITE_DONE) {
96 fprintf(stderr, "WARNING: Error updating cache for target %s\n", target);
97 sqlite3_finalize(stm);
98 return (JIM_ERR);
100 sqlite3_finalize(stm);
102 Adding this return to squelch compilation warning.
103 Cory should review this code
105 return (JIM_OK);
109 /* Return 1 if target was updated, else return 0.
111 int was_updated(const char *target)
113 return target_exists(target, updated_targets);
117 /* Return 1 if a given target is out of date, else return 0.
118 * Takes a SQLite database handle and the name of the TMakefile
119 * being evaluated along with the name of the target.
121 int needs_update(sqlite3 *db, const char *tmfile, const char *target)
123 unsigned char digest[CRYPTO_HASH_SIZE];
124 const char *oldhash = NULL;
125 char newhash[CRYPTO_HASH_STRING_LENGTH];
126 target_list *deps = NULL;
127 tm_rule *rule = NULL;
128 const char *fmt = "SELECT Hash FROM TMCache WHERE TMakefile = ? AND Target = ?";
129 const char *stmtail = NULL;
130 sqlite3_stmt *stm = NULL;
131 int sqlrc;
133 rule = find_rule(target, tm_rules);
135 if (!rule) {
136 goto yes;
139 for (deps = rule->deps; deps; deps = deps->next) {
140 if (was_updated(deps->name)) {
141 goto yes;
145 sqlrc = sqlite3_prepare(db, fmt, -1, &stm, &stmtail);
146 if (sqlrc != SQLITE_OK) {
147 fprintf(stderr, "WARNING: Unable to prepare database statement.\n"
148 " Assuming %s needs update.\n", target);
149 return 1;
152 sqlite3_bind_text(stm, 1, tmfile, -1, SQLITE_TRANSIENT);
153 sqlite3_bind_text(stm, 2, target, -1, SQLITE_TRANSIENT);
154 sqlrc = sqlite3_step(stm);
155 if (sqlrc != SQLITE_ROW) {
156 /* There was no row in the cache for this target, so update it */
157 goto yes;
160 oldhash = (const char *)sqlite3_column_text(stm, 0);
161 if (!oldhash) {
162 fprintf(stderr, "WARNING: Error getting cache for target %s\n", target);
163 goto yes;
166 if (rule->type == TM_EXPLICIT) {
167 TM_CRYPTO_HASH_DATA(rule->recipe, digest);
168 } else if (rule->type == TM_FILENAME) {
169 TM_CRYPTO_HASH_FILE(target, digest);
170 } else {
171 fprintf(stderr, "WARNING: Unexpected rule type %d\n"
172 " Assuming %s needs update.\n", rule->type, target);
173 goto yes;
175 TM_CRYPTO_HASH_TO_STRING(digest, newhash);
177 if (strcmp(oldhash, newhash) == 0) {
178 goto no;
179 } else {
180 goto yes;
184 sqlite3_finalize(stm);
185 return 0;
186 yes:
187 sqlite3_finalize(stm);
188 return 1;
191 /* Take a list of targets to check and return a sublist containing only
192 * the targets that are out of date.
193 * Takes a SQLite database handle and the name of the TMakefile being
194 * evaluated along with the list of targets.
196 target_list *need_update(sqlite3 *db, const char *tmfile, target_list *targets)
198 target_list *oodate = NULL;
200 for (; targets; targets = targets->next) {
201 if (needs_update(db, tmfile, targets->name)) {
202 oodate = target_cons(targets->name, oodate);
206 return oodate;
210 /* Update all the rules in sorted_rules that need updating.
211 * Takes a SQLite database handle, a Jim interpreter containing
212 * the recipe definitions, the name of the TMakefile that was
213 * evaluated in the interpreter, the rules to be updated if needed
214 * (in the order they are to be updated), whether or not to force
215 * updates regardless of out-of-date status, and whether or not
216 * we're running in silent mode.
218 void update_rules(sqlite3 *db, Jim_Interp *interp,
219 const char *tmfile,
220 tm_rule_list *sorted_rules,
221 int force,
222 int silence)
224 if (!sorted_rules) {
225 /* nothing to do */
226 return;
229 for (; sorted_rules; sorted_rules = sorted_rules->next) {
230 tm_rule *rule = sorted_rules->rule;
231 if (rule->type == TM_EXPLICIT) {
232 /* If the rule has a recipe and needs an update */
233 if (rule->recipe
234 && (force || needs_update(db, tmfile, rule->target))) {
235 /* Execute the recipe for the current rule */
236 const char *fmt = "recipe::%s {%s} {%s} {%s}";
237 int len = 0;
238 char *cmd = NULL;
239 const char *target = rule->target;
240 char *inputs = target_list_to_string(rule->deps);
241 target_list *oodate_deps = need_update(db, tmfile, rule->deps);
242 char *oodate = target_list_to_string(oodate_deps);
243 tm_rule_list *oodate_rules = find_rules(oodate_deps, tm_rules);
245 if (was_updated(target)) {
246 goto done;
249 update_rules(db, interp, tmfile, oodate_rules, force, silence);
251 if (!silence)
252 printf("Making target %s:\n", rule->target);
253 len = strlen(fmt) + strlen(target)*2 + strlen(inputs) + strlen(oodate) + 1;
254 cmd = malloc(len);
255 sprintf(cmd, fmt, target, target, inputs, oodate);
256 wrap(interp, Jim_Eval(interp, cmd));
258 update(db, tmfile, rule->target);
259 if (!silence)
260 printf("\n");
262 done:
263 free(cmd);
264 free(oodate);
265 free(inputs);
266 free_rule_list(oodate_rules);
267 free_target_list(oodate_deps);
269 rule->type = TM_UPDATED;
271 } else if (rule->type == TM_FILENAME) {
272 /* Check that the file actually exists */
273 if (!file_exists(rule->target)) {
274 fprintf(stderr, "ERROR: Unable to find rule for target %s\n", rule->target);
275 exit(EXIT_FAILURE);
277 if (force || needs_update(db, tmfile, rule->target)) {
278 update(db, tmfile, rule->target);
279 rule->type = TM_UPDATED;