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 freely, as
9 * long as this copyright notice is retained and modifications are clearly
10 * marked.
11 *
12 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
13 */
14
15 /* This file is ALSO:
16 * Copyright 2001-2004 David Abrahams.
17 * Distributed under the Boost Software License, Version 1.0.
18 * (See accompanying file LICENSE_1_0.txt or copy at
19 * http://www.boost.org/LICENSE_1_0.txt)
20 */
21
22 /*
23 * jam.c - make redux
24 *
25 * See Jam.html for usage information.
26 *
27 * These comments document the code.
28 *
29 * The top half of the code is structured such:
30 *
31 * jam
32 * / | \
33 * +---+ | \
34 * / | \
35 * jamgram option \
36 * / | \ \
37 * / | \ \
38 * / | \ |
39 * scan | compile make
40 * | | / | \ / | \
41 * | | / | \ / | \
42 * | | / | \ / | \
43 * jambase parse | rules search make1
44 * | | | \
45 * | | | \
46 * | | | \
47 * builtins timestamp command execute
48 * |
49 * |
50 * |
51 * filesys
52 *
53 *
54 * The support routines are called by all of the above, but themselves are
55 * layered thus:
56 *
57 * variable|expand
58 * / | |
59 * / | |
60 * / | |
61 * lists | pathsys
62 * \ |
63 * \ hash
64 * \ |
65 * \ |
66 * \ |
67 * \ |
68 * \ |
69 * object
70 *
71 * Roughly, the modules are:
72 *
73 * builtins.c - jam's built-in rules
74 * command.c - maintain lists of commands
75 * compile.c - compile parsed jam statements
76 * exec*.c - execute a shell script on a specific OS
77 * file*.c - scan directories and archives on a specific OS
78 * hash.c - simple in-memory hashing routines
79 * hdrmacro.c - handle header file parsing for filename macro definitions
80 * headers.c - handle #includes in source files
81 * jambase.c - compilable copy of Jambase
82 * jamgram.y - jam grammar
83 * lists.c - maintain lists of strings
84 * make.c - bring a target up to date, once rules are in place
85 * make1.c - execute command to bring targets up to date
86 * object.c - string manipulation routines
87 * option.c - command line option processing
88 * parse.c - make and destroy parse trees as driven by the parser
89 * path*.c - manipulate file names on a specific OS
90 * hash.c - simple in-memory hashing routines
91 * regexp.c - Henry Spencer's regexp
92 * rules.c - access to RULEs, TARGETs, and ACTIONs
93 * scan.c - the jam yacc scanner
94 * search.c - find a target along $(SEARCH) or $(LOCATE)
95 * timestamp.c - get the timestamp of a file or archive member
96 * variable.c - handle jam multi-element variables
97 */
98
99
100 #include "jam.h"
101 #include "patchlevel.h"
102
103 #include "builtins.h"
104 #include "class.h"
105 #include "compile.h"
106 #include "constants.h"
107 #include "filesys.h"
108 #include "function.h"
109 #include "hcache.h"
110 #include "lists.h"
111 #include "make.h"
112 #include "object.h"
113 #include "option.h"
114 #include "output.h"
115 #include "parse.h"
116 #include "cwd.h"
117 #include "rules.h"
118 #include "scan.h"
119 #include "search.h"
120 #include "strings.h"
121 #include "timestamp.h"
122 #include "variable.h"
123
124 /* Macintosh is "special" */
125 #ifdef OS_MAC
126 # include <QuickDraw.h>
127 #endif
128
129 /* And UNIX for this. */
130 #ifdef unix
131 # include <sys/utsname.h>
132 # include <signal.h>
133 #endif
134
135 struct globs globs =
136 {
137 0, /* noexec */
138 1, /* jobs */
139 0, /* quitquick */
140 0, /* newestfirst */
141 0, /* pipes action stdout and stderr merged to action output */
142 #ifdef OS_MAC
143 { 0, 0 }, /* debug - suppress tracing output */
144 #else
145 { 0, 1 }, /* debug ... */
146 #endif
147 0, /* output commands, not run them */
148 0, /* action timeout */
149 0 /* maximum buffer size zero is all output */
150 };
151
152 /* Symbols to be defined as true for use in Jambase. */
153 static char * othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 };
154
155
156 /* Known for sure:
157 * mac needs arg_enviro
158 * OS2 needs extern environ
159 */
160
161 #ifdef OS_MAC
162 # define use_environ arg_environ
163 # ifdef MPW
164 QDGlobals qd;
165 # endif
166 #endif
167
168 /* on Win32-LCC */
169 #if defined( OS_NT ) && defined( __LCC__ )
170 # define use_environ _environ
171 #endif
172
173 #if defined( __MWERKS__)
174 # define use_environ _environ
175 extern char * * _environ;
176 #endif
177
178 #ifndef use_environ
179 # define use_environ environ
180 # if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
181 extern char **environ;
182 # endif
183 #endif
184
185 #if YYDEBUG != 0
186 extern int yydebug;
187 #endif
188
189 #ifndef NDEBUG
run_unit_tests()190 static void run_unit_tests()
191 {
192 # if defined( USE_EXECNT )
193 extern void execnt_unit_test();
194 execnt_unit_test();
195 # endif
196 string_unit_test();
197 }
198 #endif
199
200 int anyhow = 0;
201
202 #ifdef HAVE_PYTHON
203 extern PyObject * bjam_call ( PyObject * self, PyObject * args );
204 extern PyObject * bjam_import_rule ( PyObject * self, PyObject * args );
205 extern PyObject * bjam_define_action( PyObject * self, PyObject * args );
206 extern PyObject * bjam_variable ( PyObject * self, PyObject * args );
207 extern PyObject * bjam_backtrace ( PyObject * self, PyObject * args );
208 extern PyObject * bjam_caller ( PyObject * self, PyObject * args );
209 #endif
210
211 void regex_done();
212
213 char const * saved_argv0;
214
main(int argc,char ** argv,char ** arg_environ)215 int main( int argc, char * * argv, char * * arg_environ )
216 {
217 int n;
218 char * s;
219 struct bjam_option optv[ N_OPTS ];
220 char const * all = "all";
221 int status;
222 int arg_c = argc;
223 char * * arg_v = argv;
224 char const * progname = argv[ 0 ];
225 module_t * environ_module;
226
227 saved_argv0 = argv[ 0 ];
228
229 BJAM_MEM_INIT();
230
231 #ifdef OS_MAC
232 InitGraf( &qd.thePort );
233 #endif
234
235 --argc;
236 ++argv;
237
238 if ( getoptions( argc, argv, "-:l:m:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
239 {
240 err_printf( "\nusage: %s [ options ] targets...\n\n", progname );
241
242 err_printf( "-a Build all targets, even if they are current.\n" );
243 err_printf( "-dx Set the debug level to x (0-9).\n" );
244 err_printf( "-fx Read x instead of Jambase.\n" );
245 /* err_printf( "-g Build from newest sources first.\n" ); */
246 err_printf( "-jx Run up to x shell commands concurrently.\n" );
247 err_printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" );
248 err_printf( "-mx Maximum target output saved (kb), default is to save all output.\n" );
249 err_printf( "-n Don't actually execute the updating actions.\n" );
250 err_printf( "-ox Mirror all output to file x.\n" );
251 err_printf( "-px x=0, pipes action stdout and stderr merged into action output.\n" );
252 err_printf( "-q Quit quickly as soon as a target fails.\n" );
253 err_printf( "-sx=y Set variable x=y, overriding environment.\n" );
254 err_printf( "-tx Rebuild x, even if it is up-to-date.\n" );
255 err_printf( "-v Print the version of jam and exit.\n" );
256 err_printf( "--x Option is ignored.\n\n" );
257
258 exit( EXITBAD );
259 }
260
261 /* Version info. */
262 if ( ( s = getoptval( optv, 'v', 0 ) ) )
263 {
264 out_printf( "Boost.Jam Version %s. %s.\n", VERSION, OSMINOR );
265 out_printf( " Copyright 1993-2002 Christopher Seiwald and Perforce "
266 "Software, Inc.\n" );
267 out_printf( " Copyright 2001 David Turner.\n" );
268 out_printf( " Copyright 2001-2004 David Abrahams.\n" );
269 out_printf( " Copyright 2002-2015 Rene Rivera.\n" );
270 out_printf( " Copyright 2003-2015 Vladimir Prus.\n" );
271 return EXITOK;
272 }
273
274 /* Pick up interesting options. */
275 if ( ( s = getoptval( optv, 'n', 0 ) ) )
276 {
277 ++globs.noexec;
278 globs.debug[ 2 ] = 1;
279 }
280
281 if ( ( s = getoptval( optv, 'p', 0 ) ) )
282 {
283 /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action
284 * stdout and stderr.
285 */
286 globs.pipe_action = atoi( s );
287 if ( globs.pipe_action < 0 || 3 < globs.pipe_action )
288 {
289 err_printf( "Invalid pipe descriptor '%d', valid values are -p[0..3]."
290 "\n", globs.pipe_action );
291 exit( EXITBAD );
292 }
293 }
294
295 if ( ( s = getoptval( optv, 'q', 0 ) ) )
296 globs.quitquick = 1;
297
298 if ( ( s = getoptval( optv, 'a', 0 ) ) )
299 anyhow++;
300
301 if ( ( s = getoptval( optv, 'j', 0 ) ) )
302 {
303 globs.jobs = atoi( s );
304 if ( globs.jobs < 1 || globs.jobs > MAXJOBS )
305 {
306 err_printf( "Invalid value for the '-j' option, valid values are 1 "
307 "through %d.\n", MAXJOBS );
308 exit( EXITBAD );
309 }
310 }
311
312 if ( ( s = getoptval( optv, 'g', 0 ) ) )
313 globs.newestfirst = 1;
314
315 if ( ( s = getoptval( optv, 'l', 0 ) ) )
316 globs.timeout = atoi( s );
317
318 if ( ( s = getoptval( optv, 'm', 0 ) ) )
319 globs.max_buf = atoi( s ) * 1024; /* convert to kb */
320
321 /* Turn on/off debugging */
322 for ( n = 0; ( s = getoptval( optv, 'd', n ) ); ++n )
323 {
324 int i;
325
326 /* First -d, turn off defaults. */
327 if ( !n )
328 for ( i = 0; i < DEBUG_MAX; ++i )
329 globs.debug[i] = 0;
330
331 i = atoi( s );
332
333 if ( ( i < 0 ) || ( i >= DEBUG_MAX ) )
334 {
335 out_printf( "Invalid debug level '%s'.\n", s );
336 continue;
337 }
338
339 /* n turns on levels 1-n. */
340 /* +n turns on level n. */
341 if ( *s == '+' )
342 globs.debug[ i ] = 1;
343 else while ( i )
344 globs.debug[ i-- ] = 1;
345 }
346
347 /* If an output file is specified, set globs.out to that. */
348 if ( ( s = getoptval( optv, 'o', 0 ) ) )
349 {
350 if ( !( globs.out = fopen( s, "w" ) ) )
351 {
352 err_printf( "Failed to write to '%s'\n", s );
353 exit( EXITBAD );
354 }
355 /* ++globs.noexec; */
356 }
357
358 constants_init();
359 cwd_init();
360
361 {
362 PROFILE_ENTER( MAIN );
363
364 #ifdef HAVE_PYTHON
365 {
366 PROFILE_ENTER( MAIN_PYTHON );
367 Py_Initialize();
368 {
369 static PyMethodDef BjamMethods[] = {
370 {"call", bjam_call, METH_VARARGS,
371 "Call the specified bjam rule."},
372 {"import_rule", bjam_import_rule, METH_VARARGS,
373 "Imports Python callable to bjam."},
374 {"define_action", bjam_define_action, METH_VARARGS,
375 "Defines a command line action."},
376 {"variable", bjam_variable, METH_VARARGS,
377 "Obtains a variable from bjam's global module."},
378 {"backtrace", bjam_backtrace, METH_VARARGS,
379 "Returns bjam backtrace from the last call into Python."},
380 {"caller", bjam_caller, METH_VARARGS,
381 "Returns the module from which the last call into Python is made."},
382 {NULL, NULL, 0, NULL}
383 };
384
385 Py_InitModule( "bjam", BjamMethods );
386 }
387 PROFILE_EXIT( MAIN_PYTHON );
388 }
389 #endif
390
391 #ifndef NDEBUG
392 run_unit_tests();
393 #endif
394 #if YYDEBUG != 0
395 if ( DEBUG_PARSE )
396 yydebug = 1;
397 #endif
398
399 /* Set JAMDATE. */
400 {
401 timestamp current;
402 timestamp_current( ¤t );
403 var_set( root_module(), constant_JAMDATE, list_new( outf_time(
404 ¤t ) ), VAR_SET );
405 }
406
407 /* Set JAM_VERSION. */
408 var_set( root_module(), constant_JAM_VERSION,
409 list_push_back( list_push_back( list_new(
410 object_new( VERSION_MAJOR_SYM ) ),
411 object_new( VERSION_MINOR_SYM ) ),
412 object_new( VERSION_PATCH_SYM ) ),
413 VAR_SET );
414
415 /* Set JAMUNAME. */
416 #ifdef unix
417 {
418 struct utsname u;
419
420 if ( uname( &u ) >= 0 )
421 {
422 var_set( root_module(), constant_JAMUNAME,
423 list_push_back(
424 list_push_back(
425 list_push_back(
426 list_push_back(
427 list_new(
428 object_new( u.sysname ) ),
429 object_new( u.nodename ) ),
430 object_new( u.release ) ),
431 object_new( u.version ) ),
432 object_new( u.machine ) ), VAR_SET );
433 }
434 }
435 #endif /* unix */
436
437 /* Set JAM_TIMESTAMP_RESOLUTION. */
438 {
439 timestamp fmt_resolution[ 1 ];
440 file_supported_fmt_resolution( fmt_resolution );
441 var_set( root_module(), constant_JAM_TIMESTAMP_RESOLUTION, list_new(
442 object_new( timestamp_timestr( fmt_resolution ) ) ), VAR_SET );
443 }
444
445 /* Load up environment variables. */
446
447 /* First into the global module, with splitting, for backward
448 * compatibility.
449 */
450 var_defines( root_module(), use_environ, 1 );
451
452 environ_module = bindmodule( constant_ENVIRON );
453 /* Then into .ENVIRON, without splitting. */
454 var_defines( environ_module, use_environ, 0 );
455
456 /*
457 * Jam defined variables OS & OSPLAT. We load them after environment, so
458 * that setting OS in environment does not change Jam's notion of the
459 * current platform.
460 */
461 var_defines( root_module(), othersyms, 1 );
462
463 /* Load up variables set on command line. */
464 for ( n = 0; ( s = getoptval( optv, 's', n ) ); ++n )
465 {
466 char * symv[ 2 ];
467 symv[ 0 ] = s;
468 symv[ 1 ] = 0;
469 var_defines( root_module(), symv, 1 );
470 var_defines( environ_module, symv, 0 );
471 }
472
473 /* Set the ARGV to reflect the complete list of arguments of invocation.
474 */
475 for ( n = 0; n < arg_c; ++n )
476 var_set( root_module(), constant_ARGV, list_new( object_new(
477 arg_v[ n ] ) ), VAR_APPEND );
478
479 /* Initialize built-in rules. */
480 load_builtins();
481
482 /* Add the targets in the command line to the update list. */
483 for ( n = 1; n < arg_c; ++n )
484 {
485 if ( arg_v[ n ][ 0 ] == '-' )
486 {
487 char * f = "-:l:d:j:f:gs:t:ano:qv";
488 for ( ; *f; ++f ) if ( *f == arg_v[ n ][ 1 ] ) break;
489 if ( ( f[ 1 ] == ':' ) && ( arg_v[ n ][ 2 ] == '\0' ) ) ++n;
490 }
491 else
492 {
493 OBJECT * const target = object_new( arg_v[ n ] );
494 mark_target_for_updating( target );
495 object_free( target );
496 }
497 }
498
499 if ( list_empty( targets_to_update() ) )
500 mark_target_for_updating( constant_all );
501
502 /* Parse ruleset. */
503 {
504 FRAME frame[ 1 ];
505 frame_init( frame );
506 for ( n = 0; ( s = getoptval( optv, 'f', n ) ); ++n )
507 {
508 OBJECT * const filename = object_new( s );
509 parse_file( filename, frame );
510 object_free( filename );
511 }
512
513 if ( !n )
514 parse_file( constant_plus, frame );
515 }
516
517 status = yyanyerrors();
518
519 /* Manually touch -t targets. */
520 for ( n = 0; ( s = getoptval( optv, 't', n ) ); ++n )
521 {
522 OBJECT * const target = object_new( s );
523 touch_target( target );
524 object_free( target );
525 }
526
527 /* The build system may set the PARALLELISM variable to override -j
528 * options.
529 */
530 {
531 LIST * const p = var_get( root_module(), constant_PARALLELISM );
532 if ( !list_empty( p ) )
533 {
534 int const j = atoi( object_str( list_front( p ) ) );
535 if ( j < 1 || j > MAXJOBS )
536 out_printf( "Invalid value of PARALLELISM: %s. Valid values "
537 "are 1 through %d.\n", object_str( list_front( p ) ),
538 MAXJOBS );
539 else
540 globs.jobs = j;
541 }
542 }
543
544 /* KEEP_GOING overrides -q option. */
545 {
546 LIST * const p = var_get( root_module(), constant_KEEP_GOING );
547 if ( !list_empty( p ) )
548 globs.quitquick = atoi( object_str( list_front( p ) ) ) ? 0 : 1;
549 }
550
551 /* Now make target. */
552 {
553 PROFILE_ENTER( MAIN_MAKE );
554 LIST * const targets = targets_to_update();
555 if ( !list_empty( targets ) )
556 status |= make( targets, anyhow );
557 else
558 status = last_update_now_status;
559 PROFILE_EXIT( MAIN_MAKE );
560 }
561
562 PROFILE_EXIT( MAIN );
563 }
564
565 if ( DEBUG_PROFILE )
566 profile_dump();
567
568
569 #ifdef OPT_HEADER_CACHE_EXT
570 hcache_done();
571 #endif
572
573 clear_targets_to_update();
574
575 /* Widely scattered cleanup. */
576 property_set_done();
577 file_done();
578 rules_done();
579 timestamp_done();
580 search_done();
581 class_done();
582 modules_done();
583 regex_done();
584 cwd_done();
585 path_done();
586 function_done();
587 list_done();
588 constants_done();
589 object_done();
590
591 /* Close log out. */
592 if ( globs.out )
593 fclose( globs.out );
594
595 #ifdef HAVE_PYTHON
596 Py_Finalize();
597 #endif
598
599 BJAM_MEM_CLOSE();
600
601 return status ? EXITBAD : EXITOK;
602 }
603
604
605 /*
606 * executable_path()
607 */
608
609 #if defined(_WIN32)
610 # define WIN32_LEAN_AND_MEAN
611 # include <windows.h>
executable_path(char const * argv0)612 char * executable_path( char const * argv0 )
613 {
614 char buf[ 1024 ];
615 DWORD const ret = GetModuleFileName( NULL, buf, sizeof( buf ) );
616 return ( !ret || ret == sizeof( buf ) ) ? NULL : strdup( buf );
617 }
618 #elif defined(__APPLE__) /* Not tested */
619 # include <mach-o/dyld.h>
executable_path(char const * argv0)620 char *executable_path( char const * argv0 )
621 {
622 char buf[ 1024 ];
623 uint32_t size = sizeof( buf );
624 return _NSGetExecutablePath( buf, &size ) ? NULL : strdup( buf );
625 }
626 #elif defined(sun) || defined(__sun) /* Not tested */
627 # include <stdlib.h>
executable_path(char const * argv0)628 char * executable_path( char const * argv0 )
629 {
630 const char * execname = getexecname();
631 return execname ? strdup( execname ) : NULL;
632 }
633 #elif defined(__FreeBSD__)
634 # include <sys/sysctl.h>
executable_path(char const * argv0)635 char * executable_path( char const * argv0 )
636 {
637 int mib[ 4 ] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
638 char buf[ 1024 ];
639 size_t size = sizeof( buf );
640 sysctl( mib, 4, buf, &size, NULL, 0 );
641 return ( !size || size == sizeof( buf ) ) ? NULL : strndup( buf, size );
642 }
643 #elif defined(__linux__)
644 # include <unistd.h>
executable_path(char const * argv0)645 char * executable_path( char const * argv0 )
646 {
647 char buf[ 1024 ];
648 ssize_t const ret = readlink( "/proc/self/exe", buf, sizeof( buf ) );
649 return ( !ret || ret == sizeof( buf ) ) ? NULL : strndup( buf, ret );
650 }
651 #else
executable_path(char const * argv0)652 char * executable_path( char const * argv0 )
653 {
654 /* If argv0 is an absolute path, assume it is the right absolute path. */
655 return argv0[ 0 ] == '/' ? strdup( argv0 ) : NULL;
656 }
657 #endif
658