From 1c8db11b601b65eb68c42f8a5bf06fa07b1df81b Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Wed, 2 Oct 2002 11:56:32 +0000 Subject: [PATCH] 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] --- historic/jam/src/builtins.c | 16 +++++ historic/jam/src/builtins.h | 1 + historic/jam/src/make.c | 5 ++ historic/jam/src/rules.c | 124 ++++++++++++++++++++++++++++++++++++ historic/jam/src/rules.h | 2 + historic/jam/src/search.c | 6 +- jam_src/builtins.c | 16 +++++ jam_src/builtins.h | 1 + jam_src/make.c | 5 ++ jam_src/rules.c | 124 ++++++++++++++++++++++++++++++++++++ jam_src/rules.h | 2 + jam_src/search.c | 6 +- new/builtin.jam | 55 +++++++++++++++- test/dependency-test/b.h | 0 test/dependency_test.py | 40 ++++++++++++ v2/test/dependency-test/b.h | 0 v2/test/dependency_test.py | 40 ++++++++++++ v2/tools/builtin.jam | 55 +++++++++++++++- 18 files changed, 490 insertions(+), 8 deletions(-) create mode 100644 test/dependency-test/b.h create mode 100644 v2/test/dependency-test/b.h diff --git a/historic/jam/src/builtins.c b/historic/jam/src/builtins.c index 2fb6472a6..02a0b1423 100644 --- a/historic/jam/src/builtins.c +++ b/historic/jam/src/builtins.c @@ -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; diff --git a/historic/jam/src/builtins.h b/historic/jam/src/builtins.h index 5c915faa0..867edb125 100644 --- a/historic/jam/src/builtins.h +++ b/historic/jam/src/builtins.h @@ -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 diff --git a/historic/jam/src/make.c b/historic/jam/src/make.c index 801157f5c..63b36d5fa 100644 --- a/historic/jam/src/make.c +++ b/historic/jam/src/make.c @@ -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] ); diff --git a/historic/jam/src/rules.c b/historic/jam/src/rules.c index 3ba1fac57..949314118 100644 --- a/historic/jam/src/rules.c +++ b/historic/jam/src/rules.c @@ -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 = < + 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 **)<a); + } + + 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 = < + 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 **)<a ) ) + { + 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 */ diff --git a/historic/jam/src/rules.h b/historic/jam/src/rules.h index 5f96c8c32..cc3dd6c91 100644 --- a/historic/jam/src/rules.h +++ b/historic/jam/src/rules.h @@ -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 ); diff --git a/historic/jam/src/search.c b/historic/jam/src/search.c index f41ed34fb..82e59fee3 100644 --- a/historic/jam/src/search.c +++ b/historic/jam/src/search.c @@ -23,7 +23,7 @@ # include "strings.h" # include -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 ); diff --git a/jam_src/builtins.c b/jam_src/builtins.c index 2fb6472a6..02a0b1423 100644 --- a/jam_src/builtins.c +++ b/jam_src/builtins.c @@ -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; diff --git a/jam_src/builtins.h b/jam_src/builtins.h index 5c915faa0..867edb125 100644 --- a/jam_src/builtins.h +++ b/jam_src/builtins.h @@ -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 diff --git a/jam_src/make.c b/jam_src/make.c index 801157f5c..63b36d5fa 100644 --- a/jam_src/make.c +++ b/jam_src/make.c @@ -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] ); diff --git a/jam_src/rules.c b/jam_src/rules.c index 3ba1fac57..949314118 100644 --- a/jam_src/rules.c +++ b/jam_src/rules.c @@ -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 = < + 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 **)<a); + } + + 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 = < + 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 **)<a ) ) + { + 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 */ diff --git a/jam_src/rules.h b/jam_src/rules.h index 5f96c8c32..cc3dd6c91 100644 --- a/jam_src/rules.h +++ b/jam_src/rules.h @@ -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 ); diff --git a/jam_src/search.c b/jam_src/search.c index f41ed34fb..82e59fee3 100644 --- a/jam_src/search.c +++ b/jam_src/search.c @@ -23,7 +23,7 @@ # include "strings.h" # include -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 ); diff --git a/new/builtin.jam b/new/builtin.jam index 0084e0022..f2f2ba2a3 100644 --- a/new/builtin.jam +++ b/new/builtin.jam @@ -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) ; + } } } diff --git a/test/dependency-test/b.h b/test/dependency-test/b.h new file mode 100644 index 000000000..e69de29bb diff --git a/test/dependency_test.py b/test/dependency_test.py index 54e86a884..bf0dc2811 100644 --- a/test/dependency_test.py +++ b/test/dependency_test.py @@ -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 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() diff --git a/v2/test/dependency-test/b.h b/v2/test/dependency-test/b.h new file mode 100644 index 000000000..e69de29bb diff --git a/v2/test/dependency_test.py b/v2/test/dependency_test.py index 54e86a884..bf0dc2811 100644 --- a/v2/test/dependency_test.py +++ b/v2/test/dependency_test.py @@ -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 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() diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index 0084e0022..f2f2ba2a3 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -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) ; + } } }