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( &current );
403             var_set( root_module(), constant_JAMDATE, list_new( outf_time(
404                 &current ) ), 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