aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.c107
1 files changed, 73 insertions, 34 deletions
diff --git a/build.c b/build.c
index 6db1aed..aedb455 100644
--- a/build.c
+++ b/build.c
@@ -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;
}