1 /*
2  * Copyright 1993, 2000 Christopher Seiwald.
3  *
4  * This file is part of Jam - see jam.c for Copyright information.
5  */
6 
7 /*  This file is ALSO:
8  *  Copyright 2001-2004 David Abrahams.
9  *  Distributed under the Boost Software License, Version 1.0.
10  *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
11  */
12 
13 /*
14  * compile.c - compile parsed jam statements
15  *
16  * External routines:
17  *  evaluate_rule() - execute a rule invocation
18  *
19  * Internal routines:
20  *  debug_compile() - printf with indent to show rule expansion
21  */
22 
23 #include "jam.h"
24 #include "compile.h"
25 
26 #include "builtins.h"
27 #include "class.h"
28 #include "constants.h"
29 #include "hash.h"
30 #include "hdrmacro.h"
31 #include "make.h"
32 #include "modules.h"
33 #include "parse.h"
34 #include "rules.h"
35 #include "search.h"
36 #include "strings.h"
37 #include "variable.h"
38 #include "output.h"
39 
40 #include <assert.h>
41 #include <stdarg.h>
42 #include <string.h>
43 
44 
45 static void debug_compile( int which, char const * s, FRAME * );
46 
47 /* Internal functions from builtins.c */
48 void backtrace( FRAME * );
49 void backtrace_line( FRAME * );
50 void print_source_line( FRAME * );
51 void unknown_rule( FRAME *, char const * key, module_t *, OBJECT * rule_name );
52 
53 
54 /*
55  * evaluate_rule() - execute a rule invocation
56  */
57 
evaluate_rule(RULE * rule,OBJECT * rulename,FRAME * frame)58 LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame )
59 {
60     LIST          * result = L0;
61     profile_frame   prof[ 1 ];
62     module_t      * prev_module = frame->module;
63 
64     if ( DEBUG_COMPILE )
65     {
66         /* Try hard to indicate in which module the rule is going to execute. */
67         char buf[ 256 ] = "";
68         if ( rule->module->name )
69         {
70             strncat( buf, object_str( rule->module->name ), sizeof( buf ) -
71                 1 );
72             strncat( buf, ".", sizeof( buf ) - 1 );
73             if ( strncmp( buf, object_str( rule->name ), strlen( buf ) ) == 0 )
74             {
75                 buf[ 0 ] = 0;
76             }
77         }
78         strncat( buf, object_str( rule->name ), sizeof( buf ) - 1 );
79         debug_compile( 1, buf, frame );
80 
81         lol_print( frame->args );
82         out_printf( "\n" );
83     }
84 
85     if ( rule->procedure && rule->module != prev_module )
86     {
87         /* Propagate current module to nested rule invocations. */
88         frame->module = rule->module;
89     }
90 
91     /* Record current rule name in frame. */
92     if ( rule->procedure )
93     {
94         frame->rulename = object_str( rulename );
95         /* And enter record profile info. */
96         if ( DEBUG_PROFILE )
97             profile_enter( function_rulename( rule->procedure ), prof );
98     }
99 
100     /* Check traditional targets $(<) and sources $(>). */
101     if ( !rule->actions && !rule->procedure )
102         unknown_rule( frame, NULL, frame->module, rule->name );
103 
104     /* If this rule will be executed for updating the targets then construct the
105      * action for make().
106      */
107     if ( rule->actions )
108     {
109         TARGETS * t;
110 
111         /* The action is associated with this instance of this rule. */
112         ACTION * const action = (ACTION *)BJAM_MALLOC( sizeof( ACTION ) );
113         memset( (char *)action, '\0', sizeof( *action ) );
114 
115         action->rule = rule;
116         action->targets = targetlist( (TARGETS *)0, lol_get( frame->args, 0 ) );
117         action->sources = targetlist( (TARGETS *)0, lol_get( frame->args, 1 ) );
118         action->refs = 1;
119 
120         /* If we have a group of targets all being built using the same action
121          * and any of these targets is updated, then we have to consider them
122          * all to be out-dated.  We do this by adding a REBUILDS in both directions
123          * between the first target and all the other targets.
124          */
125         if ( action->targets )
126         {
127             TARGET * const t0 = action->targets->target;
128             for ( t = action->targets->next; t; t = t->next )
129             {
130                 t->target->rebuilds = targetentry( t->target->rebuilds, t0 );
131                 t0->rebuilds = targetentry( t0->rebuilds, t->target );
132             }
133         }
134 
135         /* Append this action to the actions of each target. */
136         for ( t = action->targets; t; t = t->next )
137             t->target->actions = actionlist( t->target->actions, action );
138 
139         action_free( action );
140     }
141 
142     /* Now recursively compile any parse tree associated with this rule.
143      * function_refer()/function_free() call pair added to ensure the rule does
144      * not get freed while in use.
145      */
146     if ( rule->procedure )
147     {
148         FUNCTION * const function = rule->procedure;
149         function_refer( function );
150         result = function_run( function, frame, stack_global() );
151         function_free( function );
152     }
153 
154     if ( DEBUG_PROFILE && rule->procedure )
155         profile_exit( prof );
156 
157     if ( DEBUG_COMPILE )
158         debug_compile( -1, 0, frame );
159 
160     return result;
161 }
162 
163 
164 /*
165  * Call the given rule with the specified parameters. The parameters should be
166  * of type LIST* and end with a NULL pointer. This differs from 'evaluate_rule'
167  * in that frame for the called rule is prepared inside 'call_rule'.
168  *
169  * This function is useful when a builtin rule (in C) wants to call another rule
170  * which might be implemented in Jam.
171  */
172 
call_rule(OBJECT * rulename,FRAME * caller_frame,...)173 LIST * call_rule( OBJECT * rulename, FRAME * caller_frame, ... )
174 {
175     va_list va;
176     LIST * result;
177 
178     FRAME inner[ 1 ];
179     frame_init( inner );
180     inner->prev = caller_frame;
181     inner->prev_user = caller_frame->module->user_module
182         ? caller_frame
183         : caller_frame->prev_user;
184     inner->module = caller_frame->module;
185 
186     va_start( va, caller_frame );
187     for ( ; ; )
188     {
189         LIST * const l = va_arg( va, LIST * );
190         if ( !l )
191             break;
192         lol_add( inner->args, l );
193     }
194     va_end( va );
195 
196     result = evaluate_rule( bindrule( rulename, inner->module ), rulename, inner );
197 
198     frame_free( inner );
199 
200     return result;
201 }
202 
203 
204 /*
205  * debug_compile() - printf with indent to show rule expansion
206  */
207 
debug_compile(int which,char const * s,FRAME * frame)208 static void debug_compile( int which, char const * s, FRAME * frame )
209 {
210     static int level = 0;
211     static char indent[ 36 ] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
212 
213     if ( which >= 0 )
214     {
215         int i;
216 
217         print_source_line( frame );
218 
219         i = ( level + 1 ) * 2;
220         while ( i > 35 )
221         {
222             out_puts( indent );
223             i -= 35;
224         }
225 
226         out_printf( "%*.*s ", i, i, indent );
227     }
228 
229     if ( s )
230         out_printf( "%s ", s );
231 
232     level += which;
233 }
234