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