diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/build.c | 139 | ||||
| -rw-r--r-- | src/build.h | 6 | ||||
| -rw-r--r-- | src/redo-ifchange.c | 1 | ||||
| -rw-r--r-- | src/redo.c | 5 | ||||
| -rw-r--r-- | src/util.h | 2 | 
5 files changed, 148 insertions, 5 deletions
| diff --git a/src/build.c b/src/build.c index 24d0a4b..4b9b960 100644 --- a/src/build.c +++ b/src/build.c @@ -7,8 +7,10 @@  #include <sys/wait.h>  #include <sys/types.h>  #include <sys/stat.h> +#include <fcntl.h>  #include <libgen.h> /* dirname(), basename() */ +#include <openssl/sha.h>  #include "build.h"  #include "util.h" @@ -16,12 +18,18 @@  #define _FILENAME "build.c"  #include "dbg.h" -/* TODO: more useful return codes? */ +/* TODO: for some reason these headers aren't included */ +char *realpath(const char *path, char *resolved_path); +int setenv(const char *name, const char *value, int overwrite);  const char do_file_ext[] = ".do";  const char default_name[] = "default";  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"; +/* TODO: more useful return codes? */  int build_target(const char *target) {      assert(target); @@ -33,7 +41,7 @@ int build_target(const char *target) {          if (fexists(target)) {              /* if our target file has no do file associated but exists,                 then we treat it as a source */ -            /* TODO: write dependencies */ +            write_dep_hash(target);              goto exit;          }          log_err("%s couldn't be built because no " @@ -69,6 +77,10 @@ int build_target(const char *target) {          char **argv = parse_shebang(btarget, bdo_file, btemp_output); +        /* set REDO_PARENT_TARGET */ +        if (setenv(redo_parent_target, target, 1)) +            fatal(ERRM_SETENV, redo_parent_target, target); +          /* excelp() has nearly everything we want: automatic parsing of the             shebang line through execve() and fallback to /bin/sh if no valid             shebang could be found. However, it fails if the target doesn't have @@ -108,6 +120,8 @@ int build_target(const char *target) {      } else {          if (rename(temp_output, target))              fatal(ERRM_RENAME, temp_output, target); + +        write_dep_hash(target);      }      free(temp_output); @@ -212,3 +226,124 @@ char **parsecmd(char *cmd, size_t *i, size_t keep_free) {          }      }  } + +/* Custom version of realpath that doesn't fail if the last part of path +   doesn't exit and allocates memory for the result itself */ +char *xrealpath(const char *path) { +    char *dirc = safe_strdup(path); +    char *dname = dirname(dirc); +    char *absdir = realpath(dname, NULL); +    if (!absdir) +        return NULL; +    char *abstarget = concat(3, absdir, "/", xbasename(path)); + +    free(dirc); +    free(absdir); +    return abstarget; +} + +/* Return the relative path against REDO_ROOT of target */ +char *get_relpath(const char *target) { +    assert(getenv(redo_root)); +    assert(target); + +    char *root = getenv(redo_root); +    char *abstarget = xrealpath(target); + +    if (!abstarget) +        fatal(ERRM_REALPATH, target); + +    char *relpath = safe_strdup(make_relative(root, abstarget)); +    free(abstarget); +    return relpath; +} + +/* Return the dependency file path of target */ +char *get_dep_path(const char *target) { +    assert(target); +    assert(getenv(redo_root)); + +    char *root = getenv(redo_root); +    char *reltarget = get_relpath(target); +    char *safe_target = transform_path(reltarget); +    char *dep_path = concat(3, root, deps_dir, safe_target); + +    free(reltarget); +    free(safe_target); +    return dep_path; +} + +/* Add the dependency target, with the identifier indent */ +void add_dep(const char *target, int indent) { +    assert(target); +    assert(getenv(redo_parent_target)); + +    char *parent_target = getenv(redo_parent_target); +    char *dep_path = get_dep_path(parent_target); + +    FILE *fp = fopen(dep_path, "rb+"); +    if (!fp) { +        if (errno == ENOENT) { +            fp = fopen(dep_path, "w"); +            if (!fp) +                fatal(ERRM_FOPEN, dep_path); +            /* skip the first n bytes that are reserved for the hash */ +            fseek(fp, 20, SEEK_SET); +        } else { +            fatal(ERRM_FOPEN, dep_path); +        } +    } else { +        fseek(fp, 0L, SEEK_END); +    } + +    char *reltarget = get_relpath(target); + +    putc(indent, fp); +    fputs(reltarget, fp); +    putc('\0', fp); + +    if (ferror(fp)) +        fatal(ERRM_WRITE, dep_path); + +    if (fclose(fp)) +        fatal(ERRM_FCLOSE, dep_path); +    free(dep_path); +    free(reltarget); +} + +/* Hash target, storing the result in hash */ +void hash_file(const char *target, unsigned char (*hash)[20]) { +    FILE *in = fopen(target, "rb"); +    if (!in) +        fatal(ERRM_FOPEN, target); + +    SHA_CTX context; +    unsigned char data[8192]; +    size_t read; + +    SHA1_Init(&context); +    while ((read = fread(data, 1, sizeof data, in))) +        SHA1_Update(&context, data, read); +    SHA1_Final(*hash, &context); +    if (fclose(in)) +        fatal(ERRM_FCLOSE, target); +} + +/* Calculate and store the hash of target in the right dependency file */ +void write_dep_hash(const char *target) { +    unsigned char hash[SHA_DIGEST_LENGTH]; + +    hash_file(target, &hash); + +    char *dep_path = get_dep_path(target); +    int out = open(dep_path, O_WRONLY | O_CREAT, 0644); +    if (out < 0) +        fatal("redo: failed to open() '%s'", dep_path); + +    if (write(out, hash, sizeof hash) < (ssize_t) sizeof hash) +        fatal("redo: failed to write hash to '%s'", dep_path); + +    if (close(out)) +        fatal("redo: failed to close file descriptor."); +    free(dep_path); +} diff --git a/src/build.h b/src/build.h index 3f05d5a..54093bb 100644 --- a/src/build.h +++ b/src/build.h @@ -4,9 +4,15 @@  #include <stdbool.h>  #include <stddef.h> +char *realpath(const char *path, char *resolved_path); +int setenv(const char *name, const char *value, int overwrite);  extern char *get_do_file(const char *target);  extern int build_target(const char *target);  extern char **parse_shebang(char *target, char *dofile, char *temp_output);  extern char **parsecmd(char *cmd, size_t *i, size_t keep_free); +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);  #endif diff --git a/src/redo-ifchange.c b/src/redo-ifchange.c index b778673..219a0a1 100644 --- a/src/redo-ifchange.c +++ b/src/redo-ifchange.c @@ -6,5 +6,6 @@ int main(int argc, char *argv[]) {      int i;      for (i = 1; i < argc; ++i) {          build_target(argv[i]); +        add_dep(argv[i], 'c');      }  } @@ -13,8 +13,9 @@ int setenv(const char *name, const char *value, int overwrite);  int main(int argc, char *argv[]) {      /* create .redo directory */ -    if (mkdir(".redo", 0744)) -        fatal(ERRM_MKDIR, ".redo"); +    if (mkdir(".redo/deps", 0744)) +        if (errno != EEXIST) /* TODO: unsafe, dir could be a file or broken symlink */ +            fatal(ERRM_MKDIR, ".redo");      /* set REDO_ROOT */      char *cwd = getcwd(NULL, 0); @@ -21,7 +21,7 @@  #define ERRM_FORK _PROGNAME": failed to fork() new process"  #define ERRM_REALPATH _PROGNAME": failed to get realpath() of %s"  #define ERRM_STAT _PROGNAME": failed to aquire stat() information about %s" -#define ERRM_MKDIR _PROGNAME": failed to mkdir() %s" +#define ERRM_MKDIR _PROGNAME": failed to mkdir() '%s'"  #define safe_malloc(size) safe_malloc_(size, _FILENAME, __LINE__)  #define safe_realloc(ptr, size) safe_realloc_(ptr, size, _FILENAME, __LINE__) | 
