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
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, version 3 of the License ONLY.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * See Jam.html for usage information.
29 * These comments document the code.
31 * The top half of the code is structured such:
45 * jambase parse | rules search make1
49 * builtins timestamp command execute
56 * The support routines are called by all of the above, but themselves
73 * Roughly, the modules are:
75 * builtins.c - jam's built-in rules
76 * command.c - maintain lists of commands
77 * compile.c - compile parsed jam statements
78 * execcmd.c - execute a shell script on UNIX
79 * expand.c - expand a buffer, given variable values
80 * file*.c - scan directories and archives on *
81 * hash.c - simple in-memory hashing routines
82 * headers.c - handle #includes in source files
83 * jambase.c - compilable copy of Jambase
84 * jamgram.y - jam grammar
85 * lists.c - maintain lists of strings
86 * make.c - bring a target up to date, once rules are in place
87 * make1.c - execute command to bring targets up to date
88 * newstr.c - string manipulation routines
89 * option.c - command line option processing
90 * parse.c - make and destroy parse trees as driven by the parser
91 * path*.c - manipulate file names on *
92 * hash.c - simple in-memory hashing routines
93 * re9.c - regexp engine
94 * rules.c - access to RULEs, TARGETs, and ACTIONs
95 * scan.c - the jam yacc scanner
96 * search.c - find a target along $(SEARCH) or $(LOCATE)
97 * timestamp.c - get the timestamp of a file or archive member
98 * variable.c - handle jam multi-element variables
103 #include "patchlevel.h"
105 /* These get various function declarations. */
108 #include "variable.h"
110 #include "builtins.h"
114 #include "timestamp.h"
120 /* And UNIX for this */
122 # include <sys/utsname.h>
127 char *stpcpy (char *dst
, const char *src
) {
128 while ((*dst
++ = *src
++));
134 struct globs globs
= {
138 .newestfirst
= 0, /* newestfirst */
139 .debug
= { 0, 1 }, /* display actions */
140 .cmdout
= NULL
, /* output commands, not run them */
141 //#ifdef OPT_IMPROVED_PROGRESS_EXT
148 /* symbols to be defined as true for use in Jambase */
149 static const char *othersyms
[] = { OSMAJOR
, OSMINOR
, OSPLAT
, JAMVERSYM
, JAMVERSYM_HI
, JAMVERSYM_MID
, JAMVERSYM_LO
, 0 } ;
153 * OS2 needs extern environ
156 # define use_environ environ
157 # if !defined(__WATCOM__) && !defined(OS_NT)
158 extern char **environ
;
163 static void fixJobs (void) {
164 LIST
*var
= var_get("K8JAM-JOBS"); /*FIXME: a perfectly idiotic name*/
168 int cnt
= list_length(var
);
173 int jj
= atoi(var
->string
);
175 if (jj
< 1) jj
= 1; else if (jj
> 64) jj
= 64;
180 if (globs
.jobs
< 1) globs
.jobs
= 1; else if (globs
.jobs
> 64) globs
.jobs
= 64;
181 snprintf(buf
, sizeof(buf
), "%d", globs
.jobs
);
182 var
= list_new(L0
, buf
, 0);
183 var_set("K8JAM-JOBS", var
, VAR_SET
);
187 int main (int argc
, char **argv
, char **arg_environ
) {
190 struct option optv
[N_OPTS
];
191 char *targets
[N_TARGETS
];
192 const char *all
= "all";
196 const char *mybin
= argv
[0];
197 if (!mybin
) mybin
= "k8jam";
200 if ((num_targets
= getoptions(argc
, argv
, "H:d:j:f:gs:t:ano:qvZW", optv
, targets
)) < 0) {
201 printf("usage: jam [ options ] targets...\n\n"
202 "-a Build all targets, even if they are current.\n"
203 "-dx Display (a)actions (c)causes (d)dependencies\n"
204 " (m)make tree (x)commands ([+]0-9) debug levels.\n"
205 "-fx Read x instead of Jambase.\n"
206 "-g Build from newest sources first.\n"
207 "-jx Run up to x shell commands concurrently.\n"
208 "-n Don't actually execute the updating actions.\n"
209 "-ox Write the updating actions to file x.\n"
210 "-q Quit quickly as soon as a target fails.\n"
211 "-sx=y Set variable x=y, overriding environment.\n"
212 "-tx Rebuild x, even if it is up-to-date.\n"
213 "-v Print the version of jam and exit.\n"
214 "-Hx Show header cache statistics (a)ll, (h)its, (s)tats.\n"
215 "-W Write built-in Jambase and exit.\n"
217 " 1 same as -da, but don't show quiet actions\n"
220 " 4 execcmds()'s work\n"
221 " 5 rule invocations\n"
222 " 6 result of header scan, dir scan and attempts at binding\n"
223 " 7 variable settings\n"
224 " 8 variable fetches and expansions, 'if' calculations\n"
225 " 9 list manipulation, scanner tokens, memory use\n"
227 " jammod genman infile.txt [outfile.man]\n"
232 if ((s
= getoptval(optv
, 'v', 0))) {
233 printf("K8Jam %s %s\n", VERSION
, OSMINOR
);
235 "(C) 1993-2003 Christopher Seiwald at all\n"
236 "changes by Ketmar // Vampire Avalon, psyc://ketmar.no-ip.org/~ketmar\n"
237 "http://repo.or.cz/w/k8jam.git\n"
241 /* pick up interesting options */
242 if ((s
= getoptval(optv
, 'W', 0))) {
243 FILE *fl
= fopen("Jambase", "w");
245 printf("writing Jambase...\n");
247 for (int f
= 0; jambase
[f
]; ++f
) fprintf(fl
, "%s", jambase
[f
]);
252 if ((s
= getoptval(optv
, 'n', 0))) ++globs
.noexec
, DEBUG_MAKE
= DEBUG_MAKEQ
= DEBUG_EXEC
= 1;
253 if ((s
= getoptval(optv
, 'q', 0))) globs
.quitquick
= 1;
254 if ((s
= getoptval(optv
, 'a', 0))) ++anyhow
;
255 if ((s
= getoptval(optv
, 'j', 0))) globs
.jobs
= atoi(s
);
256 if ((s
= getoptval(optv
, 'g', 0))) globs
.newestfirst
= 1;
257 /* turn on/off debugging */
258 for (n
= 0; (s
= getoptval(optv
, 'd', n
)) != 0; ++n
) {
260 /* first -d, turn off defaults. */
261 if (!n
) DEBUG_MAKE
= DEBUG_MAKEQ
= DEBUG_EXEC
= 0;
262 /* n turns on levels 1-n */
263 /* +n turns on level n */
264 /* c turns on named display c */
265 if (i
< 0 || i
>= DEBUG_MAX
) {
266 printf( "Invalid debug level '%s'.\n", s
);
267 } else if (*s
== '+') {
270 for (; i
> 0; --i
) globs
.debug
[i
] = 1;
274 case 'a': DEBUG_MAKE
= DEBUG_MAKEQ
= 1; break;
275 case 'c': DEBUG_CAUSES
= 1; break;
276 case 'd': DEBUG_DEPENDS
= 1; break;
277 case 'm': DEBUG_MAKEPROG
= 1; break;
278 case 'x': DEBUG_EXEC
= 1; break;
279 case 'h': DEBUG_HEADER
= 1; break;
280 case 's': DEBUG_SCAN
= 1; break;
282 default: printf("Invalid debug flag '%c'.\n", s
[-1]);
287 /* header cache statistics */
288 for (n
= 0; (s
= getoptval(optv
, 'H', n
)) != 0; ++n
) {
292 case 'a': optShowHCacheStats
= optShowHCacheInfo
= 1; break;
293 case 'h': optShowHCacheInfo
= 1; break;
294 case 's': optShowHCacheStats
= 1; break;
298 if (wasOptH
&& (optShowHCacheStats
== 0 && optShowHCacheInfo
== 0)) optShowHCacheStats
= optShowHCacheInfo
= 1;
299 /* set JAMDATE first */
305 strcpy(buf
, ctime(&clock
));
306 /* trim newline from date */
308 while (p
>= buf
&& *((unsigned char *)p
) <= ' ') --p
;
310 var_set("JAMDATE", list_new(L0
, buf
, 0), VAR_SET
);
317 if (uname(&u
) >= 0) {
319 l
= list_new(l
, u
.machine
, 0);
320 l
= list_new(l
, u
.version
, 0);
321 l
= list_new(l
, u
.release
, 0);
322 l
= list_new(l
, u
.nodename
, 0);
323 l
= list_new(l
, u
.sysname
, 0);
324 var_set("JAMUNAME", l
, VAR_SET
);
329 if (num_targets
> 0 &&
330 (strcmp(targets
[0], "jammod") == 0 || strcmp(targets
[0], "jammodule") == 0)) {
331 if (num_targets
< 2) {
332 fprintf(stderr
, "ERROR: module name missing!\n");
335 if (strcmp(targets
[1], "genman") == 0) {
336 if (num_targets
< 3 || num_targets
> 4) {
337 fprintf(stderr
, "ERROR: invalid genman module invocation!\n");
340 static char destmanfile
[4096];
341 if (num_targets
== 3) {
342 strcpy(destmanfile
, targets
[2]);
343 char *ext
= strrchr(destmanfile
, '.');
344 if (!ext
|| strchr(ext
, '/')) {
345 strcat(destmanfile
, ".man");
350 strcpy(destmanfile
, targets
[3]);
352 if (DEBUG_EXEC
) printf("jammod genman \"%s\" \"%s\"\n", targets
[2], destmanfile
);
354 generate_man(targets
[2], destmanfile
);
358 fprintf(stderr
, "ERROR: unknown module '%s'!\n", targets
[1]);
361 /* load up environment variables */
362 var_defines((const char **)use_environ
, 0);
363 /* Jam defined variables OS, OSPLAT */
364 var_defines(othersyms
, 1);
365 /* load up variables set on command line. */
366 for (n
= 0; (s
= getoptval(optv
, 's', n
)) != 0; ++n
) {
370 var_defines(symv
, 1);
372 var_set("JAM_BINARY", list_new(L0
, mybin
, 0), VAR_SET
);
375 LIST
*l0
= L0
, *l1
= L0
;
377 if (num_targets
< 1) {
378 l0
= list_new(l0
, "all", 0);
379 l1
= list_new(l1
, "all", 0);
381 for (n
= 0; n
< num_targets
; ++n
) {
382 //printf("target %i: %s\n", n, targets[n]);
383 l0
= list_new(l0
, targets
[n
], 0);
384 l1
= list_new(l1
, targets
[n
], 0);
387 var_set("JAMCMDARGS", l0
, VAR_SET
);
389 var_set("JAM_TARGETS", l1
, VAR_SET
);
391 /* add JAMCONFIGARGS */
395 for (n
= 0; n
< num_cfgargs
; ++n
) l0
= list_new(l0
, cfgargs
[n
], 0);
396 var_set("JAMCONFIGARGS", l0
, VAR_SET
);
398 /* initialize built-in rules */
401 for (n
= 0; (s
= getoptval(optv
, 'f', n
)) != 0; ++n
) parse_file(s
);
402 if (!n
) parse_file("::Jambase");
403 status
= 0; /*yyanyerrors();*/
405 printf("FATAL: parsing error occured!\n");
408 /* manually touch -t targets */
409 for (n
= 0; (s
= getoptval(optv
, 't', n
)) != 0; ++n
) touchtarget(s
);
410 /* if an output file is specified, set globs.cmdout to that */
411 if ((s
= getoptval(optv
, 'o', 0)) != 0) {
412 if (!(globs
.cmdout
= fopen(s
, "w"))) {
413 printf("Failed to write to '%s'\n", s
);
418 /* fix number of jobs (if necessary) */
420 #ifndef NO_OPT_JAM_TARGETS_VARIABLE_EXT
421 /* ported from Haiku */
422 /* get value of variable JAM_TARGETS and build the targets */
424 LIST
*l
= var_get("JAM_TARGETS");
425 int targetCount
= list_length(l
);
428 if (targetCount
== 0) {
429 /* no targets: nothing to do */
430 printf("No targets. Nothing to do.\n");
433 if (targetCount
>= N_TARGETS
) {
434 printf("ERROR: Too many targets!\n");
437 for (i
= 0; i
< targetCount
; ++i
) {
438 const char *s0
= l
->string
;
439 targets
[i
] = malloc((strlen(s0
)+1)*sizeof(char));
441 printf("ERROR: Out of memory!\n");
444 strcpy(targets
[i
], s0
);
447 num_targets
= targetCount
;
449 printf("new targets:\n");
450 for (i = 0; i < num_targets; i++) {
451 printf("target %i: %s\n", i, targets[i]);
456 /* now make target */
457 if (!num_targets
) status
|= make(1, &all
, anyhow
);
458 else status
|= make(num_targets
, (const char **)targets
, anyhow
);
459 /* widely scattered cleanup */
460 //#ifdef OPT_HEADER_CACHE_EXT
461 /* note that cache will not be written if it is not updated, so 'clean' will work ok */
462 if (!globs
.noexec
) hcache_done();
470 if (globs
.cmdout
) fclose(globs
.cmdout
);
471 return (status
? EXITBAD
: EXITOK
);