/* * Copyright 1993, 2000 Christopher Seiwald. * * This file is part of Jam - see jam.c for Copyright information. */ # include "jam.h" # include "lists.h" # include "parse.h" # include "compile.h" # include "variable.h" # include "expand.h" # include "rules.h" # include "newstr.h" # include "make.h" # include "search.h" /* * compile.c - compile parsed jam statements * * External routines: * * compile_append() - append list results of two statements * compile_foreach() - compile the "for x in y" statement * compile_if() - compile 'if' rule * compile_include() - support for 'include' - call include() on file * compile_list() - expand and return a list * compile_local() - declare (and set) local variables * compile_null() - do nothing -- a stub for parsing * compile_rule() - compile a single user defined rule * compile_rules() - compile a chain of rules * compile_set() - compile the "set variable" statement * compile_setcomp() - support for `rule` - save parse tree * compile_setexec() - support for `actions` - save execution string * compile_settings() - compile the "on =" (set variable on exec) statement * compile_switch() - compile 'switch' rule * * Internal routines: * * debug_compile() - printf with indent to show rule expansion. * * evaluate_if() - evaluate if to determine which leg to compile * evaluate_rule() - execute a rule invocation * * builtin_depends() - DEPENDS/INCLUDES rule * builtin_echo() - ECHO rule * builtin_exit() - EXIT rule * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule * * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of * the awkward sounding "settings". * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). * 04/12/94 (seiwald) - actionlist() now just appends a single action. * 04/13/94 (seiwald) - added shorthand L0 for null list pointer * 05/13/94 (seiwald) - include files are now bound as targets, and thus * can make use of $(SEARCH) * 06/01/94 (seiwald) - new 'actions existing' does existing sources * 08/23/94 (seiwald) - Support for '+=' (append to variable) * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. * 01/22/95 (seiwald) - Exit rule. * 02/02/95 (seiwald) - Always rule; LEAVES rule. * 02/14/95 (seiwald) - NoUpdate rule. * 09/11/00 (seiwald) - new evaluate_rule() for headers(). * 09/11/00 (seiwald) - compile_xxx() now return LIST *. * New compile_append() and compile_list() in * support of building lists here, rather than * in jamgram.yy. */ static void debug_compile( int which, char *s ); static int evaluate_if( PARSE *parse, LOL *args ); static LIST *builtin_depends( PARSE *parse, LOL *args ); static LIST *builtin_echo( PARSE *parse, LOL *args ); static LIST *builtin_exit( PARSE *parse, LOL *args ); static LIST *builtin_flags( PARSE *parse, LOL *args ); int glob( char *s, char *c ); /* * compile_builtin() - define builtin rules */ # define P0 (PARSE *)0 # define C0 (char *)0 void compile_builtins() { bindrule( "Always" )->procedure = bindrule( "ALWAYS" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED ); bindrule( "Depends" )->procedure = bindrule( "DEPENDS" )->procedure = parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_DEPENDS ); bindrule( "Echo" )->procedure = bindrule( "ECHO" )->procedure = parse_make( builtin_echo, P0, P0, P0, C0, C0, 0 ); bindrule( "Exit" )->procedure = bindrule( "EXIT" )->procedure = parse_make( builtin_exit, P0, P0, P0, C0, C0, 0 ); bindrule( "Includes" )->procedure = bindrule( "INCLUDES" )->procedure = parse_make( builtin_depends, P0, P0, P0, C0, C0, T_DEPS_INCLUDES ); bindrule( "Leaves" )->procedure = bindrule( "LEAVES" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES ); bindrule( "NoCare" )->procedure = bindrule( "NOCARE" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE ); bindrule( "NOTIME" )->procedure = bindrule( "NotFile" )->procedure = bindrule( "NOTFILE" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE ); bindrule( "NoUpdate" )->procedure = bindrule( "NOUPDATE" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE ); bindrule( "Temporary" )->procedure = bindrule( "TEMPORARY" )->procedure = parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP ); } /* * compile_append() - append list results of two statements * * parse->left more compile_append() by left-recursion * parse->right single rule */ LIST * compile_append( PARSE *parse, LOL *args ) { /* Append right to left. */ return list_append( (*parse->left->func)( parse->left, args ), (*parse->right->func)( parse->right, args ) ); } /* * compile_foreach() - compile the "for x in y" statement * * Compile_foreach() resets the given variable name to each specified * value, executing the commands enclosed in braces for each iteration. * * parse->string index variable * parse->left variable values * parse->right rule to compile */ LIST * compile_foreach( PARSE *parse, LOL *args ) { LIST *nv = (*parse->left->func)( parse->left, args ); LIST *l; /* Call var_set to reset $(parse->string) for each val. */ for( l = nv; l; l = list_next( l ) ) { LIST *val = list_new( L0, copystr( l->string ) ); var_set( parse->string, val, VAR_SET ); list_free( (*parse->right->func)( parse->right, args ) ); } list_free( nv ); return L0; } /* * compile_if() - compile 'if' rule * * parse->left condition tree * parse->right then tree * parse->third else tree */ LIST * compile_if( PARSE *p, LOL *args ) { if( evaluate_if( p->left, args ) ) { return (*p->right->func)( p->right, args ); } else { return (*p->third->func)( p->third, args ); } } /* * evaluate_if() - evaluate if to determine which leg to compile * * Returns: * !0 if expression true - compile 'then' clause * 0 if expression false - compile 'else' clause */ static int evaluate_if( PARSE *parse, LOL *args ) { int status; if( parse->num <= COND_OR ) { /* Handle one of the logical operators */ switch( parse->num ) { case COND_NOT: status = !evaluate_if( parse->left, args ); break; case COND_AND: status = evaluate_if( parse->left, args ) && evaluate_if( parse->right, args ); break; case COND_OR: status = evaluate_if( parse->left, args ) || evaluate_if( parse->right, args ); break; default: status = 0; /* can't happen */ } } else { /* Handle one of the comparison operators */ /* Expand targets and sources */ LIST *nt = (*parse->left->func)( parse->left, args ); LIST *ns = (*parse->right->func)( parse->right, args ); /* "a in b" make sure each of a is equal to something in b. */ /* Otherwise, step through pairwise comparison. */ if( parse->num == COND_IN ) { LIST *s, *t; /* Try each t until failure. */ for( status = 1, t = nt; status && t; t = list_next( t ) ) { int stat1; /* Try each s until success */ for( stat1 = 0, s = ns; !stat1 && s; s = list_next( s ) ) stat1 = !strcmp( t->string, s->string ); status = stat1; } } else { LIST *s = ns, *t = nt; status = 0; while( !status && ( t || s ) ) { char *st = t ? t->string : ""; char *ss = s ? s->string : ""; status = strcmp( st, ss ); t = t ? list_next( t ) : t; s = s ? list_next( s ) : s; } } switch( parse->num ) { case COND_EXISTS: status = status > 0 ; break; case COND_EQUALS: status = !status; break; case COND_NOTEQ: status = status != 0; break; case COND_LESS: status = status < 0; break; case COND_LESSEQ: status = status <= 0; break; case COND_MORE: status = status > 0; break; case COND_MOREEQ: status = status >= 0; break; case COND_IN: /* status = status */ break; } if( DEBUG_IF ) { debug_compile( 0, "if" ); list_print( nt ); printf( "(%d)", status ); list_print( ns ); printf( "\n" ); } list_free( nt ); list_free( ns ); } return status; } /* * compile_include() - support for 'include' - call include() on file * * parse->left list of files to include (can only do 1) */ LIST * compile_include( PARSE *parse, LOL *args ) { LIST *nt = (*parse->left->func)( parse->left, args ); if( DEBUG_COMPILE ) { debug_compile( 0, "include" ); list_print( nt ); printf( "\n" ); } if( nt ) { TARGET *t = bindtarget( nt->string ); /* Bind the include file under the influence of */ /* "on-target" variables. Though they are targets, */ /* include files are not built with make(). */ pushsettings( t->settings ); t->boundname = search( t->name, &t->time ); popsettings( t->settings ); parse_file( t->boundname ); } list_free( nt ); return L0; } /* * compile_list() - expand and return a list * * parse->string - character string to expand */ LIST * compile_list( PARSE *parse, LOL *args ) { /* voodoo 1 means: s is a copyable string */ char *s = parse->string; return var_expand( L0, s, s + strlen( s ), args, 1 ); } /* * compile_local() - declare (and set) local variables * * parse->left list of variables * parse->right list of values * parse->third rules to execute */ LIST * compile_local( PARSE *parse, LOL *args ) { LIST *l; SETTINGS *s = 0; LIST *nt = (*parse->left->func)( parse->left, args ); LIST *ns = (*parse->right->func)( parse->right, args ); LIST *result; if( DEBUG_COMPILE ) { debug_compile( 0, "local" ); list_print( nt ); printf( " = " ); list_print( ns ); printf( "\n" ); } /* Initial value is ns */ for( l = nt; l; l = list_next( l ) ) s = addsettings( s, 0, l->string, list_copy( (LIST*)0, ns ) ); list_free( ns ); list_free( nt ); /* Note that callees of the current context get this "local" */ /* variable, making it not so much local as layered. */ pushsettings( s ); result = (*parse->third->func)( parse->third, args ); popsettings( s ); freesettings( s ); return result; } /* * compile_null() - do nothing -- a stub for parsing */ LIST * compile_null( PARSE *parse, LOL *args ) { return L0; } /* * compile_rule() - compile a single user defined rule * * parse->string name of user defined rule * parse->left parameters (list of lists) to rule, recursing left * * Wrapped around evaluate_rule() so that headers() can share it. */ LIST * compile_rule( PARSE *parse, LOL *args ) { LOL nargs[1]; LIST *result; PARSE *p; /* Build up the list of arg lists */ lol_init( nargs ); for( p = parse->left; p; p = p->left ) lol_add( nargs, (*p->right->func)( p->right, args ) ); /* And invoke rule */ result = evaluate_rule( parse->string, nargs ); lol_free( nargs ); return result; } /* * evaluate_rule() - execute a rule invocation */ LIST * evaluate_rule( char *rulename, LOL *args ) { LIST *result = L0; RULE *rule = bindrule( rulename ); if( DEBUG_COMPILE ) { debug_compile( 1, rulename ); lol_print( args ); printf( "\n" ); } /* Check traditional targets $(<) and sources $(>) */ if( !rule->actions && !rule->procedure ) printf( "warning: unknown rule %s\n", rule->name ); /* If this rule will be executed for updating the targets */ /* then construct the action for make(). */ if( rule->actions ) { TARGETS *t; ACTION *action; /* The action is associated with this instance of this rule */ action = (ACTION *)malloc( sizeof( ACTION ) ); memset( (char *)action, '\0', sizeof( *action ) ); action->rule = rule; action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) ); action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) ); /* Append this action to the actions of each target */ for( t = action->targets; t; t = t->next ) t->target->actions = actionlist( t->target->actions, action ); } /* Now recursively compile any parse tree associated with this rule */ /* refer/free to ensure rule not freed during use */ if( rule->procedure ) { PARSE *parse = rule->procedure; parse_refer( parse ); result = (*parse->func)( parse, args ); parse_free( parse ); } if( DEBUG_COMPILE ) debug_compile( -1, 0 ); return result; } /* * compile_rules() - compile a chain of rules * * parse->left more compile_rules() by left-recursion * parse->right single rule */ LIST * compile_rules( PARSE *parse, LOL *args ) { /* Ignore result from first statement; return the 2nd. */ list_free( (*parse->left->func)( parse->left, args ) ); return (*parse->right->func)( parse->right, args ); } /* * compile_set() - compile the "set variable" statement * * parse->left variable names * parse->right variable values * parse->num ASSIGN_SET/APPEND/DEFAULT */ LIST * compile_set( PARSE *parse, LOL *args ) { LIST *nt = (*parse->left->func)( parse->left, args ); LIST *ns = (*parse->right->func)( parse->right, args ); LIST *l; int setflag; char *trace; switch( parse->num ) { case ASSIGN_SET: setflag = VAR_SET; trace = "="; break; case ASSIGN_APPEND: setflag = VAR_APPEND; trace = "+="; break; case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break; default: setflag = VAR_SET; trace = ""; break; } if( DEBUG_COMPILE ) { debug_compile( 0, "set" ); list_print( nt ); printf( " %s ", trace ); list_print( ns ); printf( "\n" ); } /* Call var_set to set variable */ /* var_set keeps ns, so need to copy it */ for( l = nt; l; l = list_next( l ) ) var_set( l->string, list_copy( L0, ns ), setflag ); list_free( nt ); return ns; } /* * compile_setcomp() - support for `rule` - save parse tree * * parse->string rule name * parse->left rules for rule */ LIST * compile_setcomp( PARSE *parse, LOL *args ) { RULE *rule = bindrule( parse->string ); /* Free old one, if present */ if( rule->procedure ) parse_free( rule->procedure ); rule->procedure = parse->left; /* we now own this parse tree */ /* don't let parse_free() release it */ parse->left = 0; return L0; } /* * compile_setexec() - support for `actions` - save execution string * * parse->string rule name * parse->string1 OS command string * parse->num flags * parse->left `bind` variables * * Note that the parse flags (as defined in compile.h) are transfered * directly to the rule flags (as defined in rules.h). */ LIST * compile_setexec( PARSE *parse, LOL *args ) { RULE *rule = bindrule( parse->string ); LIST *bindlist = (*parse->left->func)( parse->left, args ); /* Free old one, if present */ if( rule->actions ) { freestr( rule->actions ); list_free( rule->bindlist ); } rule->actions = copystr( parse->string1 ); rule->bindlist = bindlist; rule->flags = parse->num; /* XXX translate this properly */ return L0; } /* * compile_settings() - compile the "on =" (set variable on exec) statement * * parse->left variable names * parse->right target name * parse->third variable value * parse->num ASSIGN_SET/APPEND */ LIST * compile_settings( PARSE *parse, LOL *args ) { LIST *nt = (*parse->left->func)( parse->left, args ); LIST *ns = (*parse->third->func)( parse->third, args ); LIST *targets = (*parse->right->func)( parse->right, args ); LIST *ts; int append = parse->num == ASSIGN_APPEND; if( DEBUG_COMPILE ) { debug_compile( 0, "set" ); list_print( nt ); printf( "on " ); list_print( targets ); printf( " %s ", append ? "+=" : "=" ); list_print( ns ); printf( "\n" ); } /* Call addsettings to save variable setting */ /* addsettings keeps ns, so need to copy it */ /* Pass append flag to addsettings() */ for( ts = targets; ts; ts = list_next( ts ) ) { TARGET *t = bindtarget( ts->string ); LIST *l; for( l = nt; l; l = list_next( l ) ) t->settings = addsettings( t->settings, append, l->string, list_copy( (LIST*)0, ns ) ); } list_free( nt ); list_free( targets ); return ns; } /* * compile_switch() - compile 'switch' rule * * parse->left switch value (only 1st used) * parse->right cases * * cases->left 1st case * cases->right next cases * * case->string argument to match * case->left parse tree to execute */ LIST * compile_switch( PARSE *parse, LOL *args ) { LIST *nt = (*parse->left->func)( parse->left, args ); LIST *result = 0; if( DEBUG_COMPILE ) { debug_compile( 0, "switch" ); list_print( nt ); printf( "\n" ); } /* Step through cases */ for( parse = parse->right; parse; parse = parse->right ) { if( !glob( parse->left->string, nt ? nt->string : "" ) ) { /* Get & exec parse tree for this case */ parse = parse->left->left; result = (*parse->func)( parse, args ); break; } } list_free( nt ); return result; } /* * builtin_depends() - DEPENDS/INCLUDES rule * * The DEPENDS builtin rule appends each of the listed sources on the * dependency list of each of the listed targets. It binds both the * targets and sources as TARGETs. */ static LIST * builtin_depends( PARSE *parse, LOL *args ) { LIST *targets = lol_get( args, 0 ); LIST *sources = lol_get( args, 1 ); int which = parse->num; LIST *l; for( l = targets; l; l = list_next( l ) ) { TARGET *t = bindtarget( l->string ); t->deps[ which ] = targetlist( t->deps[ which ], sources ); } return L0; } /* * builtin_echo() - ECHO rule * * The ECHO builtin rule echoes the targets to the user. No other * actions are taken. */ static LIST * builtin_echo( PARSE *parse, LOL *args ) { list_print( lol_get( args, 0 ) ); printf( "\n" ); return L0; } /* * builtin_exit() - EXIT rule * * The EXIT builtin rule echoes the targets to the user and exits * the program with a failure status. */ static LIST * builtin_exit( PARSE *parse, LOL *args ) { list_print( lol_get( args, 0 ) ); printf( "\n" ); exit( EXITBAD ); /* yeech */ return L0; } /* * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule * * Builtin_flags() marks the target with the appropriate flag, for use * by make0(). It binds each target as a TARGET. */ static LIST * builtin_flags( PARSE *parse, LOL *args ) { LIST *l = lol_get( args, 0 ); for( ; l; l = list_next( l ) ) bindtarget( l->string )->flags |= parse->num; return L0; } /* * debug_compile() - printf with indent to show rule expansion. */ static void debug_compile( int which, char *s ) { static int level = 0; static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; int i = ((1+level) * 2) % 35; if( which >= 0 ) printf( "%*.*s ", i, i, indent ); if( s ) printf( "%s ", s ); level += which; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#19 | 9903 | Perforce staff |
Jam -dr now reports file/line numbers, at no small effort. New functionality documented in RELNOTES. p4transfer.py: Transferred from production |
||
#18 | 2559 | rmg |
Fix 'var on target ?= value' so that var is only set if it did not have a target-specific value. Previously, it would just overwrite the var's value. Bug fix documented in RELNOTES. === computer:1666: Change 39566 by seiwald@play-seiwald on 2002/12/27 14:44:01 |
||
#17 | 2557 | rmg |
Shuffle mechanism for optional Jamrules includes: now no error message is issued for a missing include file marked with NOCARE. Previously, we used Glob to try to find the optional Jamrules files, but on VMS that doesn't work so well: Glob returns all uppercase file names with .'s at the end, which doesn't match "Jamrules" at all. The NOCARE part is a user-visible change documented in RELNOTES. === computer:1666: Change 39273 by seiwald@waffle-cyg-seiwald on 2002/12/19 22:44:03 |
||
#16 | 2529 | rmg |
Fix "on target" variables during header scan, from Matt Armstrong. Setting target-specific variables while under the influence of the target's target-specific variables caused the _global_ values to be modified. This happened both during header file scanning and with the "on target statement" syntax. The manifestation of this was if a file #included itself, HdrRule would accidentally set HDRRULE/HDRSCAN globally, and then all files (executables, etc) would get scanned for includes. While this borrows from Matt's fix, it is a slightly different implementation. User visible fix documented in RELNOTES. === computer:1666: Change 39095 by seiwald@play-seiwald on 2002/12/17 14:00:58 |
||
#15 | 2495 | rmg |
Fix up comments to compile_on() to reflect the fact that (a) it is no longer experimental and (b) it is not the same code as compile_include() (duh). No functional change. === computer:1666: Change 38001 by seiwald@play-seiwald on 2002/11/18 12:25:03 |
||
#14 | 2494 | rmg |
Remove bogus search() call in 'on' statement processing, as pointed out by Ingo on the jamming mailing list. === computer:1666: Change 38000 by seiwald@play-seiwald on 2002/11/18 12:19:24 |
||
#13 | 2493 | rmg |
Rewrite the past: update all jam's source with comments to reflect changes since about 2.3, very early 2001. Whitespace only change. === computer:1666: Change 37660 by seiwald@play-seiwald on 2002/11/06 22:41:35 Note: I regenerated jamgram.c on my linux 7.3 system prior to the submit, since patch was so unhappy trying to lay down the changes from Christopher's change. Presumably this is just due to different yacc/bison/whatever particulars on the system where Christopher made the changes originally. - rmg |
||
#12 | 2491 | rmg |
Some consting in jam to make it more compilable by C++ compilers. No functional change. === computer:1666: Change 37433 by perforce@perforce on 2002/10/30 16:08:51 Recreational const-ing of jam, for compilers that don't allow "string" to be passed as a non-const char *. This included a few places where we were modifying what could possibly have been read-only storage, oddly enough. No functional change. === computer:1666: Change 37602 by seiwald@play-seiwald on 2002/11/04 17:25:40 |
||
#11 | 2490 | rmg |
Jam langauge work: make 'return' actually return from the rule, rather than just setting the return value. Introduce new break/continue statements for managing loops. User visible change to be documented in Jam.html. === computer:1666: Change 37200 by seiwald@play-seiwald on 2002/10/22 15:41:28 Gross rework of Jam.html documentation, including: - the description of parameters for rules - description of -g flag - a new description of targets - more about rules and their return values - better separation of rules and updating actions - putting borders around the tables (Undocumented) change to documentation. === computer:1666: Change 37551 by seiwald@waffle-cyg-seiwald on 2002/11/03 23:17:12 Document jam's new and working break/continue/return statements. === computer:1666: Change 37574 by seiwald@play-seiwald on 2002/11/04 13:13:01 |
||
#10 | 2489 | rmg |
Jam tinkering: since all calls to list_new() must either newstr() or copystr() the added string, instead just pass a flag and let list_new() do the newstr/copystr. No functional change. === computer:1666: Change 37164 by seiwald@spice on 2002/10/22 01:21:58 |
||
#9 | 2482 | rmg |
Jam.html partial rewrite and the support for named parameters to rules. === computer:1666: Change 34516 by seiwald@play-seiwald on 2002/06/21 23:59:12 |
||
#8 | 1587 | rmg |
if ( "" a b ) once again returns true. reported by Vladimir Prus <ghost@cs.msu.su> Christopher's fix. Bugfix to unreleased behavior. |
||
#7 | 1537 | Perforce staff |
Fix to 1319: make jam's &&, &, |, and || operators short circuit as they did before. 'in' now short-circuits as well. This corrects behavior altered since jam 2.3 and prior to 2.4. |
||
#6 | 1531 | Perforce staff |
Fix to 1497: finish code that appends results of multiple rules invoked by expansion of $(rule). |
||
#5 | 1497 | Perforce staff |
Support for invoking rules via variable expansion, i.e. "$(rule) targets : sources ;". Not yet documented. Documented in Jam.html (rmg change 1536) |
||
#4 | 1492 | Perforce staff |
Replace action modifiers EXEC_* from compile.h with RULE_* flags from rules.h. They've always been the same quantity defined in two places. Note that RULE_NEWSRCS is now RULE_UPDATED, to be consistent with 'actions updated'. No functional change. |
||
#3 | 1319 | rmg |
Jam 2.3 + Perforce's internal changes. This change is a drop of the Perforce internal Jam changes since the 2.3 public release. The individual changes represented herein are preserved in the //guest/richard_geiger/intjam/ branch. The intent of this drop is to provide a base, from which other contributors' Jam branches may be integrated into. It is not intended to become a packaged release in this state. We will be integrating changes from other users prior to creating the next packaged release. Please refer to the src/RELNOTES file for an overview of the changes present in this integration. - Richard Geiger Open Source Engineer at Perforce |
||
#2 | 486 | Perforce staff |
Jam 2.3. See RELNOTES for a list of changes from 2.2.x. Just about every source file was touched when jam got ANSI-fied. |
||
#1 | 2 | laura | Add Jam/MR 2.2 source |