2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-13 12:22:17 +00:00
Files
build/src/engine/builtins.c
Dave Abrahams 2e6f58d76f Better error reporting for IMPORT
[SVN r13584]
2002-04-30 14:13:09 +00:00

693 lines
17 KiB
C

/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "builtins.h"
# include "rules.h"
# include "filesys.h"
# include "newstr.h"
# include "regexp.h"
# include "frames.h"
# include "hash.h"
# include "strings.h"
# include "pwd.h"
# include "pathsys.h"
/*
* builtins.c - builtin jam rules
*
* External routines:
*
* load_builtin() - define builtin rules
*
* Internal routines:
*
* builtin_depends() - DEPENDS/INCLUDES rule
* builtin_echo() - ECHO rule
* builtin_exit() - EXIT rule
* builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
* builtin_glob() - GLOB rule
* builtin_match() - MATCH rule
*
* 01/10/01 (seiwald) - split from compile.c
*/
/*
* compile_builtin() - define builtin rules
*/
# define P0 (PARSE *)0
# define C0 (char *)0
int glob( char *s, char *c );
static void lol_build( LOL* lol, char** elements );
void backtrace( FRAME *frame );
void backtrace_line( FRAME *frame );
void print_source_line( PARSE* p );
RULE* bind_builtin( char* name, LIST*(*f)(PARSE*, FRAME*), int flags, char** args )
{
argument_list* arg_list = 0;
if ( args )
{
arg_list = args_new();
lol_build( arg_list->data, args );
}
return new_rule_body( root_module(), name, arg_list,
parse_make( f, P0, P0, P0, C0, C0, flags ), 1 );
}
RULE* duplicate_rule( char* name, RULE* other )
{
return import_rule( other, root_module(), name );
}
void
load_builtins()
{
duplicate_rule( "Always" ,
bind_builtin( "ALWAYS" ,
builtin_flags, T_FLAG_TOUCHED, 0 ) );
duplicate_rule( "Depends" ,
bind_builtin( "DEPENDS" ,
builtin_depends, T_DEPS_DEPENDS, 0 ) );
duplicate_rule( "echo" ,
duplicate_rule( "Echo" ,
bind_builtin( "ECHO" ,
builtin_echo, 0, 0 ) ) );
duplicate_rule( "exit" ,
duplicate_rule( "Exit" ,
bind_builtin( "EXIT" ,
builtin_exit, 0, 0 ) ) );
duplicate_rule( "Glob" ,
bind_builtin( "GLOB" ,
builtin_glob, 0, 0 ) );
duplicate_rule( "Includes" ,
bind_builtin( "INCLUDES" ,
builtin_depends, T_DEPS_INCLUDES, 0 ) );
duplicate_rule( "Leaves" ,
bind_builtin( "LEAVES" ,
builtin_flags, T_FLAG_LEAVES, 0 ) );
duplicate_rule( "Match" ,
bind_builtin( "MATCH" ,
builtin_match, 0, 0 ) );
duplicate_rule( "NoCare" ,
bind_builtin( "NOCARE" ,
builtin_flags, T_FLAG_NOCARE, 0 ) );
duplicate_rule( "NOTIME" ,
duplicate_rule( "NotFile" ,
bind_builtin( "NOTFILE" ,
builtin_flags, T_FLAG_NOTFILE, 0 ) ) );
duplicate_rule( "NoUpdate" ,
bind_builtin( "NOUPDATE" ,
builtin_flags, T_FLAG_NOUPDATE, 0 ) );
duplicate_rule( "Temporary" ,
bind_builtin( "TEMPORARY" ,
builtin_flags, T_FLAG_TEMP, 0 ) );
duplicate_rule( "HdrMacro" ,
bind_builtin( "HDRMACRO" ,
builtin_hdrmacro, 0, 0 ) );
# if 0
/* FAIL_EXPECTED is an experimental built-in that is used to indicate */
/* that the result of a target build action should be inverted (ok <=> fail) */
/* this can be useful when performing test runs from Jamfiles.. */
/* */
/* Beware that this rule might disappear or be renamed in the future.. */
/* contact david.turner@freetype.org for more details.. */
bind_builtin( "FAIL_EXPECTED" ,
builtin_flags, T_FLAG_FAIL_EXPECTED, 0 );
# endif
bind_builtin( "RMOLD" , builtin_flags, T_FLAG_RMOLD, 0 );
{
char * args[] = { "string", "pattern", "replacements", "+", 0 };
duplicate_rule( "subst" ,
bind_builtin( "SUBST" ,
builtin_subst, 0, args ) );
}
{
char * args[] = { "module", "?", 0 };
bind_builtin( "RULENAMES" ,
builtin_rulenames, 0, args );
}
{
char * args[] = { "source_module", "?",
":", "source_rules", "*",
":", "target_module", "?",
":", "target_rules", "*",
":", "localize", "?", 0 };
bind_builtin( "IMPORT" ,
builtin_import, 0, args );
}
{
char * args[] = { "module", "?", ":", "rules", "*", 0 };
bind_builtin( "EXPORT" ,
builtin_export, 0, args );
}
{
char * args[] = { "levels", "?", 0 };
bind_builtin( "CALLER_MODULE" ,
builtin_caller_module, 0, args );
}
{
char * args[] = { 0 };
bind_builtin( "BACKTRACE" ,
builtin_backtrace, 0, args );
}
{
char * args[] = { 0 };
bind_builtin( "PWD" ,
builtin_pwd, 0, args );
}
}
/*
* builtin_depends() - DEPENDS/INCLUDES rule
*
* The DEPENDS builtin rule appends each of the listed sources on the
* dependency list of each of the listed targets. It binds both the
* targets and sources as TARGETs.
*/
LIST *
builtin_depends(
PARSE *parse,
FRAME *frame )
{
LIST *targets = lol_get( frame->args, 0 );
LIST *sources = lol_get( frame->args, 1 );
int which = parse->num;
LIST *l;
for( l = targets; l; l = list_next( l ) )
{
TARGET *t = bindtarget( l->string );
t->deps[ which ] = targetlist( t->deps[ which ], sources );
}
return L0;
}
/*
* builtin_echo() - ECHO rule
*
* The ECHO builtin rule echoes the targets to the user. No other
* actions are taken.
*/
LIST *
builtin_echo(
PARSE *parse,
FRAME *frame )
{
list_print( lol_get( frame->args, 0 ) );
printf( "\n" );
return L0;
}
/*
* builtin_exit() - EXIT rule
*
* The EXIT builtin rule echoes the targets to the user and exits
* the program with a failure status.
*/
LIST *
builtin_exit(
PARSE *parse,
FRAME *frame )
{
list_print( lol_get( frame->args, 0 ) );
printf( "\n" );
exit( EXITBAD ); /* yeech */
return L0;
}
/*
* builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
*
* Builtin_flags() marks the target with the appropriate flag, for use
* by make0(). It binds each target as a TARGET.
*/
LIST *
builtin_flags(
PARSE *parse,
FRAME *frame )
{
LIST *l = lol_get( frame->args, 0 );
for( ; l; l = list_next( l ) )
bindtarget( l->string )->flags |= parse->num;
return L0;
}
/*
* builtin_globbing() - GLOB rule
*/
struct globbing {
LIST *patterns;
LIST *results;
} ;
static void
builtin_glob_back(
void *closure,
char *file,
int status,
time_t time )
{
struct globbing *globbing = (struct globbing *)closure;
LIST *l;
PATHNAME f;
string buf[1];
/* Null out directory for matching. */
/* We wish we had file_dirscan() pass up a PATHNAME. */
path_parse( file, &f );
f.f_dir.len = 0;
string_new( buf );
path_build( &f, buf, 0 );
for( l = globbing->patterns; l; l = l->next )
if( !glob( l->string, buf->value ) )
{
globbing->results = list_new( globbing->results, newstr( file ) );
break;
}
string_free( buf );
}
LIST *
builtin_glob(
PARSE *parse,
FRAME *frame )
{
LIST *l = lol_get( frame->args, 0 );
LIST *r = lol_get( frame->args, 1 );
struct globbing globbing;
globbing.results = L0;
globbing.patterns = r;
for( ; l; l = list_next( l ) )
file_dirscan( l->string, builtin_glob_back, &globbing );
return globbing.results;
}
/*
* builtin_match() - MATCH rule, regexp matching
*/
LIST *
builtin_match(
PARSE *parse,
FRAME *frame )
{
LIST *l, *r;
LIST *result = 0;
extern regexp* regex_compile( char* );
string buf[1];
string_new(buf);
/* For each pattern */
for( l = lol_get( frame->args, 0 ); l; l = l->next )
{
/* Result is cached and intentionally never freed */
regexp *re = regex_compile( l->string );
/* For each string to match against */
for( r = lol_get( frame->args, 1 ); r; r = r->next )
{
if( regexec( re, r->string ) )
{
int i, top;
/* Find highest parameter */
for( top = NSUBEXP; top-- > 1; )
if( re->startp[top] )
break;
/* And add all parameters up to highest onto list. */
/* Must have parameters to have results! */
for( i = 1; i <= top; i++ )
{
string_append_range( buf, re->startp[i], re->endp[i] );
result = list_new( result, newstr( buf->value ) );
string_truncate( buf, 0 );
}
}
}
}
string_free( buf );
return result;
}
LIST *
builtin_hdrmacro(
PARSE *parse,
FRAME *frame )
{
LIST* l = lol_get( frame->args, 0 );
for ( ; l; l = list_next(l) )
{
TARGET* t = bindtarget( l->string );
/* scan file for header filename macro definitions */
if ( DEBUG_HEADER )
printf( "scanning '%s' for header file macro definitions\n",
l->string );
macro_headers( t );
}
return L0;
}
/* builtin_rulenames() - RULENAMES ( MODULE ? )
*
* Returns a list of the non-local rule names in the given MODULE. If
* MODULE is not supplied, returns the list of rule names in the
* global module.
*/
/* helper function for builtin_rulenames(), below */
static void add_rule_name( void* r_, void* result_ )
{
RULE* r = r_;
LIST** result = result_;
if ( r->exported )
*result = list_new( *result, copystr( r->name ) );
}
LIST *
builtin_rulenames(
PARSE *parse,
FRAME *frame )
{
LIST *arg0 = lol_get( frame->args, 0 );
LIST *result = L0;
module* source_module = bindmodule( arg0 ? arg0->string : 0 );
hashenumerate( source_module->rules, add_rule_name, &result );
return result;
}
static void unknown_rule( FRAME *frame, char* key, char *module_name, char *rule_name )
{
backtrace_line( frame->prev );
printf( "%s error: rule \"%s\" unknown in module \"%s\"\n", key, rule_name, module_name );
backtrace( frame->prev );
exit(1);
}
/*
* builtin_import() - IMPORT ( SOURCE_MODULE ? : SOURCE_RULES * : TARGET_MODULE ? : TARGET_RULES * : LOCALIZE ? )
*
* The IMPORT rule imports rules from the SOURCE_MODULE into the
* TARGET_MODULE as local rules. If either SOURCE_MODULE or
* TARGET_MODULE is not supplied, it refers to the global
* module. SOURCE_RULES specifies which rules from the SOURCE_MODULE
* to import; TARGET_RULES specifies the names to give those rules in
* TARGET_MODULE. If SOURCE_RULES contains a name which doesn't
* correspond to a rule in SOURCE_MODULE, or if it contains a
* different number of items than TARGET_RULES, an error is issued.
* if LOCALIZE is specified, the rules will be executed in
* TARGET_MODULE, with corresponding access to its module local
* variables.
*/
LIST *
builtin_import(
PARSE *parse,
FRAME *frame )
{
LIST *source_module_list = lol_get( frame->args, 0 );
LIST *source_rules = lol_get( frame->args, 1 );
LIST *target_module_list = lol_get( frame->args, 2 );
LIST *target_rules = lol_get( frame->args, 3 );
LIST *localize = lol_get( frame->args, 4 );
module* target_module = bindmodule( target_module_list ? target_module_list->string : 0 );
module* source_module = bindmodule( source_module_list ? source_module_list->string : 0 );
LIST *source_name, *target_name;
for ( source_name = source_rules, target_name = target_rules;
source_name && target_name;
source_name = list_next( source_name )
, target_name = list_next( target_name ) )
{
RULE r_, *r = &r_, *imported;
r_.name = source_name->string;
if ( !hashcheck( source_module->rules, (HASHDATA**)&r ) )
unknown_rule( frame, "IMPORT", source_module->name, r_.name );
imported = import_rule( r, target_module, target_name->string );
if ( localize )
imported->module = target_module;
imported->exported = 0; /* this rule is really part of some other module; just refer to it here, but don't let it out */
}
if ( source_name || target_name )
{
backtrace_line( frame->prev );
printf( "import error: length of source and target rule name lists don't match!\n" );
printf( " source: " );
list_print( source_rules );
printf( "\n target: " );
list_print( target_rules );
printf( "\n" );
backtrace( frame->prev );
exit(1);
}
return L0;
}
/*
* builtin_export() - EXPORT ( MODULE ? : RULES * )
*
* The EXPORT rule marks RULES from the SOURCE_MODULE as non-local
* (and thus exportable). If an element of RULES does not name a rule
* in MODULE, an error is issued.
*/
LIST *
builtin_export(
PARSE *parse,
FRAME *frame )
{
LIST *module_list = lol_get( frame->args, 0 );
LIST *rules = lol_get( frame->args, 1 );
module* m = bindmodule( module_list ? module_list->string : 0 );
for ( ; rules; rules = list_next( rules ) )
{
RULE r_, *r = &r_;
r_.name = rules->string;
if ( !hashcheck( m->rules, (HASHDATA**)&r ) )
unknown_rule( frame, "EXPORT", m->name, r_.name );
r->exported = 1;
}
return L0;
}
/* Retrieve the file and line number that should be indicated for a
* given procedure in debug output or an error backtrace
*/
static void get_source_line( PARSE* procedure, char** file, int* line )
{
if ( procedure )
{
char* f = procedure->file;
int l = procedure->line;
if ( !strcmp( f, "+" ) )
{
f = "jambase.c";
l += 3;
}
*file = f;
*line = l;
}
else
{
*file = "(builtin)";
*line = -1;
}
}
void print_source_line( PARSE* p )
{
char* file;
int line;
get_source_line( p, &file, &line );
if ( line < 0 )
printf( "(builtin):" );
else
printf( "%s:%d:", file, line);
}
/* Print a single line of error backtrace for the given frame */
void backtrace_line( FRAME *frame )
{
print_source_line( frame->procedure );
printf( " in %s\n", frame->rulename );
}
/* Print the entire backtrace from the given frame to the Jambase
* which invoked it.
*/
void backtrace( FRAME *frame )
{
while ( frame = frame->prev )
{
backtrace_line( frame );
}
}
/* A Jam version of the backtrace function, taking no arguments and
* returning a list of quadruples: FILENAME LINE MODULE. RULENAME
* describing each frame. Note that the module-name is always
* followed by a period.
*/
LIST *builtin_backtrace( PARSE *parse, FRAME *frame )
{
LIST* result = L0;
while ( frame = frame->prev )
{
char* file;
int line;
char buf[32];
get_source_line( frame->procedure, &file, &line );
sprintf( buf, "%d", line );
result = list_new( result, newstr( file ) );
result = list_new( result, newstr( buf ) );
result = list_new( result, newstr( frame->module->name ) );
result = list_new( result, newstr( frame->rulename ) );
}
return result;
}
/*
* builtin_caller_module() - CALLER_MODULE ( levels ? )
*
* If levels is not supplied, returns the name of the module of the rule which
* called the one calling this one. If levels is supplied, it is interpreted as
* an integer specifying a number of additional levels of call stack to traverse
* in order to locate the module in question. If no such module exists,
* returns the empty list. Also returns the empty list when the module in
* question is the global module. This rule is needed for implementing module
* import behavior.
*/
LIST *builtin_caller_module( PARSE *parse, FRAME *frame )
{
LIST* levels_arg = lol_get( frame->args, 0 );
int levels = levels_arg ? atoi( levels_arg->string ) : 0 ;
int i;
for (i = 0; i < levels + 2 && frame->prev; ++i)
frame = frame->prev;
if ( frame->module == root_module() )
{
return L0;
}
else
{
LIST* result;
string name;
string_copy( &name, frame->module->name );
string_pop_back( &name );
result = list_new( L0, newstr(name.value) );
string_free( &name );
return result;
}
}
/*
* Return the current working directory.
*
* Usage: pwd = [ PWD ] ;
*/
LIST*
builtin_pwd( PARSE *parse, FRAME *frame )
{
return pwd();
}
static void lol_build( LOL* lol, char** elements )
{
LIST* l = L0;
lol_init( lol );
while ( elements && *elements )
{
if ( !strcmp( *elements, ":" ) )
{
lol_add( lol, l );
l = L0 ;
}
else
{
l = list_new( l, newstr( *elements ) );
}
++elements;
}
if ( l != L0 )
lol_add( lol, l );
}