]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. | |
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. | |
1e59de90 TL |
10 | * (See accompanying file LICENSE.txt or copy at |
11 | * https://www.bfgroup.xyz/b2/LICENSE.txt) | |
7c673cae FG |
12 | */ |
13 | ||
14 | #include "jam.h" | |
15 | #include "search.h" | |
16 | ||
17 | #include "compile.h" | |
18 | #include "filesys.h" | |
19 | #include "hash.h" | |
20 | #include "lists.h" | |
21 | #include "object.h" | |
22 | #include "pathsys.h" | |
f67539c2 | 23 | #include "jam_strings.h" |
7c673cae FG |
24 | #include "timestamp.h" |
25 | #include "variable.h" | |
26 | #include "output.h" | |
27 | ||
28 | #include <string.h> | |
29 | ||
30 | ||
31 | typedef struct _binding | |
32 | { | |
33 | OBJECT * binding; | |
34 | OBJECT * target; | |
35 | } BINDING; | |
36 | ||
37 | static struct hash * explicit_bindings = 0; | |
38 | ||
39 | ||
40 | void call_bind_rule( OBJECT * target_, OBJECT * boundname_ ) | |
41 | { | |
42 | LIST * const bind_rule = var_get( root_module(), constant_BINDRULE ); | |
43 | if ( !list_empty( bind_rule ) ) | |
44 | { | |
45 | OBJECT * target = object_copy( target_ ); | |
46 | OBJECT * boundname = object_copy( boundname_ ); | |
47 | if ( boundname && target ) | |
48 | { | |
49 | /* Prepare the argument list. */ | |
50 | FRAME frame[ 1 ]; | |
51 | frame_init( frame ); | |
52 | ||
53 | /* First argument is the target name. */ | |
54 | lol_add( frame->args, list_new( target ) ); | |
55 | ||
56 | lol_add( frame->args, list_new( boundname ) ); | |
57 | if ( lol_get( frame->args, 1 ) ) | |
58 | { | |
59 | OBJECT * rulename = list_front( bind_rule ); | |
60 | list_free( evaluate_rule( bindrule( rulename, root_module() ), rulename, frame ) ); | |
61 | } | |
62 | ||
63 | /* Clean up */ | |
64 | frame_free( frame ); | |
65 | } | |
66 | else | |
67 | { | |
68 | if ( boundname ) | |
69 | object_free( boundname ); | |
70 | if ( target ) | |
71 | object_free( target ); | |
72 | } | |
73 | } | |
74 | } | |
75 | ||
76 | /* Records the binding of a target with an explicit LOCATE. */ | |
77 | void set_explicit_binding( OBJECT * target, OBJECT * locate ) | |
78 | { | |
79 | OBJECT * boundname; | |
80 | OBJECT * key; | |
81 | PATHNAME f[ 1 ]; | |
82 | string buf[ 1 ]; | |
83 | int found; | |
84 | BINDING * ba; | |
85 | ||
86 | if ( !explicit_bindings ) | |
87 | explicit_bindings = hashinit( sizeof( BINDING ), "explicitly specified " | |
88 | "locations" ); | |
89 | ||
90 | string_new( buf ); | |
91 | ||
92 | /* Parse the filename. */ | |
93 | path_parse( object_str( target ), f ); | |
94 | ||
95 | /* Ignore the grist. */ | |
96 | f->f_grist.ptr = 0; | |
97 | f->f_grist.len = 0; | |
98 | ||
99 | /* Root the target path at the given location. */ | |
100 | f->f_root.ptr = object_str( locate ); | |
1e59de90 | 101 | f->f_root.len = int32_t(strlen( object_str( locate ) )); |
7c673cae FG |
102 | |
103 | path_build( f, buf ); | |
104 | boundname = object_new( buf->value ); | |
105 | if ( DEBUG_SEARCH ) | |
106 | out_printf( "explicit locate %s: %s\n", object_str( target ), buf->value ); | |
107 | string_free( buf ); | |
108 | key = path_as_key( boundname ); | |
109 | object_free( boundname ); | |
110 | ||
111 | ba = (BINDING *)hash_insert( explicit_bindings, key, &found ); | |
112 | if ( !found ) | |
113 | { | |
114 | ba->binding = key; | |
115 | ba->target = target; | |
116 | } | |
117 | else | |
118 | object_free( key ); | |
119 | } | |
120 | ||
121 | /* | |
122 | * search.c - find a target along $(SEARCH) or $(LOCATE). | |
123 | * | |
124 | * First, check if LOCATE is set. If so, use it to determine the location of | |
125 | * target and return, regardless of whether anything exists at that location. | |
126 | * | |
127 | * Second, examine all directories in SEARCH. If the file exists there or there | |
128 | * is another target with the same name already placed at this location via the | |
129 | * LOCATE setting, stop and return the location. In case of a previous target, | |
130 | * return its name via the 'another_target' argument. | |
131 | * | |
132 | * This behaviour allows handling dependencies on generated files. | |
133 | * | |
134 | * If caller does not expect that the target is generated, 0 can be passed as | |
135 | * 'another_target'. | |
136 | */ | |
137 | ||
138 | OBJECT * search( OBJECT * target, timestamp * const time, | |
139 | OBJECT * * another_target, int const file ) | |
140 | { | |
141 | PATHNAME f[ 1 ]; | |
142 | LIST * varlist; | |
143 | string buf[ 1 ]; | |
144 | int found = 0; | |
145 | OBJECT * boundname = 0; | |
146 | ||
147 | if ( another_target ) | |
148 | *another_target = 0; | |
149 | ||
150 | if ( !explicit_bindings ) | |
151 | explicit_bindings = hashinit( sizeof( BINDING ), "explicitly specified " | |
152 | "locations" ); | |
153 | ||
154 | string_new( buf ); | |
155 | ||
156 | /* Parse the filename. */ | |
157 | path_parse( object_str( target ), f ); | |
158 | ||
159 | f->f_grist.ptr = 0; | |
160 | f->f_grist.len = 0; | |
161 | ||
162 | varlist = var_get( root_module(), constant_LOCATE ); | |
163 | if ( !list_empty( varlist ) ) | |
164 | { | |
165 | OBJECT * key; | |
166 | f->f_root.ptr = object_str( list_front( varlist ) ); | |
1e59de90 | 167 | f->f_root.len = int32_t(strlen( object_str( list_front( varlist ) ) )); |
7c673cae FG |
168 | |
169 | path_build( f, buf ); | |
170 | ||
171 | if ( DEBUG_SEARCH ) | |
172 | out_printf( "locate %s: %s\n", object_str( target ), buf->value ); | |
173 | ||
174 | key = object_new( buf->value ); | |
175 | timestamp_from_path( time, key ); | |
176 | object_free( key ); | |
177 | found = 1; | |
178 | } | |
179 | else if ( varlist = var_get( root_module(), constant_SEARCH ), | |
180 | !list_empty( varlist ) ) | |
181 | { | |
182 | LISTITER iter = list_begin( varlist ); | |
183 | LISTITER const end = list_end( varlist ); | |
184 | for ( ; iter != end; iter = list_next( iter ) ) | |
185 | { | |
186 | BINDING * ba; | |
187 | file_info_t * ff; | |
188 | OBJECT * key; | |
189 | OBJECT * test_path; | |
190 | ||
191 | f->f_root.ptr = object_str( list_item( iter ) ); | |
1e59de90 | 192 | f->f_root.len = int32_t(strlen( object_str( list_item( iter ) ) )); |
7c673cae FG |
193 | |
194 | string_truncate( buf, 0 ); | |
195 | path_build( f, buf ); | |
196 | ||
197 | if ( DEBUG_SEARCH ) | |
198 | out_printf( "search %s: %s\n", object_str( target ), buf->value ); | |
199 | ||
200 | test_path = object_new( buf->value ); | |
201 | key = path_as_key( test_path ); | |
202 | object_free( test_path ); | |
203 | ff = file_query( key ); | |
204 | timestamp_from_path( time, key ); | |
205 | ||
206 | if ( ( ba = (BINDING *)hash_find( explicit_bindings, key ) ) ) | |
207 | { | |
208 | if ( DEBUG_SEARCH ) | |
209 | out_printf(" search %s: found explicitly located target %s\n", | |
210 | object_str( target ), object_str( ba->target ) ); | |
211 | if ( another_target ) | |
212 | *another_target = ba->target; | |
213 | found = 1; | |
214 | object_free( key ); | |
215 | break; | |
216 | } | |
217 | else if ( ff ) | |
218 | { | |
219 | if ( !file || ff->is_file ) | |
220 | { | |
221 | found = 1; | |
222 | object_free( key ); | |
223 | break; | |
224 | } | |
225 | } | |
226 | object_free( key ); | |
227 | } | |
228 | } | |
229 | ||
230 | if ( !found ) | |
231 | { | |
232 | /* Look for the obvious. */ | |
233 | /* This is a questionable move. Should we look in the obvious place if | |
234 | * SEARCH is set? | |
235 | */ | |
236 | OBJECT * key; | |
237 | ||
238 | f->f_root.ptr = 0; | |
239 | f->f_root.len = 0; | |
240 | ||
241 | string_truncate( buf, 0 ); | |
242 | path_build( f, buf ); | |
243 | ||
244 | if ( DEBUG_SEARCH ) | |
245 | out_printf( "search %s: %s\n", object_str( target ), buf->value ); | |
246 | ||
247 | key = object_new( buf->value ); | |
248 | timestamp_from_path( time, key ); | |
249 | object_free( key ); | |
250 | } | |
251 | ||
252 | boundname = object_new( buf->value ); | |
253 | string_free( buf ); | |
254 | ||
255 | /* Prepare a call to BINDRULE if the variable is set. */ | |
256 | call_bind_rule( target, boundname ); | |
257 | ||
258 | return boundname; | |
259 | } | |
260 | ||
261 | ||
262 | static void free_binding( void * xbinding, void * data ) | |
263 | { | |
264 | object_free( ( (BINDING *)xbinding )->binding ); | |
265 | } | |
266 | ||
267 | ||
268 | void search_done( void ) | |
269 | { | |
270 | if ( explicit_bindings ) | |
271 | { | |
272 | hashenumerate( explicit_bindings, free_binding, 0 ); | |
273 | hashdone( explicit_bindings ); | |
274 | } | |
275 | } |