diff options
author | Tharre <tharre3@gmail.com> | 2014-08-13 17:30:02 +0200 |
---|---|---|
committer | Tharre <tharre3@gmail.com> | 2014-08-13 17:34:08 +0200 |
commit | d09eb6c95b85fcb22d6b36353c82c3b4293e6fb7 (patch) | |
tree | 4574d2a1fbc04671c889caf49da64a73d4d74904 | |
parent | 325c8e3992bf7b73714d10a5d2202c89ddbac189 (diff) | |
download | redo-d09eb6c95b85fcb22d6b36353c82c3b4293e6fb7.tar.gz redo-d09eb6c95b85fcb22d6b36353c82c3b4293e6fb7.tar.xz redo-d09eb6c95b85fcb22d6b36353c82c3b4293e6fb7.zip |
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.
-rwxr-xr-x | print_dep.py | 55 | ||||
-rw-r--r-- | src/build.c | 110 | ||||
-rw-r--r-- | src/build.h | 3 | ||||
-rw-r--r-- | src/redo-ifchange.c | 10 | ||||
-rw-r--r-- | src/redo.c | 3 |
5 files changed, 170 insertions, 11 deletions
diff --git a/print_dep.py b/print_dep.py new file mode 100755 index 0000000..803b5e9 --- /dev/null +++ b/print_dep.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# quick and dirty utility to make the binary mess, produced by my redo program +# into something more friendly to the human eye +# requires termcolor (pip install termcolor) + +import struct +import sys +import hashlib +from binascii import hexlify +from os.path import basename +from termcolor import colored + +def convert_back(s): + return s.replace('!', '/') + +if (len(sys.argv) < 2): + print("Need an argument.") + exit(1) + +hasher = hashlib.sha1() +file = open(sys.argv[1], 'rb') +magic = file.read(4) +hash = file.read(20) +subdeps = file.read() +org_file = convert_back(basename(sys.argv[1])) + +hash_str = str(hexlify(hash), 'ascii') + +with open(org_file, 'rb') as f: + buf = f.read() + hasher.update(buf) + +print("Target: " + org_file) +print("Hash: " + hash_str + " ", end="") +if hasher.hexdigest() == hash_str: + print(colored(u"\u2714", "green", attrs=['bold'])) +else: + print(colored(u"\u2718", "red", attrs=['bold'])) + +print("Magic number: " + str(struct.unpack('i', magic)[0])) +print("Dependencies:") +start = True +thing = "" +for byte in subdeps: + if start: + print(" " + chr(byte) + "-", end="") + start = False + elif byte == 0: + start = True + print(thing) + thing = "" + else: + thing += chr(byte) + continue 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 <stdio.h> +#include <stdbool.h> #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'); } } @@ -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()"); |