mirror of
https://github.com/boostorg/build.git
synced 2026-02-14 12:42:11 +00:00
1924 lines
47 KiB
C
1924 lines
47 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"
|
|
# include "make.h"
|
|
# include "hdrmacro.h"
|
|
# include "compile.h"
|
|
# include "native.h"
|
|
# include "variable.h"
|
|
# include "timestamp.h"
|
|
# include <ctype.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
|
|
|
|
# if defined( OS_NT ) || defined( OS_CYGWIN )
|
|
LIST* builtin_system_registry( PARSE *parse, FRAME *frame );
|
|
LIST* builtin_system_registry_names( PARSE *parse, FRAME *frame );
|
|
# endif
|
|
|
|
int glob( char *s, char *c );
|
|
|
|
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, 0, 0 ) );
|
|
|
|
duplicate_rule( "echo" ,
|
|
duplicate_rule( "Echo" ,
|
|
bind_builtin( "ECHO" ,
|
|
builtin_echo, 0, 0 ) ) );
|
|
|
|
|
|
{
|
|
char * args[] = { "message", "*", ":", "result-value", "?", 0 };
|
|
duplicate_rule( "exit" ,
|
|
duplicate_rule( "Exit" ,
|
|
bind_builtin( "EXIT" ,
|
|
builtin_exit, 0, args ) ) );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "directories", "*", ":", "patterns", "*", ":", "case-insensitive", "?", 0 };
|
|
duplicate_rule(
|
|
"Glob" ,
|
|
bind_builtin( "GLOB" , builtin_glob, 0, args )
|
|
);
|
|
}
|
|
|
|
{
|
|
char * args[] = { "patterns", "*", 0 };
|
|
bind_builtin( "GLOB-RECURSIVELY" , builtin_glob_recursive, 0, args );
|
|
}
|
|
|
|
|
|
duplicate_rule( "Includes" ,
|
|
bind_builtin( "INCLUDES" ,
|
|
builtin_depends, 1, 0 ) );
|
|
|
|
{
|
|
char * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", 0 };
|
|
bind_builtin( "REBUILDS" ,
|
|
builtin_rebuilds, 0, args );
|
|
}
|
|
|
|
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 ) );
|
|
|
|
{
|
|
char * args[] = { "targets", "*", 0 };
|
|
bind_builtin(
|
|
"ISFILE",
|
|
builtin_flags, T_FLAG_ISFILE, 0 );
|
|
}
|
|
|
|
duplicate_rule( "HdrMacro" ,
|
|
bind_builtin( "HDRMACRO" ,
|
|
builtin_hdrmacro, 0, 0 ) );
|
|
|
|
/* FAIL_EXPECTED 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.. */
|
|
bind_builtin( "FAIL_EXPECTED" ,
|
|
builtin_flags, T_FLAG_FAIL_EXPECTED, 0 );
|
|
|
|
bind_builtin( "RMOLD" , builtin_flags, T_FLAG_RMOLD, 0 );
|
|
|
|
{
|
|
char * args[] = { "targets", "*", 0 };
|
|
bind_builtin( "UPDATE", builtin_update, 0, args );
|
|
}
|
|
|
|
{
|
|
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[] = { "module", "?", 0 };
|
|
bind_builtin( "VARNAMES" ,
|
|
builtin_varnames, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "module", "?", 0 };
|
|
bind_builtin( "DELETE_MODULE" ,
|
|
builtin_delete_module, 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[] = { "levels", "?", 0 };
|
|
bind_builtin( "BACKTRACE" ,
|
|
builtin_backtrace, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { 0 };
|
|
bind_builtin( "PWD" ,
|
|
builtin_pwd, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "target", "*", ":", "path", "*", 0 };
|
|
bind_builtin( "SEARCH_FOR_TARGET",
|
|
builtin_search_for_target, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "modules_to_import", "+", ":", "target_module", "?", 0 };
|
|
bind_builtin( "IMPORT_MODULE",
|
|
builtin_import_module, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "module", "?", 0 };
|
|
bind_builtin( "IMPORTED_MODULES",
|
|
builtin_imported_modules, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "instance_module", ":", "class_module", 0 };
|
|
bind_builtin( "INSTANCE",
|
|
builtin_instance, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "sequence", "*", 0 };
|
|
bind_builtin( "SORT",
|
|
builtin_sort, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "path_parts", "*", 0 };
|
|
bind_builtin( "NORMALIZE_PATH",
|
|
builtin_normalize_path, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "args", "*", 0 };
|
|
bind_builtin( "CALC",
|
|
builtin_calc, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "module", ":", "rule", 0 };
|
|
bind_builtin( "NATIVE_RULE",
|
|
builtin_native_rule, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "module", ":", "rule", ":", "version", 0 };
|
|
bind_builtin( "HAS_NATIVE_RULE",
|
|
builtin_has_native_rule, 0, args );
|
|
}
|
|
|
|
|
|
{
|
|
char * args[] = { "module", "*", 0 };
|
|
bind_builtin( "USER_MODULE",
|
|
builtin_user_module, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { 0 };
|
|
bind_builtin( "NEAREST_USER_LOCATION",
|
|
builtin_nearest_user_location, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "file", 0 };
|
|
bind_builtin( "CHECK_IF_FILE",
|
|
builtin_check_if_file, 0, args );
|
|
}
|
|
|
|
#ifdef HAVE_PYTHON
|
|
{
|
|
char * args[] = { "python-module", ":", "function", ":",
|
|
"jam-module", ":", "rule-name", 0 };
|
|
bind_builtin( "PYTHON_IMPORT_RULE",
|
|
builtin_python_import_rule, 0, args );
|
|
}
|
|
#endif
|
|
|
|
# if defined( OS_NT ) || defined( OS_CYGWIN )
|
|
{
|
|
char * args[] = { "key_path", ":", "data", "?", 0 };
|
|
bind_builtin( "W32_GETREG",
|
|
builtin_system_registry, 0, args );
|
|
}
|
|
|
|
{
|
|
char * args[] = { "key_path", ":", "result-type", 0 };
|
|
bind_builtin( "W32_GETREGNAMES",
|
|
builtin_system_registry_names, 0, args );
|
|
}
|
|
# endif
|
|
|
|
{
|
|
char * args[] = { "command", ":", "*", 0 };
|
|
bind_builtin( "SHELL",
|
|
builtin_shell, 0, args );
|
|
bind_builtin( "COMMAND",
|
|
builtin_shell, 0, args );
|
|
}
|
|
|
|
/* Initialize builtin modules */
|
|
init_set();
|
|
init_path();
|
|
init_regex();
|
|
init_property_set();
|
|
init_sequence();
|
|
init_order();
|
|
}
|
|
|
|
/*
|
|
* builtin_calc() - CALC rule
|
|
*
|
|
* The CALC rule performs simple mathematical operations on two arguments.
|
|
*/
|
|
|
|
LIST *
|
|
builtin_calc(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST *arg = lol_get( frame->args, 0 );
|
|
|
|
LIST *result = 0;
|
|
long lhs_value;
|
|
long rhs_value;
|
|
long result_value;
|
|
char buffer [16];
|
|
const char* lhs;
|
|
const char* op;
|
|
const char* rhs;
|
|
|
|
if (arg == 0) return L0;
|
|
lhs = arg->string;
|
|
|
|
arg = list_next( arg );
|
|
if (arg == 0) return L0;
|
|
op = arg->string;
|
|
|
|
arg = list_next( arg );
|
|
if (arg == 0) return L0;
|
|
rhs = arg->string;
|
|
|
|
lhs_value = atoi (lhs);
|
|
rhs_value = atoi (rhs);
|
|
|
|
if (strcmp ("+", op) == 0)
|
|
{
|
|
result_value = lhs_value + rhs_value;
|
|
}
|
|
else if (strcmp ("-", op) == 0)
|
|
{
|
|
result_value = lhs_value - rhs_value;
|
|
}
|
|
else
|
|
{
|
|
return L0;
|
|
}
|
|
|
|
sprintf (buffer, "%ld", result_value);
|
|
result = list_new( result, newstr( buffer ) );
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* 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 );
|
|
LIST *l;
|
|
|
|
for( l = targets; l; l = list_next( l ) )
|
|
{
|
|
TARGET *t = bindtarget( l->string );
|
|
|
|
/* If doing INCLUDES, switch to the TARGET's include */
|
|
/* TARGET, creating it if needed. The internal include */
|
|
/* TARGET shares the name of its parent. */
|
|
|
|
if( parse->num )
|
|
{
|
|
if( !t->includes ) {
|
|
t->includes = copytarget( t );
|
|
t->includes->original_target = t;
|
|
}
|
|
t = t->includes;
|
|
}
|
|
|
|
t->depends = targetlist( t->depends, sources );
|
|
}
|
|
|
|
/* Enter reverse links */
|
|
for( l = sources; l; l = list_next( l ) )
|
|
{
|
|
TARGET *s = bindtarget( l->string );
|
|
s->dependents = targetlist( s->dependents, targets );
|
|
}
|
|
|
|
return L0;
|
|
}
|
|
|
|
/*
|
|
* builtin_rebuilds() - REBUILDS rule
|
|
*
|
|
* The REBUILDS builtin rule appends each of the listed
|
|
* rebuild-targets in its 2nd argument on the rebuilds list of each of
|
|
* the listed targets in its first argument.
|
|
*/
|
|
|
|
LIST *
|
|
builtin_rebuilds(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST *targets = lol_get( frame->args, 0 );
|
|
LIST *rebuilds = lol_get( frame->args, 1 );
|
|
LIST *l;
|
|
|
|
for( l = targets; l; l = list_next( l ) )
|
|
{
|
|
TARGET *t = bindtarget( l->string );
|
|
t->rebuilds = targetlist( t->rebuilds, rebuilds );
|
|
}
|
|
|
|
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" );
|
|
if ( lol_get( frame->args, 1 ) )
|
|
{
|
|
exit ( atoi( lol_get( frame->args, 1 )->string ) );
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
LIST *case_insensitive;
|
|
} ;
|
|
|
|
static void downcase_inplace( char* p )
|
|
{
|
|
for ( ; *p; ++p )
|
|
{
|
|
*p = tolower(*p);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
builtin_glob_back(
|
|
void *closure,
|
|
char *file,
|
|
int status,
|
|
time_t time )
|
|
{
|
|
PROFILE_ENTER(BUILTIN_GLOB_BACK);
|
|
|
|
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;
|
|
|
|
/* For globbing, we unconditionally ignore current and parent
|
|
directory items. Since they items always exist, there's not
|
|
reason why caller of GLOB would want to see them.
|
|
We could also change file_dirscan, but then paths with embedded
|
|
"." and ".." won't work anywhere.
|
|
*/
|
|
if (strcmp(f.f_base.ptr, ".") == 0 || strcmp(f.f_base.ptr, "..") == 0)
|
|
{
|
|
PROFILE_EXIT(BUILTIN_GLOB_BACK);
|
|
return;
|
|
}
|
|
|
|
string_new( buf );
|
|
path_build( &f, buf, 0 );
|
|
|
|
if (globbing->case_insensitive) {
|
|
downcase_inplace( buf->value );
|
|
}
|
|
|
|
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 );
|
|
|
|
PROFILE_EXIT(BUILTIN_GLOB_BACK);
|
|
}
|
|
|
|
static LIST* downcase_list( LIST *in )
|
|
{
|
|
LIST* result = 0;
|
|
|
|
string s[1];
|
|
string_new( s );
|
|
|
|
while (in)
|
|
{
|
|
string_copy( s, in->string );
|
|
downcase_inplace( s->value );
|
|
result = list_append( result, list_new( 0, newstr( s->value ) ) );
|
|
in = in->next;
|
|
}
|
|
|
|
string_free( s );
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
|
|
globbing.case_insensitive
|
|
# if defined( OS_NT ) || defined( OS_CYGWIN )
|
|
= l; /* always case-insensitive if any files can be found */
|
|
# else
|
|
= lol_get( frame->args, 2 );
|
|
# endif
|
|
|
|
if ( globbing.case_insensitive )
|
|
{
|
|
globbing.patterns = downcase_list( r );
|
|
}
|
|
|
|
for( ; l; l = list_next( l ) )
|
|
file_dirscan( l->string, builtin_glob_back, &globbing );
|
|
|
|
if ( globbing.case_insensitive )
|
|
{
|
|
list_free( globbing.patterns );
|
|
}
|
|
return globbing.results;
|
|
}
|
|
|
|
static int has_wildcards(const char* str)
|
|
{
|
|
size_t index = strcspn(str, "[]*?");
|
|
if (str[index] == '\0')
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
/** If 'file' exists, append 'file' to 'list'.
|
|
Returns 'list'.
|
|
*/
|
|
static LIST* append_if_exists(LIST* list, char* file)
|
|
{
|
|
time_t time;
|
|
timestamp(file, &time);
|
|
if (time > 0)
|
|
return list_new(list, newstr(file));
|
|
else
|
|
return list;
|
|
}
|
|
|
|
|
|
LIST* glob1(char* dirname, char* pattern)
|
|
{
|
|
LIST* plist = list_new(L0, pattern);
|
|
struct globbing globbing;
|
|
|
|
globbing.results = L0;
|
|
globbing.patterns = plist;
|
|
|
|
globbing.case_insensitive
|
|
# if defined( OS_NT ) || defined( OS_CYGWIN )
|
|
= plist; /* always case-insensitive if any files can be found */
|
|
# else
|
|
= L0;
|
|
# endif
|
|
|
|
if ( globbing.case_insensitive )
|
|
{
|
|
globbing.patterns = downcase_list( plist );
|
|
}
|
|
|
|
file_dirscan( dirname, builtin_glob_back, &globbing );
|
|
|
|
if ( globbing.case_insensitive )
|
|
{
|
|
list_free( globbing.patterns );
|
|
}
|
|
|
|
list_free(plist);
|
|
|
|
return globbing.results;
|
|
}
|
|
|
|
|
|
LIST* glob_recursive(char* pattern)
|
|
{
|
|
LIST* result = L0;
|
|
|
|
/* Check if there's metacharacters in pattern */
|
|
if (!has_wildcards(pattern))
|
|
{
|
|
/* No metacharacters. Check if the path exists. */
|
|
result = append_if_exists(result, pattern);
|
|
}
|
|
else
|
|
{
|
|
/* Have metacharacters in the pattern. Split into dir/name */
|
|
PATHNAME path[1];
|
|
path_parse(pattern, path);
|
|
|
|
if (path->f_dir.ptr)
|
|
{
|
|
LIST* dirs = L0;
|
|
string dirname[1];
|
|
string basename[1];
|
|
string_new(dirname);
|
|
string_new(basename);
|
|
|
|
string_append_range(dirname, path->f_dir.ptr,
|
|
path->f_dir.ptr + path->f_dir.len);
|
|
|
|
path->f_grist.ptr = 0;
|
|
path->f_grist.len = 0;
|
|
path->f_dir.ptr = 0;
|
|
path->f_dir.len = 0;
|
|
path_build(path, basename, 0);
|
|
|
|
if (has_wildcards(dirname->value))
|
|
{
|
|
dirs = glob_recursive(dirname->value);
|
|
}
|
|
else
|
|
{
|
|
dirs = list_new(dirs, dirname->value);
|
|
}
|
|
|
|
if (has_wildcards(basename->value))
|
|
{
|
|
for(; dirs; dirs = dirs->next)
|
|
{
|
|
result = list_append(result,
|
|
glob1(dirs->string, basename->value));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string file_string[1];
|
|
string_new(file_string);
|
|
|
|
/** No wildcard in basename. */
|
|
for(; dirs; dirs = dirs->next)
|
|
{
|
|
path->f_dir.ptr = dirs->string;
|
|
path->f_dir.len = strlen(dirs->string);
|
|
path_build(path, file_string, 0);
|
|
|
|
result = append_if_exists(result, file_string->value);
|
|
|
|
string_truncate(file_string, 0);
|
|
}
|
|
|
|
string_free(file_string);
|
|
}
|
|
|
|
string_free(dirname);
|
|
string_free(basename);
|
|
}
|
|
else
|
|
{
|
|
/** No directory, just a pattern. */
|
|
result = list_append(result, glob1(".", pattern));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
LIST *
|
|
builtin_glob_recursive(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST* result = L0;
|
|
LIST* l = lol_get( frame->args, 0 );
|
|
|
|
for(; l; l = l->next)
|
|
{
|
|
result = list_append(result, glob_recursive(l->string));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* builtin_match() - MATCH rule, regexp matching
|
|
*/
|
|
|
|
LIST *
|
|
builtin_match(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST *l, *r;
|
|
LIST *result = 0;
|
|
|
|
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 = (RULE*)r_;
|
|
LIST** result = (LIST**)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_t* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
|
|
|
if ( source_module->rules )
|
|
hashenumerate( source_module->rules, add_rule_name, &result );
|
|
return result;
|
|
}
|
|
|
|
|
|
/* builtin_varnames() - VARNAMES ( MODULE ? )
|
|
*
|
|
* Returns a list of the variable names in the given MODULE. If
|
|
* MODULE is not supplied, returns the list of variable names in the
|
|
* global module.
|
|
*/
|
|
|
|
|
|
/* helper function for builtin_varnames(), below. Used with
|
|
* hashenumerate, will prepend the key of each element to a list
|
|
*/
|
|
static void add_hash_key( void* np, void* result_ )
|
|
{
|
|
LIST** result = (LIST**)result_;
|
|
|
|
*result = list_new( *result, copystr( *(char**)np ) );
|
|
}
|
|
|
|
|
|
static struct hash *get_running_module_vars()
|
|
{
|
|
struct hash *dummy, *vars = NULL;
|
|
/* Get the global variables pointer (that of the currently running module) */
|
|
var_hash_swap(&vars);
|
|
dummy = vars;
|
|
/* Put the global variables pointer in its right place */
|
|
var_hash_swap(&dummy);
|
|
return vars;
|
|
}
|
|
|
|
|
|
LIST *
|
|
builtin_varnames(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST *arg0 = lol_get( frame->args, 0 );
|
|
LIST *result = L0;
|
|
module_t* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
|
|
|
/* The running module _always_ has its 'variables' member set to NULL
|
|
* due to the way enter_module and var_hash_swap work */
|
|
struct hash *vars =
|
|
source_module == frame->module ?
|
|
get_running_module_vars() : source_module->variables;
|
|
|
|
if ( vars )
|
|
hashenumerate( vars, add_hash_key, &result );
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* builtin_delete_module() - MODULE ?
|
|
*
|
|
* Clears all rules and variables from the given module.
|
|
*/
|
|
LIST *
|
|
builtin_delete_module(
|
|
PARSE *parse,
|
|
FRAME *frame )
|
|
{
|
|
LIST *arg0 = lol_get( frame->args, 0 );
|
|
LIST *result = L0;
|
|
module_t* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
|
|
|
delete_module( source_module );
|
|
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_t* target_module = bindmodule( target_module_list ? target_module_list->string : 0 );
|
|
module_t* 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 ( !source_module->rules
|
|
|| !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_t* m = bindmodule( module_list ? module_list->string : 0 );
|
|
|
|
for ( ; rules; rules = list_next( rules ) )
|
|
{
|
|
RULE r_, *r = &r_;
|
|
r_.name = rules->string;
|
|
|
|
if ( !m->rules || !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 )
|
|
{
|
|
if ( frame == 0 )
|
|
{
|
|
printf( "(no frame):" );
|
|
}
|
|
else
|
|
{
|
|
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 )
|
|
{
|
|
if ( !frame ) return;
|
|
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* levels_arg = lol_get( frame->args, 0 );
|
|
int levels = levels_arg ? atoi( levels_arg->string ) : ((unsigned int)(-1) >> 1) ;
|
|
|
|
LIST* result = L0;
|
|
for(; (frame = frame->prev) && levels ; --levels )
|
|
{
|
|
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();
|
|
}
|
|
|
|
|
|
/*
|
|
* Adds targets to the list of target that jam will attempt to update.
|
|
*/
|
|
LIST*
|
|
builtin_update( PARSE *parse, FRAME *frame)
|
|
{
|
|
LIST* result = list_copy( L0, targets_to_update() );
|
|
LIST* arg1 = lol_get( frame->args, 0 );
|
|
clear_targets_to_update();
|
|
for ( ; arg1; arg1 = list_next( arg1 ) )
|
|
mark_target_for_updating( newstr(arg1->string) );
|
|
return result;
|
|
}
|
|
|
|
|
|
LIST*
|
|
builtin_search_for_target( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* arg1 = lol_get( frame->args, 0 );
|
|
LIST* arg2 = lol_get( frame->args, 1 );
|
|
|
|
TARGET* t = search_for_target( arg1->string, arg2 );
|
|
return list_new( L0, t->name );
|
|
}
|
|
|
|
|
|
LIST *builtin_import_module( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* arg1 = lol_get( frame->args, 0 );
|
|
LIST* arg2 = lol_get( frame->args, 1 );
|
|
|
|
module_t* m = arg2 ? bindmodule(arg2->string) : root_module();
|
|
|
|
import_module(arg1, m);
|
|
|
|
return L0;
|
|
}
|
|
|
|
|
|
LIST *builtin_imported_modules( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST *arg0 = lol_get( frame->args, 0 );
|
|
module_t* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
|
|
|
return imported_modules(source_module);
|
|
}
|
|
|
|
|
|
LIST *builtin_instance( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* arg1 = lol_get( frame->args, 0 );
|
|
LIST* arg2 = lol_get( frame->args, 1 );
|
|
|
|
module_t* instance = bindmodule( arg1->string );
|
|
module_t* class_module = bindmodule( arg2->string );
|
|
instance->class_module = class_module;
|
|
|
|
return L0;
|
|
}
|
|
|
|
|
|
LIST*
|
|
builtin_sort( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* arg1 = lol_get( frame->args, 0 );
|
|
|
|
return list_sort(arg1);
|
|
}
|
|
|
|
|
|
LIST *builtin_normalize_path( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* arg = lol_get( frame->args, 0 );
|
|
|
|
/* First, we iterate over all '/'-separated elements, starting from the end
|
|
of string. If we see a '..', we remove a previous path elements. If we
|
|
see '.', we remove it. The removal is done by overwriting data using '\1'
|
|
in the string. After the whole string has been processed, we do a second
|
|
pass, removing all the entered '\1' characters.
|
|
*/
|
|
|
|
string in[1];
|
|
string out[1];
|
|
char * end; /* Last character of the part of string still to be processed. */
|
|
char * current; /* Working pointer. */
|
|
int dotdots = 0; /* Number of '..' elements seen and not processed yet. */
|
|
int rooted = 0;
|
|
char * result = 0;
|
|
|
|
/* Make a copy of input: we should not change it. Prepend a '/' before it as
|
|
a guard for the algorithm later on and remember whether it was originally
|
|
rooted or not. */
|
|
|
|
string_new(in);
|
|
string_push_back(in, '/');
|
|
for (; arg; arg = list_next(arg) )
|
|
{
|
|
if (arg->string[0] != '\0')
|
|
{
|
|
if (in->size == 1)
|
|
rooted = ( (arg->string[0] == '/' ) || (arg->string[0] == '\\') );
|
|
else
|
|
string_append(in, "/");
|
|
string_append(in, arg->string);
|
|
}
|
|
}
|
|
|
|
/* Convert \ into /. On Windows, paths using / and \ are equivalent, and we
|
|
want this function to obtain a canonic representation. */
|
|
|
|
for (current = in->value, end = in->value + in->size;
|
|
current < end; ++current)
|
|
if (*current == '\\')
|
|
*current = '/';
|
|
|
|
/* Now we remove any extra path elements by overwriting them with '\1'
|
|
characters and cound how many more unused '..' path elements there are
|
|
remaining. Note that each remaining path element with always starts with
|
|
a '/' character. */
|
|
|
|
for (end = in->value + in->size - 1; end >= in->value; )
|
|
{
|
|
/* Set 'current' to the next occurence of '/', which always exists. */
|
|
for (current = end; *current != '/'; --current)
|
|
;
|
|
|
|
if (current == end)
|
|
{
|
|
/* Found a trailing or duplicate '/'. Remove it. */
|
|
*current = '\1';
|
|
}
|
|
else if (end - current == 1 && *(current+1) == '.')
|
|
{
|
|
/* Found '/.'. Remove them all. */
|
|
*current = '\1';
|
|
*(current+1) = '\1';
|
|
}
|
|
else if (end - current == 2 && *(current+1) == '.' && *(current+2) == '.')
|
|
{
|
|
/* Found '/..'. Remove them all. */
|
|
*current = '\1';
|
|
*(current+1) = '\1';
|
|
*(current+2) = '\1';
|
|
++dotdots;
|
|
}
|
|
else if (dotdots)
|
|
{
|
|
memset(current, '\1', end-current+1);
|
|
--dotdots;
|
|
}
|
|
end = current-1;
|
|
}
|
|
|
|
string_new(out);
|
|
|
|
/* Now we know that we need to add exactly dotdots '..' path elements to the
|
|
front and that our string is either empty or has a '/' as its first
|
|
significant character. If we have any dotdots remaining then the passed
|
|
path must not have been rooted or else it is invalid we return an empty
|
|
list. */
|
|
|
|
if (dotdots)
|
|
{
|
|
if (rooted) return L0;
|
|
do
|
|
string_append(out, "/..");
|
|
while (--dotdots);
|
|
}
|
|
|
|
/* Now we actually remove all the path characters marked for removal. */
|
|
|
|
for (current = in->value; *current; ++current)
|
|
if (*current != '\1')
|
|
string_push_back(out, *current);
|
|
|
|
/* Here we know that our string contains no '\1' characters and is either
|
|
empty or has a '/' as its initial character. If the original path was not
|
|
rooted and we have a non-empty path we need to drop the initial '/'. If
|
|
the original path was rooted and we have an empty path we need to add
|
|
back the '/'. */
|
|
|
|
result = newstr( out->size ? out->value + !rooted : (rooted ? "/" : "."));
|
|
|
|
string_free(out);
|
|
string_free(in);
|
|
|
|
return list_new(0, result);
|
|
}
|
|
|
|
|
|
LIST *builtin_native_rule( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* module_name = lol_get( frame->args, 0 );
|
|
LIST* rule_name = lol_get( frame->args, 1 );
|
|
|
|
module_t* module = bindmodule(module_name->string);
|
|
|
|
native_rule_t n, *np = &n;
|
|
n.name = rule_name->string;
|
|
if (module->native_rules && hashcheck(module->native_rules, (HASHDATA**)&np))
|
|
{
|
|
new_rule_body(module, np->name, np->arguments, np->procedure, 1);
|
|
}
|
|
else
|
|
{
|
|
backtrace_line( frame->prev );
|
|
printf( "error: no native rule \"%s\" defined in module \"%s\"\n",
|
|
n.name, module->name);
|
|
backtrace( frame->prev );
|
|
exit(1);
|
|
}
|
|
return L0;
|
|
}
|
|
|
|
|
|
LIST *builtin_has_native_rule( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* module_name = lol_get( frame->args, 0 );
|
|
LIST* rule_name = lol_get( frame->args, 1 );
|
|
LIST* version = lol_get( frame->args, 2 );
|
|
|
|
module_t* module = bindmodule(module_name->string);
|
|
|
|
native_rule_t n, *np = &n;
|
|
n.name = rule_name->string;
|
|
if (module->native_rules && hashcheck(module->native_rules, (HASHDATA**)&np))
|
|
{
|
|
int expected_version = atoi(version->string);
|
|
if (np->version == expected_version)
|
|
return list_new(0, newstr("true"));
|
|
}
|
|
return L0;
|
|
}
|
|
|
|
|
|
LIST *builtin_user_module( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* module_name = lol_get( frame->args, 0 );
|
|
for(; module_name; module_name = module_name->next)
|
|
{
|
|
module_t* m = bindmodule( module_name->string);
|
|
m->user_module = 1;
|
|
}
|
|
return L0;
|
|
}
|
|
|
|
LIST *builtin_nearest_user_location( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* result = 0;
|
|
FRAME* nearest_user_frame =
|
|
frame->module->user_module ? frame : frame->prev_user;
|
|
|
|
if (nearest_user_frame)
|
|
{
|
|
char* file;
|
|
int line;
|
|
char buf[32];
|
|
get_source_line( nearest_user_frame->procedure, &file, &line );
|
|
sprintf( buf, "%d", line );
|
|
result = list_new( result, newstr( file ) );
|
|
result = list_new( result, newstr( buf ) );
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return L0;
|
|
}
|
|
}
|
|
|
|
LIST *builtin_check_if_file( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* name = lol_get( frame->args, 0 );
|
|
if (file_is_file(name->string) == 1) {
|
|
return list_new(0, newstr("true"));
|
|
} else {
|
|
return L0;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef HAVE_PYTHON
|
|
|
|
LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame )
|
|
{
|
|
static int first_time = 1;
|
|
char* python_module = lol_get( frame->args, 0 )->string;
|
|
char* python_function = lol_get( frame->args, 1 )->string;
|
|
char* jam_module = lol_get( frame->args, 2 )->string;
|
|
char* jam_rule = lol_get( frame->args, 3 )->string;
|
|
|
|
PyObject *pName, *pModule, *pDict, *pFunc;
|
|
|
|
if (first_time)
|
|
{
|
|
/* At the first invocation, we add the value of the
|
|
global EXTRA_PYTHONPATH to the sys.path Python
|
|
variable.
|
|
*/
|
|
LIST* extra = 0;
|
|
module_t* outer_module = frame->module;
|
|
|
|
first_time = 0;
|
|
|
|
if ( outer_module != root_module())
|
|
{
|
|
exit_module( outer_module );
|
|
enter_module( root_module());
|
|
}
|
|
|
|
extra = var_get("EXTRA_PYTHONPATH");
|
|
|
|
if ( outer_module != root_module())
|
|
{
|
|
exit_module( root_module());
|
|
enter_module( outer_module );
|
|
}
|
|
|
|
for(; extra; extra = extra->next)
|
|
{
|
|
string buf[1];
|
|
string_new(buf);
|
|
string_append(buf, "import sys\nsys.path.append(\"");
|
|
string_append(buf, extra->string);
|
|
string_append(buf, "\")\n");
|
|
PyRun_SimpleString(buf->value);
|
|
string_free(buf);
|
|
}
|
|
}
|
|
|
|
|
|
pName = PyString_FromString(python_module);
|
|
|
|
pModule = PyImport_Import(pName);
|
|
Py_DECREF(pName);
|
|
|
|
if (pModule != NULL) {
|
|
pDict = PyModule_GetDict(pModule);
|
|
pFunc = PyDict_GetItemString(pDict, python_function);
|
|
|
|
if (pFunc && PyCallable_Check(pFunc)) {
|
|
|
|
module_t* m = bindmodule(jam_module);
|
|
RULE* r = bindrule( jam_rule, m );
|
|
|
|
/* Make pFunc owned */
|
|
Py_INCREF(pFunc);
|
|
|
|
r->python_function = pFunc;
|
|
}
|
|
else {
|
|
if (PyErr_Occurred())
|
|
PyErr_Print();
|
|
fprintf(stderr, "Cannot find function \"%s\"\n", python_function);
|
|
}
|
|
Py_DECREF(pModule);
|
|
}
|
|
else {
|
|
PyErr_Print();
|
|
fprintf(stderr, "Failed to load \"%s\"\n", python_module);
|
|
}
|
|
return L0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
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 );
|
|
}
|
|
|
|
#ifdef HAVE_PYTHON
|
|
|
|
/** Calls the bjam rule specified by name passed in 'args'.
|
|
The name is looked up in context of bjam's 'python_interface'
|
|
module. Returns the list of string retured by the rule.
|
|
*/
|
|
PyObject*
|
|
bjam_call(PyObject* self, PyObject* args)
|
|
{
|
|
FRAME inner[1];
|
|
LIST *result;
|
|
PARSE *p;
|
|
char* rulename;
|
|
|
|
/* Build up the list of arg lists */
|
|
|
|
frame_init( inner );
|
|
inner->prev = 0;
|
|
inner->prev_user = 0;
|
|
inner->module = bindmodule("python_interface");
|
|
inner->procedure = 0;
|
|
|
|
/* Extract the rule name and arguments from 'args' */
|
|
|
|
/* PyTuple_GetItem returns borrowed reference */
|
|
rulename = PyString_AsString(PyTuple_GetItem(args, 0));
|
|
{
|
|
int i = 1;
|
|
int size = PyTuple_Size(args);
|
|
for( ; i < size; ++i) {
|
|
PyObject* a = PyTuple_GetItem(args, i);
|
|
if (PyString_Check(a))
|
|
{
|
|
lol_add(inner->args,
|
|
list_new(0, newstr(PyString_AsString(a))));
|
|
}
|
|
else if (PySequence_Check(a))
|
|
{
|
|
LIST* l = 0;
|
|
int s = PySequence_Size(a);
|
|
int i = 0;
|
|
for(; i < s; ++i)
|
|
{
|
|
/* PySequence_GetItem returns new reference. */
|
|
PyObject* e = PySequence_GetItem(a, i);
|
|
char* s = PyString_AsString(e);
|
|
if (!s)
|
|
{
|
|
printf("Invalid parameter type passed from Python\n");
|
|
exit(1);
|
|
}
|
|
l = list_new(l, newstr(s));
|
|
Py_DECREF(e);
|
|
}
|
|
lol_add(inner->args, l);
|
|
}
|
|
}
|
|
}
|
|
|
|
result = evaluate_rule( rulename, inner );
|
|
|
|
frame_free( inner );
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/** Accepts three arguments: module name, rule name and Python callable.
|
|
|
|
Creates bjam rule with the specified name in the specified module,
|
|
which will invoke the Python callable.
|
|
*/
|
|
PyObject*
|
|
bjam_import_rule(PyObject* self, PyObject* args)
|
|
{
|
|
char* module;
|
|
char* rule;
|
|
PyObject* func;
|
|
module_t* m;
|
|
RULE* r;
|
|
|
|
if (!PyArg_ParseTuple(args, "ssO:import_rule", &module, &rule, &func))
|
|
return NULL;
|
|
|
|
if (!PyCallable_Check(func))
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"Non-callable object passed to bjam.import_rule");
|
|
return NULL;
|
|
}
|
|
|
|
m = bindmodule(*module ? module : 0);
|
|
r = bindrule(rule, m);
|
|
|
|
/* Make pFunc owned */
|
|
Py_INCREF(func);
|
|
|
|
r->python_function = func;
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
|
|
/* Accepts four arguments:
|
|
- an action name
|
|
- an action body
|
|
- a list of variable that will be bound inside the action
|
|
- integer flags.
|
|
Defines an action on bjam side.
|
|
*/
|
|
PyObject*
|
|
bjam_define_action(PyObject* self, PyObject *args)
|
|
{
|
|
char* name;
|
|
char* body;
|
|
module_t* m;
|
|
PyObject *bindlist_python;
|
|
int flags;
|
|
LIST *bindlist = L0;
|
|
int n;
|
|
int i;
|
|
|
|
if (!PyArg_ParseTuple(args, "ssO!i:define_action", &name, &body,
|
|
&PyList_Type, &bindlist_python, &flags))
|
|
return NULL;
|
|
|
|
n = PyList_Size (bindlist_python);
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
PyObject *next = PyList_GetItem(bindlist_python, i);
|
|
if (!PyString_Check(next))
|
|
{
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"bind list has non-string type");
|
|
return NULL;
|
|
}
|
|
bindlist = list_new(bindlist, PyString_AsString(next));
|
|
}
|
|
|
|
new_rule_actions(root_module(), name, newstr(body), bindlist, flags);
|
|
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
/* Returns the value of a variable in root Jam module. */
|
|
PyObject*
|
|
bjam_variable(PyObject* self, PyObject* args)
|
|
{
|
|
char *name;
|
|
LIST* value;
|
|
PyObject *result;
|
|
int i;
|
|
|
|
if (!PyArg_ParseTuple(args, "s", &name))
|
|
return NULL;
|
|
|
|
enter_module(root_module());
|
|
value = var_get(name);
|
|
exit_module(root_module());
|
|
|
|
result = PyList_New(list_length(value));
|
|
for (i = 0; value; value = list_next(value), ++i)
|
|
PyList_SetItem(result, i, PyString_FromString(value->string));
|
|
|
|
return result;
|
|
}
|
|
|
|
PyObject*
|
|
bjam_backtrace(PyObject* self, PyObject *args)
|
|
{
|
|
PyObject *result = PyList_New(0);
|
|
struct frame *f = frame_before_python_call;
|
|
|
|
for(; f = f->prev;)
|
|
{
|
|
PyObject *tuple = PyTuple_New(4);
|
|
char* file;
|
|
int line;
|
|
char buf[32];
|
|
get_source_line( f->procedure, &file, &line );
|
|
sprintf( buf, "%d", line );
|
|
|
|
/* PyTuple_SetItem steals reference. */
|
|
PyTuple_SetItem(tuple, 0, PyString_FromString(file));
|
|
PyTuple_SetItem(tuple, 1, PyString_FromString(buf));
|
|
PyTuple_SetItem(tuple, 2, PyString_FromString(f->module->name));
|
|
PyTuple_SetItem(tuple, 3, PyString_FromString(f->rulename));
|
|
|
|
PyList_Append(result, tuple);
|
|
Py_DECREF(tuple);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_POPEN
|
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
|
#define popen _popen
|
|
#define pclose _pclose
|
|
#endif
|
|
|
|
LIST *builtin_shell( PARSE *parse, FRAME *frame )
|
|
{
|
|
LIST* command = lol_get( frame->args, 0 );
|
|
LIST* result = 0;
|
|
string s;
|
|
int ret;
|
|
char buffer[1024];
|
|
FILE *p = NULL;
|
|
int exit_status = -1;
|
|
int exit_status_opt = 0;
|
|
int no_output_opt = 0;
|
|
|
|
/* Process the variable args options. */
|
|
{
|
|
int a = 1;
|
|
LIST * arg = lol_get( frame->args, a );
|
|
while ( arg )
|
|
{
|
|
if ( strcmp("exit-status", arg->string) == 0 )
|
|
{
|
|
exit_status_opt = 1;
|
|
}
|
|
else if ( strcmp("no-output", arg->string) == 0 )
|
|
{
|
|
no_output_opt = 1;
|
|
}
|
|
arg = lol_get( frame->args, ++a );
|
|
}
|
|
}
|
|
|
|
string_new( &s );
|
|
|
|
fflush(NULL);
|
|
|
|
p = popen(command->string, "r");
|
|
if ( p == NULL )
|
|
return L0;
|
|
|
|
while ( (ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0 )
|
|
{
|
|
buffer[ret] = 0;
|
|
if ( ! no_output_opt )
|
|
{
|
|
string_append( &s, buffer );
|
|
}
|
|
}
|
|
|
|
exit_status = pclose(p);
|
|
|
|
/* The command output is returned first. */
|
|
result = list_new( L0, newstr(s.value) );
|
|
string_free(&s);
|
|
|
|
/* The command exit result next. */
|
|
if ( exit_status_opt )
|
|
{
|
|
sprintf (buffer, "%d", exit_status);
|
|
result = list_new( result, newstr( buffer ) );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
|
|
LIST *builtin_shell( PARSE *parse, FRAME *frame )
|
|
{
|
|
return L0;
|
|
}
|
|
|
|
#endif
|