~ chicken-core (master) /chicken-do.c
Trap1/* 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}