2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 13:02:11 +00:00

Dependency scanning implementation.

* jam_src/builtins.c: New builtin SEARCH_FOR_TARGET.

    * jam_src/rules.c
         (bind_explicitly_located_targets): New function.
         (search_for_target): New function.

    * jam_src/make.c (make): Call 'bind_explicitly_located_targets()'
        immediately on entering.

    * jam_src/search.c (call_bind_rule): No longer 'static'.

    * new/builtin.jam (c-scanner.process): Real implementation.

    * test/dependency_test.py: Really test for dependencies.


[SVN r15646]
This commit is contained in:
Vladimir Prus
2002-10-02 11:56:32 +00:00
parent f1ae04c4a9
commit 1c8db11b60
18 changed files with 490 additions and 8 deletions

View File

@@ -188,6 +188,12 @@ load_builtins()
bind_builtin( "PWD" ,
builtin_pwd, 0, args );
}
{
char * args[] = { "target", "*", ":", "path", "*", 0 };
bind_builtin( "SEARCH_FOR_TARGET",
builtin_search_for_target, 0, args );
}
}
/*
@@ -682,6 +688,16 @@ builtin_update( PARSE *parse, FRAME *frame)
return L0;
}
LIST*
builtin_search_for_target( PARSE *parse, FRAME *frame )
{
LIST* arg1 = lol_get( frame->args, 0 );
LIST* arg2 = lol_get( frame->args, 1 );
TARGET* t = search_for_target( arg1->string, arg2 );
return list_new( L0, t->name );
}
static void lol_build( LOL* lol, char** elements )
{
LIST* l = L0;

View File

@@ -30,5 +30,6 @@ LIST *builtin_caller_module( PARSE *parse, FRAME *args );
LIST *builtin_backtrace( PARSE *parse, FRAME *args );
LIST *builtin_pwd( PARSE *parse, FRAME *args );
LIST *builtin_update( PARSE *parse, FRAME *args );
LIST *builtin_search_for_target( PARSE *parse, FRAME *args );
#endif

View File

@@ -129,6 +129,11 @@ make(
memset( (char *)counts, 0, sizeof( *counts ) );
/* First bind all targets with LOCATE_TARGET setting. This is
needed to correctly handle dependencies to generated headers.
*/
bind_explicitly_located_targets();
for( i = 0; i < n_targets; i++ )
{
TARGET *t = bindtarget( targets[i] );

View File

@@ -12,6 +12,9 @@
# include "newstr.h"
# include "hash.h"
# include "modules.h"
# include "search.h"
# include "lists.h"
# include "pathsys.h"
/* This file is ALSO:
* (C) Copyright David Abrahams 2001. Permission to copy, use,
@@ -49,6 +52,15 @@ static void set_rule_actions( RULE* rule, rule_actions* actions );
static void set_rule_body( RULE* rule, argument_list* args, PARSE* procedure );
static struct hash *targethash = 0;
typedef struct _located_target LOCATED_TARGET ;
struct _located_target {
char* file_name;
TARGET* target;
};
static struct hash *located_targets = 0;
/*
@@ -119,6 +131,118 @@ bindtarget( char *targetname )
return t;
}
static void bind_explicitly_located_target(void* xtarget, void* data)
{
TARGET* t = (TARGET*)xtarget;
if (! (t->flags & T_FLAG_NOTFILE) )
{
/* Check if there's a setting for LOCATE_TARGET */
SETTINGS* s = t->settings;
for(; s ; s = s->next)
{
if (strcmp(s->symbol, "LOCATE") == 0)
{
pushsettings(t->settings);
t->boundname = search( t->name, &t->time );
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
popsettings(t->settings);
{
LOCATED_TARGET lt = { t->boundname, t }, *lta = &lt;
if (!located_targets)
located_targets = hashinit( sizeof(LOCATED_TARGET),
"located targets" );
/* TODO: should check if we've entered the item or not. */
hashenter(located_targets, (HASHDATA **)&lta);
}
break;
}
}
}
}
void bind_explicitly_located_targets()
{
hashenumerate(targethash, bind_explicitly_located_target, (void*)0);
}
/* TODO: this is probably not a good idea to use functions in other modules like
that. */
void call_bind_rule(char* target, char* boundname);
TARGET* search_for_target ( char * name, LIST* search_path )
{
PATHNAME f[1];
string buf[1];
LOCATED_TARGET lt, *lta = &lt;
time_t time;
int found = 0;
TARGET* result;
string_new( buf );
path_parse( name, f );
f->f_grist.ptr = 0;
f->f_grist.len = 0;
while( search_path )
{
f->f_root.ptr = search_path->string;
f->f_root.len = strlen( search_path->string );
string_truncate( buf, 0 );
path_build( f, buf, 1 );
lt.file_name = buf->value ;
if (! located_targets )
located_targets = hashinit( sizeof(LOCATED_TARGET),
"located targets" );
if ( hashcheck( located_targets, (HASHDATA **)&lta ) )
{
return lta->target;
}
timestamp( buf->value, &time );
if (time)
{
found = 1;
break;
}
search_path = list_next( search_path );
}
if ( ! found )
{
f->f_root.ptr = 0;
f->f_root.len = 0;
string_truncate( buf, 0 );
path_build( f, buf, 1 );
timestamp( buf->value, &time );
}
result = bindtarget( name );
result->boundname = newstr( buf->value );
result->time = time;
result->binding = time ? T_BIND_EXISTS : T_BIND_MISSING;
call_bind_rule( result->name, result->boundname );
string_free( buf );
return result;
}
/*
* touchtarget() - mark a target to simulate being new
*/

View File

@@ -216,6 +216,8 @@ RULE* import_rule( RULE* source, module* m, char* name );
RULE* new_rule_body( module* m, char* rulename, argument_list* args, PARSE* procedure, int export );
RULE* new_rule_actions( module* m, char* rulename, char* command, LIST* bindlist, int flags );
TARGET *bindtarget( char *targetname );
void bind_explicitly_located_targets();
TARGET* search_for_targets ( char * name, LIST* search_path );
void touchtarget( char *t );
TARGETS *targetlist( TARGETS *chain, LIST *targets );
TARGETS *targetentry( TARGETS *chain, TARGET *target );

View File

@@ -23,7 +23,7 @@
# include "strings.h"
# include <string.h>
static void call_bind_rule(
void call_bind_rule(
char* target_,
char* boundname_ )
{
@@ -70,7 +70,7 @@ static void call_bind_rule(
char *
search(
char *target,
time_t *time )
time_t *time )
{
PATHNAME f[1];
LIST *varlist;
@@ -87,7 +87,7 @@ search(
f->f_grist.len = 0;
if( varlist = var_get( "LOCATE" ) )
{
{
f->f_root.ptr = varlist->string;
f->f_root.len = strlen( varlist->string );

View File

@@ -188,6 +188,12 @@ load_builtins()
bind_builtin( "PWD" ,
builtin_pwd, 0, args );
}
{
char * args[] = { "target", "*", ":", "path", "*", 0 };
bind_builtin( "SEARCH_FOR_TARGET",
builtin_search_for_target, 0, args );
}
}
/*
@@ -682,6 +688,16 @@ builtin_update( PARSE *parse, FRAME *frame)
return L0;
}
LIST*
builtin_search_for_target( PARSE *parse, FRAME *frame )
{
LIST* arg1 = lol_get( frame->args, 0 );
LIST* arg2 = lol_get( frame->args, 1 );
TARGET* t = search_for_target( arg1->string, arg2 );
return list_new( L0, t->name );
}
static void lol_build( LOL* lol, char** elements )
{
LIST* l = L0;

View File

@@ -30,5 +30,6 @@ LIST *builtin_caller_module( PARSE *parse, FRAME *args );
LIST *builtin_backtrace( PARSE *parse, FRAME *args );
LIST *builtin_pwd( PARSE *parse, FRAME *args );
LIST *builtin_update( PARSE *parse, FRAME *args );
LIST *builtin_search_for_target( PARSE *parse, FRAME *args );
#endif

View File

@@ -129,6 +129,11 @@ make(
memset( (char *)counts, 0, sizeof( *counts ) );
/* First bind all targets with LOCATE_TARGET setting. This is
needed to correctly handle dependencies to generated headers.
*/
bind_explicitly_located_targets();
for( i = 0; i < n_targets; i++ )
{
TARGET *t = bindtarget( targets[i] );

View File

@@ -12,6 +12,9 @@
# include "newstr.h"
# include "hash.h"
# include "modules.h"
# include "search.h"
# include "lists.h"
# include "pathsys.h"
/* This file is ALSO:
* (C) Copyright David Abrahams 2001. Permission to copy, use,
@@ -49,6 +52,15 @@ static void set_rule_actions( RULE* rule, rule_actions* actions );
static void set_rule_body( RULE* rule, argument_list* args, PARSE* procedure );
static struct hash *targethash = 0;
typedef struct _located_target LOCATED_TARGET ;
struct _located_target {
char* file_name;
TARGET* target;
};
static struct hash *located_targets = 0;
/*
@@ -119,6 +131,118 @@ bindtarget( char *targetname )
return t;
}
static void bind_explicitly_located_target(void* xtarget, void* data)
{
TARGET* t = (TARGET*)xtarget;
if (! (t->flags & T_FLAG_NOTFILE) )
{
/* Check if there's a setting for LOCATE_TARGET */
SETTINGS* s = t->settings;
for(; s ; s = s->next)
{
if (strcmp(s->symbol, "LOCATE") == 0)
{
pushsettings(t->settings);
t->boundname = search( t->name, &t->time );
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
popsettings(t->settings);
{
LOCATED_TARGET lt = { t->boundname, t }, *lta = &lt;
if (!located_targets)
located_targets = hashinit( sizeof(LOCATED_TARGET),
"located targets" );
/* TODO: should check if we've entered the item or not. */
hashenter(located_targets, (HASHDATA **)&lta);
}
break;
}
}
}
}
void bind_explicitly_located_targets()
{
hashenumerate(targethash, bind_explicitly_located_target, (void*)0);
}
/* TODO: this is probably not a good idea to use functions in other modules like
that. */
void call_bind_rule(char* target, char* boundname);
TARGET* search_for_target ( char * name, LIST* search_path )
{
PATHNAME f[1];
string buf[1];
LOCATED_TARGET lt, *lta = &lt;
time_t time;
int found = 0;
TARGET* result;
string_new( buf );
path_parse( name, f );
f->f_grist.ptr = 0;
f->f_grist.len = 0;
while( search_path )
{
f->f_root.ptr = search_path->string;
f->f_root.len = strlen( search_path->string );
string_truncate( buf, 0 );
path_build( f, buf, 1 );
lt.file_name = buf->value ;
if (! located_targets )
located_targets = hashinit( sizeof(LOCATED_TARGET),
"located targets" );
if ( hashcheck( located_targets, (HASHDATA **)&lta ) )
{
return lta->target;
}
timestamp( buf->value, &time );
if (time)
{
found = 1;
break;
}
search_path = list_next( search_path );
}
if ( ! found )
{
f->f_root.ptr = 0;
f->f_root.len = 0;
string_truncate( buf, 0 );
path_build( f, buf, 1 );
timestamp( buf->value, &time );
}
result = bindtarget( name );
result->boundname = newstr( buf->value );
result->time = time;
result->binding = time ? T_BIND_EXISTS : T_BIND_MISSING;
call_bind_rule( result->name, result->boundname );
string_free( buf );
return result;
}
/*
* touchtarget() - mark a target to simulate being new
*/

View File

@@ -216,6 +216,8 @@ RULE* import_rule( RULE* source, module* m, char* name );
RULE* new_rule_body( module* m, char* rulename, argument_list* args, PARSE* procedure, int export );
RULE* new_rule_actions( module* m, char* rulename, char* command, LIST* bindlist, int flags );
TARGET *bindtarget( char *targetname );
void bind_explicitly_located_targets();
TARGET* search_for_targets ( char * name, LIST* search_path );
void touchtarget( char *t );
TARGETS *targetlist( TARGETS *chain, LIST *targets );
TARGETS *targetentry( TARGETS *chain, TARGET *target );

View File

@@ -23,7 +23,7 @@
# include "strings.h"
# include <string.h>
static void call_bind_rule(
void call_bind_rule(
char* target_,
char* boundname_ )
{
@@ -70,7 +70,7 @@ static void call_bind_rule(
char *
search(
char *target,
time_t *time )
time_t *time )
{
PATHNAME f[1];
LIST *varlist;
@@ -87,7 +87,7 @@ search(
f->f_grist.len = 0;
if( varlist = var_get( "LOCATE" ) )
{
{
f->f_root.ptr = varlist->string;
f->f_root.len = strlen( varlist->string );

View File

@@ -46,7 +46,60 @@ rule c-scanner ( includes * )
rule process ( target : matches * )
{
ECHO "c-scanner: processing target" $(target) with $(matches) ;
local angle = [ regex.transform $(matches) : "<(.*)>" ] ;
local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ;
# CONSIDER: the new scoping rule seem to defeat "on target" variables.
local g = [ on $(target) return $(HDRGRIST) ] ;
local b = [ path.native [ path.parent [ path.make
[ virtual-target.binding $(target) ] ] ] ] ;
# Attach binding of including file to included targets.
# When target is directly created from virtual target
# this extra information is unnecessary. But in other
# cases, it allows to distinguish between two headers of the
# same name included from different places.
# We don't need this extra information for angle includes,
# since they should not depend on including file (we can't
# get literal "." in include path).
local g2 = $(g)"#"$(b) ;
angle = $(angle:G=$(g)) ;
quoted = $(quoted:G=$(g2)) ;
for local i in $(angle)
{
local i2 = [ SEARCH_FOR_TARGET $(i) : $(self.includes:G=) ] ;
INCLUDES $(target) : $(i2) ;
if $(i2) = $(i)
{
NOCARE $(i) ;
SEARCH on $(i) = $(self.includes:G=) ;
}
}
ECHO "Binding is" [ virtual-target.binding $(target) ] ;
for local i in $(quoted)
{
local path = $(b) $(self.includes:G=) ;
local i2 = [ SEARCH_FOR_TARGET $(i) : $(path) ] ;
INCLUDES $(target) : $(i2) ;
if $(i2) = $(i)
{
NOCARE $(i) ;
SEARCH on $(i) = $(path) ;
}
}
BINDRULE on $(angle) $(quoted) = virtual-target.remember-binding ;
# Just propagate current scanner to includes, in a hope
# that includes do not change scanners.
for local a in $(angle)
{
scanner.install $(__name__) : $(a) : $(target) ;
}
}
}

0
test/dependency-test/b.h Normal file
View File

View File

@@ -9,4 +9,44 @@ t.run_build_system()
# Do not bother checking for created files now.
# Check handling of first level includes.
# Both 'a' and 'b' include "a.h" and should be updated
t.touch("a.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/b")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/b.o")
t.expect_nothing_more()
# Only 'a' include <a.h> and should be updated
t.touch("src1/a.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_nothing_more()
# "src/a.h" includes "b.h" (in the same dir)
t.touch("src1/b.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_nothing_more()
t.touch("b.h")
t.run_build_system()
t.expect_nothing_more()
# Test dependency on generated header.
# TODO: we have also to check that generated header is found correctly
# if it is different for different subvariants. Lacking any toolset
# support, this check will be implemented later.
t.touch("x.foo")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.cleanup()

View File

View File

@@ -9,4 +9,44 @@ t.run_build_system()
# Do not bother checking for created files now.
# Check handling of first level includes.
# Both 'a' and 'b' include "a.h" and should be updated
t.touch("a.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/b")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/b.o")
t.expect_nothing_more()
# Only 'a' include <a.h> and should be updated
t.touch("src1/a.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_nothing_more()
# "src/a.h" includes "b.h" (in the same dir)
t.touch("src1/b.h")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a")
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.expect_nothing_more()
t.touch("b.h")
t.run_build_system()
t.expect_nothing_more()
# Test dependency on generated header.
# TODO: we have also to check that generated header is found correctly
# if it is different for different subvariants. Lacking any toolset
# support, this check will be implemented later.
t.touch("x.foo")
t.run_build_system()
t.expect_touch("bin/gcc/debug/shared-false/threading-single/a.o")
t.cleanup()

View File

@@ -46,7 +46,60 @@ rule c-scanner ( includes * )
rule process ( target : matches * )
{
ECHO "c-scanner: processing target" $(target) with $(matches) ;
local angle = [ regex.transform $(matches) : "<(.*)>" ] ;
local quoted = [ regex.transform $(matches) : "\"(.*)\"" ] ;
# CONSIDER: the new scoping rule seem to defeat "on target" variables.
local g = [ on $(target) return $(HDRGRIST) ] ;
local b = [ path.native [ path.parent [ path.make
[ virtual-target.binding $(target) ] ] ] ] ;
# Attach binding of including file to included targets.
# When target is directly created from virtual target
# this extra information is unnecessary. But in other
# cases, it allows to distinguish between two headers of the
# same name included from different places.
# We don't need this extra information for angle includes,
# since they should not depend on including file (we can't
# get literal "." in include path).
local g2 = $(g)"#"$(b) ;
angle = $(angle:G=$(g)) ;
quoted = $(quoted:G=$(g2)) ;
for local i in $(angle)
{
local i2 = [ SEARCH_FOR_TARGET $(i) : $(self.includes:G=) ] ;
INCLUDES $(target) : $(i2) ;
if $(i2) = $(i)
{
NOCARE $(i) ;
SEARCH on $(i) = $(self.includes:G=) ;
}
}
ECHO "Binding is" [ virtual-target.binding $(target) ] ;
for local i in $(quoted)
{
local path = $(b) $(self.includes:G=) ;
local i2 = [ SEARCH_FOR_TARGET $(i) : $(path) ] ;
INCLUDES $(target) : $(i2) ;
if $(i2) = $(i)
{
NOCARE $(i) ;
SEARCH on $(i) = $(path) ;
}
}
BINDRULE on $(angle) $(quoted) = virtual-target.remember-binding ;
# Just propagate current scanner to includes, in a hope
# that includes do not change scanners.
for local a in $(angle)
{
scanner.install $(__name__) : $(a) : $(target) ;
}
}
}