~ chicken-core (master) /chicken-do.c


  1/* chicken-do
  2;
  3; Execute command if dependency changed or target is out of date.
  4;
  5; Copyright (c) 2017-2022, The CHICKEN Team
  6; All rights reserved.
  7;
  8; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
  9; conditions are met:
 10;
 11;   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 12;     disclaimer.
 13;   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 14;     disclaimer in the documentation and/or other materials provided with the distribution.
 15;   Neither the name of the author nor the names of its contributors may be used to endorse or promote
 16;     products derived from this software without specific prior written permission.
 17;
 18; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 19; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 20; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 21; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 23; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 24; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 25; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 26; POSSIBILITY OF SUCH DAMAGE.
 27*/
 28
 29
 30#include "chicken.h"
 31
 32#ifdef WIN32
 33# include <windows.h>
 34# include <sys/types.h>
 35#else
 36# include <sys/wait.h>
 37#endif
 38
 39#include <sys/stat.h>
 40#include <errno.h>
 41
 42#define MAX_TARGETS 256
 43#define MAX_DEPENDS 1024
 44
 45#ifdef WIN32
 46# define MAX_COMMAND_LEN 32767
 47#endif
 48
 49static char *targets[ MAX_TARGETS ];
 50static char *depends[ MAX_DEPENDS ];
 51static struct stat tstats[ MAX_TARGETS ];
 52static char **cmd;
 53static int opts = 1;
 54static int quiet = 0;
 55
 56
 57static void usage(int code)
 58{
 59  fputs("usage: chicken-do [-q] [-h] [--] TARGET ... : DEPENDENCY ... : COMMAND ...\n", stderr);
 60  exit(code);
 61}
 62
 63
 64static void cleanup()
 65{
 66  char **t;
 67
 68  for(t = targets; *t != NULL; ++t)
 69#ifdef WIN32
 70    DeleteFile(*t);
 71#else
 72    unlink(*t);
 73#endif
 74}
 75
 76
 77static int execute(char **argv)
 78{
 79#ifdef WIN32
 80  static PROCESS_INFORMATION process_info;
 81  static STARTUPINFO startup_info;
 82  static TCHAR cmdline[ MAX_COMMAND_LEN ];
 83  static int len;
 84
 85  ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
 86  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
 87  startup_info.cb = sizeof(STARTUPINFO);
 88
 89  /* quote command arguments */
 90  while(*argv != NULL) {
 91    len += snprintf(cmdline + len, sizeof(cmdline) - len, "%s ", *(argv++));
 92    if(len > sizeof(cmdline)) {
 93      fprintf(stderr, "argument list too long\n");
 94      exit(1);
 95    }
 96  }
 97
 98  if(!CreateProcess(NULL, cmdline, NULL, NULL, TRUE,
 99                    NORMAL_PRIORITY_CLASS, NULL, NULL, &startup_info,
100                    &process_info)) {
101    fprintf(stderr, "creating subprocess failed (%ld)\n", GetLastError());
102    exit(1);
103  }
104
105  WaitForSingleObject(process_info.hProcess, INFINITE);
106  DWORD code;
107
108  if(!GetExitCodeProcess(process_info.hProcess, &code)) {
109    fprintf(stderr, "unable to obtain exit status of subprocess\n");
110    exit(1);
111  }
112  CloseHandle(process_info.hProcess);
113  CloseHandle(process_info.hThread);
114
115
116  return code;
117#else
118  pid_t child = fork();
119
120  if(child == -1) {
121    perror("forking subprocess failed");
122    exit(1);
123  }
124
125  if(child == 0) {
126    execvp(argv[ 0 ], argv);
127    /* returns only in case of error */
128    perror("executing command failed");
129    cleanup();
130    exit(1);
131  }
132
133  for(;;) {
134    int status;
135    pid_t w = waitpid(child, &status, 0);
136
137    if(w == -1) {
138      perror("waiting for subprocess failed");
139      cleanup();
140      exit(1);
141    }
142
143    if(WIFEXITED(status))
144      return WEXITSTATUS(status);
145
146    if(WIFSIGNALED(status)) {
147      fprintf(stderr, "subprocess killed by signal %d\n", WTERMSIG(status));
148      cleanup();
149      exit(1);
150    }
151  }
152#endif
153}
154
155
156int main(int argc, char *argv[])
157{
158  int i, a = 0;
159  struct stat *st, sd;
160  char **t = targets;
161  char **d = depends;
162
163  if(argc < 3) usage(1);
164
165  for(i = 1; i < argc; ++i) {
166    if(!strcmp(argv[ i ], ":")) {
167      *t = NULL;
168      break;
169    }
170
171    if(opts && *argv[ i ] == '-') {
172      switch(argv[ i ][ 1 ]) {
173      case 'q': quiet = 1; break;
174      case 'h': usage(0);
175      case '-': opts = 0; break;
176      default: usage(1);
177      }
178    }
179    else if(t >= targets + MAX_TARGETS) {
180      fprintf(stderr, "too many targets\n");
181      exit(1);
182    }
183    else *(t++) = argv[ i ];
184  }
185
186  if(i == argc) usage(1);
187
188  while(++i < argc) {
189    if(!strcmp(argv[ i ], ":")) {
190      *d = NULL;
191      break;
192    }
193
194    if(d >= depends + MAX_DEPENDS) {
195      fprintf(stderr, "too many dependencies\n");
196      exit(1);
197    }
198
199    *(d++) = argv[ i ];
200  }
201
202  if(i == argc) usage(1);
203
204  cmd = argv + i + 1;
205  st = tstats;
206
207  for(t = targets; *t != NULL; ++t) {
208    if(stat(*t, st++) == -1) {
209      if(errno == ENOENT) goto build;
210
211      fprintf(stderr, "%s: %s\n", *t, strerror(errno));
212      exit(1);
213    }
214  }
215
216  for(d = depends; *d != NULL; ++d) {
217    if(stat(*d, &sd) == -1) {
218      fprintf(stderr, "%s: %s\n", *d, strerror(errno));
219      exit(1);
220    }
221
222    st = tstats;
223
224    for(t = targets; *t != NULL; ++t) {
225      if(sd.st_mtime > (st++)->st_mtime) goto build;
226    }
227  }
228
229  return 0;
230
231build:
232  if(!quiet) {
233    fputs("  ", stdout);
234
235    for(t = cmd; *t != NULL; ++t)
236      printf(" %s", *t);
237
238    putchar('\n');
239    fflush(stdout);
240  }
241
242  int s = execute(cmd);
243
244  if(s != 0) cleanup();
245
246  return s;
247}
Trap