----------------------------------------------------------------------
committing in tools/build Modified Files: build_system.htm Documented: local rules the RULENAMES rule the EXPORT rule the BACKTRACE rule new IMPORT semantics -d+12 Dependency Graph Output Crude Argument Binding Variable numbers of arguments jam_src/compile.c implemented RULENAMES, EXPORT, varargs support, new IMPORT semantics removed unused variables jam_src/make1.c jam_src/hdrmacro.c removed unused variables jam_src/jamgram.{c,h,y,yy} "module local x" does not change module local value of x if it is already set. jam_src/lists.[ch] added list_pop_front() new/assert.jam new/boost-build.jam new/build-system.jam new/errors.jam new/modules.jam new/os.path.jam beginnings of new build system test/check-arguments.jam test/check-jam-patches.jam test/echo_args.jam Added tests for recent core modifications; comments Added Files: new/feature.jam new/property.jam new/readme.txt new/sequence.jam new/test.jam beginnings of new build system ---------------------------------------------------------------------- [SVN r11789]
This commit is contained in:
parent
7e05a852bf
commit
2a015f5761
156
build_system.htm
156
build_system.htm
@ -138,7 +138,10 @@
|
||||
<ul>
|
||||
<li><a href="#module_declaration">Declaration</a>
|
||||
<li><a href="#module_locals">Local Variables</a>
|
||||
<li><a href="#local_rules">Local Rules</a>
|
||||
<li><a href="#RULENAMES_rule">The <tt>RULENAMES</tt> rule</a>
|
||||
<li><a href="#IMPORT_rule">The <tt>IMPORT</tt> rule</a>
|
||||
<li><a href="#EXPORT_rule">The <tt>EXPORT</tt> rule</a>
|
||||
<li><a href="#CALLER_MODULE_rule">The <tt>CALLER_MODULE</tt> rule</a>
|
||||
</ul>
|
||||
|
||||
@ -154,8 +157,10 @@
|
||||
<a href="#debugging_support">Debugging Support</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="#BACKTRACE_rule">The BACKTRACE rule</a>
|
||||
<li><a href="#profiling">Profiling</a>
|
||||
<li><a href="#parse_debugging">Parser Debugging</a>
|
||||
<li><a href="#dependency_graph">Dependency Graph Output</a>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@ -1312,8 +1317,35 @@ rule foobar { ECHO foobar ; } # a trivial rule
|
||||
$(x)bar ; # invokes foobar
|
||||
|
||||
</pre></blockquote>
|
||||
|
||||
Furthermore, if the first expression expands to more than one
|
||||
list item, everything after the first item becomes part of the
|
||||
first argument. This allows a crude form of argument binding:
|
||||
|
||||
<h4><a name="argument_lists">Argument lists</a></h4>
|
||||
<blockquote><pre>
|
||||
# return the elements of sequence for which predicate returns non-nil
|
||||
rule filter ( sequence * : predicate + )
|
||||
{
|
||||
local result ;
|
||||
for local x in $(sequence)
|
||||
{
|
||||
if [ $(predicate) $(x) ] { result += $(x); }
|
||||
}
|
||||
return $(result);
|
||||
}
|
||||
|
||||
# true iff x == y
|
||||
rule equal ( x y )
|
||||
{
|
||||
if $(x) = $(y) { return true; }
|
||||
}
|
||||
|
||||
# bind 3 to the first argument of equal
|
||||
ECHO [ filter 1 2 3 4 5 4 3 : equal 3 ] ; # prints "3 3"
|
||||
|
||||
</pre></blockquote>
|
||||
|
||||
<h4><a name="argument_lists">Argument lists</a></h4>
|
||||
|
||||
<p>You can now describe the arguments accepted by a rule, and refer to
|
||||
them by name within the rule. For example, the following prints ``I'm
|
||||
@ -1350,7 +1382,11 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<tr>
|
||||
<td><tt>*</tt>
|
||||
|
||||
<td>Bind to zero or more unbound elements of the actual argument.
|
||||
<td>Bind to zero or more unbound elements of the actual
|
||||
argument. When ``<tt>*</tt>'' appears where an argument name
|
||||
is expected, any number of additional arguments are
|
||||
accepted. This feature can be used to implement
|
||||
"varargs" rules.
|
||||
|
||||
<tr>
|
||||
<td><tt>+</tt>
|
||||
@ -1358,7 +1394,7 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<td>Bind to one or more unbound elements of the actual argument.
|
||||
</table>
|
||||
|
||||
<p>The acutal and formal arguments are checked for inconsistencies, which
|
||||
<p>The actual and formal arguments are checked for inconsistencies, which
|
||||
cause Jam to exit with an error code:
|
||||
|
||||
<blockquote>
|
||||
@ -1382,8 +1418,11 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<h4><a name="module_support">Module Support</a></h4>
|
||||
|
||||
<p>Boost Jam introduces support for modules, which provide some
|
||||
rudimentary namespace protection for rules and variables. A new keyword,
|
||||
``<tt>module</tt>'' was also introduced.
|
||||
rudimentary namespace protection for rules and variables. A new
|
||||
keyword, ``<tt>module</tt>'' was also introduced. The features
|
||||
described in this section are <i>primitives</i>, meaning that
|
||||
they are meant to provide the operations needed to write Jam
|
||||
rules which provide a more elegant module interface.
|
||||
|
||||
<p>
|
||||
|
||||
@ -1423,13 +1462,6 @@ module your_module
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>Note that, like the <a
|
||||
href="#IMPORT_rule"><tt>IMPORT</tt></a> and <a
|
||||
href="#CALLER_MODULE_rule"><tt>CALLER_MODULE</tt></a> rules, module
|
||||
declaration is really a <i>primitive</i>, and is best used through a
|
||||
wrapper interface which implements a system of module files using the
|
||||
built-in <tt>INCLUDE</tt> rule.
|
||||
|
||||
<p>
|
||||
|
||||
<h5><a name="module_locals">Local Variables</a></h5>
|
||||
@ -1493,35 +1525,81 @@ ECHO [ <b>M.get</b> x ] ; # prints "a b c"
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="local_rules">Local Rules</a></h5>
|
||||
|
||||
<blockquote>
|
||||
<tt>local rule</tt> <i>rulename...</i>
|
||||
</blockquote>
|
||||
|
||||
<p>The rule is declared locally to the current module. It is
|
||||
not entered in the global module with qualification, and its
|
||||
name will not appear in the result of
|
||||
<blockquote>
|
||||
<tt>[ RULENAMES</tt> <i>module-name</i><tt> ]</tt>.
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="RULENAMES_rule">The <tt>RULENAMES</tt> Rule</a></h5>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule RULENAMES ( module ? )
|
||||
</pre>
|
||||
</blockquote>
|
||||
Returns a list of the names of all non-local rules in the
|
||||
given module. If <tt>module</tt> is ommitted, the names of all
|
||||
non-local rules in the global module are returned.
|
||||
|
||||
<h5><a name="IMPORT_rule">The <tt>IMPORT</tt> Rule</a></h5>
|
||||
|
||||
<tt>IMPORT</tt> allows rule name aliasing across modules:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule IMPORT ( target_module ? : source_module ?
|
||||
: rule_names * : target_names * )
|
||||
rule IMPORT ( source_module ? : source_rules *
|
||||
: target_module ? : target_rules * )
|
||||
</pre>
|
||||
</blockquote>
|
||||
Rules are copied from the <tt>source_module</tt> into the
|
||||
<tt>target_module</tt>. If either module name is missing, the global
|
||||
module is used in its place. If any <tt>rule_names</tt> are supplied,
|
||||
they specify which rules from the <tt>source_module</tt> to import;
|
||||
otherwise all rules are imported. The rules are given the names in
|
||||
<tt>target_names</tt>; if not enough <tt>target_names</tt> are supplied,
|
||||
the excess rules are given the same names as they had in
|
||||
<tt>source_module</tt>. For example,
|
||||
The <tt>IMPORT</tt> rule copies rules from the <tt>source_module</tt> into the
|
||||
<tt>target_module</tt> as <tt>local</tt> rules. If either <tt>source_module</tt> or
|
||||
<tt>target_module</tt> is not supplied, it refers to the global
|
||||
module. <tt>source_rules</tt> specifies which rules from the <tt>source_module</tt> to
|
||||
import; <tt>TARGET_RULES</tt> specifies the names to give those rules in
|
||||
<tt>target_module</tt>. If <tt>source_rules</tt> contains a name which doesn't
|
||||
correspond to a rule in <tt>source_module</tt>, or if it contains a different
|
||||
number of items than <tt>target_rules</tt>, an error is issued. For example,
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
IMPORT m1 : m2 ; # imports all rules from m2 into m1
|
||||
IMPORT : m2 : my-rule ; # imports m2.my-rule into the global module
|
||||
IMPORT m1 : m2 : r1 x : r2 y ; # imports m2.r1 as r2 and m2.x as y into m1
|
||||
# import m1.rule1 into m2 as local rule m1-rule1.
|
||||
IMPORT m1 : rule1 : m2 : m1-rule1 ;
|
||||
|
||||
# import all non-local rules from m1 into m2
|
||||
IMPORT m1 : [ RULENAMES m1 ] : m2 : [ RULENAMES m1 ] ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="EXPORT_rule">The <tt>EXPORT</tt> Rule</a></h5>
|
||||
|
||||
<tt>EXPORT</tt> allows rule name aliasing across modules:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule EXPORT ( module ? : rules * )
|
||||
</pre>
|
||||
</blockquote>
|
||||
The <tt>EXPORT</tt> rule marks <tt>rules</tt> from the <tt>source_module</tt> as non-local
|
||||
(and thus exportable). If an element of <tt>rules</tt> does not name a
|
||||
rule in <tt>module</tt>, an error is issued. For example,
|
||||
<blockquote>
|
||||
<pre>
|
||||
module X {
|
||||
local rule r { ECHO X.r ; }
|
||||
}
|
||||
IMPORT X : r : : r ; # error - r is local in X
|
||||
EXPORT X : r ;
|
||||
IMPORT X : r : : r ; # OK.
|
||||
</pre>
|
||||
</blockquote>
|
||||
Like the <a href="#module_declaration">module declaration syntax</a> and
|
||||
the <a href="#CALLER_MODULE_rule"><tt>CALLER_MODULE</tt></a> rule, this
|
||||
rule is a <i>primitive</i>, and is probably best wrapped in a Jam rule.
|
||||
|
||||
<h5><a name="CALLER_MODULE_rule">The <tt>CALLER_MODULE</tt> Rule</a></h5>
|
||||
|
||||
@ -1555,10 +1633,6 @@ callers = [ X.get-caller ] [ Y.call-X ] [ X.call-Y ] ;
|
||||
ECHO {$(callers)} ;
|
||||
|
||||
</pre>
|
||||
Like the <a href="#module_declaration">module declaration syntax</a> and
|
||||
the <a href="#IMPORT_rule"><tt>IMPORT</tt></a> rule, this rule is a
|
||||
<i>primitive</i>, and is probably best wrapped in a Jam rule.
|
||||
|
||||
</blockquote>
|
||||
|
||||
</blockquote>
|
||||
@ -1717,6 +1791,17 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
|
||||
|
||||
<h4><a name="debugging_support">Debugging Support</a></h4>
|
||||
|
||||
<h5><a name="BACKTRACE_rule">The BACKTRACE rule</a></h5>
|
||||
|
||||
<blockquote><pre>
|
||||
rule BACKTRACE ( )
|
||||
</pre></blockquote>
|
||||
|
||||
Returns a list of quadruples: <i>filename line module
|
||||
rulename</i>..., describing each shallower level of the call
|
||||
stack. This rule can be used to generate useful diagnostic
|
||||
messages from Jam rules.
|
||||
|
||||
<p>The <tt>-d</tt> command-line option admits new arguments:
|
||||
|
||||
<ul>
|
||||
@ -1728,6 +1813,13 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
|
||||
<li> <tt>-d+11</tt> - enables <a name="parse_debugging"><b>parser
|
||||
debugging</b></a>, if Jam has been compiled with the "--debug"
|
||||
option to the parser generator named by $(YACC).
|
||||
|
||||
<li> <tt>-d+12</tt> - enables <a
|
||||
name="dependency_graph"><b>dependency graph output
|
||||
</b></a>. This feature was ``stolen'' from a version of Jam
|
||||
modified by <a href="mailto:cmcpheeters@aw.sgi.com">Craig
|
||||
McPheeters</a>.
|
||||
|
||||
</ul>
|
||||
|
||||
</blockquote>
|
||||
|
@ -97,7 +97,9 @@ static LIST *builtin_echo( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_exit( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_flags( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_hdrmacro( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_rulenames( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_import( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_export( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_caller_module( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_backtrace( PARSE *parse, FRAME *frame );
|
||||
|
||||
@ -154,7 +156,6 @@ static void lol_build( LOL* lol, char** elements )
|
||||
static RULE* bind_builtin( char* name, LIST*(*f)(PARSE*, FRAME*), int flags, char** args )
|
||||
{
|
||||
argument_list* arg_list = 0;
|
||||
RULE* builtin_rule;
|
||||
|
||||
if ( args )
|
||||
{
|
||||
@ -204,15 +205,33 @@ compile_builtins()
|
||||
|
||||
{
|
||||
char* args[] = {
|
||||
"target_module", "?"
|
||||
, ":", "source_module", "?"
|
||||
, ":", "rule_names", "*"
|
||||
, ":", "target_names", "*", 0
|
||||
"module", "?", 0
|
||||
};
|
||||
|
||||
bind_builtin( "RULENAMES", builtin_rulenames, 0, args );
|
||||
}
|
||||
|
||||
{
|
||||
char* args[] = {
|
||||
"source_module", "?"
|
||||
, ":", "source_rules", "*"
|
||||
, ":", "target_module", "?"
|
||||
, ":", "target_rules", "*", 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 );
|
||||
@ -636,7 +655,7 @@ static void argument_error( char* message, RULE* rule, FRAME* frame, LIST* arg )
|
||||
LOL* actual = frame->args;
|
||||
assert( frame->procedure != 0 );
|
||||
backtrace_line( frame->prev );
|
||||
printf( "*** argument error\n* rule %s ( ", frame->procedure->file, frame->procedure->line, frame->rulename );
|
||||
printf( "*** argument error\n* rule %s ( ", frame->rulename );
|
||||
lol_print( rule->arguments->data );
|
||||
printf( ")\n* called with: ( " );
|
||||
lol_print( actual );
|
||||
@ -667,11 +686,17 @@ collect_arguments( RULE* rule, FRAME* frame )
|
||||
{
|
||||
LIST *formal = lol_get( all_formal, n );
|
||||
LIST *actual = lol_get( all_actual, n );
|
||||
|
||||
while ( formal )
|
||||
{
|
||||
char* name = formal->string;
|
||||
char modifier = 0;
|
||||
LIST* value = 0;
|
||||
|
||||
/* Stop now if a variable number of arguments are specified */
|
||||
if ( name[0] == '*' && name[1] == 0 )
|
||||
return locals;
|
||||
|
||||
if ( formal->next )
|
||||
{
|
||||
char *next = formal->next->string;
|
||||
@ -814,6 +839,7 @@ evaluate_rule(
|
||||
module *prev_module = frame->module;
|
||||
|
||||
LIST* l = var_expand( L0, rulename, rulename+strlen(rulename), frame->args, 0 );
|
||||
LIST* more_args = L0;
|
||||
|
||||
if ( !l )
|
||||
{
|
||||
@ -840,7 +866,11 @@ evaluate_rule(
|
||||
enter_module( rule->procedure->module );
|
||||
}
|
||||
|
||||
list_free( l );
|
||||
/* drop the rule name */
|
||||
l = list_pop_front( l );
|
||||
|
||||
/* tack the rest of the expansion onto the front of the first argument */
|
||||
frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) );
|
||||
|
||||
/* record current rule name in frame */
|
||||
if ( rule->procedure )
|
||||
@ -993,13 +1023,21 @@ compile_set_module(
|
||||
LIST *nt = parse_evaluate( parse->left, frame );
|
||||
LIST *ns = parse_evaluate( parse->right, frame );
|
||||
LIST *l;
|
||||
int setflag;
|
||||
char *trace;
|
||||
|
||||
switch( parse->num )
|
||||
{
|
||||
case ASSIGN_SET: setflag = VAR_SET; trace = "="; break;
|
||||
default: setflag = VAR_APPEND; trace = ""; break;
|
||||
}
|
||||
|
||||
if( DEBUG_COMPILE )
|
||||
{
|
||||
debug_compile( 0, "set module", frame);
|
||||
printf( "(%s)", frame->module->name );
|
||||
list_print( nt );
|
||||
printf( " = " );
|
||||
printf( " %s ", trace );
|
||||
list_print( ns );
|
||||
printf( "\n" );
|
||||
}
|
||||
@ -1010,7 +1048,7 @@ compile_set_module(
|
||||
for( l = nt; l; l = list_next( l ) )
|
||||
{
|
||||
bind_module_var( frame->module, l->string );
|
||||
var_set( l->string, list_copy( L0, ns ), VAR_SET );
|
||||
var_set( l->string, list_copy( L0, ns ), setflag );
|
||||
}
|
||||
|
||||
list_free( nt );
|
||||
@ -1275,84 +1313,131 @@ builtin_hdrmacro(
|
||||
return L0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* builtin_import() - IMPORT ( TARGET_MODULE ? : SOURCE_MODULE ? : RULE_NAMES * : TARGET_NAMES * )
|
||||
/* builtin_rulenames() - RULENAMES ( MODULE ? )
|
||||
*
|
||||
* The IMPORT rule imports rules from the SOURCE_MODULE into the
|
||||
* TARGET_MODULE. If either SOURCE_MODULE or TARGET_MODULE is not supplied, it
|
||||
* refers to the root module. If any RULE_NAMES are supplied, they specify which
|
||||
* rules from the SOURCE_MODULE to import, otherwise all rules are imported. The
|
||||
* rules are given the names in TARGET_NAMES; if not enough TARGET_NAMES are
|
||||
* supplied, the excess rules are given the names in RULE_NAMES. If RULE_NAMES
|
||||
* is not supplied, TARGET_NAMES is ignored.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct import_data
|
||||
{
|
||||
module* target_module;
|
||||
LIST* target_names;
|
||||
};
|
||||
typedef struct import_data import_data;
|
||||
|
||||
static void import_rule1( void* r_, void* data_ )
|
||||
/* helper function for builtin_rulenames(), below */
|
||||
static void add_rule_name( void* r_, void* result_ )
|
||||
{
|
||||
RULE* r = r_;
|
||||
import_data* data = data_;
|
||||
|
||||
char* target_name = data->target_names ? data->target_names->string : r->name;
|
||||
if (data->target_names)
|
||||
data->target_names = list_next(data->target_names);
|
||||
LIST** result = result_;
|
||||
|
||||
if ( !r->local_only )
|
||||
import_rule( r, data->target_module, target_name );
|
||||
*result = list_new( *result, copystr( r->name ) );
|
||||
}
|
||||
|
||||
static LIST *
|
||||
builtin_import(
|
||||
builtin_rulenames(
|
||||
PARSE *parse,
|
||||
FRAME *frame )
|
||||
{
|
||||
LIST *target_module_name = lol_get( frame->args, 0 );
|
||||
LIST *source_module_name = lol_get( frame->args, 1 );
|
||||
LIST *rule_names = lol_get( frame->args, 2 );
|
||||
LIST *target_names = lol_get( frame->args, 3 );
|
||||
LIST *arg0 = lol_get( frame->args, 0 );
|
||||
LIST *result = L0;
|
||||
module* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
||||
|
||||
module* target_module = bindmodule( target_module_name ? target_module_name->string : 0 );
|
||||
module* source_module = bindmodule( source_module_name ? source_module_name->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);
|
||||
|
||||
if ( rule_names == 0 )
|
||||
{
|
||||
import_data data;
|
||||
data.target_module = target_module;
|
||||
data.target_names = target_names;
|
||||
hashenumerate( source_module->rules, import_rule1, &data );
|
||||
}
|
||||
else
|
||||
{
|
||||
LIST *old_name, *target_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* builtin_import() - IMPORT ( SOURCE_MODULE ? : SOURCE_RULES * : TARGET_MODULE ? : TARGET_RULES * )
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
static 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 );
|
||||
|
||||
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 ( old_name = rule_names, target_name = target_names;
|
||||
old_name;
|
||||
old_name = list_next( old_name )
|
||||
, target_name = list_next( target_name ) )
|
||||
{
|
||||
RULE r_, *r = &r_;
|
||||
r_.name = old_name->string;
|
||||
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 ( !target_name )
|
||||
target_name = old_name;
|
||||
|
||||
if ( hashcheck( source_module->rules, (HASHDATA**)&r ) )
|
||||
{
|
||||
import_rule( r, target_module, target_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 );
|
||||
imported->local_only = 1;
|
||||
}
|
||||
|
||||
if ( source_name || target_name )
|
||||
{
|
||||
backtrace_line( frame->prev );
|
||||
printf( "import error: length of source and target rule name lists don't match" );
|
||||
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.
|
||||
*/
|
||||
static 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->local_only = 0;
|
||||
}
|
||||
return L0;
|
||||
}
|
||||
|
||||
/* Retrieve the file and line number that should be indicated for a
|
||||
* given frame in debug output or an error backtrace
|
||||
*/
|
||||
@ -1439,8 +1524,6 @@ static 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 ;
|
||||
char buffer[4096] = "";
|
||||
int len;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < levels + 2 && frame->prev; ++i)
|
||||
|
@ -69,7 +69,6 @@ static struct hash* header_macros_hash = 0;
|
||||
void
|
||||
macro_headers( TARGET *t )
|
||||
{
|
||||
LIST *hdrrule;
|
||||
static regexp *re = 0;
|
||||
FILE *f;
|
||||
char buf[ 1024 ];
|
||||
|
@ -73,7 +73,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -836,10 +836,10 @@ case 8:
|
||||
{ yyval.parse = pnull(); ;
|
||||
break;}
|
||||
case 9:
|
||||
{ yyval.parse = yyvsp[0].parse; ;
|
||||
{ yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_SET; ;
|
||||
break;}
|
||||
case 10:
|
||||
{ yyval.parse = yyvsp[0].parse; ;
|
||||
{ yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_APPEND; ;
|
||||
break;}
|
||||
case 11:
|
||||
{ yyval.parse = yyvsp[-1].parse; ;
|
||||
@ -866,7 +866,7 @@ case 18:
|
||||
{ yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); ;
|
||||
break;}
|
||||
case 19:
|
||||
{ yyval.parse = psetmodule( yyvsp[-2].parse, yyvsp[-1].parse ); ;
|
||||
{ yyval.parse = psetmodule( yyvsp[-2].parse, yyvsp[-1].parse, yyvsp[-1].number ); ;
|
||||
break;}
|
||||
case 20:
|
||||
{ yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); ;
|
||||
|
@ -115,7 +115,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -161,9 +161,9 @@ null : /* empty */
|
||||
;
|
||||
|
||||
assign_list_opt : _EQUALS list
|
||||
{ $$.parse = $2.parse; }
|
||||
{ $$.parse = $2.parse; $$.number = ASSIGN_SET; }
|
||||
| null
|
||||
{ $$.parse = $1.parse; }
|
||||
{ $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
|
||||
;
|
||||
|
||||
arglist_opt : _LPAREN lol _RPAREN
|
||||
@ -187,7 +187,7 @@ rule : _LBRACE block _RBRACE
|
||||
| arg assign list _SEMIC
|
||||
{ $$.parse = pset( $1.parse, $3.parse, $2.number ); }
|
||||
| MODULE LOCAL list assign_list_opt _SEMIC
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse ); }
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse, $4.number ); }
|
||||
| arg ON list assign list _SEMIC
|
||||
{ $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
|
||||
| RETURN list _SEMIC
|
||||
|
@ -74,7 +74,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -120,9 +120,9 @@ null : /* empty */
|
||||
;
|
||||
|
||||
assign_list_opt : `=` list
|
||||
{ $$.parse = $2.parse; }
|
||||
{ $$.parse = $2.parse; $$.number = ASSIGN_SET; }
|
||||
| null
|
||||
{ $$.parse = $1.parse; }
|
||||
{ $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
|
||||
;
|
||||
|
||||
arglist_opt : `(` lol `)`
|
||||
@ -146,7 +146,7 @@ rule : `{` block `}`
|
||||
| arg assign list `;`
|
||||
{ $$.parse = pset( $1.parse, $3.parse, $2.number ); }
|
||||
| `module` `local` list assign_list_opt `;`
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse ); }
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse, $4.number ); }
|
||||
| arg `on` list assign list `;`
|
||||
{ $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
|
||||
| `return` list `;`
|
||||
|
@ -149,6 +149,22 @@ list_free( LIST *head )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* list_pop_front() - remove the front element from a list of strings
|
||||
*/
|
||||
LIST * list_pop_front( LIST *l )
|
||||
{
|
||||
LIST * result = l->next;
|
||||
if( result )
|
||||
{
|
||||
result->tail = l->tail;
|
||||
l->next = L0;
|
||||
l->tail = l;
|
||||
}
|
||||
list_free( l );
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* list_print() - print a list of strings to stdout
|
||||
*/
|
||||
|
@ -79,6 +79,7 @@ LIST * list_new( LIST *head, char *string );
|
||||
void list_print( LIST *l );
|
||||
int list_length( LIST *l );
|
||||
LIST * list_sublist( LIST *l, int start, int count );
|
||||
LIST * list_pop_front( LIST *l );
|
||||
|
||||
# define list_next( l ) ((l)->next)
|
||||
|
||||
|
@ -440,7 +440,6 @@ make1cmds( ACTIONS *a0 )
|
||||
SETTINGS *boundvars;
|
||||
LIST *nt, *ns;
|
||||
ACTIONS *a1;
|
||||
CMD *cmd;
|
||||
int start, chunk, length;
|
||||
|
||||
/* Only do rules with commands to execute. */
|
||||
|
@ -97,7 +97,9 @@ static LIST *builtin_echo( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_exit( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_flags( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_hdrmacro( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_rulenames( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_import( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_export( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_caller_module( PARSE *parse, FRAME *frame );
|
||||
static LIST *builtin_backtrace( PARSE *parse, FRAME *frame );
|
||||
|
||||
@ -154,7 +156,6 @@ static void lol_build( LOL* lol, char** elements )
|
||||
static RULE* bind_builtin( char* name, LIST*(*f)(PARSE*, FRAME*), int flags, char** args )
|
||||
{
|
||||
argument_list* arg_list = 0;
|
||||
RULE* builtin_rule;
|
||||
|
||||
if ( args )
|
||||
{
|
||||
@ -204,15 +205,33 @@ compile_builtins()
|
||||
|
||||
{
|
||||
char* args[] = {
|
||||
"target_module", "?"
|
||||
, ":", "source_module", "?"
|
||||
, ":", "rule_names", "*"
|
||||
, ":", "target_names", "*", 0
|
||||
"module", "?", 0
|
||||
};
|
||||
|
||||
bind_builtin( "RULENAMES", builtin_rulenames, 0, args );
|
||||
}
|
||||
|
||||
{
|
||||
char* args[] = {
|
||||
"source_module", "?"
|
||||
, ":", "source_rules", "*"
|
||||
, ":", "target_module", "?"
|
||||
, ":", "target_rules", "*", 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 );
|
||||
@ -636,7 +655,7 @@ static void argument_error( char* message, RULE* rule, FRAME* frame, LIST* arg )
|
||||
LOL* actual = frame->args;
|
||||
assert( frame->procedure != 0 );
|
||||
backtrace_line( frame->prev );
|
||||
printf( "*** argument error\n* rule %s ( ", frame->procedure->file, frame->procedure->line, frame->rulename );
|
||||
printf( "*** argument error\n* rule %s ( ", frame->rulename );
|
||||
lol_print( rule->arguments->data );
|
||||
printf( ")\n* called with: ( " );
|
||||
lol_print( actual );
|
||||
@ -667,11 +686,17 @@ collect_arguments( RULE* rule, FRAME* frame )
|
||||
{
|
||||
LIST *formal = lol_get( all_formal, n );
|
||||
LIST *actual = lol_get( all_actual, n );
|
||||
|
||||
while ( formal )
|
||||
{
|
||||
char* name = formal->string;
|
||||
char modifier = 0;
|
||||
LIST* value = 0;
|
||||
|
||||
/* Stop now if a variable number of arguments are specified */
|
||||
if ( name[0] == '*' && name[1] == 0 )
|
||||
return locals;
|
||||
|
||||
if ( formal->next )
|
||||
{
|
||||
char *next = formal->next->string;
|
||||
@ -814,6 +839,7 @@ evaluate_rule(
|
||||
module *prev_module = frame->module;
|
||||
|
||||
LIST* l = var_expand( L0, rulename, rulename+strlen(rulename), frame->args, 0 );
|
||||
LIST* more_args = L0;
|
||||
|
||||
if ( !l )
|
||||
{
|
||||
@ -840,7 +866,11 @@ evaluate_rule(
|
||||
enter_module( rule->procedure->module );
|
||||
}
|
||||
|
||||
list_free( l );
|
||||
/* drop the rule name */
|
||||
l = list_pop_front( l );
|
||||
|
||||
/* tack the rest of the expansion onto the front of the first argument */
|
||||
frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) );
|
||||
|
||||
/* record current rule name in frame */
|
||||
if ( rule->procedure )
|
||||
@ -993,13 +1023,21 @@ compile_set_module(
|
||||
LIST *nt = parse_evaluate( parse->left, frame );
|
||||
LIST *ns = parse_evaluate( parse->right, frame );
|
||||
LIST *l;
|
||||
int setflag;
|
||||
char *trace;
|
||||
|
||||
switch( parse->num )
|
||||
{
|
||||
case ASSIGN_SET: setflag = VAR_SET; trace = "="; break;
|
||||
default: setflag = VAR_APPEND; trace = ""; break;
|
||||
}
|
||||
|
||||
if( DEBUG_COMPILE )
|
||||
{
|
||||
debug_compile( 0, "set module", frame);
|
||||
printf( "(%s)", frame->module->name );
|
||||
list_print( nt );
|
||||
printf( " = " );
|
||||
printf( " %s ", trace );
|
||||
list_print( ns );
|
||||
printf( "\n" );
|
||||
}
|
||||
@ -1010,7 +1048,7 @@ compile_set_module(
|
||||
for( l = nt; l; l = list_next( l ) )
|
||||
{
|
||||
bind_module_var( frame->module, l->string );
|
||||
var_set( l->string, list_copy( L0, ns ), VAR_SET );
|
||||
var_set( l->string, list_copy( L0, ns ), setflag );
|
||||
}
|
||||
|
||||
list_free( nt );
|
||||
@ -1275,84 +1313,131 @@ builtin_hdrmacro(
|
||||
return L0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* builtin_import() - IMPORT ( TARGET_MODULE ? : SOURCE_MODULE ? : RULE_NAMES * : TARGET_NAMES * )
|
||||
/* builtin_rulenames() - RULENAMES ( MODULE ? )
|
||||
*
|
||||
* The IMPORT rule imports rules from the SOURCE_MODULE into the
|
||||
* TARGET_MODULE. If either SOURCE_MODULE or TARGET_MODULE is not supplied, it
|
||||
* refers to the root module. If any RULE_NAMES are supplied, they specify which
|
||||
* rules from the SOURCE_MODULE to import, otherwise all rules are imported. The
|
||||
* rules are given the names in TARGET_NAMES; if not enough TARGET_NAMES are
|
||||
* supplied, the excess rules are given the names in RULE_NAMES. If RULE_NAMES
|
||||
* is not supplied, TARGET_NAMES is ignored.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct import_data
|
||||
{
|
||||
module* target_module;
|
||||
LIST* target_names;
|
||||
};
|
||||
typedef struct import_data import_data;
|
||||
|
||||
static void import_rule1( void* r_, void* data_ )
|
||||
/* helper function for builtin_rulenames(), below */
|
||||
static void add_rule_name( void* r_, void* result_ )
|
||||
{
|
||||
RULE* r = r_;
|
||||
import_data* data = data_;
|
||||
|
||||
char* target_name = data->target_names ? data->target_names->string : r->name;
|
||||
if (data->target_names)
|
||||
data->target_names = list_next(data->target_names);
|
||||
LIST** result = result_;
|
||||
|
||||
if ( !r->local_only )
|
||||
import_rule( r, data->target_module, target_name );
|
||||
*result = list_new( *result, copystr( r->name ) );
|
||||
}
|
||||
|
||||
static LIST *
|
||||
builtin_import(
|
||||
builtin_rulenames(
|
||||
PARSE *parse,
|
||||
FRAME *frame )
|
||||
{
|
||||
LIST *target_module_name = lol_get( frame->args, 0 );
|
||||
LIST *source_module_name = lol_get( frame->args, 1 );
|
||||
LIST *rule_names = lol_get( frame->args, 2 );
|
||||
LIST *target_names = lol_get( frame->args, 3 );
|
||||
LIST *arg0 = lol_get( frame->args, 0 );
|
||||
LIST *result = L0;
|
||||
module* source_module = bindmodule( arg0 ? arg0->string : 0 );
|
||||
|
||||
module* target_module = bindmodule( target_module_name ? target_module_name->string : 0 );
|
||||
module* source_module = bindmodule( source_module_name ? source_module_name->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);
|
||||
|
||||
if ( rule_names == 0 )
|
||||
{
|
||||
import_data data;
|
||||
data.target_module = target_module;
|
||||
data.target_names = target_names;
|
||||
hashenumerate( source_module->rules, import_rule1, &data );
|
||||
}
|
||||
else
|
||||
{
|
||||
LIST *old_name, *target_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* builtin_import() - IMPORT ( SOURCE_MODULE ? : SOURCE_RULES * : TARGET_MODULE ? : TARGET_RULES * )
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
static 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 );
|
||||
|
||||
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 ( old_name = rule_names, target_name = target_names;
|
||||
old_name;
|
||||
old_name = list_next( old_name )
|
||||
, target_name = list_next( target_name ) )
|
||||
{
|
||||
RULE r_, *r = &r_;
|
||||
r_.name = old_name->string;
|
||||
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 ( !target_name )
|
||||
target_name = old_name;
|
||||
|
||||
if ( hashcheck( source_module->rules, (HASHDATA**)&r ) )
|
||||
{
|
||||
import_rule( r, target_module, target_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 );
|
||||
imported->local_only = 1;
|
||||
}
|
||||
|
||||
if ( source_name || target_name )
|
||||
{
|
||||
backtrace_line( frame->prev );
|
||||
printf( "import error: length of source and target rule name lists don't match" );
|
||||
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.
|
||||
*/
|
||||
static 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->local_only = 0;
|
||||
}
|
||||
return L0;
|
||||
}
|
||||
|
||||
/* Retrieve the file and line number that should be indicated for a
|
||||
* given frame in debug output or an error backtrace
|
||||
*/
|
||||
@ -1439,8 +1524,6 @@ static 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 ;
|
||||
char buffer[4096] = "";
|
||||
int len;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < levels + 2 && frame->prev; ++i)
|
||||
|
@ -69,7 +69,6 @@ static struct hash* header_macros_hash = 0;
|
||||
void
|
||||
macro_headers( TARGET *t )
|
||||
{
|
||||
LIST *hdrrule;
|
||||
static regexp *re = 0;
|
||||
FILE *f;
|
||||
char buf[ 1024 ];
|
||||
|
@ -73,7 +73,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -836,10 +836,10 @@ case 8:
|
||||
{ yyval.parse = pnull(); ;
|
||||
break;}
|
||||
case 9:
|
||||
{ yyval.parse = yyvsp[0].parse; ;
|
||||
{ yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_SET; ;
|
||||
break;}
|
||||
case 10:
|
||||
{ yyval.parse = yyvsp[0].parse; ;
|
||||
{ yyval.parse = yyvsp[0].parse; yyval.number = ASSIGN_APPEND; ;
|
||||
break;}
|
||||
case 11:
|
||||
{ yyval.parse = yyvsp[-1].parse; ;
|
||||
@ -866,7 +866,7 @@ case 18:
|
||||
{ yyval.parse = pset( yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); ;
|
||||
break;}
|
||||
case 19:
|
||||
{ yyval.parse = psetmodule( yyvsp[-2].parse, yyvsp[-1].parse ); ;
|
||||
{ yyval.parse = psetmodule( yyvsp[-2].parse, yyvsp[-1].parse, yyvsp[-1].number ); ;
|
||||
break;}
|
||||
case 20:
|
||||
{ yyval.parse = pset1( yyvsp[-5].parse, yyvsp[-3].parse, yyvsp[-1].parse, yyvsp[-2].number ); ;
|
||||
|
@ -115,7 +115,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -161,9 +161,9 @@ null : /* empty */
|
||||
;
|
||||
|
||||
assign_list_opt : _EQUALS list
|
||||
{ $$.parse = $2.parse; }
|
||||
{ $$.parse = $2.parse; $$.number = ASSIGN_SET; }
|
||||
| null
|
||||
{ $$.parse = $1.parse; }
|
||||
{ $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
|
||||
;
|
||||
|
||||
arglist_opt : _LPAREN lol _RPAREN
|
||||
@ -187,7 +187,7 @@ rule : _LBRACE block _RBRACE
|
||||
| arg assign list _SEMIC
|
||||
{ $$.parse = pset( $1.parse, $3.parse, $2.number ); }
|
||||
| MODULE LOCAL list assign_list_opt _SEMIC
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse ); }
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse, $4.number ); }
|
||||
| arg ON list assign list _SEMIC
|
||||
{ $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
|
||||
| RETURN list _SEMIC
|
||||
|
@ -74,7 +74,7 @@
|
||||
# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 )
|
||||
# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 )
|
||||
# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a )
|
||||
# define psetmodule( l,r ) parse_make( compile_set_module,l,r,P0,S0,S0,0 )
|
||||
# define psetmodule( l,r,a ) parse_make( compile_set_module,l,r,P0,S0,S0,a )
|
||||
# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a )
|
||||
# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l )
|
||||
# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f )
|
||||
@ -120,9 +120,9 @@ null : /* empty */
|
||||
;
|
||||
|
||||
assign_list_opt : `=` list
|
||||
{ $$.parse = $2.parse; }
|
||||
{ $$.parse = $2.parse; $$.number = ASSIGN_SET; }
|
||||
| null
|
||||
{ $$.parse = $1.parse; }
|
||||
{ $$.parse = $1.parse; $$.number = ASSIGN_APPEND; }
|
||||
;
|
||||
|
||||
arglist_opt : `(` lol `)`
|
||||
@ -146,7 +146,7 @@ rule : `{` block `}`
|
||||
| arg assign list `;`
|
||||
{ $$.parse = pset( $1.parse, $3.parse, $2.number ); }
|
||||
| `module` `local` list assign_list_opt `;`
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse ); }
|
||||
{ $$.parse = psetmodule( $3.parse, $4.parse, $4.number ); }
|
||||
| arg `on` list assign list `;`
|
||||
{ $$.parse = pset1( $1.parse, $3.parse, $5.parse, $4.number ); }
|
||||
| `return` list `;`
|
||||
|
@ -149,6 +149,22 @@ list_free( LIST *head )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* list_pop_front() - remove the front element from a list of strings
|
||||
*/
|
||||
LIST * list_pop_front( LIST *l )
|
||||
{
|
||||
LIST * result = l->next;
|
||||
if( result )
|
||||
{
|
||||
result->tail = l->tail;
|
||||
l->next = L0;
|
||||
l->tail = l;
|
||||
}
|
||||
list_free( l );
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* list_print() - print a list of strings to stdout
|
||||
*/
|
||||
|
@ -79,6 +79,7 @@ LIST * list_new( LIST *head, char *string );
|
||||
void list_print( LIST *l );
|
||||
int list_length( LIST *l );
|
||||
LIST * list_sublist( LIST *l, int start, int count );
|
||||
LIST * list_pop_front( LIST *l );
|
||||
|
||||
# define list_next( l ) ((l)->next)
|
||||
|
||||
|
@ -440,7 +440,6 @@ make1cmds( ACTIONS *a0 )
|
||||
SETTINGS *boundvars;
|
||||
LIST *nt, *ns;
|
||||
ACTIONS *a1;
|
||||
CMD *cmd;
|
||||
int start, chunk, length;
|
||||
|
||||
/* Only do rules with commands to execute. */
|
||||
|
@ -3,53 +3,64 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import errors : error ;
|
||||
import errors : error-skip-frames lol->list ;
|
||||
|
||||
# assert the equality of A and B
|
||||
rule equal ( a * : b * )
|
||||
{
|
||||
if $(a) != $(b)
|
||||
{
|
||||
error assertion failure: \"$(a)\" "!=" \"$(b)\" ;
|
||||
error-skip-frames 3 assertion failure: \"$(a)\" "!=" \"$(b)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
rule result ( expected * : rule-name args * )
|
||||
# assert that EXPECTED is the result of calling RULE-NAME with the
|
||||
# given arguments
|
||||
rule result ( expected * : rule-name args * : * )
|
||||
{
|
||||
|
||||
local result__ ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
result = [ $(rule-name) $(args) ] ;
|
||||
result__ = [
|
||||
$(rule-name) $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ] ;
|
||||
}
|
||||
|
||||
if $(result) != $(expected)
|
||||
if $(result__) != $(expected)
|
||||
{
|
||||
error assertion failure: "[" $(rule-name) \"$(args)\" "]"
|
||||
error-skip-frames 3 assertion failure: "[" $(rule-name)
|
||||
[ lol->list $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ]
|
||||
"]"
|
||||
: expected: \"$(expected)\"
|
||||
: got: \"$(result)\" ;
|
||||
: got: \"$(result__)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
# assert that the given variable is nonempty.
|
||||
rule nonempty-variable ( name )
|
||||
{
|
||||
local empty ;
|
||||
if $($(variable)) = $(empty)
|
||||
{
|
||||
error assertion failure: expecting non-empty variable $(variable) ;
|
||||
error-skip-frames 3 assertion failure: expecting non-empty variable $(variable) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule true ( rule-name args * )
|
||||
# assert that the result of calling RULE-NAME on the given arguments
|
||||
# has a true logical value (is neither an empty list nor all empty
|
||||
# strings).
|
||||
rule true ( rule-name args * : * )
|
||||
{
|
||||
local result caller-module = [ CALLER_MODULE ] ;
|
||||
|
||||
module $(caller-module)
|
||||
local result__ ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
result = [ $(rule-name) $(args) ] ;
|
||||
result__ = [
|
||||
$(rule-name) $(args) : $(2) $(3) : $(4)
|
||||
: $(5) : $(6) : $(7) : $(8) : $(9) ] ;
|
||||
}
|
||||
|
||||
if ! $(result)
|
||||
if ! $(result__)
|
||||
{
|
||||
error assertion failure: expecting true result from
|
||||
error-skip-frames 3 assertion failure: expecting true result from
|
||||
"[" $(rule-name) \"$(args)\" "]" ;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
SEARCH on <module@>modules.jam = $(BOOST_BUILD_PATH) ;
|
||||
module modules { include <module@>modules.jam ; }
|
||||
# Bring the import rule into the global module
|
||||
IMPORT : modules : import ;
|
||||
IMPORT modules : import : : import ;
|
||||
import modules ; # The modules module can tolerate being included twice
|
||||
|
||||
import build-system ;
|
||||
|
@ -3,4 +3,13 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import os ;
|
||||
# this rule will be used to generate build instructions for the
|
||||
# given module (Jamfile) once its declarations have been read.
|
||||
rule construct ( module-name )
|
||||
{
|
||||
}
|
||||
|
||||
JAMFILE ?= Jamfile ;
|
||||
import $(JAMFILE) ;
|
||||
|
||||
construct $(JAMFILE) ;
|
||||
|
147
new/errors.jam
147
new/errors.jam
@ -3,44 +3,153 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
# A utility rule used to report the including module when there is an error, so
|
||||
# that editors may find it.
|
||||
rule report-module ( prefix ? : suffix ? : frames ? )
|
||||
# Print a stack backtrace leading to this rule's caller. Each
|
||||
# argument represents a line of output to be printed after the first
|
||||
# line of the backtrace.
|
||||
rule backtrace ( skip-frames messages * : * )
|
||||
{
|
||||
frames ?= 1 ;
|
||||
# We report some large line number so that emacs, etc., will at least locate the file.
|
||||
ECHO $(prefix) [ modules.binding [ CALLER_MODULE $(frames) ] ] ":" line 99999 $(suffix) ;
|
||||
}
|
||||
|
||||
rule backtrace
|
||||
{
|
||||
local digits = 1 2 3 4 5 6 7 8 9 ;
|
||||
local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ;
|
||||
local drop-elements = $(frame-skips[$(skip-frames)]) ;
|
||||
if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 )
|
||||
{
|
||||
ECHO warning: backtrace doesn't support skipping
|
||||
$(skip-frames) frames; using 1 instead. ;
|
||||
drop-elements = 5 ;
|
||||
}
|
||||
|
||||
# get the whole backtrace, then drop the initial quadruples
|
||||
# corresponding to the frames that must be skipped.
|
||||
local bt = [ BACKTRACE ] ;
|
||||
bt = $(bt[5-]) ;
|
||||
bt = $(bt[$(drop-elements)-]) ;
|
||||
|
||||
local args = messages 2 3 4 5 6 7 8 9 ;
|
||||
while $(bt)
|
||||
{
|
||||
ECHO $(bt[1]):$(bt[2]): "in" $(bt[4]) ;
|
||||
|
||||
for local n in $(digits)
|
||||
# the first time through, print each argument on a separate
|
||||
# line
|
||||
for local n in $(args)
|
||||
{
|
||||
if $($(n))-is-not-empty
|
||||
{
|
||||
ECHO $($(n)) ;
|
||||
}
|
||||
}
|
||||
digits = ;
|
||||
|
||||
args = ; # kill args so that this never happens again
|
||||
|
||||
# Move on to the next quadruple
|
||||
bt = $(bt[5-]) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule error
|
||||
module local args = messages 2 3 4 5 6 7 8 9 ;
|
||||
module local disabled last-$(args) ;
|
||||
|
||||
# try-catch --
|
||||
#
|
||||
# This is not really an exception-handling mechanism, but it does
|
||||
# allow us to perform some error-checking on our
|
||||
# error-checking. Errors are suppressed after a try, and the first one
|
||||
# is recorded. Use catch to check that the error message matched
|
||||
# expectations.
|
||||
|
||||
# begin looking for error messages
|
||||
rule try ( )
|
||||
{
|
||||
backtrace error: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
EXIT ;
|
||||
disabled += true ;
|
||||
last-$(args) = ;
|
||||
}
|
||||
|
||||
# stop looking for error messages; generate an error if an argument of
|
||||
# messages is not found in the corresponding argument in the error call.
|
||||
rule catch ( messages * : * )
|
||||
{
|
||||
import sequence ;
|
||||
disabled = $(disabled[2-]) ; # pop the stack
|
||||
|
||||
for local n in $(args)
|
||||
{
|
||||
if ! $($(n)) in $(last-$(n))
|
||||
{
|
||||
local v = [ sequence.join $($(n)) : " " ] ;
|
||||
v ?= "" ;
|
||||
local joined = [ sequence.join $(last-$(n)) : " " ] ;
|
||||
|
||||
last-$(args) = ;
|
||||
error-skip-frames 3 expected \"$(v)\" in argument $(n) of error
|
||||
: got \"$(joined)\" instead ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule error-skip-frames ( skip-frames messages * : * )
|
||||
{
|
||||
if ! $(disabled)
|
||||
{
|
||||
backtrace $(skip-frames) error: $(messages) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
EXIT ;
|
||||
}
|
||||
else if ! $(last-$(args))
|
||||
{
|
||||
for local n in $(args)
|
||||
{
|
||||
last-$(n) = $($(n)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Print an error message with a stack backtrace and exit.
|
||||
rule error ( messages * : * )
|
||||
{
|
||||
error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
}
|
||||
|
||||
# Print a warning message with a stack backtrace and exit.
|
||||
rule warning
|
||||
{
|
||||
backtrace warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
backtrace 2 warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
}
|
||||
|
||||
# convert an arbitrary argument list into a list with ":" separators
|
||||
# and quoted elements representing the same information. This is
|
||||
# mostly useful for formatting descriptions of the arguments with
|
||||
# which a rule was called when reporting an error.
|
||||
rule lol->list ( * )
|
||||
{
|
||||
local result ;
|
||||
local remaining = 1 2 3 4 5 6 7 8 9 ;
|
||||
while $($(remaining))
|
||||
{
|
||||
local n = $(remaining[1]) ;
|
||||
remaining = $(remaining[2-]) ;
|
||||
|
||||
if $(n) != 1
|
||||
{
|
||||
result += ":" ;
|
||||
}
|
||||
result += \"$(n)\" ;
|
||||
}
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
rule __test__ ( )
|
||||
{
|
||||
# show that we can correctly catch an expected error
|
||||
try ;
|
||||
{
|
||||
error an error occurred : somewhere ;
|
||||
}
|
||||
catch an error occurred : somewhere ;
|
||||
|
||||
# show that unexpected errors generate real errors
|
||||
try ;
|
||||
{
|
||||
try ;
|
||||
{
|
||||
error an error occurred : somewhere ;
|
||||
}
|
||||
catch an error occurred : nowhere ;
|
||||
}
|
||||
catch expected \"nowhere\" in argument 2 ;
|
||||
}
|
||||
|
383
new/feature.jam
Normal file
383
new/feature.jam
Normal file
@ -0,0 +1,383 @@
|
||||
# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import errors : error lol->list ;
|
||||
import property ;
|
||||
import sequence ;
|
||||
import regex ;
|
||||
import set ;
|
||||
|
||||
module local all-attributes =
|
||||
|
||||
implicit # features whose values alone identify the
|
||||
# feature. For example, a user is not required to
|
||||
# write "<toolset>gcc", but can simply write
|
||||
# "gcc". Implicit feature names also don't appear in
|
||||
# variant paths, although the values do. Thus:
|
||||
# bin/gcc/... as opposed to bin/toolset-gcc/.... There
|
||||
# should typically be only a few such features, to
|
||||
# avoid possible name clashes.
|
||||
|
||||
executed # the feature corresponds to the name of a module
|
||||
# containing an execute rule, used to actually prepare
|
||||
# the build. For example, the toolset feature would be
|
||||
# executed.
|
||||
|
||||
|
||||
composite # features which actually correspond to groups of
|
||||
# properties. For example, a build variant is a
|
||||
# composite feature. When generating targets from a
|
||||
# set of build properties, composite features are
|
||||
# recursively expanded and /added/ to the build
|
||||
# property set, so rules can find them if
|
||||
# neccessary. Non-composite non-free features override
|
||||
# components of composite features in a build property
|
||||
# set.
|
||||
|
||||
|
||||
optional # An optional feature is allowed to have no value at
|
||||
# all in a particular build. Normal non-free features
|
||||
# are always given the first of their values if no
|
||||
# value is otherwise specified.
|
||||
|
||||
|
||||
symmetric # A symmetric feature has no default value, and is
|
||||
# therefore not automatically included in all
|
||||
# variants. A symmetric feature, when relevant to the
|
||||
# toolset, always generates a corresponding subvariant
|
||||
# directory.
|
||||
|
||||
free # as described in previous documentation
|
||||
|
||||
path # the (free) feature's value describes a path which
|
||||
# might be given relative to the directory of the
|
||||
# Jamfile.
|
||||
|
||||
dependency # the value of the (free) feature specifies a
|
||||
# dependency of the target.
|
||||
|
||||
propagated # when used in a build request, the (free) feature is
|
||||
# propagated to top-level targets which are
|
||||
# dependencies of the requested build. Propagated
|
||||
# features would be most useful for settings such as
|
||||
# warning levels, output message style, etc., which
|
||||
# don't affect the built products at all.
|
||||
;
|
||||
|
||||
module local all-features ;
|
||||
module local all-implicit-values ;
|
||||
|
||||
# declare a new feature with the given name, values, and attributes.
|
||||
rule feature ( name : values * : attributes * )
|
||||
{
|
||||
local error ;
|
||||
|
||||
# if there are any unknown attributes...
|
||||
if ! ( $(attributes) in $(all-attributes) )
|
||||
{
|
||||
error = unknown attributes:
|
||||
[ set.difference $(attributes) : $(all-attributes) ] ;
|
||||
}
|
||||
else if $(name) in $(all-features)
|
||||
{
|
||||
error = feature already defined: ;
|
||||
}
|
||||
else if implicit in $(attributes)
|
||||
{
|
||||
if free in $(attributes)
|
||||
{
|
||||
error = free features cannot also be implicit ;
|
||||
}
|
||||
}
|
||||
|
||||
if $(error)
|
||||
{
|
||||
error $(error)
|
||||
: "in" feature declaration:
|
||||
: feature [ errors.lol->list $(1) : $(2) : $(3) ] ;
|
||||
}
|
||||
|
||||
module local $(name).values ;
|
||||
module local $(name).attributes = $(attributes) ;
|
||||
module local $(name).subfeatures = ;
|
||||
|
||||
all-features += $(name) ;
|
||||
extend $(name) : $(values) ;
|
||||
}
|
||||
|
||||
# returns true iff all elements of names are valid features.
|
||||
rule valid ( name + )
|
||||
{
|
||||
if $(names) in $(all-features)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
# return the attibutes of the given feature
|
||||
rule attributes ( feature )
|
||||
{
|
||||
return $($(feature).attributes) ;
|
||||
}
|
||||
|
||||
# return the values of the given feature
|
||||
rule values ( feature )
|
||||
{
|
||||
return $($(feature).values) ;
|
||||
}
|
||||
|
||||
rule implied-feature ( implicit-value )
|
||||
{
|
||||
local feature = $($(implicit-value).implicit-feature) ;
|
||||
if ! $(feature)
|
||||
{
|
||||
error \"$(implicit-value)\" is not a value of an implicit feature ;
|
||||
}
|
||||
return $(feature) ;
|
||||
}
|
||||
|
||||
local rule find-implied-subfeature ( feature subvalue : value-string ? )
|
||||
{
|
||||
local v
|
||||
= subfeature($(feature),$(subvalue))
|
||||
subfeature($(feature),$(value-string),$(subvalue)) ;
|
||||
|
||||
# declaring these module local here prevents us from picking up
|
||||
# enclosing definitions.
|
||||
module local $(v) ;
|
||||
|
||||
local subfeature = $($(v)) ;
|
||||
return $(subfeature[1]) ;
|
||||
}
|
||||
|
||||
rule implied-subfeature ( feature subvalue : value-string ? )
|
||||
{
|
||||
local subfeature = [ find-implied-subfeature $(feature) $(subvalue)
|
||||
: $(value-string) ] ;
|
||||
|
||||
if ! $(subfeature)
|
||||
{
|
||||
error \"$(subvalue)\" is not a known subfeature value of
|
||||
feature \"$(feature)\" ;
|
||||
}
|
||||
|
||||
return $(subfeature) ;
|
||||
}
|
||||
|
||||
# generate an error if the feature is unknown
|
||||
local rule validate-feature ( feature )
|
||||
{
|
||||
if ! $(feature) in $(all-features)
|
||||
{
|
||||
error unknown feature \"$(feature)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
# expand-subfeatures toolset : gcc-2.95.2-linux-x86 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
|
||||
# equivalent to:
|
||||
# expand-subfeatures : gcc-2.95.2-linux-x86
|
||||
local rule expand-subfeatures ( feature ? : value )
|
||||
{
|
||||
if $(feature)
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
}
|
||||
|
||||
local components = [ regex.split $(value) "-" ] ;
|
||||
if ! $(feature)
|
||||
{
|
||||
feature = [ implied-feature $(components[1]) ] ;
|
||||
}
|
||||
|
||||
# get the top-level feature's value
|
||||
local value = $(components[1]:G=) ;
|
||||
|
||||
local result = $(components[1]:G=$(feature)) ;
|
||||
for local subvalue in $(components[2-])
|
||||
{
|
||||
local subfeature = [ implied-subfeature $(feature) $(subvalue) : $(value) ] ;
|
||||
result += $(subvalue:G=$(feature)-$(subfeature)) ;
|
||||
}
|
||||
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
local rule extend-feature ( feature : values * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if implicit in $(attributes)
|
||||
{
|
||||
for local v in $(values)
|
||||
{
|
||||
module local $(v).implicit-feature ;
|
||||
if $($(v).implicit-feature)
|
||||
{
|
||||
error $(v) is already associated with the \"$($(v).implicit-feature)\" feature ;
|
||||
}
|
||||
$(v).implicit-feature = $(feature) ;
|
||||
}
|
||||
|
||||
all-implicit-values += $(values) ;
|
||||
}
|
||||
$(feature).values += $(values) ;
|
||||
}
|
||||
|
||||
local rule validate-value-string ( feature value-string )
|
||||
{
|
||||
local values = $(value-string) ;
|
||||
if $($(feature).subfeatures)
|
||||
{
|
||||
values = [ regex.split $(value-string) - ] ;
|
||||
}
|
||||
|
||||
if ! ( $(values[1]) in $($(feature).values) )
|
||||
{
|
||||
return \"$(values[1])\" is not a known value of feature \"$(feature)\" ;
|
||||
}
|
||||
|
||||
if $(values[2])
|
||||
{
|
||||
# this will validate any subfeature values in value-string
|
||||
implied-subfeature $(feature) [ sequence.join $(values[2-]) - ]
|
||||
: $(values[1]) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ;
|
||||
# extend-subfeature toolset : target-platform : mingw ;
|
||||
local rule extend-subfeature ( feature value-string ? : subfeature : subvalues * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if $(value-string)
|
||||
{
|
||||
validate-value-string $(feature) $(value-string) ;
|
||||
}
|
||||
|
||||
for local subvalue in $(subvalues)
|
||||
{
|
||||
local v
|
||||
= subfeature($(feature),$(value-string),$(subvalue))
|
||||
subfeature($(feature),$(subvalue)) ;
|
||||
module local $(v[1]) = $(subfeature) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule extend ( feature-or-property subfeature ? : values * )
|
||||
{
|
||||
local feature value-string ;
|
||||
if $(feature-or-property:G)
|
||||
{
|
||||
feature = [ property.get-feature $(feature-or-property) ] ;
|
||||
value-string = $(feature-or-property:G=) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
feature = $(feature-or-property) ;
|
||||
}
|
||||
|
||||
if $(subfeature)
|
||||
{
|
||||
extend-subfeature $(feature) $(value-string)
|
||||
: $(subfeature) : $(values) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if $(value-string)
|
||||
{
|
||||
error can only be specify a property as the first argument
|
||||
when extending a subfeature
|
||||
: usage:
|
||||
: " extend" feature ":" values...
|
||||
: " | extend" <feature>value-string subfeature ":" values...
|
||||
;
|
||||
}
|
||||
|
||||
extend-feature $(feature) : $(values) ;
|
||||
}
|
||||
}
|
||||
|
||||
# subfeature
|
||||
#
|
||||
# subfeature toolset gcc-2.95.2 target-platform : aix linux mac cygwin
|
||||
#
|
||||
rule subfeature ( feature value-string ? : subfeature : subvalues * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if $(subfeature) in $($(feature).subfeatures)
|
||||
{
|
||||
error \"$(subfeature)\" already declared as a subfeature of \"$(feature)\" ;
|
||||
}
|
||||
$(feature).subfeatures += $(subfeature) ;
|
||||
extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ;
|
||||
}
|
||||
|
||||
# tests of module features
|
||||
local rule __test__ ( )
|
||||
{
|
||||
import errors : try catch ;
|
||||
import assert ;
|
||||
feature __test__toolset : gcc : implicit executed ;
|
||||
feature __test__define : : free ;
|
||||
|
||||
extend-feature __test__toolset : msvc metrowerks ;
|
||||
subfeature __test__toolset gcc : version : 2.95.2 2.95.3 2.95.4
|
||||
3.0 3.0.1 3.0.2 ;
|
||||
|
||||
assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 :
|
||||
expand-subfeatures __test__toolset : gcc-3.0.1 ;
|
||||
|
||||
assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 :
|
||||
expand-subfeatures : gcc-3.0.1 ;
|
||||
|
||||
feature __test__dummy : dummy1 dummy2 ;
|
||||
subfeature __test__dummy : subdummy : x y z ;
|
||||
|
||||
# test error checking
|
||||
try ;
|
||||
{
|
||||
validate-feature __test__foobar ;
|
||||
}
|
||||
catch unknown feature ;
|
||||
|
||||
try ;
|
||||
{
|
||||
feature __test__foobar : : baz ;
|
||||
}
|
||||
catch unknown attributes: baz ;
|
||||
|
||||
feature __test__feature1 ;
|
||||
try ;
|
||||
{
|
||||
feature __test__feature1 ;
|
||||
}
|
||||
catch feature already defined: ;
|
||||
|
||||
try ;
|
||||
{
|
||||
feature __test__feature2 : : free implicit ;
|
||||
}
|
||||
catch free features cannot also be implicit ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-feature lackluster ;
|
||||
}
|
||||
catch \"lackluster\" is not a value of an implicit feature ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-subfeature __test__toolset 3.0.1 ;
|
||||
}
|
||||
catch \"3.0.1\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-subfeature __test__toolset not-a-version : gcc ;
|
||||
}
|
||||
catch \"not-a-version\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
}
|
@ -5,6 +5,8 @@
|
||||
|
||||
# Keep a record so that no module is included multiple times
|
||||
module local loaded-modules ;
|
||||
module local loading-modules ;
|
||||
module local untested ;
|
||||
|
||||
# meant to be invoked from import when no __test__ rule is defined in a given
|
||||
# module
|
||||
@ -19,24 +21,22 @@ rule binding ( module )
|
||||
return $($(module).__binding__) ;
|
||||
}
|
||||
|
||||
# load the indicated module. Any members of rules-opt will be available without
|
||||
# qualification in the caller's module. Any members of rename-opt will be taken
|
||||
# as the names of the rules in the caller's module, in place of the names they
|
||||
# have in the imported module. If rules-opt = '*', all rules from the indicated
|
||||
# module are imported into the caller's module.
|
||||
rule import ( module-name : rules-opt * : rename-opt * )
|
||||
# load the indicated module if it is not already loaded.
|
||||
rule load ( module-name )
|
||||
{
|
||||
# First see if the module needs to be loaded
|
||||
if ! ( $(module-name) in $(loaded-modules) )
|
||||
{
|
||||
loaded-modules += $(module-name) ;
|
||||
loading-modules += $(module-name) ;
|
||||
local suppress-test = $(untested[1]) ; # suppress tests until all recursive loads are complete.
|
||||
untested += $(module-name) ; # add the module to the stack of untested modules
|
||||
|
||||
module $(module-name)
|
||||
{
|
||||
module local __name__ = $(module-name) ;
|
||||
|
||||
# Prepare a default behavior, in case no __test__ is defined.
|
||||
IMPORT $(module-name) : modules : no_test_defined : __test__ ;
|
||||
IMPORT modules : no_test_defined : $(module-name) : __test__ ;
|
||||
|
||||
# Add some grist so that the module will have a unique target name
|
||||
local module-target = $(module-name:G=module@:S=.jam) ;
|
||||
@ -45,34 +45,61 @@ rule import ( module-name : rules-opt * : rename-opt * )
|
||||
BINDRULE on $(module-target) = modules.record-binding ;
|
||||
include $(module-name:G=module@:S=.jam) ;
|
||||
|
||||
# run the module's test, if any.
|
||||
if nonempty$(BOOST_BUILD_TEST)
|
||||
}
|
||||
loading-modules = $(loading-modules[1--2]) ;
|
||||
|
||||
if ! $(suppress-test) && $(BOOST_BUILD_TEST)-is-nonempty
|
||||
{
|
||||
# run any pending tests
|
||||
for local m in $(untested)
|
||||
{
|
||||
ECHO testing module $(module-name)... ;
|
||||
local ignored = [ __test__ ] ;
|
||||
ECHO testing module $(m)... ;
|
||||
module $(m)
|
||||
{
|
||||
__test__ ;
|
||||
}
|
||||
}
|
||||
untested = ;
|
||||
}
|
||||
}
|
||||
|
||||
# If any rules are to be imported, do so now.
|
||||
if $(rules-opt)
|
||||
else if $(module-name) in $(loading-modules)
|
||||
{
|
||||
if $(rules-opt) = *
|
||||
{
|
||||
rules-opt = ;
|
||||
}
|
||||
IMPORT [ CALLER_MODULE ]
|
||||
: $(module-name) : $(rules-opt) : $(rename-opt) ;
|
||||
ECHO loading \"$(module-name)\" ;
|
||||
ECHO circular module loading dependency: ;
|
||||
EXIT $(loading-modules) $(module-name) ;
|
||||
}
|
||||
}
|
||||
|
||||
# This helper is used by import (above) to record the binding (path) of
|
||||
# This helper is used by load (above) to record the binding (path) of
|
||||
# each loaded module.
|
||||
rule record-binding ( module-target : binding )
|
||||
{
|
||||
module local $(module-target:G=:S=).__binding__ = $(binding) ;
|
||||
}
|
||||
|
||||
# load the indicated module and import rule names into the current
|
||||
# module. Any members of rules-opt will be available without
|
||||
# qualification in the caller's module. Any members of rename-opt will
|
||||
# be taken as the names of the rules in the caller's module, in place
|
||||
# of the names they have in the imported module. If rules-opt = '*',
|
||||
# all rules from the indicated module are imported into the caller's
|
||||
# module. If rename-opt is supplied, it must have the same number of
|
||||
# elements as rules-opt.
|
||||
rule import ( module-name : rules-opt * : rename-opt * )
|
||||
{
|
||||
load $(module-name) ;
|
||||
|
||||
local source-names = $(rules-opt) ;
|
||||
if $(rules-opt) = *
|
||||
{
|
||||
source-names = [ RULENAMES module-name ] ;
|
||||
}
|
||||
|
||||
local target-names = $(rename-opt) ;
|
||||
target-names ?= $(source-names) ;
|
||||
IMPORT $(module-name) : $(source-names) : [ CALLER_MODULE ] : $(target-names) ;
|
||||
}
|
||||
|
||||
# Returns the module-local value of a variable.
|
||||
rule peek ( module-name variable )
|
||||
{
|
||||
@ -82,9 +109,10 @@ rule peek ( module-name variable )
|
||||
}
|
||||
}
|
||||
|
||||
rule __test__ ( )
|
||||
local rule __test__ ( )
|
||||
{
|
||||
import assert ;
|
||||
|
||||
module modules.__test__
|
||||
{
|
||||
module local foo = bar ;
|
||||
|
@ -3,7 +3,28 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
if $(NT)
|
||||
{
|
||||
module local slash = \\ ;
|
||||
}
|
||||
else
|
||||
{
|
||||
module local slash = / ;
|
||||
}
|
||||
|
||||
rule split ( path )
|
||||
{
|
||||
|
||||
return [ SUBST $(<[1]) "^([/$(SLASH)]+).*" $1 ] # rooting slash(es), if any
|
||||
[ split $(<) "[/$(SLASH)]" ] # the rest.
|
||||
;
|
||||
}
|
||||
|
||||
rule join ( elements * )
|
||||
{
|
||||
local slashes = $(slash) / ;
|
||||
local result prev = $(elements[1]) ;
|
||||
for local e in $(elements[2-])
|
||||
{`
|
||||
if ! ( $(prev) in $(slashes) ) &&
|
||||
}
|
||||
}
|
33
new/property.jam
Normal file
33
new/property.jam
Normal file
@ -0,0 +1,33 @@
|
||||
import errors ;
|
||||
|
||||
# Given a property name, return the corresponding feature name
|
||||
rule get-feature ( property )
|
||||
{
|
||||
return [ SUBST $(property:G) ^<(.*)> $1 ] ;
|
||||
}
|
||||
|
||||
rule is-valid ( property )
|
||||
{
|
||||
import feature ;
|
||||
if ! $(property:G)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
else
|
||||
{
|
||||
local f = [ get-feature $(property) ] ;
|
||||
local value = $(property:G=) ;
|
||||
|
||||
if [ features.is-valid $(f) ] &&
|
||||
( free in [ features.attributes $(f) ]
|
||||
|| $(value) in [ features.values $(f) ] )
|
||||
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
9
new/readme.txt
Normal file
9
new/readme.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Development code for new build system. To test, execute:
|
||||
|
||||
jam -sBOOST_BUILD_PATH=.:$BOOST_ROOT -sBOOST_BUILD_TEST=1 -sJAMFILE=test.jam
|
||||
|
||||
on unix, or
|
||||
|
||||
jam -sBOOST_BUILD_PATH=.;%BOOST_ROOT% -sBOOST_BUILD_TEST=1 -sJAMFILE=test.jam
|
||||
|
||||
on windows
|
120
new/sequence.jam
Normal file
120
new/sequence.jam
Normal file
@ -0,0 +1,120 @@
|
||||
# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import assert ;
|
||||
|
||||
# Note that algorithms in this module execute largely in the caller's
|
||||
# module namespace, so that local rules can be used as function
|
||||
# objects. Also note that most predicates can be multi-element
|
||||
# lists. In that case, all but the first element are prepended to the
|
||||
# first argument which is passed to the rule named by the first
|
||||
# element.
|
||||
|
||||
# Return the elements e of $(sequence) for which [ $(predicate) e ] is
|
||||
# has a non-null value.
|
||||
rule filter ( predicate + : sequence * )
|
||||
{
|
||||
# trailing underscores hopefully prevent collisions with module
|
||||
# locals in the caller
|
||||
local result__ ;
|
||||
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
for local e in $(sequence)
|
||||
{
|
||||
if [ $(predicate) $(e) ]
|
||||
{
|
||||
result__ += $(e) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $(result__) ;
|
||||
}
|
||||
|
||||
rule less ( a b )
|
||||
{
|
||||
if $(a) < $(b)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
# insertion-sort s using the BinaryPredicate ordered.
|
||||
rule insertion-sort ( s * : ordered * )
|
||||
{
|
||||
ordered ?= sequence.less ;
|
||||
local result__ = $(s[1]) ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
for local x in $(s[2-])
|
||||
{
|
||||
local head tail ;
|
||||
tail = $(result__) ;
|
||||
while $(tail) && [ $(ordered) $(tail[1]) $(x) ]
|
||||
{
|
||||
head += $(tail[1]) ;
|
||||
tail = $(tail[2-]) ;
|
||||
}
|
||||
result__ = $(head) $(x) $(tail) ;
|
||||
}
|
||||
}
|
||||
return $(result__) ;
|
||||
}
|
||||
|
||||
# join the elements of s into one long string. If joint is supplied, it is used as a separator.
|
||||
rule join ( s * : joint ? )
|
||||
{
|
||||
local result ;
|
||||
joint ?= "" ;
|
||||
for local x in $(s)
|
||||
{
|
||||
result = $(result)$(joint)$(x) ;
|
||||
result ?= $(x) ;
|
||||
}
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
local rule __test__ ( )
|
||||
{
|
||||
# use a unique module so we can test the use of local rules.
|
||||
module sequence.__test__
|
||||
{
|
||||
|
||||
local rule is-even ( n )
|
||||
{
|
||||
if $(n) in 0 2 4 6 8
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 4 6 4 2 8
|
||||
: sequence.filter is-even : 1 4 6 3 4 7 2 3 8 ;
|
||||
|
||||
# test that argument binding works
|
||||
local rule is-equal-test ( x y )
|
||||
{
|
||||
if $(x) = $(y)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 3 3 3 : sequence.filter is-equal-test 3 : 1 2 3 4 3 5 3 5 7 ;
|
||||
|
||||
local rule test-greater ( a b )
|
||||
{
|
||||
if $(a) > $(b)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ;
|
||||
assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ;
|
||||
assert.result foo-bar-baz : sequence.join foo bar baz : - ;
|
||||
assert.result substandard : sequence.join sub stan dard ;
|
||||
}
|
||||
}
|
2
new/test.jam
Normal file
2
new/test.jam
Normal file
@ -0,0 +1,2 @@
|
||||
import feature ;
|
||||
import os ;
|
@ -10,10 +10,13 @@
|
||||
|
||||
include recursive.jam ;
|
||||
|
||||
# A prefix for all of the jam code we're going to test
|
||||
# Prefixes for all of the jam code we're going to test
|
||||
local ECHO_ARGS = "include echo_args.jam ; echo_args "
|
||||
;
|
||||
|
||||
local ECHO_VARARGS = "include echo_args.jam ; echo_varargs "
|
||||
;
|
||||
|
||||
# Check that it will find missing arguments
|
||||
Jam-fail $(ECHO_ARGS)";"
|
||||
: "missing argument a"
|
||||
@ -52,3 +55,19 @@ Jam $(ECHO_ARGS)"1 : 2 : 3 4 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4" ;
|
||||
Jam $(ECHO_ARGS)"1 : 2 : 3 4 5 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5" ;
|
||||
|
||||
#
|
||||
# Check varargs
|
||||
#
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 : 8 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 : 8 : 9 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : 9" ;
|
||||
|
||||
|
||||
|
@ -14,6 +14,36 @@ if $(NT)
|
||||
Jam "include test_nt_line_length.jam ;" ;
|
||||
}
|
||||
|
||||
# a little utility for assertions
|
||||
rule identity ( list * )
|
||||
{
|
||||
return $(list) ;
|
||||
}
|
||||
|
||||
#
|
||||
# test rule indirection
|
||||
#
|
||||
rule select ( n list * )
|
||||
{
|
||||
return $(list[$(n)]) ;
|
||||
}
|
||||
|
||||
rule indirect1 ( rule + : args * )
|
||||
{
|
||||
return [ $(rule) $(args) ] ;
|
||||
}
|
||||
|
||||
assert-equal a : indirect1 select 1 : a b c d e ;
|
||||
assert-equal b : indirect1 select 2 : a b c d e ;
|
||||
|
||||
x = reset ;
|
||||
rule reset-x ( new-value )
|
||||
{
|
||||
x = $(new-value) ;
|
||||
}
|
||||
$(x)-x bar ; # invokes reset-x...
|
||||
assert-equal bar : identity $(x) ; # which changes x
|
||||
|
||||
# Check that unmatched subst returns an empty list
|
||||
assert-equal # nothing
|
||||
: SUBST "abc" "d+" x ;
|
||||
@ -92,12 +122,6 @@ assert-equal x y x-y
|
||||
assert-index -3--2 : c d ;
|
||||
}
|
||||
|
||||
# a little utility for assertions
|
||||
rule identity ( list * )
|
||||
{
|
||||
return $(list) ;
|
||||
}
|
||||
|
||||
#
|
||||
# test module primitives
|
||||
#
|
||||
@ -109,7 +133,8 @@ rule identity ( list * )
|
||||
|
||||
rule my_module.not_really ( ) { return something ; }
|
||||
|
||||
IMPORT my_module : : identity : id ;
|
||||
# import the identity rule into my_module as "id"
|
||||
IMPORT : identity : my_module : id ;
|
||||
module my_module
|
||||
{
|
||||
# assert-equal operates in its own module, so call id in here and use
|
||||
@ -120,7 +145,10 @@ rule identity ( list * )
|
||||
module local w y ;
|
||||
module local x2 x3 z = 1 2 3 ;
|
||||
|
||||
module local x3 ; # should reset x3
|
||||
module local x3 ; # should not affect x3
|
||||
assert-equal 1 2 3 : identity $(x3) ;
|
||||
|
||||
module local x3 = ; # should reset x3
|
||||
|
||||
rule shift1 ( )
|
||||
{
|
||||
@ -144,8 +172,27 @@ rule identity ( list * )
|
||||
local rule not_really ( ) { return nothing ; }
|
||||
}
|
||||
|
||||
local expected = shift1 shift2 get ;
|
||||
if ! ( $(expected) in [ RULENAMES my_module ] )
|
||||
|| ! ( [ RULENAMES my_module ] in $(expected) )
|
||||
{
|
||||
EXIT "[ RULENAMES my_module ] =" [ RULENAMES my_module ] "!=" shift1 shift2 get ;
|
||||
}
|
||||
|
||||
|
||||
# show that not_really was actually a local definition
|
||||
assert-equal something : my_module.not_really ;
|
||||
|
||||
if not_really in [ RULENAMES my_module ]
|
||||
{
|
||||
EXIT unexpectedly found local rule "not_really" in "my_module" ;
|
||||
}
|
||||
EXPORT my_module : not_really ;
|
||||
|
||||
if ! ( not_really in [ RULENAMES my_module ] )
|
||||
{
|
||||
EXIT unexpectedly failed to find exported rule "not_really" in "my_module" ;
|
||||
}
|
||||
|
||||
my_module.shift1 ;
|
||||
y = $(y[2-]) ;
|
||||
@ -167,7 +214,9 @@ rule identity ( list * )
|
||||
shift1 nothing ;
|
||||
assert-equal $(x) : identity $(y) ;
|
||||
|
||||
IMPORT : my_module : shift1 shift2 : shifty ;
|
||||
# import my_module.shift1 into the global module as "shifty", and
|
||||
# my_module.shift2 into the global module as "shift2".
|
||||
IMPORT my_module : shift1 shift2 : : shifty shift2 ;
|
||||
|
||||
shifty ;
|
||||
y = $(y[2-]) ;
|
||||
@ -177,8 +226,9 @@ rule identity ( list * )
|
||||
y = $(y[2-]) ;
|
||||
assert-equal $(x) : identity $(y) ;
|
||||
|
||||
|
||||
IMPORT : my_module ;
|
||||
# import everything from my_module into the global module using
|
||||
# the same names.
|
||||
IMPORT my_module : [ RULENAMES my_module ] : : [ RULENAMES my_module ] ;
|
||||
|
||||
shift1 ;
|
||||
y = $(y[2-]) ;
|
||||
|
@ -1,4 +1,16 @@
|
||||
rule echo_args ( a b ? c ? : d + : e * )
|
||||
{
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
|
||||
}
|
||||
|
||||
rule echo_varargs ( a b ? c ? : d + : e * : * )
|
||||
{
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e)
|
||||
": rest= "$(4[1]) $(4[2])
|
||||
": "$(5[1]) $(5[2])
|
||||
": "$(6[1]) $(6[2])
|
||||
": "$(7[1]) $(7[2])
|
||||
": "$(8[1]) $(8[2])
|
||||
": "$(9[1]) $(9[2]) ;
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,10 @@
|
||||
<ul>
|
||||
<li><a href="#module_declaration">Declaration</a>
|
||||
<li><a href="#module_locals">Local Variables</a>
|
||||
<li><a href="#local_rules">Local Rules</a>
|
||||
<li><a href="#RULENAMES_rule">The <tt>RULENAMES</tt> rule</a>
|
||||
<li><a href="#IMPORT_rule">The <tt>IMPORT</tt> rule</a>
|
||||
<li><a href="#EXPORT_rule">The <tt>EXPORT</tt> rule</a>
|
||||
<li><a href="#CALLER_MODULE_rule">The <tt>CALLER_MODULE</tt> rule</a>
|
||||
</ul>
|
||||
|
||||
@ -154,8 +157,10 @@
|
||||
<a href="#debugging_support">Debugging Support</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="#BACKTRACE_rule">The BACKTRACE rule</a>
|
||||
<li><a href="#profiling">Profiling</a>
|
||||
<li><a href="#parse_debugging">Parser Debugging</a>
|
||||
<li><a href="#dependency_graph">Dependency Graph Output</a>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
@ -1312,8 +1317,35 @@ rule foobar { ECHO foobar ; } # a trivial rule
|
||||
$(x)bar ; # invokes foobar
|
||||
|
||||
</pre></blockquote>
|
||||
|
||||
Furthermore, if the first expression expands to more than one
|
||||
list item, everything after the first item becomes part of the
|
||||
first argument. This allows a crude form of argument binding:
|
||||
|
||||
<h4><a name="argument_lists">Argument lists</a></h4>
|
||||
<blockquote><pre>
|
||||
# return the elements of sequence for which predicate returns non-nil
|
||||
rule filter ( sequence * : predicate + )
|
||||
{
|
||||
local result ;
|
||||
for local x in $(sequence)
|
||||
{
|
||||
if [ $(predicate) $(x) ] { result += $(x); }
|
||||
}
|
||||
return $(result);
|
||||
}
|
||||
|
||||
# true iff x == y
|
||||
rule equal ( x y )
|
||||
{
|
||||
if $(x) = $(y) { return true; }
|
||||
}
|
||||
|
||||
# bind 3 to the first argument of equal
|
||||
ECHO [ filter 1 2 3 4 5 4 3 : equal 3 ] ; # prints "3 3"
|
||||
|
||||
</pre></blockquote>
|
||||
|
||||
<h4><a name="argument_lists">Argument lists</a></h4>
|
||||
|
||||
<p>You can now describe the arguments accepted by a rule, and refer to
|
||||
them by name within the rule. For example, the following prints ``I'm
|
||||
@ -1350,7 +1382,11 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<tr>
|
||||
<td><tt>*</tt>
|
||||
|
||||
<td>Bind to zero or more unbound elements of the actual argument.
|
||||
<td>Bind to zero or more unbound elements of the actual
|
||||
argument. When ``<tt>*</tt>'' appears where an argument name
|
||||
is expected, any number of additional arguments are
|
||||
accepted. This feature can be used to implement
|
||||
"varargs" rules.
|
||||
|
||||
<tr>
|
||||
<td><tt>+</tt>
|
||||
@ -1358,7 +1394,7 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<td>Bind to one or more unbound elements of the actual argument.
|
||||
</table>
|
||||
|
||||
<p>The acutal and formal arguments are checked for inconsistencies, which
|
||||
<p>The actual and formal arguments are checked for inconsistencies, which
|
||||
cause Jam to exit with an error code:
|
||||
|
||||
<blockquote>
|
||||
@ -1382,8 +1418,11 @@ report I 2 : sorry : Joe Dave Pete ;
|
||||
<h4><a name="module_support">Module Support</a></h4>
|
||||
|
||||
<p>Boost Jam introduces support for modules, which provide some
|
||||
rudimentary namespace protection for rules and variables. A new keyword,
|
||||
``<tt>module</tt>'' was also introduced.
|
||||
rudimentary namespace protection for rules and variables. A new
|
||||
keyword, ``<tt>module</tt>'' was also introduced. The features
|
||||
described in this section are <i>primitives</i>, meaning that
|
||||
they are meant to provide the operations needed to write Jam
|
||||
rules which provide a more elegant module interface.
|
||||
|
||||
<p>
|
||||
|
||||
@ -1423,13 +1462,6 @@ module your_module
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<p>Note that, like the <a
|
||||
href="#IMPORT_rule"><tt>IMPORT</tt></a> and <a
|
||||
href="#CALLER_MODULE_rule"><tt>CALLER_MODULE</tt></a> rules, module
|
||||
declaration is really a <i>primitive</i>, and is best used through a
|
||||
wrapper interface which implements a system of module files using the
|
||||
built-in <tt>INCLUDE</tt> rule.
|
||||
|
||||
<p>
|
||||
|
||||
<h5><a name="module_locals">Local Variables</a></h5>
|
||||
@ -1493,35 +1525,81 @@ ECHO [ <b>M.get</b> x ] ; # prints "a b c"
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="local_rules">Local Rules</a></h5>
|
||||
|
||||
<blockquote>
|
||||
<tt>local rule</tt> <i>rulename...</i>
|
||||
</blockquote>
|
||||
|
||||
<p>The rule is declared locally to the current module. It is
|
||||
not entered in the global module with qualification, and its
|
||||
name will not appear in the result of
|
||||
<blockquote>
|
||||
<tt>[ RULENAMES</tt> <i>module-name</i><tt> ]</tt>.
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="RULENAMES_rule">The <tt>RULENAMES</tt> Rule</a></h5>
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule RULENAMES ( module ? )
|
||||
</pre>
|
||||
</blockquote>
|
||||
Returns a list of the names of all non-local rules in the
|
||||
given module. If <tt>module</tt> is ommitted, the names of all
|
||||
non-local rules in the global module are returned.
|
||||
|
||||
<h5><a name="IMPORT_rule">The <tt>IMPORT</tt> Rule</a></h5>
|
||||
|
||||
<tt>IMPORT</tt> allows rule name aliasing across modules:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule IMPORT ( target_module ? : source_module ?
|
||||
: rule_names * : target_names * )
|
||||
rule IMPORT ( source_module ? : source_rules *
|
||||
: target_module ? : target_rules * )
|
||||
</pre>
|
||||
</blockquote>
|
||||
Rules are copied from the <tt>source_module</tt> into the
|
||||
<tt>target_module</tt>. If either module name is missing, the global
|
||||
module is used in its place. If any <tt>rule_names</tt> are supplied,
|
||||
they specify which rules from the <tt>source_module</tt> to import;
|
||||
otherwise all rules are imported. The rules are given the names in
|
||||
<tt>target_names</tt>; if not enough <tt>target_names</tt> are supplied,
|
||||
the excess rules are given the same names as they had in
|
||||
<tt>source_module</tt>. For example,
|
||||
The <tt>IMPORT</tt> rule copies rules from the <tt>source_module</tt> into the
|
||||
<tt>target_module</tt> as <tt>local</tt> rules. If either <tt>source_module</tt> or
|
||||
<tt>target_module</tt> is not supplied, it refers to the global
|
||||
module. <tt>source_rules</tt> specifies which rules from the <tt>source_module</tt> to
|
||||
import; <tt>TARGET_RULES</tt> specifies the names to give those rules in
|
||||
<tt>target_module</tt>. If <tt>source_rules</tt> contains a name which doesn't
|
||||
correspond to a rule in <tt>source_module</tt>, or if it contains a different
|
||||
number of items than <tt>target_rules</tt>, an error is issued. For example,
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
IMPORT m1 : m2 ; # imports all rules from m2 into m1
|
||||
IMPORT : m2 : my-rule ; # imports m2.my-rule into the global module
|
||||
IMPORT m1 : m2 : r1 x : r2 y ; # imports m2.r1 as r2 and m2.x as y into m1
|
||||
# import m1.rule1 into m2 as local rule m1-rule1.
|
||||
IMPORT m1 : rule1 : m2 : m1-rule1 ;
|
||||
|
||||
# import all non-local rules from m1 into m2
|
||||
IMPORT m1 : [ RULENAMES m1 ] : m2 : [ RULENAMES m1 ] ;
|
||||
</pre>
|
||||
</blockquote>
|
||||
|
||||
<h5><a name="EXPORT_rule">The <tt>EXPORT</tt> Rule</a></h5>
|
||||
|
||||
<tt>EXPORT</tt> allows rule name aliasing across modules:
|
||||
|
||||
<blockquote>
|
||||
<pre>
|
||||
rule EXPORT ( module ? : rules * )
|
||||
</pre>
|
||||
</blockquote>
|
||||
The <tt>EXPORT</tt> rule marks <tt>rules</tt> from the <tt>source_module</tt> as non-local
|
||||
(and thus exportable). If an element of <tt>rules</tt> does not name a
|
||||
rule in <tt>module</tt>, an error is issued. For example,
|
||||
<blockquote>
|
||||
<pre>
|
||||
module X {
|
||||
local rule r { ECHO X.r ; }
|
||||
}
|
||||
IMPORT X : r : : r ; # error - r is local in X
|
||||
EXPORT X : r ;
|
||||
IMPORT X : r : : r ; # OK.
|
||||
</pre>
|
||||
</blockquote>
|
||||
Like the <a href="#module_declaration">module declaration syntax</a> and
|
||||
the <a href="#CALLER_MODULE_rule"><tt>CALLER_MODULE</tt></a> rule, this
|
||||
rule is a <i>primitive</i>, and is probably best wrapped in a Jam rule.
|
||||
|
||||
<h5><a name="CALLER_MODULE_rule">The <tt>CALLER_MODULE</tt> Rule</a></h5>
|
||||
|
||||
@ -1555,10 +1633,6 @@ callers = [ X.get-caller ] [ Y.call-X ] [ X.call-Y ] ;
|
||||
ECHO {$(callers)} ;
|
||||
|
||||
</pre>
|
||||
Like the <a href="#module_declaration">module declaration syntax</a> and
|
||||
the <a href="#IMPORT_rule"><tt>IMPORT</tt></a> rule, this rule is a
|
||||
<i>primitive</i>, and is probably best wrapped in a Jam rule.
|
||||
|
||||
</blockquote>
|
||||
|
||||
</blockquote>
|
||||
@ -1717,6 +1791,17 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
|
||||
|
||||
<h4><a name="debugging_support">Debugging Support</a></h4>
|
||||
|
||||
<h5><a name="BACKTRACE_rule">The BACKTRACE rule</a></h5>
|
||||
|
||||
<blockquote><pre>
|
||||
rule BACKTRACE ( )
|
||||
</pre></blockquote>
|
||||
|
||||
Returns a list of quadruples: <i>filename line module
|
||||
rulename</i>..., describing each shallower level of the call
|
||||
stack. This rule can be used to generate useful diagnostic
|
||||
messages from Jam rules.
|
||||
|
||||
<p>The <tt>-d</tt> command-line option admits new arguments:
|
||||
|
||||
<ul>
|
||||
@ -1728,6 +1813,13 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
|
||||
<li> <tt>-d+11</tt> - enables <a name="parse_debugging"><b>parser
|
||||
debugging</b></a>, if Jam has been compiled with the "--debug"
|
||||
option to the parser generator named by $(YACC).
|
||||
|
||||
<li> <tt>-d+12</tt> - enables <a
|
||||
name="dependency_graph"><b>dependency graph output
|
||||
</b></a>. This feature was ``stolen'' from a version of Jam
|
||||
modified by <a href="mailto:cmcpheeters@aw.sgi.com">Craig
|
||||
McPheeters</a>.
|
||||
|
||||
</ul>
|
||||
|
||||
</blockquote>
|
||||
|
383
v2/build/feature.jam
Normal file
383
v2/build/feature.jam
Normal file
@ -0,0 +1,383 @@
|
||||
# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import errors : error lol->list ;
|
||||
import property ;
|
||||
import sequence ;
|
||||
import regex ;
|
||||
import set ;
|
||||
|
||||
module local all-attributes =
|
||||
|
||||
implicit # features whose values alone identify the
|
||||
# feature. For example, a user is not required to
|
||||
# write "<toolset>gcc", but can simply write
|
||||
# "gcc". Implicit feature names also don't appear in
|
||||
# variant paths, although the values do. Thus:
|
||||
# bin/gcc/... as opposed to bin/toolset-gcc/.... There
|
||||
# should typically be only a few such features, to
|
||||
# avoid possible name clashes.
|
||||
|
||||
executed # the feature corresponds to the name of a module
|
||||
# containing an execute rule, used to actually prepare
|
||||
# the build. For example, the toolset feature would be
|
||||
# executed.
|
||||
|
||||
|
||||
composite # features which actually correspond to groups of
|
||||
# properties. For example, a build variant is a
|
||||
# composite feature. When generating targets from a
|
||||
# set of build properties, composite features are
|
||||
# recursively expanded and /added/ to the build
|
||||
# property set, so rules can find them if
|
||||
# neccessary. Non-composite non-free features override
|
||||
# components of composite features in a build property
|
||||
# set.
|
||||
|
||||
|
||||
optional # An optional feature is allowed to have no value at
|
||||
# all in a particular build. Normal non-free features
|
||||
# are always given the first of their values if no
|
||||
# value is otherwise specified.
|
||||
|
||||
|
||||
symmetric # A symmetric feature has no default value, and is
|
||||
# therefore not automatically included in all
|
||||
# variants. A symmetric feature, when relevant to the
|
||||
# toolset, always generates a corresponding subvariant
|
||||
# directory.
|
||||
|
||||
free # as described in previous documentation
|
||||
|
||||
path # the (free) feature's value describes a path which
|
||||
# might be given relative to the directory of the
|
||||
# Jamfile.
|
||||
|
||||
dependency # the value of the (free) feature specifies a
|
||||
# dependency of the target.
|
||||
|
||||
propagated # when used in a build request, the (free) feature is
|
||||
# propagated to top-level targets which are
|
||||
# dependencies of the requested build. Propagated
|
||||
# features would be most useful for settings such as
|
||||
# warning levels, output message style, etc., which
|
||||
# don't affect the built products at all.
|
||||
;
|
||||
|
||||
module local all-features ;
|
||||
module local all-implicit-values ;
|
||||
|
||||
# declare a new feature with the given name, values, and attributes.
|
||||
rule feature ( name : values * : attributes * )
|
||||
{
|
||||
local error ;
|
||||
|
||||
# if there are any unknown attributes...
|
||||
if ! ( $(attributes) in $(all-attributes) )
|
||||
{
|
||||
error = unknown attributes:
|
||||
[ set.difference $(attributes) : $(all-attributes) ] ;
|
||||
}
|
||||
else if $(name) in $(all-features)
|
||||
{
|
||||
error = feature already defined: ;
|
||||
}
|
||||
else if implicit in $(attributes)
|
||||
{
|
||||
if free in $(attributes)
|
||||
{
|
||||
error = free features cannot also be implicit ;
|
||||
}
|
||||
}
|
||||
|
||||
if $(error)
|
||||
{
|
||||
error $(error)
|
||||
: "in" feature declaration:
|
||||
: feature [ errors.lol->list $(1) : $(2) : $(3) ] ;
|
||||
}
|
||||
|
||||
module local $(name).values ;
|
||||
module local $(name).attributes = $(attributes) ;
|
||||
module local $(name).subfeatures = ;
|
||||
|
||||
all-features += $(name) ;
|
||||
extend $(name) : $(values) ;
|
||||
}
|
||||
|
||||
# returns true iff all elements of names are valid features.
|
||||
rule valid ( name + )
|
||||
{
|
||||
if $(names) in $(all-features)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
# return the attibutes of the given feature
|
||||
rule attributes ( feature )
|
||||
{
|
||||
return $($(feature).attributes) ;
|
||||
}
|
||||
|
||||
# return the values of the given feature
|
||||
rule values ( feature )
|
||||
{
|
||||
return $($(feature).values) ;
|
||||
}
|
||||
|
||||
rule implied-feature ( implicit-value )
|
||||
{
|
||||
local feature = $($(implicit-value).implicit-feature) ;
|
||||
if ! $(feature)
|
||||
{
|
||||
error \"$(implicit-value)\" is not a value of an implicit feature ;
|
||||
}
|
||||
return $(feature) ;
|
||||
}
|
||||
|
||||
local rule find-implied-subfeature ( feature subvalue : value-string ? )
|
||||
{
|
||||
local v
|
||||
= subfeature($(feature),$(subvalue))
|
||||
subfeature($(feature),$(value-string),$(subvalue)) ;
|
||||
|
||||
# declaring these module local here prevents us from picking up
|
||||
# enclosing definitions.
|
||||
module local $(v) ;
|
||||
|
||||
local subfeature = $($(v)) ;
|
||||
return $(subfeature[1]) ;
|
||||
}
|
||||
|
||||
rule implied-subfeature ( feature subvalue : value-string ? )
|
||||
{
|
||||
local subfeature = [ find-implied-subfeature $(feature) $(subvalue)
|
||||
: $(value-string) ] ;
|
||||
|
||||
if ! $(subfeature)
|
||||
{
|
||||
error \"$(subvalue)\" is not a known subfeature value of
|
||||
feature \"$(feature)\" ;
|
||||
}
|
||||
|
||||
return $(subfeature) ;
|
||||
}
|
||||
|
||||
# generate an error if the feature is unknown
|
||||
local rule validate-feature ( feature )
|
||||
{
|
||||
if ! $(feature) in $(all-features)
|
||||
{
|
||||
error unknown feature \"$(feature)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
# expand-subfeatures toolset : gcc-2.95.2-linux-x86 -> <toolset>gcc <toolset-version>2.95.2 <toolset-os>linux <toolset-cpu>x86
|
||||
# equivalent to:
|
||||
# expand-subfeatures : gcc-2.95.2-linux-x86
|
||||
local rule expand-subfeatures ( feature ? : value )
|
||||
{
|
||||
if $(feature)
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
}
|
||||
|
||||
local components = [ regex.split $(value) "-" ] ;
|
||||
if ! $(feature)
|
||||
{
|
||||
feature = [ implied-feature $(components[1]) ] ;
|
||||
}
|
||||
|
||||
# get the top-level feature's value
|
||||
local value = $(components[1]:G=) ;
|
||||
|
||||
local result = $(components[1]:G=$(feature)) ;
|
||||
for local subvalue in $(components[2-])
|
||||
{
|
||||
local subfeature = [ implied-subfeature $(feature) $(subvalue) : $(value) ] ;
|
||||
result += $(subvalue:G=$(feature)-$(subfeature)) ;
|
||||
}
|
||||
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
local rule extend-feature ( feature : values * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if implicit in $(attributes)
|
||||
{
|
||||
for local v in $(values)
|
||||
{
|
||||
module local $(v).implicit-feature ;
|
||||
if $($(v).implicit-feature)
|
||||
{
|
||||
error $(v) is already associated with the \"$($(v).implicit-feature)\" feature ;
|
||||
}
|
||||
$(v).implicit-feature = $(feature) ;
|
||||
}
|
||||
|
||||
all-implicit-values += $(values) ;
|
||||
}
|
||||
$(feature).values += $(values) ;
|
||||
}
|
||||
|
||||
local rule validate-value-string ( feature value-string )
|
||||
{
|
||||
local values = $(value-string) ;
|
||||
if $($(feature).subfeatures)
|
||||
{
|
||||
values = [ regex.split $(value-string) - ] ;
|
||||
}
|
||||
|
||||
if ! ( $(values[1]) in $($(feature).values) )
|
||||
{
|
||||
return \"$(values[1])\" is not a known value of feature \"$(feature)\" ;
|
||||
}
|
||||
|
||||
if $(values[2])
|
||||
{
|
||||
# this will validate any subfeature values in value-string
|
||||
implied-subfeature $(feature) [ sequence.join $(values[2-]) - ]
|
||||
: $(values[1]) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# extend-subfeature toolset gcc-2.95.2 : target-platform : mingw ;
|
||||
# extend-subfeature toolset : target-platform : mingw ;
|
||||
local rule extend-subfeature ( feature value-string ? : subfeature : subvalues * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if $(value-string)
|
||||
{
|
||||
validate-value-string $(feature) $(value-string) ;
|
||||
}
|
||||
|
||||
for local subvalue in $(subvalues)
|
||||
{
|
||||
local v
|
||||
= subfeature($(feature),$(value-string),$(subvalue))
|
||||
subfeature($(feature),$(subvalue)) ;
|
||||
module local $(v[1]) = $(subfeature) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule extend ( feature-or-property subfeature ? : values * )
|
||||
{
|
||||
local feature value-string ;
|
||||
if $(feature-or-property:G)
|
||||
{
|
||||
feature = [ property.get-feature $(feature-or-property) ] ;
|
||||
value-string = $(feature-or-property:G=) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
feature = $(feature-or-property) ;
|
||||
}
|
||||
|
||||
if $(subfeature)
|
||||
{
|
||||
extend-subfeature $(feature) $(value-string)
|
||||
: $(subfeature) : $(values) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if $(value-string)
|
||||
{
|
||||
error can only be specify a property as the first argument
|
||||
when extending a subfeature
|
||||
: usage:
|
||||
: " extend" feature ":" values...
|
||||
: " | extend" <feature>value-string subfeature ":" values...
|
||||
;
|
||||
}
|
||||
|
||||
extend-feature $(feature) : $(values) ;
|
||||
}
|
||||
}
|
||||
|
||||
# subfeature
|
||||
#
|
||||
# subfeature toolset gcc-2.95.2 target-platform : aix linux mac cygwin
|
||||
#
|
||||
rule subfeature ( feature value-string ? : subfeature : subvalues * )
|
||||
{
|
||||
validate-feature $(feature) ;
|
||||
if $(subfeature) in $($(feature).subfeatures)
|
||||
{
|
||||
error \"$(subfeature)\" already declared as a subfeature of \"$(feature)\" ;
|
||||
}
|
||||
$(feature).subfeatures += $(subfeature) ;
|
||||
extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ;
|
||||
}
|
||||
|
||||
# tests of module features
|
||||
local rule __test__ ( )
|
||||
{
|
||||
import errors : try catch ;
|
||||
import assert ;
|
||||
feature __test__toolset : gcc : implicit executed ;
|
||||
feature __test__define : : free ;
|
||||
|
||||
extend-feature __test__toolset : msvc metrowerks ;
|
||||
subfeature __test__toolset gcc : version : 2.95.2 2.95.3 2.95.4
|
||||
3.0 3.0.1 3.0.2 ;
|
||||
|
||||
assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 :
|
||||
expand-subfeatures __test__toolset : gcc-3.0.1 ;
|
||||
|
||||
assert.result <__test__toolset>gcc <__test__toolset-version>3.0.1 :
|
||||
expand-subfeatures : gcc-3.0.1 ;
|
||||
|
||||
feature __test__dummy : dummy1 dummy2 ;
|
||||
subfeature __test__dummy : subdummy : x y z ;
|
||||
|
||||
# test error checking
|
||||
try ;
|
||||
{
|
||||
validate-feature __test__foobar ;
|
||||
}
|
||||
catch unknown feature ;
|
||||
|
||||
try ;
|
||||
{
|
||||
feature __test__foobar : : baz ;
|
||||
}
|
||||
catch unknown attributes: baz ;
|
||||
|
||||
feature __test__feature1 ;
|
||||
try ;
|
||||
{
|
||||
feature __test__feature1 ;
|
||||
}
|
||||
catch feature already defined: ;
|
||||
|
||||
try ;
|
||||
{
|
||||
feature __test__feature2 : : free implicit ;
|
||||
}
|
||||
catch free features cannot also be implicit ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-feature lackluster ;
|
||||
}
|
||||
catch \"lackluster\" is not a value of an implicit feature ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-subfeature __test__toolset 3.0.1 ;
|
||||
}
|
||||
catch \"3.0.1\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
|
||||
try ;
|
||||
{
|
||||
implied-subfeature __test__toolset not-a-version : gcc ;
|
||||
}
|
||||
catch \"not-a-version\" is not a known subfeature value of
|
||||
feature \"__test__toolset\" ;
|
||||
}
|
33
v2/build/property.jam
Normal file
33
v2/build/property.jam
Normal file
@ -0,0 +1,33 @@
|
||||
import errors ;
|
||||
|
||||
# Given a property name, return the corresponding feature name
|
||||
rule get-feature ( property )
|
||||
{
|
||||
return [ SUBST $(property:G) ^<(.*)> $1 ] ;
|
||||
}
|
||||
|
||||
rule is-valid ( property )
|
||||
{
|
||||
import feature ;
|
||||
if ! $(property:G)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
else
|
||||
{
|
||||
local f = [ get-feature $(property) ] ;
|
||||
local value = $(property:G=) ;
|
||||
|
||||
if [ features.is-valid $(f) ] &&
|
||||
( free in [ features.attributes $(f) ]
|
||||
|| $(value) in [ features.values $(f) ] )
|
||||
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ;
|
||||
}
|
||||
}
|
||||
}
|
9
v2/build/readme.txt
Normal file
9
v2/build/readme.txt
Normal file
@ -0,0 +1,9 @@
|
||||
Development code for new build system. To test, execute:
|
||||
|
||||
jam -sBOOST_BUILD_PATH=.:$BOOST_ROOT -sBOOST_BUILD_TEST=1 -sJAMFILE=test.jam
|
||||
|
||||
on unix, or
|
||||
|
||||
jam -sBOOST_BUILD_PATH=.;%BOOST_ROOT% -sBOOST_BUILD_TEST=1 -sJAMFILE=test.jam
|
||||
|
||||
on windows
|
@ -7,7 +7,7 @@
|
||||
SEARCH on <module@>modules.jam = $(BOOST_BUILD_PATH) ;
|
||||
module modules { include <module@>modules.jam ; }
|
||||
# Bring the import rule into the global module
|
||||
IMPORT : modules : import ;
|
||||
IMPORT modules : import : : import ;
|
||||
import modules ; # The modules module can tolerate being included twice
|
||||
|
||||
import build-system ;
|
||||
|
147
v2/errors.jam
147
v2/errors.jam
@ -3,44 +3,153 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
# A utility rule used to report the including module when there is an error, so
|
||||
# that editors may find it.
|
||||
rule report-module ( prefix ? : suffix ? : frames ? )
|
||||
# Print a stack backtrace leading to this rule's caller. Each
|
||||
# argument represents a line of output to be printed after the first
|
||||
# line of the backtrace.
|
||||
rule backtrace ( skip-frames messages * : * )
|
||||
{
|
||||
frames ?= 1 ;
|
||||
# We report some large line number so that emacs, etc., will at least locate the file.
|
||||
ECHO $(prefix) [ modules.binding [ CALLER_MODULE $(frames) ] ] ":" line 99999 $(suffix) ;
|
||||
}
|
||||
|
||||
rule backtrace
|
||||
{
|
||||
local digits = 1 2 3 4 5 6 7 8 9 ;
|
||||
local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ;
|
||||
local drop-elements = $(frame-skips[$(skip-frames)]) ;
|
||||
if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 )
|
||||
{
|
||||
ECHO warning: backtrace doesn't support skipping
|
||||
$(skip-frames) frames; using 1 instead. ;
|
||||
drop-elements = 5 ;
|
||||
}
|
||||
|
||||
# get the whole backtrace, then drop the initial quadruples
|
||||
# corresponding to the frames that must be skipped.
|
||||
local bt = [ BACKTRACE ] ;
|
||||
bt = $(bt[5-]) ;
|
||||
bt = $(bt[$(drop-elements)-]) ;
|
||||
|
||||
local args = messages 2 3 4 5 6 7 8 9 ;
|
||||
while $(bt)
|
||||
{
|
||||
ECHO $(bt[1]):$(bt[2]): "in" $(bt[4]) ;
|
||||
|
||||
for local n in $(digits)
|
||||
# the first time through, print each argument on a separate
|
||||
# line
|
||||
for local n in $(args)
|
||||
{
|
||||
if $($(n))-is-not-empty
|
||||
{
|
||||
ECHO $($(n)) ;
|
||||
}
|
||||
}
|
||||
digits = ;
|
||||
|
||||
args = ; # kill args so that this never happens again
|
||||
|
||||
# Move on to the next quadruple
|
||||
bt = $(bt[5-]) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule error
|
||||
module local args = messages 2 3 4 5 6 7 8 9 ;
|
||||
module local disabled last-$(args) ;
|
||||
|
||||
# try-catch --
|
||||
#
|
||||
# This is not really an exception-handling mechanism, but it does
|
||||
# allow us to perform some error-checking on our
|
||||
# error-checking. Errors are suppressed after a try, and the first one
|
||||
# is recorded. Use catch to check that the error message matched
|
||||
# expectations.
|
||||
|
||||
# begin looking for error messages
|
||||
rule try ( )
|
||||
{
|
||||
backtrace error: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
EXIT ;
|
||||
disabled += true ;
|
||||
last-$(args) = ;
|
||||
}
|
||||
|
||||
# stop looking for error messages; generate an error if an argument of
|
||||
# messages is not found in the corresponding argument in the error call.
|
||||
rule catch ( messages * : * )
|
||||
{
|
||||
import sequence ;
|
||||
disabled = $(disabled[2-]) ; # pop the stack
|
||||
|
||||
for local n in $(args)
|
||||
{
|
||||
if ! $($(n)) in $(last-$(n))
|
||||
{
|
||||
local v = [ sequence.join $($(n)) : " " ] ;
|
||||
v ?= "" ;
|
||||
local joined = [ sequence.join $(last-$(n)) : " " ] ;
|
||||
|
||||
last-$(args) = ;
|
||||
error-skip-frames 3 expected \"$(v)\" in argument $(n) of error
|
||||
: got \"$(joined)\" instead ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule error-skip-frames ( skip-frames messages * : * )
|
||||
{
|
||||
if ! $(disabled)
|
||||
{
|
||||
backtrace $(skip-frames) error: $(messages) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
EXIT ;
|
||||
}
|
||||
else if ! $(last-$(args))
|
||||
{
|
||||
for local n in $(args)
|
||||
{
|
||||
last-$(n) = $($(n)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Print an error message with a stack backtrace and exit.
|
||||
rule error ( messages * : * )
|
||||
{
|
||||
error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
}
|
||||
|
||||
# Print a warning message with a stack backtrace and exit.
|
||||
rule warning
|
||||
{
|
||||
backtrace warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
backtrace 2 warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
|
||||
}
|
||||
|
||||
# convert an arbitrary argument list into a list with ":" separators
|
||||
# and quoted elements representing the same information. This is
|
||||
# mostly useful for formatting descriptions of the arguments with
|
||||
# which a rule was called when reporting an error.
|
||||
rule lol->list ( * )
|
||||
{
|
||||
local result ;
|
||||
local remaining = 1 2 3 4 5 6 7 8 9 ;
|
||||
while $($(remaining))
|
||||
{
|
||||
local n = $(remaining[1]) ;
|
||||
remaining = $(remaining[2-]) ;
|
||||
|
||||
if $(n) != 1
|
||||
{
|
||||
result += ":" ;
|
||||
}
|
||||
result += \"$(n)\" ;
|
||||
}
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
rule __test__ ( )
|
||||
{
|
||||
# show that we can correctly catch an expected error
|
||||
try ;
|
||||
{
|
||||
error an error occurred : somewhere ;
|
||||
}
|
||||
catch an error occurred : somewhere ;
|
||||
|
||||
# show that unexpected errors generate real errors
|
||||
try ;
|
||||
{
|
||||
try ;
|
||||
{
|
||||
error an error occurred : somewhere ;
|
||||
}
|
||||
catch an error occurred : nowhere ;
|
||||
}
|
||||
catch expected \"nowhere\" in argument 2 ;
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
# Keep a record so that no module is included multiple times
|
||||
module local loaded-modules ;
|
||||
module local loading-modules ;
|
||||
module local untested ;
|
||||
|
||||
# meant to be invoked from import when no __test__ rule is defined in a given
|
||||
# module
|
||||
@ -19,24 +21,22 @@ rule binding ( module )
|
||||
return $($(module).__binding__) ;
|
||||
}
|
||||
|
||||
# load the indicated module. Any members of rules-opt will be available without
|
||||
# qualification in the caller's module. Any members of rename-opt will be taken
|
||||
# as the names of the rules in the caller's module, in place of the names they
|
||||
# have in the imported module. If rules-opt = '*', all rules from the indicated
|
||||
# module are imported into the caller's module.
|
||||
rule import ( module-name : rules-opt * : rename-opt * )
|
||||
# load the indicated module if it is not already loaded.
|
||||
rule load ( module-name )
|
||||
{
|
||||
# First see if the module needs to be loaded
|
||||
if ! ( $(module-name) in $(loaded-modules) )
|
||||
{
|
||||
loaded-modules += $(module-name) ;
|
||||
loading-modules += $(module-name) ;
|
||||
local suppress-test = $(untested[1]) ; # suppress tests until all recursive loads are complete.
|
||||
untested += $(module-name) ; # add the module to the stack of untested modules
|
||||
|
||||
module $(module-name)
|
||||
{
|
||||
module local __name__ = $(module-name) ;
|
||||
|
||||
# Prepare a default behavior, in case no __test__ is defined.
|
||||
IMPORT $(module-name) : modules : no_test_defined : __test__ ;
|
||||
IMPORT modules : no_test_defined : $(module-name) : __test__ ;
|
||||
|
||||
# Add some grist so that the module will have a unique target name
|
||||
local module-target = $(module-name:G=module@:S=.jam) ;
|
||||
@ -45,34 +45,61 @@ rule import ( module-name : rules-opt * : rename-opt * )
|
||||
BINDRULE on $(module-target) = modules.record-binding ;
|
||||
include $(module-name:G=module@:S=.jam) ;
|
||||
|
||||
# run the module's test, if any.
|
||||
if nonempty$(BOOST_BUILD_TEST)
|
||||
}
|
||||
loading-modules = $(loading-modules[1--2]) ;
|
||||
|
||||
if ! $(suppress-test) && $(BOOST_BUILD_TEST)-is-nonempty
|
||||
{
|
||||
# run any pending tests
|
||||
for local m in $(untested)
|
||||
{
|
||||
ECHO testing module $(module-name)... ;
|
||||
local ignored = [ __test__ ] ;
|
||||
ECHO testing module $(m)... ;
|
||||
module $(m)
|
||||
{
|
||||
__test__ ;
|
||||
}
|
||||
}
|
||||
untested = ;
|
||||
}
|
||||
}
|
||||
|
||||
# If any rules are to be imported, do so now.
|
||||
if $(rules-opt)
|
||||
else if $(module-name) in $(loading-modules)
|
||||
{
|
||||
if $(rules-opt) = *
|
||||
{
|
||||
rules-opt = ;
|
||||
}
|
||||
IMPORT [ CALLER_MODULE ]
|
||||
: $(module-name) : $(rules-opt) : $(rename-opt) ;
|
||||
ECHO loading \"$(module-name)\" ;
|
||||
ECHO circular module loading dependency: ;
|
||||
EXIT $(loading-modules) $(module-name) ;
|
||||
}
|
||||
}
|
||||
|
||||
# This helper is used by import (above) to record the binding (path) of
|
||||
# This helper is used by load (above) to record the binding (path) of
|
||||
# each loaded module.
|
||||
rule record-binding ( module-target : binding )
|
||||
{
|
||||
module local $(module-target:G=:S=).__binding__ = $(binding) ;
|
||||
}
|
||||
|
||||
# load the indicated module and import rule names into the current
|
||||
# module. Any members of rules-opt will be available without
|
||||
# qualification in the caller's module. Any members of rename-opt will
|
||||
# be taken as the names of the rules in the caller's module, in place
|
||||
# of the names they have in the imported module. If rules-opt = '*',
|
||||
# all rules from the indicated module are imported into the caller's
|
||||
# module. If rename-opt is supplied, it must have the same number of
|
||||
# elements as rules-opt.
|
||||
rule import ( module-name : rules-opt * : rename-opt * )
|
||||
{
|
||||
load $(module-name) ;
|
||||
|
||||
local source-names = $(rules-opt) ;
|
||||
if $(rules-opt) = *
|
||||
{
|
||||
source-names = [ RULENAMES module-name ] ;
|
||||
}
|
||||
|
||||
local target-names = $(rename-opt) ;
|
||||
target-names ?= $(source-names) ;
|
||||
IMPORT $(module-name) : $(source-names) : [ CALLER_MODULE ] : $(target-names) ;
|
||||
}
|
||||
|
||||
# Returns the module-local value of a variable.
|
||||
rule peek ( module-name variable )
|
||||
{
|
||||
@ -82,9 +109,10 @@ rule peek ( module-name variable )
|
||||
}
|
||||
}
|
||||
|
||||
rule __test__ ( )
|
||||
local rule __test__ ( )
|
||||
{
|
||||
import assert ;
|
||||
|
||||
module modules.__test__
|
||||
{
|
||||
module local foo = bar ;
|
||||
|
@ -3,7 +3,28 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
if $(NT)
|
||||
{
|
||||
module local slash = \\ ;
|
||||
}
|
||||
else
|
||||
{
|
||||
module local slash = / ;
|
||||
}
|
||||
|
||||
rule split ( path )
|
||||
{
|
||||
|
||||
return [ SUBST $(<[1]) "^([/$(SLASH)]+).*" $1 ] # rooting slash(es), if any
|
||||
[ split $(<) "[/$(SLASH)]" ] # the rest.
|
||||
;
|
||||
}
|
||||
|
||||
rule join ( elements * )
|
||||
{
|
||||
local slashes = $(slash) / ;
|
||||
local result prev = $(elements[1]) ;
|
||||
for local e in $(elements[2-])
|
||||
{`
|
||||
if ! ( $(prev) in $(slashes) ) &&
|
||||
}
|
||||
}
|
@ -10,10 +10,13 @@
|
||||
|
||||
include recursive.jam ;
|
||||
|
||||
# A prefix for all of the jam code we're going to test
|
||||
# Prefixes for all of the jam code we're going to test
|
||||
local ECHO_ARGS = "include echo_args.jam ; echo_args "
|
||||
;
|
||||
|
||||
local ECHO_VARARGS = "include echo_args.jam ; echo_varargs "
|
||||
;
|
||||
|
||||
# Check that it will find missing arguments
|
||||
Jam-fail $(ECHO_ARGS)";"
|
||||
: "missing argument a"
|
||||
@ -52,3 +55,19 @@ Jam $(ECHO_ARGS)"1 : 2 : 3 4 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4" ;
|
||||
Jam $(ECHO_ARGS)"1 : 2 : 3 4 5 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5" ;
|
||||
|
||||
#
|
||||
# Check varargs
|
||||
#
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 : 8 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8" ;
|
||||
Jam $(ECHO_VARARGS)"1 : 2 : 3 4 5 : 6 7 : 8 : 9 ;"
|
||||
: "a= 1 b= c= : d= 2 : e= 3 4 5 : rest= 6 7 : 8 : 9" ;
|
||||
|
||||
|
||||
|
@ -14,6 +14,36 @@ if $(NT)
|
||||
Jam "include test_nt_line_length.jam ;" ;
|
||||
}
|
||||
|
||||
# a little utility for assertions
|
||||
rule identity ( list * )
|
||||
{
|
||||
return $(list) ;
|
||||
}
|
||||
|
||||
#
|
||||
# test rule indirection
|
||||
#
|
||||
rule select ( n list * )
|
||||
{
|
||||
return $(list[$(n)]) ;
|
||||
}
|
||||
|
||||
rule indirect1 ( rule + : args * )
|
||||
{
|
||||
return [ $(rule) $(args) ] ;
|
||||
}
|
||||
|
||||
assert-equal a : indirect1 select 1 : a b c d e ;
|
||||
assert-equal b : indirect1 select 2 : a b c d e ;
|
||||
|
||||
x = reset ;
|
||||
rule reset-x ( new-value )
|
||||
{
|
||||
x = $(new-value) ;
|
||||
}
|
||||
$(x)-x bar ; # invokes reset-x...
|
||||
assert-equal bar : identity $(x) ; # which changes x
|
||||
|
||||
# Check that unmatched subst returns an empty list
|
||||
assert-equal # nothing
|
||||
: SUBST "abc" "d+" x ;
|
||||
@ -92,12 +122,6 @@ assert-equal x y x-y
|
||||
assert-index -3--2 : c d ;
|
||||
}
|
||||
|
||||
# a little utility for assertions
|
||||
rule identity ( list * )
|
||||
{
|
||||
return $(list) ;
|
||||
}
|
||||
|
||||
#
|
||||
# test module primitives
|
||||
#
|
||||
@ -109,7 +133,8 @@ rule identity ( list * )
|
||||
|
||||
rule my_module.not_really ( ) { return something ; }
|
||||
|
||||
IMPORT my_module : : identity : id ;
|
||||
# import the identity rule into my_module as "id"
|
||||
IMPORT : identity : my_module : id ;
|
||||
module my_module
|
||||
{
|
||||
# assert-equal operates in its own module, so call id in here and use
|
||||
@ -120,7 +145,10 @@ rule identity ( list * )
|
||||
module local w y ;
|
||||
module local x2 x3 z = 1 2 3 ;
|
||||
|
||||
module local x3 ; # should reset x3
|
||||
module local x3 ; # should not affect x3
|
||||
assert-equal 1 2 3 : identity $(x3) ;
|
||||
|
||||
module local x3 = ; # should reset x3
|
||||
|
||||
rule shift1 ( )
|
||||
{
|
||||
@ -144,8 +172,27 @@ rule identity ( list * )
|
||||
local rule not_really ( ) { return nothing ; }
|
||||
}
|
||||
|
||||
local expected = shift1 shift2 get ;
|
||||
if ! ( $(expected) in [ RULENAMES my_module ] )
|
||||
|| ! ( [ RULENAMES my_module ] in $(expected) )
|
||||
{
|
||||
EXIT "[ RULENAMES my_module ] =" [ RULENAMES my_module ] "!=" shift1 shift2 get ;
|
||||
}
|
||||
|
||||
|
||||
# show that not_really was actually a local definition
|
||||
assert-equal something : my_module.not_really ;
|
||||
|
||||
if not_really in [ RULENAMES my_module ]
|
||||
{
|
||||
EXIT unexpectedly found local rule "not_really" in "my_module" ;
|
||||
}
|
||||
EXPORT my_module : not_really ;
|
||||
|
||||
if ! ( not_really in [ RULENAMES my_module ] )
|
||||
{
|
||||
EXIT unexpectedly failed to find exported rule "not_really" in "my_module" ;
|
||||
}
|
||||
|
||||
my_module.shift1 ;
|
||||
y = $(y[2-]) ;
|
||||
@ -167,7 +214,9 @@ rule identity ( list * )
|
||||
shift1 nothing ;
|
||||
assert-equal $(x) : identity $(y) ;
|
||||
|
||||
IMPORT : my_module : shift1 shift2 : shifty ;
|
||||
# import my_module.shift1 into the global module as "shifty", and
|
||||
# my_module.shift2 into the global module as "shift2".
|
||||
IMPORT my_module : shift1 shift2 : : shifty shift2 ;
|
||||
|
||||
shifty ;
|
||||
y = $(y[2-]) ;
|
||||
@ -177,8 +226,9 @@ rule identity ( list * )
|
||||
y = $(y[2-]) ;
|
||||
assert-equal $(x) : identity $(y) ;
|
||||
|
||||
|
||||
IMPORT : my_module ;
|
||||
# import everything from my_module into the global module using
|
||||
# the same names.
|
||||
IMPORT my_module : [ RULENAMES my_module ] : : [ RULENAMES my_module ] ;
|
||||
|
||||
shift1 ;
|
||||
y = $(y[2-]) ;
|
||||
|
@ -1,4 +1,16 @@
|
||||
rule echo_args ( a b ? c ? : d + : e * )
|
||||
{
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e) ;
|
||||
}
|
||||
|
||||
rule echo_varargs ( a b ? c ? : d + : e * : * )
|
||||
{
|
||||
ECHO a= $(a) b= $(b) c= $(c) ":" d= $(d) ":" e= $(e)
|
||||
": rest= "$(4[1]) $(4[2])
|
||||
": "$(5[1]) $(5[2])
|
||||
": "$(6[1]) $(6[2])
|
||||
": "$(7[1]) $(7[2])
|
||||
": "$(8[1]) $(8[2])
|
||||
": "$(9[1]) $(9[2]) ;
|
||||
}
|
||||
|
||||
|
2
v2/test/test.jam
Normal file
2
v2/test/test.jam
Normal file
@ -0,0 +1,2 @@
|
||||
import feature ;
|
||||
import os ;
|
@ -3,53 +3,64 @@
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import errors : error ;
|
||||
import errors : error-skip-frames lol->list ;
|
||||
|
||||
# assert the equality of A and B
|
||||
rule equal ( a * : b * )
|
||||
{
|
||||
if $(a) != $(b)
|
||||
{
|
||||
error assertion failure: \"$(a)\" "!=" \"$(b)\" ;
|
||||
error-skip-frames 3 assertion failure: \"$(a)\" "!=" \"$(b)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
rule result ( expected * : rule-name args * )
|
||||
# assert that EXPECTED is the result of calling RULE-NAME with the
|
||||
# given arguments
|
||||
rule result ( expected * : rule-name args * : * )
|
||||
{
|
||||
|
||||
local result__ ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
result = [ $(rule-name) $(args) ] ;
|
||||
result__ = [
|
||||
$(rule-name) $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ] ;
|
||||
}
|
||||
|
||||
if $(result) != $(expected)
|
||||
if $(result__) != $(expected)
|
||||
{
|
||||
error assertion failure: "[" $(rule-name) \"$(args)\" "]"
|
||||
error-skip-frames 3 assertion failure: "[" $(rule-name)
|
||||
[ lol->list $(args) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ]
|
||||
"]"
|
||||
: expected: \"$(expected)\"
|
||||
: got: \"$(result)\" ;
|
||||
: got: \"$(result__)\" ;
|
||||
}
|
||||
}
|
||||
|
||||
# assert that the given variable is nonempty.
|
||||
rule nonempty-variable ( name )
|
||||
{
|
||||
local empty ;
|
||||
if $($(variable)) = $(empty)
|
||||
{
|
||||
error assertion failure: expecting non-empty variable $(variable) ;
|
||||
error-skip-frames 3 assertion failure: expecting non-empty variable $(variable) ;
|
||||
}
|
||||
}
|
||||
|
||||
rule true ( rule-name args * )
|
||||
# assert that the result of calling RULE-NAME on the given arguments
|
||||
# has a true logical value (is neither an empty list nor all empty
|
||||
# strings).
|
||||
rule true ( rule-name args * : * )
|
||||
{
|
||||
local result caller-module = [ CALLER_MODULE ] ;
|
||||
|
||||
module $(caller-module)
|
||||
local result__ ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
result = [ $(rule-name) $(args) ] ;
|
||||
result__ = [
|
||||
$(rule-name) $(args) : $(2) $(3) : $(4)
|
||||
: $(5) : $(6) : $(7) : $(8) : $(9) ] ;
|
||||
}
|
||||
|
||||
if ! $(result)
|
||||
if ! $(result__)
|
||||
{
|
||||
error assertion failure: expecting true result from
|
||||
error-skip-frames 3 assertion failure: expecting true result from
|
||||
"[" $(rule-name) \"$(args)\" "]" ;
|
||||
}
|
||||
}
|
||||
|
120
v2/util/sequence.jam
Normal file
120
v2/util/sequence.jam
Normal file
@ -0,0 +1,120 @@
|
||||
# (C) Copyright David Abrahams 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears in
|
||||
# all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
|
||||
import assert ;
|
||||
|
||||
# Note that algorithms in this module execute largely in the caller's
|
||||
# module namespace, so that local rules can be used as function
|
||||
# objects. Also note that most predicates can be multi-element
|
||||
# lists. In that case, all but the first element are prepended to the
|
||||
# first argument which is passed to the rule named by the first
|
||||
# element.
|
||||
|
||||
# Return the elements e of $(sequence) for which [ $(predicate) e ] is
|
||||
# has a non-null value.
|
||||
rule filter ( predicate + : sequence * )
|
||||
{
|
||||
# trailing underscores hopefully prevent collisions with module
|
||||
# locals in the caller
|
||||
local result__ ;
|
||||
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
for local e in $(sequence)
|
||||
{
|
||||
if [ $(predicate) $(e) ]
|
||||
{
|
||||
result__ += $(e) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $(result__) ;
|
||||
}
|
||||
|
||||
rule less ( a b )
|
||||
{
|
||||
if $(a) < $(b)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
# insertion-sort s using the BinaryPredicate ordered.
|
||||
rule insertion-sort ( s * : ordered * )
|
||||
{
|
||||
ordered ?= sequence.less ;
|
||||
local result__ = $(s[1]) ;
|
||||
module [ CALLER_MODULE ]
|
||||
{
|
||||
for local x in $(s[2-])
|
||||
{
|
||||
local head tail ;
|
||||
tail = $(result__) ;
|
||||
while $(tail) && [ $(ordered) $(tail[1]) $(x) ]
|
||||
{
|
||||
head += $(tail[1]) ;
|
||||
tail = $(tail[2-]) ;
|
||||
}
|
||||
result__ = $(head) $(x) $(tail) ;
|
||||
}
|
||||
}
|
||||
return $(result__) ;
|
||||
}
|
||||
|
||||
# join the elements of s into one long string. If joint is supplied, it is used as a separator.
|
||||
rule join ( s * : joint ? )
|
||||
{
|
||||
local result ;
|
||||
joint ?= "" ;
|
||||
for local x in $(s)
|
||||
{
|
||||
result = $(result)$(joint)$(x) ;
|
||||
result ?= $(x) ;
|
||||
}
|
||||
return $(result) ;
|
||||
}
|
||||
|
||||
local rule __test__ ( )
|
||||
{
|
||||
# use a unique module so we can test the use of local rules.
|
||||
module sequence.__test__
|
||||
{
|
||||
|
||||
local rule is-even ( n )
|
||||
{
|
||||
if $(n) in 0 2 4 6 8
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 4 6 4 2 8
|
||||
: sequence.filter is-even : 1 4 6 3 4 7 2 3 8 ;
|
||||
|
||||
# test that argument binding works
|
||||
local rule is-equal-test ( x y )
|
||||
{
|
||||
if $(x) = $(y)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 3 3 3 : sequence.filter is-equal-test 3 : 1 2 3 4 3 5 3 5 7 ;
|
||||
|
||||
local rule test-greater ( a b )
|
||||
{
|
||||
if $(a) > $(b)
|
||||
{
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
|
||||
assert.result 1 2 3 4 5 6 7 8 9 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 ;
|
||||
assert.result 9 8 7 6 5 4 3 2 1 : sequence.insertion-sort 9 6 5 3 8 7 1 2 4 : test-greater ;
|
||||
assert.result foo-bar-baz : sequence.join foo bar baz : - ;
|
||||
assert.result substandard : sequence.join sub stan dard ;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user