| /* |
| * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. |
| * |
| * This file is part of Jam - see jam.c for Copyright information. |
| */ |
| |
| /* This file is ALSO: |
| * Copyright 2001-2004 David Abrahams. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
| */ |
| |
| #include "jam.h" |
| #include "lists.h" |
| #include "search.h" |
| #include "timestamp.h" |
| #include "pathsys.h" |
| #include "variable.h" |
| #include "newstr.h" |
| #include "compile.h" |
| #include "strings.h" |
| #include "hash.h" |
| #include "filesys.h" |
| #include <string.h> |
| |
| |
| typedef struct _binding |
| { |
| char * binding; |
| char * target; |
| } BINDING; |
| |
| static struct hash *explicit_bindings = 0; |
| |
| |
| void call_bind_rule |
| ( |
| char * target_, |
| char * boundname_ |
| ) |
| { |
| LIST * bind_rule = var_get( "BINDRULE" ); |
| if ( bind_rule ) |
| { |
| /* No guarantee that the target is an allocated string, so be on the |
| * safe side. |
| */ |
| char * target = copystr( target_ ); |
| |
| /* Likewise, do not rely on implementation details of newstr.c: allocate |
| * a copy of boundname. |
| */ |
| char * boundname = copystr( boundname_ ); |
| if ( boundname && target ) |
| { |
| /* Prepare the argument list. */ |
| FRAME frame[1]; |
| frame_init( frame ); |
| |
| /* First argument is the target name. */ |
| lol_add( frame->args, list_new( L0, target ) ); |
| |
| lol_add( frame->args, list_new( L0, boundname ) ); |
| if ( lol_get( frame->args, 1 ) ) |
| evaluate_rule( bind_rule->string, frame ); |
| |
| /* Clean up */ |
| frame_free( frame ); |
| } |
| else |
| { |
| if ( boundname ) |
| freestr( boundname ); |
| if ( target ) |
| freestr( target ); |
| } |
| } |
| } |
| |
| /* |
| * search.c - find a target along $(SEARCH) or $(LOCATE) |
| * First, check if LOCATE is set. If so, use it to determine |
| * the location of target and return, regardless of whether anything |
| * exists on that location. |
| * |
| * Second, examine all directories in SEARCH. If there's file already |
| * or there's another target with the same name which was placed |
| * to this location via LOCATE setting, stop and return the location. |
| * In case of previous target, return it's name via the third argument. |
| * |
| * This bevahiour allow to handle dependency on generated files. If |
| * caller does not expect that target is generated, 0 can be passed as |
| * the third argument. |
| */ |
| |
| char * |
| search( |
| char *target, |
| time_t *time, |
| char **another_target, |
| int file |
| ) |
| { |
| PATHNAME f[1]; |
| LIST *varlist; |
| string buf[1]; |
| int found = 0; |
| /* Will be set to 1 if target location is specified via LOCATE. */ |
| int explicitly_located = 0; |
| char *boundname = 0; |
| |
| if ( another_target ) |
| *another_target = 0; |
| |
| if (! explicit_bindings ) |
| explicit_bindings = hashinit( sizeof(BINDING), |
| "explicitly specified locations"); |
| |
| string_new( buf ); |
| /* Parse the filename */ |
| |
| path_parse( target, f ); |
| |
| f->f_grist.ptr = 0; |
| f->f_grist.len = 0; |
| |
| if ( ( varlist = var_get( "LOCATE" ) ) ) |
| { |
| f->f_root.ptr = varlist->string; |
| f->f_root.len = strlen( varlist->string ); |
| |
| path_build( f, buf, 1 ); |
| |
| if ( DEBUG_SEARCH ) |
| printf( "locate %s: %s\n", target, buf->value ); |
| |
| explicitly_located = 1; |
| |
| timestamp( buf->value, time ); |
| found = 1; |
| } |
| else if ( ( varlist = var_get( "SEARCH" ) ) ) |
| { |
| while ( varlist ) |
| { |
| BINDING b, *ba = &b; |
| file_info_t *ff; |
| |
| f->f_root.ptr = varlist->string; |
| f->f_root.len = strlen( varlist->string ); |
| |
| string_truncate( buf, 0 ); |
| path_build( f, buf, 1 ); |
| |
| if ( DEBUG_SEARCH ) |
| printf( "search %s: %s\n", target, buf->value ); |
| |
| ff = file_query(buf->value); |
| timestamp( buf->value, time ); |
| |
| b.binding = buf->value; |
| |
| if ( hashcheck( explicit_bindings, (HASHDATA**)&ba ) ) |
| { |
| if ( DEBUG_SEARCH ) |
| printf(" search %s: found explicitly located target %s\n", |
| target, ba->target); |
| if ( another_target ) |
| *another_target = ba->target; |
| found = 1; |
| break; |
| } |
| else if ( ff && ff->time ) |
| { |
| if ( !file || ff->is_file ) |
| { |
| found = 1; |
| break; |
| } |
| } |
| |
| varlist = list_next( varlist ); |
| } |
| } |
| |
| if ( !found ) |
| { |
| /* Look for the obvious */ |
| /* This is a questionable move. Should we look in the */ |
| /* obvious place if SEARCH is set? */ |
| |
| f->f_root.ptr = 0; |
| f->f_root.len = 0; |
| |
| string_truncate( buf, 0 ); |
| path_build( f, buf, 1 ); |
| |
| if ( DEBUG_SEARCH ) |
| printf( "search %s: %s\n", target, buf->value ); |
| |
| timestamp( buf->value, time ); |
| } |
| |
| boundname = newstr( buf->value ); |
| string_free( buf ); |
| |
| if ( explicitly_located ) |
| { |
| BINDING b; |
| BINDING * ba = &b; |
| b.binding = boundname; |
| b.target = target; |
| /* CONSIDER: we probably should issue a warning is another file |
| is explicitly bound to the same location. This might break |
| compatibility, though. */ |
| hashenter( explicit_bindings, (HASHDATA * *)&ba ); |
| } |
| |
| /* prepare a call to BINDRULE if the variable is set */ |
| call_bind_rule( target, boundname ); |
| |
| return boundname; |
| } |