diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/build.c | 108 | 
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;  }  | 
