3 * +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
6 * This file is part of jam.
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
12 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
16 * Copyright 2001-2004 David Abrahams.
17 * Copyright 2018 Rene Rivera
18 * Distributed under the Boost Software License, Version 1.0.
19 * (See accompanying file LICENSE_1_0.txt or copy at
20 * http://www.boost.org/LICENSE_1_0.txt)
26 * See Jam.html for usage information.
28 * These comments document the code.
30 * The top half of the code is structured such:
44 * jambase parse | rules search make1
48 * builtins timestamp command execute
55 * The support routines are called by all of the above, but themselves are
72 * Roughly, the modules are:
74 * builtins.c - jam's built-in rules
75 * command.c - maintain lists of commands
76 * compile.c - compile parsed jam statements
77 * exec*.c - execute a shell script on a specific OS
78 * file*.c - scan directories and archives on a specific OS
79 * hash.c - simple in-memory hashing routines
80 * hdrmacro.c - handle header file parsing for filename macro definitions
81 * headers.c - handle #includes in source files
82 * jambase.c - compilable copy of Jambase
83 * jamgram.y - jam grammar
84 * lists.c - maintain lists of strings
85 * make.c - bring a target up to date, once rules are in place
86 * make1.c - execute command to bring targets up to date
87 * object.c - string manipulation routines
88 * option.c - command line option processing
89 * parse.c - make and destroy parse trees as driven by the parser
90 * path*.c - manipulate file names on a specific OS
91 * hash.c - simple in-memory hashing routines
92 * regexp.c - Henry Spencer's regexp
93 * rules.c - access to RULEs, TARGETs, and ACTIONs
94 * scan.c - the jam yacc scanner
95 * search.c - find a target along $(SEARCH) or $(LOCATE)
96 * timestamp.c - get the timestamp of a file or archive member
97 * variable.c - handle jam multi-element variables
103 #include "patchlevel.h"
105 /* Keep JAMVERSYM in sync with VERSION. */
106 /* It can be accessed as $(JAMVERSION) in the Jamfile. */
107 #define JAM_STRINGIZE(X) JAM_DO_STRINGIZE(X)
108 #define JAM_DO_STRINGIZE(X) #X
109 #define VERSION_MAJOR_SYM JAM_STRINGIZE(VERSION_MAJOR)
110 #define VERSION_MINOR_SYM JAM_STRINGIZE(VERSION_MINOR)
111 #define VERSION_PATCH_SYM JAM_STRINGIZE(VERSION_PATCH)
112 #define VERSION VERSION_MAJOR_SYM "." VERSION_MINOR_SYM
113 #define JAMVERSYM "JAMVERSION=" VERSION
115 #include "builtins.h"
118 #include "constants.h"
119 #include "debugger.h"
121 #include "function.h"
134 #include "timestamp.h"
135 #include "variable.h"
139 /* Macintosh is "special" */
141 # include <QuickDraw.h>
144 /* And UNIX for this. */
146 # include <sys/utsname.h>
156 0, /* pipes action stdout and stderr merged to action output */
158 { 0, 0 }, /* debug - suppress tracing output */
160 { 0, 1 }, /* debug ... */
162 0, /* output commands, not run them */
163 0, /* action timeout */
164 0 /* maximum buffer size zero is all output */
167 /* Symbols to be defined as true for use in Jambase. */
168 static const char * othersyms
[] = { OSMAJOR
, OSMINOR
, OSPLAT
, JAMVERSYM
, 0 };
172 * mac needs arg_enviro
173 * OS2 needs extern environ
177 # define use_environ arg_environ
185 # define use_environ arg_environ
190 #if defined( OS_NT ) && defined( __LCC__ )
191 # define use_environ _environ
194 #if defined( __MWERKS__)
195 # define use_environ _environ
196 extern char * * _environ
;
200 # define use_environ environ
201 # if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
202 extern char **environ
;
211 static void run_unit_tests()
213 # if defined( USE_EXECNT )
214 extern void execnt_unit_test();
224 extern PyObject
* bjam_call ( PyObject
* self
, PyObject
* args
);
225 extern PyObject
* bjam_import_rule ( PyObject
* self
, PyObject
* args
);
226 extern PyObject
* bjam_define_action( PyObject
* self
, PyObject
* args
);
227 extern PyObject
* bjam_variable ( PyObject
* self
, PyObject
* args
);
228 extern PyObject
* bjam_backtrace ( PyObject
* self
, PyObject
* args
);
229 extern PyObject
* bjam_caller ( PyObject
* self
, PyObject
* args
);
230 int python_optimize
= 1; /* Set Python optimzation on by default */
235 char const * saved_argv0
;
237 static void usage( const char * progname
)
239 err_printf("\nusage: %s [ options ] targets...\n\n", progname
);
241 err_printf("-a Build all targets, even if they are current.\n");
242 err_printf("-dx Set the debug level to x (0-13,console,mi).\n");
243 err_printf("-fx Read x instead of Jambase.\n");
244 /* err_printf( "-g Build from newest sources first.\n" ); */
245 err_printf("-jx Run up to x shell commands concurrently.\n");
246 err_printf("-lx Limit actions to x number of seconds after which they are stopped.\n");
247 err_printf("-mx Maximum target output saved (kb), default is to save all output.\n");
248 err_printf("-n Don't actually execute the updating actions.\n");
249 err_printf("-ox Mirror all output to file x.\n");
250 err_printf("-px x=0, pipes action stdout and stderr merged into action output.\n");
251 err_printf("-q Quit quickly as soon as a target fails.\n");
252 err_printf("-sx=y Set variable x=y, overriding environment.\n");
253 err_printf("-tx Rebuild x, even if it is up-to-date.\n");
254 err_printf("-v Print the version of jam and exit.\n");
256 err_printf("-z Disable Python Optimization and enable asserts\n");
258 err_printf("--x Option is ignored.\n\n");
263 int main( int argc
, char * * argv
, char * * arg_environ
)
267 struct bjam_option optv
[ N_OPTS
];
268 char const * all
= "all";
271 char * * arg_v
= argv
;
272 char const * progname
= argv
[ 0 ];
273 module_t
* environ_module
;
275 b2::system_info sys_info
;
277 saved_argv0
= argv
[ 0 ];
282 InitGraf( &qd
.thePort
);
292 if ( getoptions( argc
- 1, argv
+ 1, "-:l:m:d:j:p:f:gs:t:ano:qv", optv
) < 0 )
295 if ( ( s
= getoptval( optv
, 'd', 0 ) ) )
297 if ( strcmp( s
, "mi" ) == 0 )
299 debug_interface
= DEBUG_INTERFACE_MI
;
302 else if ( strcmp( s
, "console" ) == 0 )
304 debug_interface
= DEBUG_INTERFACE_CONSOLE
;
313 /* Check whether this instance is being run by the debugger. */
314 size_t opt_len
= strlen( debugger_opt
);
315 if ( strncmp( argv
[ 1 ], debugger_opt
, opt_len
) == 0 &&
316 strncmp( argv
[ 2 ], debugger_opt
, opt_len
) == 0 )
318 debug_init_handles( argv
[ 1 ] + opt_len
, argv
[ 2 ] + opt_len
);
319 /* Fix up argc/argv to hide the internal options */
320 arg_c
= argc
= (argc
- 2);
321 argv
[ 2 ] = argv
[ 0 ];
322 arg_v
= argv
= (argv
+ 2);
323 debug_interface
= DEBUG_INTERFACE_CHILD
;
336 if ( setjmp( debug_child_data
.jmp
) != 0 )
338 arg_c
= argc
= debug_child_data
.argc
;
339 arg_v
= argv
= (char * *)debug_child_data
.argv
;
340 debug_interface
= DEBUG_INTERFACE_CHILD
;
356 #define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qvz"
358 #define OPTSTRING "-:l:m:d:j:p:f:gs:t:ano:qv"
361 if ( getoptions( argc
, argv
, OPTSTRING
, optv
) < 0 )
366 /* Set default parallel jobs to match cpu threads. This can be overridden
367 the usual way with -jX or PARALLELISM env var. */
368 globs
.jobs
= sys_info
.cpu_thread_count();
371 if ( ( s
= getoptval( optv
, 'v', 0 ) ) )
373 out_printf( "B2 Version %s. %s.\n", VERSION
, OSMINOR
);
374 out_printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.\n" );
375 out_printf( " Copyright 2001 David Turner.\n" );
376 out_printf( " Copyright 2001-2004 David Abrahams.\n" );
377 out_printf( " Copyright 2002-2019 Rene Rivera.\n" );
378 out_printf( " Copyright 2003-2015 Vladimir Prus.\n" );
379 out_printf( "\n DEFAULTS: jobs = %i\n", globs
.jobs
);
383 /* Pick up interesting options. */
384 if ( ( s
= getoptval( optv
, 'n', 0 ) ) )
387 globs
.debug
[ 2 ] = 1;
390 if ( ( s
= getoptval( optv
, 'p', 0 ) ) )
392 /* Undocumented -p3 (acts like both -p1 -p2) means separate pipe action
395 globs
.pipe_action
= atoi( s
);
396 if ( globs
.pipe_action
< 0 || 3 < globs
.pipe_action
)
398 err_printf( "Invalid pipe descriptor '%d', valid values are -p[0..3]."
399 "\n", globs
.pipe_action
);
404 if ( ( s
= getoptval( optv
, 'q', 0 ) ) )
407 if ( ( s
= getoptval( optv
, 'a', 0 ) ) )
410 if ( ( s
= getoptval( optv
, 'j', 0 ) ) )
412 globs
.jobs
= atoi( s
);
413 if ( globs
.jobs
< 1 )
415 err_printf( "Invalid value for the '-j' option.\n" );
420 if ( ( s
= getoptval( optv
, 'g', 0 ) ) )
421 globs
.newestfirst
= 1;
423 if ( ( s
= getoptval( optv
, 'l', 0 ) ) )
424 globs
.timeout
= atoi( s
);
426 if ( ( s
= getoptval( optv
, 'm', 0 ) ) )
427 globs
.max_buf
= atoi( s
) * 1024; /* convert to kb */
430 if ( ( s
= getoptval( optv
, 'z', 0 ) ) )
431 python_optimize
= 0; /* disable python optimization */
434 /* Turn on/off debugging */
435 for ( n
= 0; ( s
= getoptval( optv
, 'd', n
) ); ++n
)
439 /* First -d, turn off defaults. */
441 for ( i
= 0; i
< DEBUG_MAX
; ++i
)
446 if ( ( i
< 0 ) || ( i
>= DEBUG_MAX
) )
448 out_printf( "Invalid debug level '%s'.\n", s
);
452 /* n turns on levels 1-n. */
453 /* +n turns on level n. */
455 globs
.debug
[ i
] = 1;
457 globs
.debug
[ i
-- ] = 1;
460 /* If an output file is specified, set globs.out to that. */
461 if ( ( s
= getoptval( optv
, 'o', 0 ) ) )
463 if ( !( globs
.out
= fopen( s
, "w" ) ) )
465 err_printf( "Failed to write to '%s'\n", s
);
468 /* ++globs.noexec; */
472 PROFILE_ENTER( MAIN
);
476 PROFILE_ENTER( MAIN_PYTHON
);
477 Py_OptimizeFlag
= python_optimize
;
480 static PyMethodDef BjamMethods
[] = {
481 {"call", bjam_call
, METH_VARARGS
,
482 "Call the specified bjam rule."},
483 {"import_rule", bjam_import_rule
, METH_VARARGS
,
484 "Imports Python callable to bjam."},
485 {"define_action", bjam_define_action
, METH_VARARGS
,
486 "Defines a command line action."},
487 {"variable", bjam_variable
, METH_VARARGS
,
488 "Obtains a variable from bjam's global module."},
489 {"backtrace", bjam_backtrace
, METH_VARARGS
,
490 "Returns bjam backtrace from the last call into Python."},
491 {"caller", bjam_caller
, METH_VARARGS
,
492 "Returns the module from which the last call into Python is made."},
493 {NULL
, NULL
, 0, NULL
}
496 Py_InitModule( "bjam", BjamMethods
);
498 PROFILE_EXIT( MAIN_PYTHON
);
513 timestamp_current( ¤t
);
514 var_set( root_module(), constant_JAMDATE
, list_new( outf_time(
515 ¤t
) ), VAR_SET
);
518 /* Set JAM_VERSION. */
519 var_set( root_module(), constant_JAM_VERSION
,
520 list_push_back( list_push_back( list_new(
521 object_new( VERSION_MAJOR_SYM
) ),
522 object_new( VERSION_MINOR_SYM
) ),
523 object_new( VERSION_PATCH_SYM
) ),
531 if ( uname( &u
) >= 0 )
533 var_set( root_module(), constant_JAMUNAME
,
539 object_new( u
.sysname
) ),
540 object_new( u
.nodename
) ),
541 object_new( u
.release
) ),
542 object_new( u
.version
) ),
543 object_new( u
.machine
) ), VAR_SET
);
548 /* Set JAM_TIMESTAMP_RESOLUTION. */
550 timestamp fmt_resolution
[ 1 ];
551 file_supported_fmt_resolution( fmt_resolution
);
552 var_set( root_module(), constant_JAM_TIMESTAMP_RESOLUTION
, list_new(
553 object_new( timestamp_timestr( fmt_resolution
) ) ), VAR_SET
);
556 /* Load up environment variables. */
558 /* First into the global module, with splitting, for backward
561 var_defines( root_module(), use_environ
, 1 );
563 environ_module
= bindmodule( constant_ENVIRON
);
564 /* Then into .ENVIRON, without splitting. */
565 var_defines( environ_module
, use_environ
, 0 );
568 * Jam defined variables OS & OSPLAT. We load them after environment, so
569 * that setting OS in environment does not change Jam's notion of the
572 var_defines( root_module(), othersyms
, 1 );
574 /* Load up variables set on command line. */
575 for ( n
= 0; ( s
= getoptval( optv
, 's', n
) ); ++n
)
580 var_defines( root_module(), symv
, 1 );
581 var_defines( environ_module
, symv
, 0 );
584 /* Set the ARGV to reflect the complete list of arguments of invocation.
586 for ( n
= 0; n
< arg_c
; ++n
)
587 var_set( root_module(), constant_ARGV
, list_new( object_new(
588 arg_v
[ n
] ) ), VAR_APPEND
);
590 /* Initialize built-in rules. */
593 /* Add the targets in the command line to the update list. */
594 for ( n
= 1; n
< arg_c
; ++n
)
596 if ( arg_v
[ n
][ 0 ] == '-' )
598 const char * f
= "-:l:d:j:f:gs:t:ano:qv";
599 for ( ; *f
; ++f
) if ( *f
== arg_v
[ n
][ 1 ] ) break;
600 if ( f
[0] && f
[1] && ( f
[ 1 ] == ':' ) && ( arg_v
[ n
][ 2 ] == '\0' ) ) ++n
;
604 OBJECT
* const target
= object_new( arg_v
[ n
] );
605 mark_target_for_updating( target
);
606 object_free( target
);
610 /* The build system may set the PARALLELISM variable to override -j
614 LIST
* const p
= var_get( root_module(), constant_PARALLELISM
);
615 if ( !list_empty( p
) )
617 int const j
= atoi( object_str( list_front( p
) ) );
619 out_printf( "Invalid value of PARALLELISM: %s.\n",
620 object_str( list_front( p
) ) );
626 /* KEEP_GOING overrides -q option. */
628 LIST
* const p
= var_get( root_module(), constant_KEEP_GOING
);
629 if ( !list_empty( p
) )
630 globs
.quitquick
= atoi( object_str( list_front( p
) ) ) ? 0 : 1;
634 if ( list_empty( targets_to_update() ) )
635 mark_target_for_updating( constant_all
);
641 for ( n
= 0; ( s
= getoptval( optv
, 'f', n
) ); ++n
)
643 OBJECT
* const filename
= object_new( s
);
644 parse_file( filename
, frame
);
645 object_free( filename
);
649 parse_file( constant_plus
, frame
);
652 status
= yyanyerrors();
654 /* Manually touch -t targets. */
655 for ( n
= 0; ( s
= getoptval( optv
, 't', n
) ); ++n
)
657 OBJECT
* const target
= object_new( s
);
658 touch_target( target
);
659 object_free( target
);
663 /* Now make target. */
665 PROFILE_ENTER( MAIN_MAKE
);
666 LIST
* const targets
= targets_to_update();
667 if ( !list_empty( targets
) )
668 status
|= make( targets
, anyhow
);
670 status
= last_update_now_status
;
671 PROFILE_EXIT( MAIN_MAKE
);
674 PROFILE_EXIT( MAIN
);
681 #ifdef OPT_HEADER_CACHE_EXT
685 clear_targets_to_update();
687 /* Widely scattered cleanup. */
714 return status
? EXITBAD
: EXITOK
;
723 # define WIN32_LEAN_AND_MEAN
724 # include <windows.h>
725 char * executable_path( char const * argv0
)
728 DWORD
const ret
= GetModuleFileName( NULL
, buf
, sizeof( buf
) );
729 return ( !ret
|| ret
== sizeof( buf
) ) ? NULL
: strdup( buf
);
731 #elif defined(__APPLE__) /* Not tested */
732 # include <mach-o/dyld.h>
733 char *executable_path( char const * argv0
)
736 uint32_t size
= sizeof( buf
);
737 return _NSGetExecutablePath( buf
, &size
) ? NULL
: strdup( buf
);
739 #elif defined(sun) || defined(__sun) /* Not tested */
741 char * executable_path( char const * argv0
)
743 const char * execname
= getexecname();
744 return execname
? strdup( execname
) : NULL
;
746 #elif defined(__FreeBSD__)
747 # include <sys/sysctl.h>
748 char * executable_path( char const * argv0
)
750 int mib
[ 4 ] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
752 size_t size
= sizeof( buf
);
753 sysctl( mib
, 4, buf
, &size
, NULL
, 0 );
754 return ( !size
|| size
== sizeof( buf
) ) ? NULL
: strndup( buf
, size
);
756 #elif defined(__linux__)
758 char * executable_path( char const * argv0
)
761 ssize_t
const ret
= readlink( "/proc/self/exe", buf
, sizeof( buf
) );
762 return ( !ret
|| ret
== sizeof( buf
) ) ? NULL
: strndup( buf
, ret
);
764 #elif defined(OS_VMS)
765 # include <unixlib.h>
766 char * executable_path( char const * argv0
)
768 char * vms_path
= NULL
;
769 char * posix_path
= NULL
;
772 /* On VMS argv[0] shows absolute path to the image file.
773 * So, just remove VMS file version and translate path to POSIX-style.
775 vms_path
= strdup( argv0
);
776 if ( vms_path
&& ( p
= strchr( vms_path
, ';') ) ) *p
= '\0';
777 posix_path
= decc$
translate_vms( vms_path
);
778 if ( vms_path
) free( vms_path
);
780 return posix_path
> 0 ? strdup( posix_path
) : NULL
;
783 char * executable_path( char const * argv0
)
785 /* If argv0 is an absolute path, assume it is the right absolute path. */
786 return argv0
[ 0 ] == '/' ? strdup( argv0
) : NULL
;