diff options
-rw-r--r-- | build.c | 107 |
1 files changed, 73 insertions, 34 deletions
@@ -17,7 +17,7 @@ fprintf(stderr, __VA_ARGS__); } while (0); #define MAXBUF 1024 -#define MAXCMDS 32 +#define MAXCMDS 32 /* Maximum number of commands across all files. */ #define MAXCMD 1024 #define MAXDEP 1024 #define MAXTGT 64 @@ -37,7 +37,7 @@ main(int argc, char *argv[]) int c, dflag, fflag, i, icmd, j, s; struct stat sb, ssb; - /* allocate memory */ + /* Allocate memory. */ for (i = 0; i < MAXCMDS; i++) { cmd[i] = malloc(MAXCMD*sizeof(char)); if (!cmd[i]) err(1, "malloc"); @@ -49,7 +49,7 @@ main(int argc, char *argv[]) tgt[0] = dep[0] = 0; - /* process arguments */ + /* Process command-line flags (debug, force). */ name = argv[0]; dflag = fflag = 0; while ((c = getopt(argc, argv, "df")) != -1) @@ -71,24 +71,23 @@ main(int argc, char *argv[]) return 1; } - /* process each file */ + /* Process dependencies and commands in each file. */ for (i = icmd = 0; i < argc; i++, icmd = 0) { fp = fopen(argv[i], "r"); if (!fp) err(1, "fopen"); - /* read line by line */ + /* Read line by line, at most twenty. */ for (j = 0; j < 20 && fgets(buf, MAXBUF, fp); j++) { buf[strcspn(buf, "\n")] = 0; - - /* parse line */ b = buf; for (; *b; b++) { - /* command line */ + + /* Find command line. */ if (strncmp(b, " $ ", 3) == 0 || strncmp(b, " $ ", 3) == 0) { strncpy(cmd[icmd++], b+3, MAXBUF-1); - /* find target */ + /* Find target inside command. */ for (b = b+3; *b; b++) { if (!(*b+1)) continue; if (*b != '>') continue; @@ -101,7 +100,7 @@ main(int argc, char *argv[]) continue; } - /* dependency line */ + /* Find dependency line. */ if (strncmp(b, " % ", 3) == 0 || strncmp(b, " % ", 3) == 0) { strncpy(dep, b+3, MAXDEP-1); @@ -117,11 +116,15 @@ main(int argc, char *argv[]) goto next; } + /* Build immediately if forced or no target found. */ if (fflag || !*tgt) goto build; - /* compare source > target */ + /* Trim shell meta-characters and whitespace. */ cleantgt(&tgt); + cleandep(&dep); + + /* Build immediately if target is newer than source. */ dd("%s: target '%s'\n", argv[i], tgt); if (stat(argv[i], &sb)) err(1, argv[i]); @@ -136,25 +139,27 @@ main(int argc, char *argv[]) goto build; } + /* + * If target is newer than source and there are no + * dependencies, the target is up-to-date. + */ if (!*dep) goto uptodate; - /* compare dependencies > target */ - cleandep(&dep); + /* Build immediately if any dependency is newer than target. */ while (d = nextdep(&dep)) { - dd("%s: depend '%s'\n", - argv[i], d); + dd("%s: depend '%s'\n", argv[i], d); if (stat(d, &sb)) { - if (errno != ENOENT) - err(1, d); - fprintf(stderr, "%s: dependency %s does not " - "exist\n", argv[i], d); - continue; + if (errno == ENOENT) { + fprintf(stderr, "%s: dependency %s " + "does not exist\n", argv[i], d); + continue; + } + err(1, d); } if (sb.st_mtime > ssb.st_mtime) { - d("%s: %s is modified, building\n", - argv[i], d); + d("%s: %s is modified, building\n", argv[i], d); free(d); goto build; } @@ -162,11 +167,21 @@ main(int argc, char *argv[]) } uptodate: + /* + * As neither the source or the dependencies are newer + * than the target, the target is up-to-date. + */ fprintf(stderr, "%s: already up-to-date\n", argv[i]); goto next; build: - /* run commands */ + /* + * All commands found in the file are concatenated to a + * single string, separated by newlines, which is passed + * to system(1). The -e option makes the shell exit + * whenever a command fails. The -x option makes the shell + * print executed commands. + */ buf[0] = 0; strcat(buf, "set -ex\n"); for (j = 0; j < icmd; j++) { @@ -174,9 +189,17 @@ build: strncat(buf, "\n", MAXBUF-1); } s = system(buf); + + /* Process next file if shell command succeeded. */ if (s == 0) goto next; + /* + * As the shell command was unsuccessful, the return value + * of system(1) is processed using the macros defined in + * <sys/wait.h> and printed to the user. The program exits + * with a positive status. + */ if (WIFEXITED(s)) { fprintf(stderr, "%s: exited with %d\n", argv[i], WEXITSTATUS(s)); @@ -195,7 +218,7 @@ next: void cleandep(char **dep) { - for (; **dep; (*dep)++) + for (; **dep; ++*dep) if (!isspace(**dep)) break; } @@ -204,7 +227,7 @@ cleantgt(char **tgt) { char *t; - for (; **tgt; (*tgt)++) + for (; **tgt; ++*tgt) if (!isspace(**tgt)) break; for (t = *tgt; *t; t++) @@ -213,7 +236,8 @@ cleantgt(char **tgt) || *t == '&' || *t == ';' || *t == ')') { - *t = 0; break; + *t = 0; + break; } } @@ -223,18 +247,33 @@ nextdep(char **dep) char *d; int i; + /* Read dependency string character-by-character. */ for (i = 0; (*dep)[i]; i++) { - if (isspace((*dep)[i]) || (*dep)[i+1] == 0) { + /* + * Upon encountering a space or the final character, the + * hitherto gathered string is stored in a copy. + */ + if (isspace((*dep)[i]) || (*dep)[i+1] == 0 && i++) { d = strdup(*dep); - d[i+((*dep)[i+1]==0)] = 0; - - for (; (*dep)[i]; i++) - if (!isspace((*dep)[i])) break; - - (*dep) += i + ((*dep)[i+1]==0); - break; + d[i] = 0; + goto found; } } + /* + * The dependency string has a length of zero, meaning that the + * processing is completed. + */ + return NULL; + +found: + /* + * The original dependency string is incremented until the next + * dependency. + */ + for (; (*dep)[i]; i++) + if (!isspace((*dep)[i])) break; + *dep += i; + return d; } |