buildtools/jam/jam.c
Maciej Bałuta 19aaa79362 jam: make compile_commands.json valid JSON
There was a comma in compile_commands.json after the last entry what
prevented from using file directly in some tools.

Test plan:
verified new compile_commands.json with "python3 -m json.tool compile_commands.json"

Change-Id: I6c2582f7285a3409d95081a93f3c05b5cc9322b9
Reviewed-on: https://review.haiku-os.org/c/buildtools/+/4671
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
2021-11-03 11:48:45 +00:00

464 lines
11 KiB
C

/*
* /+\
* +\ Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
* \+/
*
* This file is part of jam.
*
* License is hereby granted to use this software and distribute it
* freely, as long as this copyright notice is retained and modifications
* are clearly marked.
*
* ALL WARRANTIES ARE HEREBY DISCLAIMED.
*/
/*
* jam.c - make redux
*
* See Jam.html for usage information.
*
* These comments document the code.
*
* The top half of the code is structured such:
*
* jam
* / | \
* +---+ | \
* / | \
* jamgram option \
* / | \ \
* / | \ \
* / | \ |
* scan | compile make
* | | / | \ / | \
* | | / | \ / | \
* | | / | \ / | \
* jambase parse | rules search make1
* | | | \
* | | | \
* | | | \
* builtins timestamp command execute
* |
* |
* |
* filesys
*
*
* The support routines are called by all of the above, but themselves
* are layered thus:
*
* variable|expand
* / | | |
* / | | |
* / | | |
* lists | | pathsys
* \ | |
* \ | |
* \ | |
* newstr |
* \ |
* \ |
* \ |
* hash
*
* Roughly, the modules are:
*
* builtins.c - jam's built-in rules
* command.c - maintain lists of commands
* compile.c - compile parsed jam statements
* execunix.c - execute a shell script on UNIX
* execvms.c - execute a shell script, ala VMS
* expand.c - expand a buffer, given variable values
* file*.c - scan directories and archives on *
* hash.c - simple in-memory hashing routines
* headers.c - handle #includes in source files
* jambase.c - compilable copy of Jambase
* jamgram.y - jam grammar
* lists.c - maintain lists of strings
* make.c - bring a target up to date, once rules are in place
* make1.c - execute command to bring targets up to date
* newstr.c - string manipulation routines
* option.c - command line option processing
* parse.c - make and destroy parse trees as driven by the parser
* path*.c - manipulate file names on *
* hash.c - simple in-memory hashing routines
* regexp.c - Henry Spencer's regexp
* rules.c - access to RULEs, TARGETs, and ACTIONs
* scan.c - the jam yacc scanner
* search.c - find a target along $(SEARCH) or $(LOCATE)
* timestamp.c - get the timestamp of a file or archive member
* variable.c - handle jam multi-element variables
*
* 05/04/94 (seiwald) - async multiprocess (-j) support
* 02/08/95 (seiwald) - -n implies -d2.
* 02/22/95 (seiwald) - -v for version info.
* 09/11/00 (seiwald) - PATCHLEVEL folded into VERSION.
* 01/10/01 (seiwald) - pathsys.h split from filesys.h
* 01/21/02 (seiwald) - new -q to quit quickly on build failure
* 03/16/02 (seiwald) - support for -g (reorder builds by source time)
* 09/19/02 (seiwald) - new -d displays
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 11/04/02 (seiwald) - const-ing for string literals
*/
# include "jam.h"
# include "option.h"
# include "patchlevel.h"
/* These get various function declarations. */
# include "lists.h"
# include "parse.h"
# include "variable.h"
# include "compile.h"
# include "builtins.h"
# include "jcache.h"
# include "rules.h"
# include "newstr.h"
# include "scan.h"
# include "timestamp.h"
# include "make.h"
/* Macintosh is "special" */
# ifdef OS_MAC
# include <QuickDraw.h>
# endif
/* And UNIX for this */
# ifdef unix
# include <sys/utsname.h>
# endif
struct globs globs = {
0, /* noexec */
1, /* jobs */
0, /* quitquick */
0, /* newestfirst */
# ifdef OS_MAC
{ 0 }, /* display - suppress actions output */
# else
{ 0, 1 }, /* display actions */
# endif
0, /* output compilation db here */
0 /* output commands, not run them */
} ;
/* Symbols to be defined as true for use in Jambase */
static const char *othersyms[] = { OSMAJOR, OSMINOR, OSPLAT, JAMVERSYM, 0 } ;
/* Known for sure:
* mac needs arg_enviro
* OS2 needs extern environ
*/
# ifdef OS_MAC
# define use_environ arg_environ
# ifdef MPW
QDGlobals qd;
# endif
# endif
# ifndef use_environ
# define use_environ environ
# if !defined( __WATCOM__ ) && !defined( OS_OS2 ) && !defined( OS_NT )
extern char **environ;
# endif
# endif
int main( int argc, char **argv, char **arg_environ )
{
int n;
const char *s;
struct option optv[N_OPTS];
const char *all = "all";
int anyhow = 0;
int status;
# ifdef OS_MAC
InitGraf(&qd.thePort);
# endif
argc--, argv++;
if( ( n = getoptions( argc, argv, "d:j:f:gs:t:ano:cqv", optv ) ) < 0 )
{
printf( "\nusage: jam [ options ] targets...\n\n" );
printf( "-a Build all targets, even if they are current.\n" );
printf( "-dx Display (a)actions (c)causes (d)dependencies\n" );
printf( " (m)make tree (x)commands (0-9) debug levels.\n" );
# ifdef OPT_RULE_PROFILING_EXT
printf( " (p)profile rules.\n" );
# endif
printf( "-fx Read x instead of Jambase.\n" );
printf( "-g Build from newest sources first.\n" );
printf( "-jx Run up to x shell commands concurrently.\n" );
printf( "-n Don't actually execute the updating actions.\n" );
printf( "-ox Write the updating actions to file x.\n" );
printf( "-c Output JSON compilation database to compile_commands.json.\n" );
printf( "-q Quit quickly as soon as a target fails.\n" );
printf( "-sx=y Set variable x=y, overriding environment.\n" );
printf( "-tx Rebuild x, even if it is up-to-date.\n" );
printf( "-v Print the version of jam and exit.\n\n" );
exit( EXITBAD );
}
argc -= n, argv += n;
/* Version info. */
if( ( s = getoptval( optv, 'v', 0 ) ) )
{
printf( "Jam %s. %s. ", VERSION, OSMINOR );
printf( "Copyright 1993-2002 Christopher Seiwald.\n" );
return EXITOK;
}
/* Pick up interesting options */
if( ( s = getoptval( optv, 'n', 0 ) ) )
globs.noexec++, DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 1;
if( ( s = getoptval( optv, 'q', 0 ) ) )
globs.quitquick = 1;
if( ( s = getoptval( optv, 'a', 0 ) ) )
anyhow++;
if( ( s = getoptval( optv, 'j', 0 ) ) )
{
globs.jobs = atoi( s );
var_set( "JAMJOBS", list_new( L0, s, 0 ), VAR_SET );
}
if( ( s = getoptval( optv, 'g', 0 ) ) )
globs.newestfirst = 1;
/* Turn on/off debugging */
for( n = 0; s = getoptval( optv, 'd', n ); n++ )
{
int i = atoi( s );
/* First -d, turn off defaults. */
if( !n )
DEBUG_MAKE = DEBUG_MAKEQ = DEBUG_EXEC = 0;
/* n turns on levels 1-n */
/* +n turns on level n */
/* c turns on named display c */
if( i < 0 || i >= DEBUG_MAX )
{
printf( "Invalid debug level '%s'.\n", s );
}
else if( *s == '+' )
{
globs.debug[i] = 1;
}
else if( i ) while( i )
{
globs.debug[i--] = 1;
}
else while( *s ) switch( *s++ )
{
case 'a': DEBUG_MAKE = DEBUG_MAKEQ = 1; break;
case 'c': DEBUG_CAUSES = 1; break;
case 'd': DEBUG_DEPENDS = 1; break;
case 'm': DEBUG_MAKEPROG = 1; break;
case 'x': DEBUG_EXEC = 1; break;
# ifdef OPT_RULE_PROFILING_EXT
case 'p': DEBUG_PROFILE_RULES = 1; break;
# endif
case '0': break;
default: printf( "Invalid debug flag '%c'.\n", s[-1] );
}
}
/* If we're asked to produce a compilation database, open the file. */
if ( ( s = getoptval( optv, 'c', 0 ) ) )
{
if ( !( globs.comp_db = fopen( "compile_commands.json", "w" ) ) )
{
printf( "Failed to write to 'compile_commands.json'\n");
exit( EXITBAD );
}
fprintf(globs.comp_db, "[\n");
}
/* Set JAMDATE first */
{
char buf[ 128 ];
time_t clock;
time( &clock );
strcpy( buf, ctime( &clock ) );
/* Trim newline from date */
if( strlen( buf ) == 25 )
buf[ 24 ] = 0;
var_set( "JAMDATE", list_new( L0, buf, 0 ), VAR_SET );
}
/* And JAMUNAME */
# ifdef unix
{
struct utsname u;
if( uname( &u ) >= 0 )
{
LIST *l = L0;
l = list_new( l, u.machine, 0 );
l = list_new( l, u.version, 0 );
l = list_new( l, u.release, 0 );
l = list_new( l, u.nodename, 0 );
l = list_new( l, u.sysname, 0 );
var_set( "JAMUNAME", l, VAR_SET );
}
}
# endif /* unix */
/*
* Jam defined variables OS, OSPLAT
*/
var_defines( othersyms );
/* load up environment variables */
var_defines( (const char **)use_environ );
#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
/* define the variable JAM_TARGETS containing the targets specified on
the command line */
{
LIST *l = L0;
int i;
char **targets = argv;
int targetCount = argc;
if (targetCount == 0) {
targets = (char**)&all;
targetCount = 1;
}
for (i = 0; i < targetCount; i++)
l = list_new( l, targets[i], 0 );
var_set( "JAM_TARGETS", l, VAR_SET );
}
#endif
/* Load up variables set on command line. */
for( n = 0; s = getoptval( optv, 's', n ); n++ )
{
const char *symv[2];
symv[0] = s;
symv[1] = 0;
var_defines( symv );
}
/* Initialize built-in rules */
load_builtins();
/* Parse ruleset */
#ifdef OPT_JAMFILE_CACHE_EXT
jcache_init();
#endif
for( n = 0; s = getoptval( optv, 'f', n ); n++ )
parse_file( s );
if( !n )
parse_file( "+" );
#ifdef OPT_JAMFILE_CACHE_EXT
jcache_done();
#endif
status = yyanyerrors();
/* Manually touch -t targets */
for( n = 0; s = getoptval( optv, 't', n ); n++ )
touchtarget( s );
/* If an output file is specified, set globs.cmdout to that */
if( s = getoptval( optv, 'o', 0 ) )
{
if( !( globs.cmdout = fopen( s, "w" ) ) )
{
printf( "Failed to write to '%s'\n", s );
exit( EXITBAD );
}
globs.noexec++;
}
#ifdef OPT_JAM_TARGETS_VARIABLE_EXT
/* get value of variable JAM_TARGETS and build the targets */
{
LIST *l = var_get( "JAM_TARGETS" );
int targetCount = list_length(l);
char **targets;
int i;
if (targetCount == 0) {
/* No targets. Nothing to do. */
exit( EXITOK );
}
targets = malloc(targetCount * sizeof(char*));
if (!targets) {
printf( "Memory allocation failed!\n" );
exit( EXITBAD );
}
for (i = 0; i < targetCount; i++) {
targets[i] = (char*)l->string;
l = l->next;
}
argv = targets;
argc = targetCount;
}
#endif
/* Now make target */
if( !argc )
status |= make( 1, &all, anyhow );
else
status |= make( argc, (const char **)argv, anyhow );
/* Widely scattered cleanup */
var_done();
donerules();
donestamps();
donestr();
/* close cmdout */
if( globs.cmdout )
fclose( globs.cmdout );
/* close compilation database output file */
if ( globs.comp_db )
{
fprintf(globs.comp_db, "\n]\n");
fclose( globs.comp_db );
}
return status ? EXITBAD : EXITOK;
}