1/*
2 * /+\
3 * +\	Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * \+/
5 *
6 * This file is part of jam.
7 *
8 * License is hereby granted to use this software and distribute it
9 * freely, as long as this copyright notice is retained and modifications
10 * are clearly marked.
11 *
12 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
13 */
14
15/*
16 * jam.c - make redux
17 *
18 * See Jam.html for usage information.
19 *
20 * These comments document the code.
21 *
22 * The top half of the code is structured such:
23 *
24 *                       jam
25 *                      / | \
26 *                 +---+  |  \
27 *                /       |   \
28 *         jamgram     option  \
29 *        /  |   \              \
30 *       /   |    \              \
31 *      /    |     \             |
32 *  scan     |     compile      make
33 *   |       |    /  | \       / |  \
34 *   |       |   /   |  \     /  |   \
35 *   |       |  /    |   \   /   |    \
36 * jambase parse     |   rules  search make1
37 *                   |           |      |   \
38 *                   |           |      |    \
39 *                   |           |      |     \
40 *               builtins    timestamp command execute
41 *                               |
42 *                               |
43 *                               |
44 *                             filesys
45 *
46 *
47 * The support routines are called by all of the above, but themselves
48 * are layered thus:
49 *
50 *                     variable|expand
51 *                      /  |   |   |
52 *                     /   |   |   |
53 *                    /    |   |   |
54 *                 lists   |   |   pathsys
55 *                    \    |   |
56 *                     \   |   |
57 *                      \  |   |
58 *                     newstr  |
59 *                        \    |
60 *                         \   |
61 *                          \  |
62 *                          hash
63 *
64 * Roughly, the modules are:
65 *
66 *	builtins.c - jam's built-in rules
67 *	command.c - maintain lists of commands
68 *	compile.c - compile parsed jam statements
69 *	execunix.c - execute a shell script on UNIX
70 *	execvms.c - execute a shell script, ala VMS
71 *	expand.c - expand a buffer, given variable values
72 *	file*.c - scan directories and archives on *
73 *	hash.c - simple in-memory hashing routines
74 *	headers.c - handle #includes in source files
75 *	jambase.c - compilable copy of Jambase
76 *	jamgram.y - jam grammar
77 *	lists.c - maintain lists of strings
78 *	make.c - bring a target up to date, once rules are in place
79 *	make1.c - execute command to bring targets up to date
80 *	newstr.c - string manipulation routines
81 *	option.c - command line option processing
82 *	parse.c - make and destroy parse trees as driven by the parser
83 *	path*.c - manipulate file names on *
84 *	hash.c - simple in-memory hashing routines
85 *	regexp.c - Henry Spencer's regexp
86 *	rules.c - access to RULEs, TARGETs, and ACTIONs
87 *	scan.c - the jam yacc scanner
88 *	search.c - find a target along $(SEARCH) or $(LOCATE)
89 *	timestamp.c - get the timestamp of a file or archive member
90 *	variable.c - handle jam multi-element variables
91 *
92 * 05/04/94 (seiwald) - async multiprocess (-j) support
93 * 02/08/95 (seiwald) - -n implies -d2.
94 * 02/22/95 (seiwald) - -v for version info.
95 * 09/11/00 (seiwald) - PATCHLEVEL folded into VERSION.
96 * 01/10/01 (seiwald) - pathsys.h split from filesys.h
97 * 01/21/02 (seiwald) - new -q to quit quickly on build failure
98 * 03/16/02 (seiwald) - support for -g (reorder builds by source time)
99 * 09/19/02 (seiwald) - new -d displays
100 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
101 * 11/04/02 (seiwald) - const-ing for string literals
102 */
103
104# include "jam.h"
105# include "option.h"
106# include "patchlevel.h"
107
108/* These get various function declarations. */
109
110# include "lists.h"
111# include "parse.h"
112# include "variable.h"
113# include "compile.h"
114# include "builtins.h"
115# include "jcache.h"
116# include "rules.h"
117# include "newstr.h"
118# include "scan.h"
119# include "timestamp.h"
120# include "make.h"
121
122/* Macintosh is "special" */
123
124# ifdef OS_MAC
125# include <QuickDraw.h>
126# endif
127
128/* And UNIX for this */
129
130# ifdef unix
131# include <sys/utsname.h>
132# endif
133
134struct globs globs = {
135	0,			/* noexec */
136	1,			/* jobs */
137	0,			/* quitquick */
138	0,			/* newestfirst */
139# ifdef OS_MAC
140	{ 0 },			/* display - suppress actions output */
141# else
142	{ 0, 1 }, 		/* display actions  */
143# endif
144	0			/* output commands, not run them */
145} ;
146
147/* Symbols to be defined as true for use in Jambase */
148
149static const char *othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 } ;
150
151/* Known for sure:
152 *	mac needs arg_enviro
153 *	OS2 needs extern environ
154 */
155
156# ifdef OS_MAC
157# define use_environ arg_environ
158# ifdef MPW
159QDGlobals qd;
160# endif
161# endif
162
163# ifndef use_environ
164# define use_environ environ
165# if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
166extern char **environ;
167# endif
168# endif
169
170main( int argc, char **argv, char **arg_environ )
171{
172	int		n;
173	const char	*s;
174	struct option	optv[N_OPTS];
175	const char	*all = "all";
176	int		anyhow = 0;
177	int		status;
178
179# ifdef OS_MAC
180	InitGraf(&qd.thePort);
181# endif
182
183	argc--, argv++;
184
185	if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:qv", optv ) ) < 0 )
186	{
187	    printf( "\nusage: jam [ options ] targets...\n\n" );
188
189            printf( "-a      Build all targets, even if they are current.\n" );
190            printf( "-dx     Display (a)actions (c)causes (d)dependencies\n" );
191	    printf( "        (m)make tree (x)commands (0-9) debug levels.\n" );
192# ifdef OPT_RULE_PROFILING_EXT
193	    printf( "        (p)profile rules.\n" );
194# endif
195            printf( "-fx     Read x instead of Jambase.\n" );
196	    printf( "-g      Build from newest sources first.\n" );
197            printf( "-jx     Run up to x shell commands concurrently.\n" );
198            printf( "-n      Don't actually execute the updating actions.\n" );
199            printf( "-ox     Write the updating actions to file x.\n" );
200            printf( "-q      Quit quickly as soon as a target fails.\n" );
201	    printf( "-sx=y   Set variable x=y, overriding environment.\n" );
202            printf( "-tx     Rebuild x, even if it is up-to-date.\n" );
203            printf( "-v      Print the version of jam and exit.\n\n" );
204
205	    exit( EXITBAD );
206	}
207
208	argc -= n, argv += n;
209
210	/* Version info. */
211
212	if( ( s = getoptval( optv, 'v', 0 ) ) )
213	{
214	    printf( "Jam %s. %s. ", VERSION, OSMINOR );
215	    printf( "Copyright 1993-2002 Christopher Seiwald.\n" );
216
217	    return EXITOK;
218	}
219
220	/* Pick up interesting options */
221
222	if( ( s = getoptval( optv, 'n', 0 ) ) )
223	    globs.noexec++, DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 1;
224
225	if( ( s = getoptval( optv, 'q', 0 ) ) )
226	    globs.quitquick = 1;
227
228	if( ( s = getoptval( optv, 'a', 0 ) ) )
229	    anyhow++;
230
231	if( ( s = getoptval( optv, 'j', 0 ) ) )
232	{
233	    globs.jobs = atoi( s );
234	    var_set( "JAMJOBS", list_new( L0, s, 0 ), VAR_SET );
235	}
236
237	if( ( s = getoptval( optv, 'g', 0 ) ) )
238	    globs.newestfirst = 1;
239
240	/* Turn on/off debugging */
241
242	for( n = 0; s = getoptval( optv, 'd', n ); n++ )
243	{
244	    int i = atoi( s );
245
246	    /* First -d, turn off defaults. */
247
248	    if( !n )
249		DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 0;
250
251	    /* n turns on levels 1-n */
252	    /* +n turns on level n */
253	    /* c turns on named display c */
254
255	    if( i < 0 || i >= DEBUG_MAX )
256	    {
257		printf( "Invalid debug level '%s'.\n", s );
258	    }
259	    else if( *s == '+' )
260	    {
261		globs.debug[i] = 1;
262	    }
263	    else if( i ) while( i )
264	    {
265		globs.debug[i--] = 1;
266	    }
267	    else while( *s ) switch( *s++ )
268	    {
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# ifdef OPT_RULE_PROFILING_EXT
275	    case 'p': DEBUG_PROFILE_RULES = 1; break;
276# endif
277	    case '0': break;
278	    default: printf( "Invalid debug flag '%c'.\n", s[-1] );
279	    }
280	}
281
282	/* Set JAMDATE first */
283
284	{
285	    char buf[ 128 ];
286	    time_t clock;
287	    time( &clock );
288	    strcpy( buf, ctime( &clock ) );
289
290	    /* Trim newline from date */
291
292	    if( strlen( buf ) == 25 )
293		buf[ 24 ] = 0;
294
295	    var_set( "JAMDATE", list_new( L0, buf, 0 ), VAR_SET );
296	}
297
298	/* And JAMUNAME */
299# ifdef unix
300	{
301	    struct utsname u;
302
303	    if( uname( &u ) >= 0 )
304	    {
305		LIST *l = L0;
306		l = list_new( l, u.machine, 0 );
307		l = list_new( l, u.version, 0 );
308		l = list_new( l, u.release, 0 );
309		l = list_new( l, u.nodename, 0 );
310		l = list_new( l, u.sysname, 0 );
311		var_set( "JAMUNAME", l, VAR_SET );
312	    }
313	}
314# endif /* unix */
315
316	/*
317	 * Jam defined variables OS, OSPLAT
318	 */
319
320	var_defines( othersyms );
321
322	/* load up environment variables */
323
324	var_defines( (const char **)use_environ );
325
326#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
327	/* define the variable JAM_TARGETS containing the targets specified on
328	   the command line */
329	{
330		LIST *l = L0;
331		int i;
332		char **targets = argv;
333		int targetCount = argc;
334		if (targetCount == 0) {
335			targets = (char**)&all;
336			targetCount = 1;
337		}
338
339		for (i = 0; i < targetCount; i++)
340			l = list_new( l, targets[i], 0 );
341
342		var_set( "JAM_TARGETS", l, VAR_SET );
343	}
344#endif
345
346	/* Load up variables set on command line. */
347
348	for( n = 0; s = getoptval( optv, 's', n ); n++ )
349	{
350	    const char *symv[2];
351	    symv[0] = s;
352	    symv[1] = 0;
353	    var_defines( symv );
354	}
355
356	/* Initialize built-in rules */
357
358	load_builtins();
359
360	/* Parse ruleset */
361#ifdef OPT_JAMFILE_CACHE_EXT
362	jcache_init();
363#endif
364
365	for( n = 0; s = getoptval( optv, 'f', n ); n++ )
366	    parse_file( s );
367
368	if( !n )
369	    parse_file( "+" );
370
371#ifdef OPT_JAMFILE_CACHE_EXT
372	jcache_done();
373#endif
374
375	status = yyanyerrors();
376
377	/* Manually touch -t targets */
378
379	for( n = 0; s = getoptval( optv, 't', n ); n++ )
380	    touchtarget( s );
381
382	/* If an output file is specified, set globs.cmdout to that */
383
384	if( s = getoptval( optv, 'o', 0 ) )
385	{
386	    if( !( globs.cmdout = fopen( s, "w" ) ) )
387	    {
388		printf( "Failed to write to '%s'\n", s );
389		exit( EXITBAD );
390	    }
391	    globs.noexec++;
392	}
393
394#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
395	/* get value of variable JAM_TARGETS and build the targets */
396	{
397		LIST *l = var_get( "JAM_TARGETS" );
398		int targetCount = list_length(l);
399		char **targets;
400		int i;
401
402		if (targetCount == 0) {
403			/* No targets. Nothing to do. */
404			exit( EXITOK );
405		}
406
407		targets = malloc(targetCount * sizeof(char*));
408		if (!targets) {
409			printf( "Memory allocation failed!\n" );
410			exit( EXITBAD );
411		}
412
413		for (i = 0; i < targetCount; i++) {
414			targets[i] = (char*)l->string;
415			l = l->next;
416		}
417
418		argv = targets;
419		argc = targetCount;
420	}
421#endif
422
423	/* Now make target */
424
425	if( !argc )
426	    status |= make( 1, &all, anyhow );
427	else
428	    status |= make( argc, (const char **)argv, anyhow );
429
430	/* Widely scattered cleanup */
431
432	var_done();
433	donerules();
434	donestamps();
435	donestr();
436
437	/* close cmdout */
438
439	if( globs.cmdout )
440	    fclose( globs.cmdout );
441
442	return status ? EXITBAD : EXITOK;
443}
444