~ chicken-core (chicken-5) 21ccd9b442f72700bb737fe095b4cf17c6eef4df


commit 21ccd9b442f72700bb737fe095b4cf17c6eef4df
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Sat Oct 20 13:54:03 2018 +0200
Commit:     Evan Hanson <evhan@foldling.org>
CommitDate: Mon Oct 29 17:16:54 2018 +1300

    Allow multiple targets in "chicken-do" invocations
    
    To handle selective deletion of files generated by egg-builds, we need a
    way to make sure targets are re-built when one of several generated
    files are missing or out of date. This patch generalizes chicken-do(1)
    to allow multiple targets. The reordering of the arguments is inspired
    by a proposal from Kooda.
    
    Signed-off-by: Evan Hanson <evhan@foldling.org>

diff --git a/chicken-do.c b/chicken-do.c
index c58acd7e..f4175229 100644
--- a/chicken-do.c
+++ b/chicken-do.c
@@ -39,22 +39,33 @@
 #include <sys/stat.h>
 #include <errno.h>
 
-static char *target;
+#define MAX_TARGETS 256
+#define MAX_DEPENDS 1024
+
+static char *targets[ MAX_TARGETS ];
+static char *depends[ MAX_DEPENDS ];
+static struct stat tstats[ MAX_TARGETS ];
+static char **cmd;
+static int opts = 1;
+static int quiet = 0;
 
 
 static void usage(int code)
 {
-  fputs("usage: chicken-do [-q] [-h] TARGET COMMAND ... : DEPENDENCIES ...\n", stderr);
+  fputs("usage: chicken-do [-q] [-h] [--] TARGET ... : DEPENDENCY ... : COMMAND ...\n", stderr);
   exit(code);
 }
 
 
 static void cleanup()
 {
+  char **t;
+
+  for(t = targets; *t != NULL; ++t)
 #ifdef WIN32
-  DeleteFile(target);
+    DeleteFile(*t);
 #else
-  unlink(target);
+    unlink(*t);
 #endif
 }
 
@@ -72,7 +83,7 @@ static int execute(char **argv)
     strcat(cmdline, *(argv++));
     strcat(cmdline, "\" ");
   }
-  
+
   if(!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 
                     NORMAL_PRIORITY_CLASS, NULL, NULL, &startup_info,
                     &process_info)) {
@@ -132,48 +143,75 @@ static int execute(char **argv)
 
 int main(int argc, char *argv[]) 
 {
-  int i, count, a = 0;
-  char **args = (char **)malloc(sizeof(char *) * argc);
-  struct stat st, sd;
-  int quiet = 0, opts = 1;
+  int i, a = 0;
+  struct stat *st, sd;
+  char **t = targets;
+  char **d = depends;
 
   if(argc < 3) usage(1);
 
-  target = argv[ 1 ];
+  for(i = 1; i < argc; ++i) {
+    if(!strcmp(argv[ i ], ":")) {
+      *t = NULL;
+      break;
+    }
 
-  for(i = 2; i < argc; ++i) {
     if(opts && *argv[ i ] == '-') {
       switch(argv[ i ][ 1 ]) {
       case 'q': quiet = 1; break;
       case 'h': usage(0);
+      case '-': opts = 0; break;
       default: usage(1);
       }
     }
-    else if(!strcmp(argv[ i ], ":")) break;
-    else {
-      args[ a++ ] = argv[ i ];
-      opts = 0;
+    else if(t >= targets + MAX_TARGETS) {
+      fprintf(stderr, "too many targets\n");
+      exit(1);
     }
+    else *(t++) = argv[ i ];
   }
 
   if(i == argc) usage(1);
 
-  args[ a ] = NULL;
+  while(++i < argc) {
+    if(!strcmp(argv[ i ], ":")) {
+      *d = NULL;
+      break;
+    }
 
-  if(stat(target, &st) == -1) {
-    if(errno == ENOENT) goto build;
+    if(d >= depends + MAX_DEPENDS) {
+      fprintf(stderr, "too many dependencies\n");
+      exit(1);
+    }
 
-    fprintf(stderr, "%s: %s\n", target, strerror(errno));
-    exit(1);
+    *(d++) = argv[ i ];
   }
 
-  for(++i; i < argc; ++i) {
-    if(stat(argv[ i ], &sd) == -1) {
-      fprintf(stderr, "%s: %s\n", argv[ i ], strerror(errno));
+  if(i == argc) usage(1);
+
+  cmd = argv + i + 1;
+  st = tstats;
+
+  for(t = targets; *t != NULL; ++t) {
+    if(stat(*t, st++) == -1) {
+      if(errno == ENOENT) goto build;
+
+      fprintf(stderr, "%s: %s\n", *t, strerror(errno));
+      exit(1);
+    }
+  }
+
+  for(d = depends; *d != NULL; ++d) {
+    if(stat(*d, &sd) == -1) {
+      fprintf(stderr, "%s: %s\n", *d, strerror(errno));
       exit(1);
     }      
 
-    if(sd.st_mtime > st.st_mtime) goto build;
+    st = tstats;
+
+    for(t = targets; *t != NULL; ++t) {
+      if(sd.st_mtime > (st++)->st_mtime) goto build;
+    }
   }
 
   return 0;
@@ -182,14 +220,14 @@ build:
   if(!quiet) {
     fputs("  ", stdout);
 
-    for(i = 0; i < a; ++i)
-      printf(" %s", args[ i ]);
+    for(t = cmd; *t != NULL; ++t)
+      printf(" %s", *t);
 
     putchar('\n');
     fflush(stdout);
   }
 
-  int s = execute(args);
+  int s = execute(cmd);
 
   if(s != 0) cleanup();
 
diff --git a/chicken-do.mdoc b/chicken-do.mdoc
index 6b6e4d2d..20cf1697 100644
--- a/chicken-do.mdoc
+++ b/chicken-do.mdoc
@@ -35,33 +35,37 @@ not newer than the target
 .Nm
 .Op Fl q
 .Op Fl h
-.Ar TARGET
-.Ar COMMAND ...
+.Op Fl -
+.Ar TARGET ...
 .Cm :
 .Ar DEPENDENCY ...
+.Cm :
+.Ar COMMAND ...
 .Sh DESCRIPTION
-If the file
-.Em TARGET
-does not exist, or if any of the files in
+If the files
+.Em TARGET ...
+do not exist, or if any of the files in
 .Em DEPENDENCY ...
-are newer than
-.Em TARGET ,
-then execute the shell commands in
+are newer than one of the target files, then execute the shell commands in
 .Em COMMAND ... .
 .Pp
-The program accepts following arguments:
+The program accepts the following options:
 .Bl -tag -width Ds
 .It Fl h
 Show usage and exit.
 .It Fl q
 Quiet operation - do not print shell commands when they are executed.
+.It Fl -
+Treat all following tokens as target filenames, even if they begin with a
+.It -
+character.
 .El
 .Sh EXIT STATUS
 .Ex -std
 .Sh EXAMPLES
 Build a C program from sources:
 .Pp
-.Dl $ chicken-do prg gcc prg.c -o prg \& : prg.c inc.h
+.Dl $ chicken-do prg : prg.c inc.h : gcc prg.c -o prg
 .Sh SEE ALSO
 .Xr chicken 1 ,
 .Xr chicken-status 1 ,
diff --git a/egg-compile.scm b/egg-compile.scm
index 52b4bded..676afdd9 100644
--- a/egg-compile.scm
+++ b/egg-compile.scm
@@ -471,80 +471,95 @@
 		       default-csc)
 		   platform))
          (sname (prefix srcdir name))
+         (tfile (qs* (prefix srcdir (conc types-file ".types"))
+                     platform))
+         (ifile (qs* (prefix srcdir (conc inline-file ".inline"))
+                     platform))
+         (lfile (qs* (conc sname +link-file-extension+) platform))
          (opts (append (if (null? options)
                            default-static-compilation-options
                            options)
                        (if (and types-file
                                 (not predefined-types))
-                           (list "-emit-types-file"
-                                 (qs* (prefix srcdir (conc types-file ".types"))
-				      platform))
+                           (list "-emit-types-file" tfile)
                            '())
                        (if inline-file
-                           (list "-emit-inline-file"
-                                 (qs* (prefix srcdir (conc inline-file ".inline"))
-				      platform))
+                           (list "-emit-inline-file" ifile)
                            '())))
          (out (qs* (target-file (conc sname
 				      ".static"
 				      (object-extension platform))
 				mode)
 		   platform))
+         (targets (append (list out lfile)
+                          (maybe types-file tfile)
+                          (maybe inline-file ifile)))
          (src (qs* (or source (conc name ".scm")) platform)))
     (when custom
       (prepare-custom-command cmd platform))
-    (print "\n" (qs* default-builder platform #t) " " out " " cmd 
+    (print "\n" (qs* default-builder platform #t) " "
+           (joins targets) " : "
+           src " " (qs* eggfile platform) " "
+           (if custom cmd "") " "
+           (filelist srcdir source-dependencies platform)
+           " : " cmd
            (if keep-generated-files " -k" "")
            " -setup-mode -static -I " srcdir 
-           " -emit-link-file "
-           (qs* (conc sname +link-file-extension+) platform)
+           " -emit-link-file " lfile
            (if (eq? mode 'host) " -host" "")
            " -D compiling-extension -c -unit " name
            " -D compiling-static-extension"
            " -C -I" srcdir (arglist opts platform) 
-           " " src " -o " out " : "
-           src " " (qs* eggfile platform) " "
-           (if custom cmd "") " "
-           (filelist srcdir source-dependencies platform))
+           " " src " -o " out)
     (print-end-command platform)))
 
 (define ((compile-dynamic-extension name #!key mode mode
                                     source (options '()) (link-options '()) 
                                     predefined-types eggfile
-                                    source-dependencies
+                                    source-dependencies modules
                                     custom types-file inline-file)
          srcdir platform)
   (let* ((cmd (qs* (or (custom-cmd custom srcdir platform)
 		       default-csc)
 		   platform))
          (sname (prefix srcdir name))
+         (tfile (qs* (prefix srcdir (conc types-file ".types"))
+                     platform))
+         (ifile (qs* (prefix srcdir (conc inline-file ".inline"))
+                     platform))
          (opts (append (if (null? options)
                            default-dynamic-compilation-options
                            options)
                        (if (and types-file
                                 (not predefined-types))
-                           (list "-emit-types-file"
-                                 (qs* (prefix srcdir (conc types-file ".types"))
-				      platform))
+                           (list "-emit-types-file" tfile)
                            '())
                        (if inline-file
-                           (list "-emit-inline-file"
-                                 (qs* (prefix srcdir (conc inline-file ".inline"))
-				      platform))
+                           (list "-emit-inline-file" ifile)
                            '())))
          (out (qs* (target-file (conc sname ".so") mode) platform))
+         (targets (append (list out)
+                          (maybe inline-file ifile)
+                          (maybe types-file tfile)
+                          (map (lambda (m)
+                                 (qs* (prefix srcdir (conc m ".import.scm"))
+                                      platform))
+                            modules)))
          (src (qs* (or source (conc name ".scm")) platform)))
     (when custom
       (prepare-custom-command cmd platform))
-    (print "\n" (qs* default-builder platform #t) " " out " " cmd 
+    (print "\n" (qs* default-builder platform #t) " "
+           (joins targets) " : "
+           src " " (qs* eggfile platform) " "
+           (if custom cmd "") " "
+           (filelist srcdir source-dependencies platform)
+           " : " cmd
            (if keep-generated-files " -k" "")
            (if (eq? mode 'host) " -host" "")
            " -D compiling-extension -J -s"
            " -setup-mode -I " srcdir " -C -I" srcdir
 	   (arglist opts platform) (arglist link-options platform)
-	   " " src " -o " out " : " src " " (qs* eggfile platform) " "
-           (if custom cmd "") " "
-           (filelist srcdir source-dependencies platform))
+	   " " src " -o " out)
     (print-end-command platform)))
 
 (define ((compile-import-library name #!key mode
@@ -559,13 +574,14 @@
          (out (qs* (target-file (conc sname ".import.so") mode)
 		   platform))
          (src (qs* (conc name ".import.scm") platform)))
-    (print "\n" (qs* default-builder platform #t) " " out " " cmd 
+    (print "\n" (qs* default-builder platform #t) " " out " : "
+           src (filelist srcdir source-dependencies platform)
+           " : " cmd
            (if keep-generated-files " -k" "")
            " -setup-mode -s"
            (if (eq? mode 'host) " -host" "")
            " -I " srcdir " -C -I" srcdir (arglist opts platform)
-           (arglist link-options platform) " " src " -o " out " : "
-           src (filelist srcdir source-dependencies platform))
+           (arglist link-options platform) " " src " -o " out)
     (print-end-command platform)))
 
 (define ((compile-dynamic-program name #!key source mode
@@ -587,15 +603,16 @@
          (src (qs* (or source (conc name ".scm")) platform)))
     (when custom
       (prepare-custom-command cmd platform))
-    (print "\n" (qs* default-builder platform #t) " " out " " cmd 
+    (print "\n" (qs* default-builder platform #t) " " out " : "
+           src " " (qs* eggfile platform) " "
+           (if custom cmd "") " "
+           (filelist srcdir source-dependencies platform)
+           " : " cmd
            (if keep-generated-files " -k" "")
            " -setup-mode"
            (if (eq? mode 'host) " -host" "")
            " -I " srcdir " -C -I" srcdir (arglist opts platform)
-           (arglist link-options platform) " " src " -o " out " : "
-           src " " (qs* eggfile platform) " "
-           (if custom cmd "") " "
-           (filelist srcdir source-dependencies platform))
+           (arglist link-options platform) " " src " -o " out)
     (print-end-command platform)))
 
 (define ((compile-static-program name #!key source
@@ -617,15 +634,16 @@
          (src (qs* (or source (conc name ".scm")) platform)))
     (when custom
       (prepare-custom-command cmd platform))
-    (print "\n" (qs* default-builder platform #t) " " out " " cmd 
+    (print "\n" (qs* default-builder platform #t) " " out " : "
+           src " " (qs* eggfile platform) " "
+           (if custom cmd "") " "
+           (filelist srcdir source-dependencies platform)
+           " : " cmd
            (if keep-generated-files " -k" "")
            (if (eq? mode 'host) " -host" "")
            " -static -setup-mode -I " srcdir " -C -I" 
            srcdir (arglist opts platform)
-           (arglist link-options platform) " " src " -o " out " : "
-           src " " (qs* eggfile platform) " "
-           (if custom cmd "") " "
-           (filelist srcdir source-dependencies platform))
+           (arglist link-options platform) " " src " -o " out)
     (print-end-command platform)))
 
 (define ((compile-generated-file name #!key source custom
@@ -635,9 +653,10 @@
         (out (qs* (or source name) platform)))
     (prepare-custom-command cmd platform)
     (print "\n" (qs* default-builder platform #t)
-           " " out " " cmd " : " cmd " "
+           " " out " : " cmd " "
            (qs* eggfile platform) " "
-           (filelist srcdir source-dependencies platform))
+           (filelist srcdir source-dependencies platform)
+           " : " cmd)
     (print-end-command platform)))
 
 
@@ -977,3 +996,7 @@ EOF
          (p1 (substring fname 0 plen)))
     (assert (string=? prefix p1) "wrong prefix")
     (substring fname (add1 plen))))
+
+(define (joins strs) (string-intersperse strs " "))
+
+(define (maybe f x) (if f (list x) '()))
Trap