diff options
author | Tharre <tharre3@gmail.com> | 2014-07-30 16:58:38 +0200 |
---|---|---|
committer | Tharre <tharre3@gmail.com> | 2014-07-30 16:58:38 +0200 |
commit | 681b13c1bf9760d8fbcbb80a2d679e0bc6d17772 (patch) | |
tree | 8b263cb54ca41ef07ffdecd1575f2f6722eae83f /src | |
parent | 2a5673b6bf86f4ba89aabf6ec5c82a2d5479d4ae (diff) | |
download | redo-681b13c1bf9760d8fbcbb80a2d679e0bc6d17772.tar.gz redo-681b13c1bf9760d8fbcbb80a2d679e0bc6d17772.tar.xz redo-681b13c1bf9760d8fbcbb80a2d679e0bc6d17772.zip |
Add several methods for storing dependencies
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__) |