aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTharre <tharre3@gmail.com>2016-08-03 19:25:42 +0200
committerTharre <tharre3@gmail.com>2016-08-03 19:33:53 +0200
commitf353af5b5b0cfa922a067aa67a1645b822933c0d (patch)
treeb0382de1fecbc42957ccc5b4d716f5ac9dac847f /src
parent90adc8bc41544ffa6047a9262a39375a9d328bf2 (diff)
downloadredo-f353af5b5b0cfa922a067aa67a1645b822933c0d.tar.gz
redo-f353af5b5b0cfa922a067aa67a1645b822933c0d.tar.xz
redo-f353af5b5b0cfa922a067aa67a1645b822933c0d.zip
Implement modification time (ctime) check
Hashing over the contents of dependencies over and over again is slow. To avoid this, we first check the modification time (ctime), and only if that check fails we generate a hash.
Diffstat (limited to 'src')
-rw-r--r--src/build.c108
1 files changed, 74 insertions, 34 deletions
diff --git a/src/build.c b/src/build.c
index cd6f8dc..d601e42 100644
--- a/src/build.c
+++ b/src/build.c
@@ -6,7 +6,7 @@
* of the MIT license. See the LICENSE file for details.
*/
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 700
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -17,6 +17,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <time.h>
#include <libgen.h> /* dirname(), basename() */
@@ -40,6 +41,7 @@ typedef struct dep_info {
const char *target;
char *path;
unsigned char *hash;
+ struct timespec ctime;
int32_t flags;
#define DEP_SOURCE (1 << 1)
} dep_info;
@@ -54,7 +56,7 @@ static char *get_dep_path(const char *target);
static void write_dep_information(dep_info *dep);
static int handle_ident(dep_info *dep, int ident);
static int handle_c(dep_info *dep);
-static unsigned char *hash_file(const char *target);
+static void update_dep_info(dep_info *dep, const char *target);
/* Build given target, using it's .do script. */
@@ -68,8 +70,8 @@ static int build_target(dep_info *dep) {
/* if our target file has no .do script associated but exists,
then we treat it as a source */
dep->flags |= DEP_SOURCE;
- if (!dep->hash)
- dep->hash = hash_file(dep->target);
+ if (!dep->hash) /* if the hash was retrieved, ctime was too */
+ update_dep_info(dep, dep->target);
write_dep_information(dep);
goto exit;
@@ -155,7 +157,7 @@ static int build_target(dep_info *dep) {
/* recalculate hash after successful build */
unsigned char *old_hash = dep->hash;
- dep->hash = hash_file(dep->target);
+ update_dep_info(dep, dep->target);
if (old_hash)
retval = memcmp(dep->hash, old_hash, 20);
@@ -174,7 +176,7 @@ static int build_target(dep_info *dep) {
};
if (!fexists(dep2.path)) {
- dep2.hash = hash_file(doscripts->chosen);
+ update_dep_info(&dep2, doscripts->chosen);
write_dep_information(&dep2);
free(dep2.hash);
}
@@ -369,11 +371,7 @@ void add_prereq(const char *target, const char *parent, int ident) {
}
/* Hash the target file, returning a pointer to the heap allocated hash. */
-static unsigned char *hash_file(const char *target) {
- FILE *in = fopen(target, "rb");
- if (!in)
- fatal("redo: failed to open %s", target);
-
+static unsigned char *hash_file(FILE *fp) {
unsigned char *hash = xmalloc(20);
SHA_CTX context;
@@ -381,17 +379,31 @@ static unsigned char *hash_file(const char *target) {
size_t read;
SHA1_Init(&context);
- while ((read = fread(data, 1, sizeof data, in)))
+ while ((read = fread(data, 1, sizeof data, fp)))
SHA1_Update(&context, data, read);
- if (ferror(in))
- fatal("redo: failed to read from %s", target);
+ if (ferror(fp))
+ fatal("redo: failed to read data");
SHA1_Final(hash, &context);
- fclose(in);
return hash;
}
+/* Update hash & ctime information stored in the given dep_info struct */
+static void update_dep_info(dep_info *dep, const char *target) {
+ FILE *fp = fopen(target, "rb");
+ if (!fp)
+ fatal("redo: failed to open %s", target);
+
+ dep->hash = hash_file(fp);
+ struct stat st;
+ if (fstat(fileno(fp), &st))
+ fatal("redo: failed to aquire stat() %s", target);
+
+ dep->ctime = st.st_ctim;
+ fclose(fp);
+}
+
static void sha1_to_hex(const unsigned char *sha1, char *buf) {
static const char hex[] = "0123456789abcdef";
@@ -422,7 +434,9 @@ static void write_dep_information(dep_info *dep) {
int magic = atoi(getenv("REDO_MAGIC"));
- if (fprintf(fd, "%s:%010u:%s\n", hash, magic, flags) < 0)
+ /* TODO: casting time_t to long long is probably not entirely portable */
+ if (fprintf(fd, "%s:%lld.%.9ld:%010u:%s\n", hash,
+ (long long)dep->ctime.tv_sec, dep->ctime.tv_nsec, magic, flags) < 0)
fatal("redo: failed to write to %s", dep->path);
if (fclose(fd))
@@ -459,8 +473,8 @@ static int handle_ident(dep_info *dep, int ident) {
}
static int handle_c(dep_info *dep) {
- FILE *fp = fopen(dep->path, "rb");
- if (!fp) {
+ FILE *pathfd = fopen(dep->path, "rb");
+ if (!pathfd) {
if (errno == ENOENT) {
/* dependency record does not exist */
return build_target(dep);
@@ -471,22 +485,25 @@ static int handle_c(dep_info *dep) {
int retval = 0;
struct dsv_ctx ctx;
- dsv_init(&ctx, 3);
+ dsv_init(&ctx, 4);
- if (dsv_parse_file(&ctx, fp)) {
+ if (dsv_parse_file(&ctx, pathfd)) {
retval = build_target(dep);
goto exit2;
}
errno = 0;
- long magic = strtol(ctx.fields[1], NULL, 10);
+ long magic = strtol(ctx.fields[2], NULL, 10);
if (errno) {
retval = build_target(dep);
goto exit;
}
- if (!fexists(dep->target)) {
- if (ctx.fields[2][0] == 's') {
+ FILE *targetfd = fopen(dep->target, "rb");
+ if (!targetfd) {
+ if (errno != ENOENT) {
+ fatal("redo: failed to open %s\n", dep->target);
+ } else if (ctx.fields[3][0] == 's') {
/* target is a source and must not be rebuild */
retval = 1;
goto exit;
@@ -498,30 +515,53 @@ static int handle_c(dep_info *dep) {
if (magic == atoi(getenv("REDO_MAGIC"))) {
/* magic number matches */
+ fclose(targetfd);
retval = 1;
goto exit;
}
- unsigned char old_hash[20];
- hex_to_sha1(ctx.fields[0], old_hash); /* TODO: error checking */
- dep->hash = hash_file(dep->target);
-
- if (memcmp(old_hash, dep->hash, 20)) {
+ struct stat curr_st;
+ if (sscanf(ctx.fields[1], "%lld.%ld", (long long*)&dep->ctime.tv_sec,
+ &dep->ctime.tv_nsec) < 2) {
+ fclose(targetfd);
retval = build_target(dep);
goto exit;
}
+ if (fstat(fileno(targetfd), &curr_st))
+ fatal("redo: failed to stat() %s", dep->target);
+
+ if (dep->ctime.tv_sec != curr_st.st_ctim.tv_sec
+ || dep->ctime.tv_nsec != curr_st.st_ctim.tv_nsec) {
+ /* ctime doesn't match */
+ dep->ctime = curr_st.st_ctim;
+
+ /* so check the hash */
+ unsigned char old_hash[20];
+ hex_to_sha1(ctx.fields[0], old_hash); /* TODO: error checking */
+ dep->hash = hash_file(targetfd);
+
+ if (memcmp(old_hash, dep->hash, 20)) {
+ fclose(targetfd);
+ retval = build_target(dep);
+ goto exit;
+ }
+
+ /* update ctime hash */
+ write_dep_information(dep);
+ }
+
for (size_t i = 0; i < ctx.fields_count; ++i)
free(ctx.fields[i]);
dsv_free(&ctx);
- fclose(fp);
-
+ fclose(targetfd);
+ fclose(pathfd);
char *prereq_path = concat(2, dep->path, ".prereq");
- fp = fopen(prereq_path, "rb");
+ pathfd = fopen(prereq_path, "rb");
free(prereq_path);
- if (!fp) {
+ if (!pathfd) {
if (errno == ENOENT)
return 0;
else
@@ -530,7 +570,7 @@ static int handle_c(dep_info *dep) {
dsv_init(&ctx, 2);
- while (!dsv_parse_file(&ctx, fp)) {
+ while (!dsv_parse_file(&ctx, pathfd)) {
char *target, *abs = NULL;
if (!is_absolute(ctx.fields[1])) {
abs = concat(3, getenv("REDO_ROOT"), "/", ctx.fields[1]);
@@ -557,7 +597,7 @@ exit:
free(ctx.fields[i]);
exit2:
dsv_free(&ctx);
- fclose(fp);
+ fclose(pathfd);
return retval;
}