welcome GPLv3! yes, k8jam is GPL'ed now, along with Jambase. because i can.
[k8jam.git] / src / jam.c
blobfeb61d898eada1c850d9ccb5994004cca1a3ac63
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * License is hereby granted to use this software and distribute it
5 * freely, as long as this copyright notice is retained and modifications
6 * are clearly marked.
8 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * jam.c - make redux
28 * See Jam.html for usage information.
30 * These comments document the code.
32 * The top half of the code is structured such:
34 * jam
35 * / | \
36 * +---+ | \
37 * / | \
38 * jamgram option \
39 * / | \ \
40 * / | \ \
41 * / | \ |
42 * scan | compile make
43 * | | / | \ / | \
44 * | | / | \ / | \
45 * | | / | \ / | \
46 * jambase parse | rules search make1
47 * | | | \
48 * | | | \
49 * | | | \
50 * builtins timestamp command execute
51 * |
52 * |
53 * |
54 * filesys
57 * The support routines are called by all of the above, but themselves
58 * are layered thus:
60 * variable|expand
61 * / | | |
62 * / | | |
63 * / | | |
64 * lists | | pathsys
65 * \ | |
66 * \ | |
67 * \ | |
68 * newstr |
69 * \ |
70 * \ |
71 * \ |
72 * hash
74 * Roughly, the modules are:
76 * builtins.c - jam's built-in rules
77 * command.c - maintain lists of commands
78 * compile.c - compile parsed jam statements
79 * execcmd.c - execute a shell script on UNIX
80 * expand.c - expand a buffer, given variable values
81 * file*.c - scan directories and archives on *
82 * hash.c - simple in-memory hashing routines
83 * headers.c - handle #includes in source files
84 * jambase.c - compilable copy of Jambase
85 * jamgram.y - jam grammar
86 * lists.c - maintain lists of strings
87 * make.c - bring a target up to date, once rules are in place
88 * make1.c - execute command to bring targets up to date
89 * newstr.c - string manipulation routines
90 * option.c - command line option processing
91 * parse.c - make and destroy parse trees as driven by the parser
92 * path*.c - manipulate file names on *
93 * hash.c - simple in-memory hashing routines
94 * re9.c - regexp engine
95 * rules.c - access to RULEs, TARGETs, and ACTIONs
96 * scan.c - the jam yacc scanner
97 * search.c - find a target along $(SEARCH) or $(LOCATE)
98 * timestamp.c - get the timestamp of a file or archive member
99 * variable.c - handle jam multi-element variables
101 #include "jam.h"
102 #include "jambase.h"
103 #include "option.h"
104 #include "patchlevel.h"
106 /* These get various function declarations. */
107 #include "lists.h"
108 #include "parse.h"
109 #include "variable.h"
110 #include "compile.h"
111 #include "builtins.h"
112 #include "rules.h"
113 #include "newstr.h"
114 #include "scan.h"
115 #include "timestamp.h"
116 #include "make.h"
117 #include "hcache.h"
119 /* And UNIX for this */
120 #ifdef unix
121 # include <sys/utsname.h>
122 #endif
125 #ifdef USE_DIETLIBC
126 char *stpcpy (char *dst, const char *src) {
127 while ((*dst++ = *src++));
128 return (dst-1);
130 #endif
133 struct globs globs = {
134 .noexec = 0,
135 .jobs = 1,
136 .quitquick = 0,
137 .newestfirst = 0, /* newestfirst */
138 .debug = { 0, 1 }, /* display actions */
139 .cmdout = NULL, /* output commands, not run them */
140 //#ifdef OPT_IMPROVED_PROGRESS_EXT
141 .updating = 0,
142 .progress = NULL,
143 //#endif
147 /* symbols to be defined as true for use in Jambase */
148 static const char *othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, JAMVERSYM_HI, JAMVERSYM_MID, JAMVERSYM_LO, 0 } ;
151 /* Known for sure:
152 * OS2 needs extern environ
154 #ifndef use_environ
155 # define use_environ environ
156 # if !defined(__WATCOM__) && !defined(OS_NT)
157 extern char **environ;
158 # endif
159 #endif
162 static void fixJobs (void) {
163 LIST *var = var_get("K8JAM-JOBS"); /*FIXME: a perfectly idiotic name*/
164 char buf[128];
166 if (var) {
167 int cnt = list_length(var);
169 if (cnt == 0) {
170 globs.jobs = 1;
171 } else {
172 int jj = atoi(var->string);
174 if (jj < 1) jj = 1; else if (jj > 64) jj = 64;
175 globs.jobs = jj;
179 if (globs.jobs < 1) globs.jobs = 1; else if (globs.jobs > 64) globs.jobs = 64;
180 snprintf(buf, sizeof(buf), "%d", globs.jobs);
181 var = list_new(L0, buf, 0);
182 var_set("K8JAM-JOBS", var, VAR_SET);
186 int main (int argc, char **argv, char **arg_environ) {
187 int n, num_targets;
188 const char *s;
189 struct option optv[N_OPTS];
190 char *targets[N_TARGETS];
191 const char *all = "all";
192 int anyhow = 0;
193 int status;
194 int wasOptH = 0;
196 --argc; ++argv;
197 if ((num_targets = getoptions(argc, argv, "H:d:j:f:gs:t:ano:qvZW", optv, targets)) < 0) {
198 printf("usage: jam [ options ] targets...\n\n"
199 "-a Build all targets, even if they are current.\n"
200 "-dx Display (a)actions (c)causes (d)dependencies\n"
201 " (m)make tree (x)commands ([+]0-9) debug levels.\n"
202 "-fx Read x instead of Jambase.\n"
203 "-g Build from newest sources first.\n"
204 "-jx Run up to x shell commands concurrently.\n"
205 "-n Don't actually execute the updating actions.\n"
206 "-ox Write the updating actions to file x.\n"
207 "-q Quit quickly as soon as a target fails.\n"
208 "-sx=y Set variable x=y, overriding environment.\n"
209 "-tx Rebuild x, even if it is up-to-date.\n"
210 "-v Print the version of jam and exit.\n"
211 "-Hx Show header cache statistics (a)ll, (h)its, (s)tats.\n"
212 "-W Write built-in Jambase and exit.\n"
213 "\ndebug levels:\n"
214 " 1 same as -da, but don't show quiet actions\n"
215 " 2 nothing\n"
216 " 3 same as -dm\n"
217 " 4 execcmds()'s work\n"
218 " 5 rule invocations\n"
219 " 6 result of header scan, dir scan and attempts at binding\n"
220 " 7 variable settings\n"
221 " 8 variable fetches and expansions, 'if' calculations\n"
222 " 9 list manipulation, scanner tokens, memory use\n"
224 exit(EXITBAD);
226 /* version info. */
227 if ((s = getoptval(optv, 'v', 0))) {
228 printf("K8Jam %s %s\n", VERSION, OSMINOR);
229 printf(
230 "(C) 1993-2003 Christopher Seiwald at all\n"
231 "changes by Ketmar // Vampire Avalon, psyc://ketmar.no-ip.org/~ketmar\n"
232 "http://repo.or.cz/w/k8jam.git\n"
234 exit(EXITOK);
236 /* pick up interesting options */
237 if ((s = getoptval(optv, 'W', 0))) {
238 FILE *fl = fopen("Jambase", "w");
239 if (fl != NULL) {
240 printf("writing Jambase...\n");
241 jambase_unpack();
242 for (int f = 0; jambase[f]; ++f) fprintf(fl, "%s", jambase[f]);
243 fclose(fl);
245 exit(EXITOK);
247 if ((s = getoptval(optv, 'n', 0))) ++globs.noexec, DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 1;
248 if ((s = getoptval(optv, 'q', 0))) globs.quitquick = 1;
249 if ((s = getoptval(optv, 'a', 0))) ++anyhow;
250 if ((s = getoptval(optv, 'j', 0))) globs.jobs = atoi(s);
251 if ((s = getoptval(optv, 'g', 0))) globs.newestfirst = 1;
252 /* turn on/off debugging */
253 for (n = 0; (s = getoptval(optv, 'd', n)) != 0; ++n) {
254 int i = atoi(s);
255 /* first -d, turn off defaults. */
256 if (!n) DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 0;
257 /* n turns on levels 1-n */
258 /* +n turns on level n */
259 /* c turns on named display c */
260 if (i < 0 || i >= DEBUG_MAX) {
261 printf( "Invalid debug level '%s'.\n", s );
262 } else if (*s == '+') {
263 globs.debug[i] = 1;
264 } else if (i) {
265 while (i) globs.debug[i--] = 1;
266 } else {
267 while (*s) {
268 switch (*s++) {
269 case 'a': DEBUG_MAKE = DEBUG_MAKEQ = 1; break;
270 case 'c': DEBUG_CAUSES = 1; break;
271 case 'd': DEBUG_DEPENDS = 1; break;
272 case 'm': DEBUG_MAKEPROG = 1; break;
273 case 'x': DEBUG_EXEC = 1; break;
274 case 'h': DEBUG_HEADER = 1; break;
275 case 's': DEBUG_SCAN = 1; break;
276 case '0': break;
277 default: printf("Invalid debug flag '%c'.\n", s[-1]);
282 /* header cache statistics */
283 for (n = 0; (s = getoptval(optv, 'H', n)) != 0; ++n) {
284 wasOptH = 1;
285 for (; *s; ++s) {
286 switch (*s) {
287 case 'a': optShowHCacheStats = optShowHCacheInfo = 1; break;
288 case 'h': optShowHCacheInfo = 1; break;
289 case 's': optShowHCacheStats = 1; break;
293 if (wasOptH && (optShowHCacheStats == 0 && optShowHCacheInfo == 0)) optShowHCacheStats = optShowHCacheInfo = 1;
294 /* set JAMDATE first */
296 char buf[128], *p;
297 time_t clock;
299 time(&clock);
300 strcpy(buf, ctime(&clock));
301 /* trim newline from date */
302 p = buf+strlen(buf);
303 while (p >= buf && *((unsigned char *)p) <= ' ') --p;
304 *(++p) = '\0';
305 var_set("JAMDATE", list_new(L0, buf, 0), VAR_SET);
307 /* And JAMUNAME */
308 #ifdef unix
310 struct utsname u;
312 if (uname(&u) >= 0) {
313 LIST *l = L0;
314 l = list_new(l, u.machine, 0);
315 l = list_new(l, u.version, 0);
316 l = list_new(l, u.release, 0);
317 l = list_new(l, u.nodename, 0);
318 l = list_new(l, u.sysname, 0);
319 var_set("JAMUNAME", l, VAR_SET);
322 #endif /* unix */
323 /* load up environment variables */
324 var_defines((const char **)use_environ, 0);
325 /* Jam defined variables OS, OSPLAT */
326 var_defines(othersyms, 1);
327 /* load up variables set on command line. */
328 for (n = 0; (s = getoptval(optv, 's', n)) != 0; ++n) {
329 const char *symv[2];
330 symv[0] = s;
331 symv[1] = 0;
332 var_defines(symv, 1);
334 /* add JAMCMDARGS */
336 LIST *l0 = L0, *l1 = L0;
338 if (num_targets < 1) {
339 l0 = list_new(l0, "all", 0);
340 l1 = list_new(l1, "all", 0);
341 } else {
342 for (n = 0; n < num_targets; ++n) {
343 //printf("target %i: %s\n", n, targets[n]);
344 l0 = list_new(l0, targets[n], 0);
345 l1 = list_new(l1, targets[n], 0);
348 var_set("JAMCMDARGS", l0, VAR_SET);
349 /* k8 */
350 var_set("JAM_TARGETS", l1, VAR_SET);
352 /* add JAMCONFIGARGS */
354 LIST *l0 = L0;
356 for (n = 0; n < num_cfgargs; ++n) l0 = list_new(l0, cfgargs[n], 0);
357 var_set("JAMCONFIGARGS", l0, VAR_SET);
359 /* initialize built-in rules */
360 load_builtins();
361 /* parse ruleset */
362 for (n = 0; (s = getoptval(optv, 'f', n)) != 0; ++n) parse_file(s);
363 if (!n) parse_file("::Jambase");
364 status = 0; /*yyanyerrors();*/
365 if (status) {
366 printf("FATAL: parsing error occured!\n");
367 exit(EXITBAD);
369 /* manually touch -t targets */
370 for (n = 0; (s = getoptval(optv, 't', n)) != 0; ++n) touchtarget(s);
371 /* if an output file is specified, set globs.cmdout to that */
372 if ((s = getoptval(optv, 'o', 0)) != 0) {
373 if (!(globs.cmdout = fopen(s, "w"))) {
374 printf("Failed to write to '%s'\n", s);
375 exit(EXITBAD);
377 ++globs.noexec;
379 /* fix number of jobs (if necessary) */
380 fixJobs();
381 #ifndef NO_OPT_JAM_TARGETS_VARIABLE_EXT
382 /* ported from Haiku */
383 /* get value of variable JAM_TARGETS and build the targets */
385 LIST *l = var_get("JAM_TARGETS");
386 int targetCount = list_length(l);
387 int i;
389 if (targetCount == 0) {
390 /* no targets: nothing to do */
391 printf("No targets. Nothing to do.\n");
392 exit(EXITOK);
394 if (targetCount >= N_TARGETS) {
395 printf("ERROR: Too many targets!\n");
396 exit(EXITBAD);
398 for (i = 0; i < targetCount; ++i) {
399 const char *s0 = l->string;
400 targets[i] = malloc((strlen(s0)+1)*sizeof(char));
401 if (!targets[i]) {
402 printf("ERROR: Out of memory!\n");
403 exit(EXITBAD);
405 strcpy(targets[i], s0);
406 l = l->next;
408 num_targets = targetCount;
410 printf("new targets:\n");
411 for (i = 0; i < num_targets; i++) {
412 printf("target %i: %s\n", i, targets[i]);
416 #endif
417 /* now make target */
418 if (!num_targets) status |= make(1, &all, anyhow);
419 else status |= make(num_targets, (const char **)targets, anyhow);
420 /* widely scattered cleanup */
421 //#ifdef OPT_HEADER_CACHE_EXT
422 /* note that cache will not be written if it is not updated, so 'clean' will work ok */
423 if (!globs.noexec) hcache_done();
424 //#endif
425 regexp_done();
426 var_done();
427 donerules();
428 donestamps();
429 donestr();
430 /* close cmdout */
431 if (globs.cmdout) fclose(globs.cmdout);
432 return (status ? EXITBAD : EXITOK);