----------------------------------------------------------------------

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:
Dave Abrahams 2001-11-27 16:41:59 +00:00
parent 7e05a852bf
commit 2a015f5761
45 changed files with 2356 additions and 373 deletions

View File

@ -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
&quot;varargs&quot; 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&nbsp;rule</tt>&nbsp;<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>[&nbsp;RULENAMES</tt>&nbsp;<i>module-name</i><tt>&nbsp;]</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 &quot;--debug&quot;
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>

View File

@ -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)

View File

@ -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 ];

View File

@ -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 ); ;

View File

@ -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

View File

@ -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 `;`

View File

@ -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
*/

View File

@ -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)

View File

@ -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. */

View File

@ -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)

View File

@ -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 ];

View File

@ -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 ); ;

View File

@ -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

View File

@ -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 `;`

View File

@ -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
*/

View File

@ -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)

View File

@ -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. */

View File

@ -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)\" "]" ;
}
}

View File

@ -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 ;

View File

@ -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) ;

View File

@ -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
View 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\" ;
}

View File

@ -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 ;

View File

@ -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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
import feature ;
import os ;

View File

@ -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" ;

View File

@ -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-]) ;

View File

@ -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]) ;
}

View File

@ -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
&quot;varargs&quot; 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&nbsp;rule</tt>&nbsp;<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>[&nbsp;RULENAMES</tt>&nbsp;<i>module-name</i><tt>&nbsp;]</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 &quot;--debug&quot;
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
View 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
View 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
View 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

View File

@ -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 ;

View File

@ -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 ;
}

View File

@ -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 ;

View File

@ -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) ) &&
}
}

View File

@ -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" ;

View File

@ -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-]) ;

View File

@ -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
View File

@ -0,0 +1,2 @@
import feature ;
import os ;

View File

@ -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
View 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 ;
}
}