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