From d09eb6c95b85fcb22d6b36353c82c3b4293e6fb7 Mon Sep 17 00:00:00 2001 From: Tharre Date: Wed, 13 Aug 2014 17:30:02 +0200 Subject: Implement (incomplete) dependency checking. Targets still do not depend on .do-files, and a lot of the edge cases are still not handled correctly. Furthermore some error-checking code is still missing, which could possibly crash the program (partially marked with comments), as well as some free() calls. An utitlity python script (print_dep.py) was also added to aid in debugging matters. --- src/build.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++----- src/build.h | 3 ++ src/redo-ifchange.c | 10 ++++- src/redo.c | 3 +- 4 files changed, 115 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/build.c b/src/build.c index d217a23..2b67fff 100644 --- a/src/build.c +++ b/src/build.c @@ -26,6 +26,9 @@ const char temp_ext[] = ".redoing.tmp"; const char deps_dir[] = "/.redo/deps/"; const char redo_root[] = "REDO_ROOT"; const char redo_parent_target[] = "REDO_PARENT_TARGET"; +const char redo_magic[] = "REDO_MAGIC"; + +#define HASHSIZE 20 /* TODO: more useful return codes? */ int build_target(const char *target) { @@ -48,8 +51,16 @@ int build_target(const char *target) { goto exit; } - printf("redo %s\n", target); - /*debug("Using do-file %s\n", do_file);*/ + char *reltarget = get_relpath(target); + printf("redo %s\n", reltarget); + free(reltarget); + + /* remove old dependency file */ + char *dep_file = get_dep_path(target); + if (remove(dep_file)) + if (errno != ENOENT) + fatal(ERRM_REMOVE, dep_file); + free(dep_file); char *temp_output = concat(2, target, temp_ext); @@ -286,7 +297,7 @@ void add_dep(const char *target, int indent) { if (!fp) fatal(ERRM_FOPEN, dep_path); /* skip the first n bytes that are reserved for the hash + magic number */ - fseek(fp, 20 + sizeof(unsigned int), SEEK_SET); + fseek(fp, HASHSIZE + sizeof(unsigned int), SEEK_SET); } else { fatal(ERRM_FOPEN, dep_path); } @@ -310,7 +321,7 @@ void add_dep(const char *target, int indent) { } /* Hash target, storing the result in hash */ -void hash_file(const char *target, unsigned char (*hash)[20]) { +void hash_file(const char *target, unsigned char (*hash)[HASHSIZE]) { FILE *in = fopen(target, "rb"); if (!in) fatal(ERRM_FOPEN, target); @@ -329,9 +340,8 @@ void hash_file(const char *target, unsigned char (*hash)[20]) { /* Calculate and store the hash of target in the right dependency file */ void write_dep_hash(const char *target) { - assert(getenv("REDO_MAGIC")); unsigned char hash[SHA_DIGEST_LENGTH]; - int magic = atoi(getenv("REDO_MAGIC")); + unsigned magic = atoi(getenv(redo_magic)); hash_file(target, &hash); @@ -340,13 +350,95 @@ void write_dep_hash(const char *target) { if (out < 0) fatal("redo: failed to open() '%s'", dep_path); + if (write(out, &magic, sizeof(unsigned)) < (ssize_t) sizeof(unsigned)) + fatal("redo: failed to write magic number to '%s'", dep_path); + if (write(out, hash, sizeof hash) < (ssize_t) sizeof hash) fatal("redo: failed to write hash to '%s'", dep_path); - if (write(out, &magic, sizeof(unsigned int)) < (ssize_t) sizeof(unsigned int)) - fatal("redo: failed to write magic number to '%s'", dep_path); - if (close(out)) fatal("redo: failed to close file descriptor."); free(dep_path); } + +bool dependencies_changed(char buf[], size_t read) { + char *ptr = buf; + for (size_t i = 0; i < read; ++i) { + if (!buf[i]) { + if (is_absolute(&ptr[1])) { + if (has_changed(&ptr[1], ptr[0], true)) + return true; + } else { + char *root = getenv(redo_root); + char *abs = concat(3, root, "/", &ptr[1]); + if (has_changed(abs, ptr[0], true)) { + free(abs); + return true; + } + free(abs); + } + ptr = &buf[i+1]; + } + } + return false; +} + +bool has_changed(const char *target, int ident, bool is_sub_dependency) { + switch(ident) { + case 'a': return true; + case 'e': return fexists(target); + case 'c': +#define HEADERSIZE HASHSIZE + sizeof(unsigned) + if (!fexists(target)) + return true; + + char *dep_path = get_dep_path(target); + + FILE *fp = fopen(dep_path, "rb"); + if (!fp) { + if (errno == ENOENT) { + /* dependency file does not exist */ + return true; + } else { + fatal(ERRM_FOPEN, dep_path); + } + } + + char buf[8096]; + if (fread(buf, 1, HEADERSIZE, fp) < HEADERSIZE) + fatal("redo: failed to read %zu bytes from %s", HEADERSIZE, dep_path); + + free(dep_path); + + unsigned magic = *(unsigned *) buf; + + if (magic == (unsigned) atoi(getenv(redo_magic))) + return is_sub_dependency; + + unsigned char hash[HASHSIZE]; + hash_file(target, &hash); + if (memcmp(hash, buf+sizeof(unsigned), HASHSIZE)) { + /*debug("Hash doesn't match for %s\n", target);*/ + return true; + } + + while (!feof(fp)) { + size_t read = fread(buf, 1, sizeof buf, fp); + + if (ferror(fp)) + fatal("redo: failed to read %zu bytes from file descriptor", sizeof buf); + + if (dependencies_changed(buf, read)) { + fclose(fp); + return true; + } + } + + fclose(fp); + return false; + + default: + log_err("Unknown identifier '%c'\n", ident); + exit(EXIT_FAILURE); + } +} diff --git a/src/build.h b/src/build.h index 54093bb..0b34241 100644 --- a/src/build.h +++ b/src/build.h @@ -14,5 +14,8 @@ extern char *get_relpath(const char *target); extern char *get_dep_path(const char *target); extern void add_dep(const char *target, int indent); extern void write_dep_hash(const char *target); +extern bool has_changed(const char *target, int ident, bool is_sub_dependency); +extern bool dependencies_changed(char buf[], size_t read); + #endif diff --git a/src/redo-ifchange.c b/src/redo-ifchange.c index 219a0a1..1efe0a5 100644 --- a/src/redo-ifchange.c +++ b/src/redo-ifchange.c @@ -1,11 +1,19 @@ #include +#include #include "build.h" +#include "dbg.h" int main(int argc, char *argv[]) { int i; for (i = 1; i < argc; ++i) { - build_target(argv[i]); + /*debug("Testing if %s is up-to-date ...\n", argv[i]);*/ + if (has_changed(argv[i], 'c', false)) { + /*printf("=> no\n");*/ + build_target(argv[i]); + } else { + /*printf("=> yes\n");*/ + } add_dep(argv[i], 'c'); } } diff --git a/src/redo.c b/src/redo.c index 0209a4f..76d8a86 100644 --- a/src/redo.c +++ b/src/redo.c @@ -12,6 +12,7 @@ #include "util.h" #include "dbg.h" + static inline int digits(unsigned n) { return (int) log10(n) + 1; } @@ -38,7 +39,7 @@ int main(int argc, char *argv[]) { char magic_str[digits(UINT_MAX) + 1]; sprintf(magic_str, "%u", magic); - printf("MAGIC: %s\n", magic_str); + debug("magic number: %s\n", magic_str); if (setenv("REDO_MAGIC", magic_str, 0)) fatal("setenv()"); -- cgit v1.2.3-70-g09d2