diff --git a/build_system.htm b/build_system.htm
index 6ec1afc93..a561ac0c1 100644
--- a/build_system.htm
+++ b/build_system.htm
@@ -138,7 +138,10 @@
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 ;
| +
@@ -1358,7 +1394,7 @@ report I 2 : sorry : Joe Dave Pete ;
| Bind to one or more unbound elements of the actual argument.
- The acutal and formal arguments are checked for inconsistencies, which
+ The actual and formal arguments are checked for inconsistencies, which
cause Jam to exit with an error code:
@@ -1382,8 +1418,11 @@ report I 2 : sorry : Joe Dave Pete ;
Boost Jam introduces support for modules, which provide some
- rudimentary namespace protection for rules and variables. A new keyword,
- ``module'' was also introduced.
+ rudimentary namespace protection for rules and variables. A new
+ keyword, ``module'' was also introduced. The features
+ described in this section are primitives, meaning that
+ they are meant to provide the operations needed to write Jam
+ rules which provide a more elegant module interface.
@@ -1423,13 +1462,6 @@ module your_module
- Note that, like the IMPORT and CALLER_MODULE rules, module
- declaration is really a primitive, and is best used through a
- wrapper interface which implements a system of module files using the
- built-in INCLUDE rule.
-
@@ -1493,35 +1525,81 @@ ECHO [ M.get x ] ; # prints "a b c"
+
+
+
+ local rule rulename...
+
+
+ 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
+
+ [ RULENAMES module-name ].
+
+
+
+
+
+
+rule RULENAMES ( module ? )
+
+
+ Returns a list of the names of all non-local rules in the
+ given module. If module is ommitted, the names of all
+ non-local rules in the global module are returned.
+
IMPORT allows rule name aliasing across modules:
-rule IMPORT ( target_module ? : source_module ?
- : rule_names * : target_names * )
+rule IMPORT ( source_module ? : source_rules *
+ : target_module ? : target_rules * )
- Rules are copied from the source_module into the
- target_module. If either module name is missing, the global
- module is used in its place. 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 same names as they had in
- source_module. For example,
+ The IMPORT rule copies 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. For example,
-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 ] ;
+
+
+
+
+
+ EXPORT allows rule name aliasing across modules:
+
+
+
+rule 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. For example,
+
+
+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.
- Like the module declaration syntax and
- the CALLER_MODULE rule, this
- rule is a primitive, and is probably best wrapped in a Jam rule.
@@ -1555,10 +1633,6 @@ callers = [ X.get-caller ] [ Y.call-X ] [ X.call-Y ] ;
ECHO {$(callers)} ;
- Like the module declaration syntax and
- the IMPORT rule, this rule is a
- primitive, and is probably best wrapped in a Jam rule.
-
@@ -1717,6 +1791,17 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
+
+
+
+rule BACKTRACE ( )
+
+
+ Returns a list of quadruples: filename line module
+ rulename..., describing each shallower level of the call
+ stack. This rule can be used to generate useful diagnostic
+ messages from Jam rules.
+
The -d command-line option admits new arguments:
@@ -1728,6 +1813,13 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
- -d+11 - enables parser
debugging, if Jam has been compiled with the "--debug"
option to the parser generator named by $(YACC).
+
+
- -d+12 - enables dependency graph output
+ . This feature was ``stolen'' from a version of Jam
+ modified by Craig
+ McPheeters.
+
diff --git a/historic/jam/src/compile.c b/historic/jam/src/compile.c
index 90205adea..aab2a36ec 100644
--- a/historic/jam/src/compile.c
+++ b/historic/jam/src/compile.c
@@ -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)
diff --git a/historic/jam/src/hdrmacro.c b/historic/jam/src/hdrmacro.c
index 702a9ccd7..739534814 100644
--- a/historic/jam/src/hdrmacro.c
+++ b/historic/jam/src/hdrmacro.c
@@ -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 ];
diff --git a/historic/jam/src/jamgram.c b/historic/jam/src/jamgram.c
index 3d85c487c..d91f5af03 100644
--- a/historic/jam/src/jamgram.c
+++ b/historic/jam/src/jamgram.c
@@ -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 ); ;
diff --git a/historic/jam/src/jamgram.y b/historic/jam/src/jamgram.y
index 7dc5eed7c..dac17eb0a 100644
--- a/historic/jam/src/jamgram.y
+++ b/historic/jam/src/jamgram.y
@@ -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
diff --git a/historic/jam/src/jamgram.yy b/historic/jam/src/jamgram.yy
index 56f45dd1d..4690159a3 100644
--- a/historic/jam/src/jamgram.yy
+++ b/historic/jam/src/jamgram.yy
@@ -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 `;`
diff --git a/historic/jam/src/lists.c b/historic/jam/src/lists.c
index 919c71210..b8d2dae79 100644
--- a/historic/jam/src/lists.c
+++ b/historic/jam/src/lists.c
@@ -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
*/
diff --git a/historic/jam/src/lists.h b/historic/jam/src/lists.h
index 22ccd9486..7e051e173 100644
--- a/historic/jam/src/lists.h
+++ b/historic/jam/src/lists.h
@@ -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)
diff --git a/historic/jam/src/make1.c b/historic/jam/src/make1.c
index d1367679e..83e1b3954 100644
--- a/historic/jam/src/make1.c
+++ b/historic/jam/src/make1.c
@@ -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. */
diff --git a/jam_src/compile.c b/jam_src/compile.c
index 90205adea..aab2a36ec 100644
--- a/jam_src/compile.c
+++ b/jam_src/compile.c
@@ -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)
diff --git a/jam_src/hdrmacro.c b/jam_src/hdrmacro.c
index 702a9ccd7..739534814 100644
--- a/jam_src/hdrmacro.c
+++ b/jam_src/hdrmacro.c
@@ -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 ];
diff --git a/jam_src/jamgram.c b/jam_src/jamgram.c
index 3d85c487c..d91f5af03 100644
--- a/jam_src/jamgram.c
+++ b/jam_src/jamgram.c
@@ -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 ); ;
diff --git a/jam_src/jamgram.y b/jam_src/jamgram.y
index 7dc5eed7c..dac17eb0a 100644
--- a/jam_src/jamgram.y
+++ b/jam_src/jamgram.y
@@ -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
diff --git a/jam_src/jamgram.yy b/jam_src/jamgram.yy
index 56f45dd1d..4690159a3 100644
--- a/jam_src/jamgram.yy
+++ b/jam_src/jamgram.yy
@@ -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 `;`
diff --git a/jam_src/lists.c b/jam_src/lists.c
index 919c71210..b8d2dae79 100644
--- a/jam_src/lists.c
+++ b/jam_src/lists.c
@@ -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
*/
diff --git a/jam_src/lists.h b/jam_src/lists.h
index 22ccd9486..7e051e173 100644
--- a/jam_src/lists.h
+++ b/jam_src/lists.h
@@ -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)
diff --git a/jam_src/make1.c b/jam_src/make1.c
index d1367679e..83e1b3954 100644
--- a/jam_src/make1.c
+++ b/jam_src/make1.c
@@ -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. */
diff --git a/new/assert.jam b/new/assert.jam
index 04f2c8282..609094c9e 100644
--- a/new/assert.jam
+++ b/new/assert.jam
@@ -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)\" "]" ;
}
}
diff --git a/new/boost-build.jam b/new/boost-build.jam
index 9787e78ec..ff7a528b2 100644
--- a/new/boost-build.jam
+++ b/new/boost-build.jam
@@ -7,7 +7,7 @@
SEARCH on modules.jam = $(BOOST_BUILD_PATH) ;
module modules { include 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 ;
diff --git a/new/build-system.jam b/new/build-system.jam
index ffc64c694..8e4ea1bd9 100644
--- a/new/build-system.jam
+++ b/new/build-system.jam
@@ -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) ;
diff --git a/new/errors.jam b/new/errors.jam
index 699eef6b3..c3b44319f 100644
--- a/new/errors.jam
+++ b/new/errors.jam
@@ -3,44 +3,153 @@
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
-# A utility rule used to report the including module when there is an error, so
-# that editors may find it.
-rule report-module ( prefix ? : suffix ? : frames ? )
+# Print a stack backtrace leading to this rule's caller. Each
+# argument represents a line of output to be printed after the first
+# line of the backtrace.
+rule backtrace ( skip-frames messages * : * )
{
- frames ?= 1 ;
- # We report some large line number so that emacs, etc., will at least locate the file.
- ECHO $(prefix) [ modules.binding [ CALLER_MODULE $(frames) ] ] ":" line 99999 $(suffix) ;
-}
-
-rule backtrace
-{
- local digits = 1 2 3 4 5 6 7 8 9 ;
+ local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ;
+ local drop-elements = $(frame-skips[$(skip-frames)]) ;
+ if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 )
+ {
+ ECHO warning: backtrace doesn't support skipping
+ $(skip-frames) frames; using 1 instead. ;
+ drop-elements = 5 ;
+ }
+
+ # get the whole backtrace, then drop the initial quadruples
+ # corresponding to the frames that must be skipped.
local bt = [ BACKTRACE ] ;
- bt = $(bt[5-]) ;
+ bt = $(bt[$(drop-elements)-]) ;
+
+ local args = messages 2 3 4 5 6 7 8 9 ;
while $(bt)
{
ECHO $(bt[1]):$(bt[2]): "in" $(bt[4]) ;
- for local n in $(digits)
+ # the first time through, print each argument on a separate
+ # line
+ for local n in $(args)
{
if $($(n))-is-not-empty
{
ECHO $($(n)) ;
}
}
- digits = ;
-
+ args = ; # kill args so that this never happens again
+
+ # Move on to the next quadruple
bt = $(bt[5-]) ;
}
}
-rule error
+module local args = messages 2 3 4 5 6 7 8 9 ;
+module local disabled last-$(args) ;
+
+# try-catch --
+#
+# This is not really an exception-handling mechanism, but it does
+# allow us to perform some error-checking on our
+# error-checking. Errors are suppressed after a try, and the first one
+# is recorded. Use catch to check that the error message matched
+# expectations.
+
+# begin looking for error messages
+rule try ( )
{
- backtrace error: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
- EXIT ;
+ disabled += true ;
+ last-$(args) = ;
}
+# stop looking for error messages; generate an error if an argument of
+# messages is not found in the corresponding argument in the error call.
+rule catch ( messages * : * )
+{
+ import sequence ;
+ disabled = $(disabled[2-]) ; # pop the stack
+
+ for local n in $(args)
+ {
+ if ! $($(n)) in $(last-$(n))
+ {
+ local v = [ sequence.join $($(n)) : " " ] ;
+ v ?= "" ;
+ local joined = [ sequence.join $(last-$(n)) : " " ] ;
+
+ last-$(args) = ;
+ error-skip-frames 3 expected \"$(v)\" in argument $(n) of error
+ : got \"$(joined)\" instead ;
+ }
+ }
+}
+
+rule error-skip-frames ( skip-frames messages * : * )
+{
+ if ! $(disabled)
+ {
+ backtrace $(skip-frames) error: $(messages) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+ EXIT ;
+ }
+ else if ! $(last-$(args))
+ {
+ for local n in $(args)
+ {
+ last-$(n) = $($(n)) ;
+ }
+ }
+}
+
+# Print an error message with a stack backtrace and exit.
+rule error ( messages * : * )
+{
+ error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+}
+
+# Print a warning message with a stack backtrace and exit.
rule warning
{
- backtrace warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+ backtrace 2 warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+}
+
+# convert an arbitrary argument list into a list with ":" separators
+# and quoted elements representing the same information. This is
+# mostly useful for formatting descriptions of the arguments with
+# which a rule was called when reporting an error.
+rule lol->list ( * )
+{
+ local result ;
+ local remaining = 1 2 3 4 5 6 7 8 9 ;
+ while $($(remaining))
+ {
+ local n = $(remaining[1]) ;
+ remaining = $(remaining[2-]) ;
+
+ if $(n) != 1
+ {
+ result += ":" ;
+ }
+ result += \"$(n)\" ;
+ }
+ return $(result) ;
+}
+
+rule __test__ ( )
+{
+ # show that we can correctly catch an expected error
+ try ;
+ {
+ error an error occurred : somewhere ;
+ }
+ catch an error occurred : somewhere ;
+
+ # show that unexpected errors generate real errors
+ try ;
+ {
+ try ;
+ {
+ error an error occurred : somewhere ;
+ }
+ catch an error occurred : nowhere ;
+ }
+ catch expected \"nowhere\" in argument 2 ;
}
diff --git a/new/feature.jam b/new/feature.jam
new file mode 100644
index 000000000..2a13b2e14
--- /dev/null
+++ b/new/feature.jam
@@ -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 "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 -> gcc 2.95.2 linux 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" 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\" ;
+}
\ No newline at end of file
diff --git a/new/modules.jam b/new/modules.jam
index 3c1869ae7..1d1ab0ffa 100644
--- a/new/modules.jam
+++ b/new/modules.jam
@@ -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 ;
diff --git a/new/os.path.jam b/new/os.path.jam
index 172598282..0e6d0c28c 100644
--- a/new/os.path.jam
+++ b/new/os.path.jam
@@ -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) ) &&
+ }
}
\ No newline at end of file
diff --git a/new/property.jam b/new/property.jam
new file mode 100644
index 000000000..8e7a064c6
--- /dev/null
+++ b/new/property.jam
@@ -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 ;
+ }
+ }
+}
diff --git a/new/readme.txt b/new/readme.txt
new file mode 100644
index 000000000..a7f7af4b8
--- /dev/null
+++ b/new/readme.txt
@@ -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
diff --git a/new/sequence.jam b/new/sequence.jam
new file mode 100644
index 000000000..6c7523432
--- /dev/null
+++ b/new/sequence.jam
@@ -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 ;
+ }
+}
\ No newline at end of file
diff --git a/new/test.jam b/new/test.jam
new file mode 100644
index 000000000..16f9a2312
--- /dev/null
+++ b/new/test.jam
@@ -0,0 +1,2 @@
+import feature ;
+import os ;
diff --git a/test/check-arguments.jam b/test/check-arguments.jam
index 4baa401f5..c87bf3a51 100644
--- a/test/check-arguments.jam
+++ b/test/check-arguments.jam
@@ -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" ;
+
+
diff --git a/test/check-jam-patches.jam b/test/check-jam-patches.jam
index 2db5f8b4d..4d8c86a73 100644
--- a/test/check-jam-patches.jam
+++ b/test/check-jam-patches.jam
@@ -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-]) ;
diff --git a/test/echo_args.jam b/test/echo_args.jam
index b6c290e84..99fff9868 100644
--- a/test/echo_args.jam
+++ b/test/echo_args.jam
@@ -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]) ;
+}
+
diff --git a/v1/build_system.htm b/v1/build_system.htm
index 6ec1afc93..a561ac0c1 100644
--- a/v1/build_system.htm
+++ b/v1/build_system.htm
@@ -138,7 +138,10 @@
@@ -154,8 +157,10 @@
Debugging Support
@@ -1312,8 +1317,35 @@ rule foobar { ECHO foobar ; } # a trivial rule
$(x)bar ; # invokes foobar
+
+ 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:
-
+
+# 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"
+
+
+
+
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 ;
| *
- | Bind to zero or more unbound elements of the actual argument.
+ | Bind to zero or more unbound elements of the actual
+ argument. When ``*'' appears where an argument name
+ is expected, any number of additional arguments are
+ accepted. This feature can be used to implement
+ "varargs" rules.
|
| +
@@ -1358,7 +1394,7 @@ report I 2 : sorry : Joe Dave Pete ;
| Bind to one or more unbound elements of the actual argument.
- The acutal and formal arguments are checked for inconsistencies, which
+ The actual and formal arguments are checked for inconsistencies, which
cause Jam to exit with an error code:
@@ -1382,8 +1418,11 @@ report I 2 : sorry : Joe Dave Pete ;
Boost Jam introduces support for modules, which provide some
- rudimentary namespace protection for rules and variables. A new keyword,
- ``module'' was also introduced.
+ rudimentary namespace protection for rules and variables. A new
+ keyword, ``module'' was also introduced. The features
+ described in this section are primitives, meaning that
+ they are meant to provide the operations needed to write Jam
+ rules which provide a more elegant module interface.
@@ -1423,13 +1462,6 @@ module your_module
- Note that, like the IMPORT and CALLER_MODULE rules, module
- declaration is really a primitive, and is best used through a
- wrapper interface which implements a system of module files using the
- built-in INCLUDE rule.
-
@@ -1493,35 +1525,81 @@ ECHO [ M.get x ] ; # prints "a b c"
+
+
+
+ local rule rulename...
+
+
+ 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
+
+ [ RULENAMES module-name ].
+
+
+
+
+
+
+rule RULENAMES ( module ? )
+
+
+ Returns a list of the names of all non-local rules in the
+ given module. If module is ommitted, the names of all
+ non-local rules in the global module are returned.
+
IMPORT allows rule name aliasing across modules:
-rule IMPORT ( target_module ? : source_module ?
- : rule_names * : target_names * )
+rule IMPORT ( source_module ? : source_rules *
+ : target_module ? : target_rules * )
- Rules are copied from the source_module into the
- target_module. If either module name is missing, the global
- module is used in its place. 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 same names as they had in
- source_module. For example,
+ The IMPORT rule copies 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. For example,
-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 ] ;
+
+
+
+
+
+ EXPORT allows rule name aliasing across modules:
+
+
+
+rule 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. For example,
+
+
+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.
- Like the module declaration syntax and
- the CALLER_MODULE rule, this
- rule is a primitive, and is probably best wrapped in a Jam rule.
@@ -1555,10 +1633,6 @@ callers = [ X.get-caller ] [ Y.call-X ] [ X.call-Y ] ;
ECHO {$(callers)} ;
- Like the module declaration syntax and
- the IMPORT rule, this rule is a
- primitive, and is probably best wrapped in a Jam rule.
-
@@ -1717,6 +1791,17 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
+
+
+
+rule BACKTRACE ( )
+
+
+ Returns a list of quadruples: filename line module
+ rulename..., describing each shallower level of the call
+ stack. This rule can be used to generate useful diagnostic
+ messages from Jam rules.
+
The -d command-line option admits new arguments:
@@ -1728,6 +1813,13 @@ ECHO [ SUBST xyz (.)(.)(.) [$1] ($2) {$3} ] ;
- -d+11 - enables parser
debugging, if Jam has been compiled with the "--debug"
option to the parser generator named by $(YACC).
+
+
- -d+12 - enables dependency graph output
+ . This feature was ``stolen'' from a version of Jam
+ modified by Craig
+ McPheeters.
+
diff --git a/v2/build/feature.jam b/v2/build/feature.jam
new file mode 100644
index 000000000..2a13b2e14
--- /dev/null
+++ b/v2/build/feature.jam
@@ -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 "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 -> gcc 2.95.2 linux 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" 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\" ;
+}
\ No newline at end of file
diff --git a/v2/build/property.jam b/v2/build/property.jam
new file mode 100644
index 000000000..8e7a064c6
--- /dev/null
+++ b/v2/build/property.jam
@@ -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 ;
+ }
+ }
+}
diff --git a/v2/build/readme.txt b/v2/build/readme.txt
new file mode 100644
index 000000000..a7f7af4b8
--- /dev/null
+++ b/v2/build/readme.txt
@@ -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
diff --git a/v2/doc/boost-build.jam b/v2/doc/boost-build.jam
index 9787e78ec..ff7a528b2 100644
--- a/v2/doc/boost-build.jam
+++ b/v2/doc/boost-build.jam
@@ -7,7 +7,7 @@
SEARCH on modules.jam = $(BOOST_BUILD_PATH) ;
module modules { include 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 ;
diff --git a/v2/errors.jam b/v2/errors.jam
index 699eef6b3..c3b44319f 100644
--- a/v2/errors.jam
+++ b/v2/errors.jam
@@ -3,44 +3,153 @@
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
-# A utility rule used to report the including module when there is an error, so
-# that editors may find it.
-rule report-module ( prefix ? : suffix ? : frames ? )
+# Print a stack backtrace leading to this rule's caller. Each
+# argument represents a line of output to be printed after the first
+# line of the backtrace.
+rule backtrace ( skip-frames messages * : * )
{
- frames ?= 1 ;
- # We report some large line number so that emacs, etc., will at least locate the file.
- ECHO $(prefix) [ modules.binding [ CALLER_MODULE $(frames) ] ] ":" line 99999 $(suffix) ;
-}
-
-rule backtrace
-{
- local digits = 1 2 3 4 5 6 7 8 9 ;
+ local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ;
+ local drop-elements = $(frame-skips[$(skip-frames)]) ;
+ if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 )
+ {
+ ECHO warning: backtrace doesn't support skipping
+ $(skip-frames) frames; using 1 instead. ;
+ drop-elements = 5 ;
+ }
+
+ # get the whole backtrace, then drop the initial quadruples
+ # corresponding to the frames that must be skipped.
local bt = [ BACKTRACE ] ;
- bt = $(bt[5-]) ;
+ bt = $(bt[$(drop-elements)-]) ;
+
+ local args = messages 2 3 4 5 6 7 8 9 ;
while $(bt)
{
ECHO $(bt[1]):$(bt[2]): "in" $(bt[4]) ;
- for local n in $(digits)
+ # the first time through, print each argument on a separate
+ # line
+ for local n in $(args)
{
if $($(n))-is-not-empty
{
ECHO $($(n)) ;
}
}
- digits = ;
-
+ args = ; # kill args so that this never happens again
+
+ # Move on to the next quadruple
bt = $(bt[5-]) ;
}
}
-rule error
+module local args = messages 2 3 4 5 6 7 8 9 ;
+module local disabled last-$(args) ;
+
+# try-catch --
+#
+# This is not really an exception-handling mechanism, but it does
+# allow us to perform some error-checking on our
+# error-checking. Errors are suppressed after a try, and the first one
+# is recorded. Use catch to check that the error message matched
+# expectations.
+
+# begin looking for error messages
+rule try ( )
{
- backtrace error: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
- EXIT ;
+ disabled += true ;
+ last-$(args) = ;
}
+# stop looking for error messages; generate an error if an argument of
+# messages is not found in the corresponding argument in the error call.
+rule catch ( messages * : * )
+{
+ import sequence ;
+ disabled = $(disabled[2-]) ; # pop the stack
+
+ for local n in $(args)
+ {
+ if ! $($(n)) in $(last-$(n))
+ {
+ local v = [ sequence.join $($(n)) : " " ] ;
+ v ?= "" ;
+ local joined = [ sequence.join $(last-$(n)) : " " ] ;
+
+ last-$(args) = ;
+ error-skip-frames 3 expected \"$(v)\" in argument $(n) of error
+ : got \"$(joined)\" instead ;
+ }
+ }
+}
+
+rule error-skip-frames ( skip-frames messages * : * )
+{
+ if ! $(disabled)
+ {
+ backtrace $(skip-frames) error: $(messages) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+ EXIT ;
+ }
+ else if ! $(last-$(args))
+ {
+ for local n in $(args)
+ {
+ last-$(n) = $($(n)) ;
+ }
+ }
+}
+
+# Print an error message with a stack backtrace and exit.
+rule error ( messages * : * )
+{
+ error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+}
+
+# Print a warning message with a stack backtrace and exit.
rule warning
{
- backtrace warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+ backtrace 2 warning: $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
+}
+
+# convert an arbitrary argument list into a list with ":" separators
+# and quoted elements representing the same information. This is
+# mostly useful for formatting descriptions of the arguments with
+# which a rule was called when reporting an error.
+rule lol->list ( * )
+{
+ local result ;
+ local remaining = 1 2 3 4 5 6 7 8 9 ;
+ while $($(remaining))
+ {
+ local n = $(remaining[1]) ;
+ remaining = $(remaining[2-]) ;
+
+ if $(n) != 1
+ {
+ result += ":" ;
+ }
+ result += \"$(n)\" ;
+ }
+ return $(result) ;
+}
+
+rule __test__ ( )
+{
+ # show that we can correctly catch an expected error
+ try ;
+ {
+ error an error occurred : somewhere ;
+ }
+ catch an error occurred : somewhere ;
+
+ # show that unexpected errors generate real errors
+ try ;
+ {
+ try ;
+ {
+ error an error occurred : somewhere ;
+ }
+ catch an error occurred : nowhere ;
+ }
+ catch expected \"nowhere\" in argument 2 ;
}
diff --git a/v2/modules.jam b/v2/modules.jam
index 3c1869ae7..1d1ab0ffa 100644
--- a/v2/modules.jam
+++ b/v2/modules.jam
@@ -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 ;
diff --git a/v2/os.path.jam b/v2/os.path.jam
index 172598282..0e6d0c28c 100644
--- a/v2/os.path.jam
+++ b/v2/os.path.jam
@@ -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) ) &&
+ }
}
\ No newline at end of file
diff --git a/v2/test/check-arguments.jam b/v2/test/check-arguments.jam
index 4baa401f5..c87bf3a51 100644
--- a/v2/test/check-arguments.jam
+++ b/v2/test/check-arguments.jam
@@ -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" ;
+
+
diff --git a/v2/test/check-jam-patches.jam b/v2/test/check-jam-patches.jam
index 2db5f8b4d..4d8c86a73 100644
--- a/v2/test/check-jam-patches.jam
+++ b/v2/test/check-jam-patches.jam
@@ -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-]) ;
diff --git a/v2/test/echo_args.jam b/v2/test/echo_args.jam
index b6c290e84..99fff9868 100644
--- a/v2/test/echo_args.jam
+++ b/v2/test/echo_args.jam
@@ -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]) ;
+}
+
diff --git a/v2/test/test.jam b/v2/test/test.jam
new file mode 100644
index 000000000..16f9a2312
--- /dev/null
+++ b/v2/test/test.jam
@@ -0,0 +1,2 @@
+import feature ;
+import os ;
diff --git a/v2/util/assert.jam b/v2/util/assert.jam
index 04f2c8282..609094c9e 100644
--- a/v2/util/assert.jam
+++ b/v2/util/assert.jam
@@ -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)\" "]" ;
}
}
diff --git a/v2/util/sequence.jam b/v2/util/sequence.jam
new file mode 100644
index 000000000..6c7523432
--- /dev/null
+++ b/v2/util/sequence.jam
@@ -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 ;
+ }
+}
\ No newline at end of file
| |