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.
10  * (See accompanying file LICENSE_1_0.txt or copy at
11  * http://www.boost.org/LICENSE_1_0.txt)
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"
23 #include "strings.h"
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 
call_bind_rule(OBJECT * target_,OBJECT * boundname_)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. */
set_explicit_binding(OBJECT * target,OBJECT * 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 );
101     f->f_root.len = strlen( object_str( locate ) );
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 
search(OBJECT * target,timestamp * const time,OBJECT ** another_target,int const file)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 ) );
167         f->f_root.len = strlen( object_str( list_front( varlist ) ) );
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 ) );
192             f->f_root.len = strlen( object_str( list_item( iter ) ) );
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 
free_binding(void * xbinding,void * data)262 static void free_binding( void * xbinding, void * data )
263 {
264     object_free( ( (BINDING *)xbinding )->binding );
265 }
266 
267 
search_done(void)268 void search_done( void )
269 {
270     if ( explicit_bindings )
271     {
272         hashenumerate( explicit_bindings, free_binding, 0 );
273         hashdone( explicit_bindings );
274     }
275 }
276