diff --git a/new/builtin.jam b/new/builtin.jam index bd2c0ad6f..41a92f497 100644 --- a/new/builtin.jam +++ b/new/builtin.jam @@ -377,15 +377,16 @@ type.set-scanner CPP : c-scanner ; type.register H : h hpp ; type.register C : c ; -# Generator for 'lib' target, which relays to generators for either -# "STATIC_LIB" or "SHARED_LIB", depending on the value of -# "shared" feature. -rule lib-generator ( ) -{ - generator.__init__ lib-generator true : : LIB : LIB ; +rule lib-target-class ( name : project + : sources * : requirements * : default-build * : usage-requirements * ) +{ + basic-target.__init__ $(name) : $(project) + : $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ; - rule run ( project name ? : property-set : sources * ) - { + IMPORT builtin : generators.construct : $(__name__) : generators.construct ; + + rule construct ( source-targets * : property-set ) + { local properties = [ $(property-set).raw ] ; # Determine the needed target type local actual-type ; @@ -401,15 +402,43 @@ rule lib-generator ( ) { actual-type = STATIC_LIB ; } + property-set = [ $(property-set).add-raw LIB ] ; # Construct the target. - return [ generators.construct $(project) $(name) : $(actual-type) - : $(property-set) : $(sources) ] ; + return [ generators.construct $(self.project) $(self.name) : $(actual-type) + : $(property-set) : $(source-targets) : return-all ] ; + } + + rule compute-usage-requirements ( rproperties ) + { + local result = [ basic-target.compute-usage-requirements $(rproperties) ] ; + local search = [ $(rproperties).get ] ; + if $(search) + { + result = [ $(result).add [ + property-set.create $(search:G=) ] ] ; + } + return $(result) ; } + } +class lib-target-class : basic-target ; -class lib-generator : generator ; - -generators.register [ new lib-generator ] ; +rule lib ( name : sources * : requirements * : default-build * + : usage-requirements * ) +{ + local project = [ CALLER_MODULE ] ; + + # This is a circular module dependency, so it must be imported here + import targets ; + targets.main-target-alternative + [ new lib-target-class $(name) : $(project) + : $(sources) + : [ targets.main-target-requirements $(requirements) : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ] + ] ; +} +IMPORT $(__name__) : lib : : lib ; rule searched-lib-generator ( ) { @@ -555,7 +584,6 @@ rule link-action ( targets + : sources * : action-name : properties * ) if [ class.is-a $(s) : searched-lib-target ] { local name = [ $(s).real-name ] ; - local search = [ $(s).search ] ; if [ $(s).shared ] { properties2 += $(name) ; @@ -568,7 +596,6 @@ rule link-action ( targets + : sources * : action-name : properties * ) { properties2 += $(name) ; } - properties2 += $(search) ; } else { @@ -585,7 +612,7 @@ rule link-action ( targets + : sources * : action-name : properties * ) { rpaths = [ sequence.unique $(rpaths) ] ; properties2 += $(rpaths) ; - } + } return $(properties2) ; } diff --git a/new/generators.jam b/new/generators.jam index 4d4a66ab3..af7700415 100644 --- a/new/generators.jam +++ b/new/generators.jam @@ -970,7 +970,8 @@ local rule construct-without-caching ( # 'construct' in stack, returns only targets of requested 'target-type', # otherwise, returns also unused sources and additionally generated # targets. -rule construct ( project name ? : target-type multiple ? : property-set * : sources * ) +rule construct ( project name ? : target-type multiple ? : property-set * : sources * + : return-all ? ) { if (.construct-stack) { @@ -1014,7 +1015,13 @@ rule construct ( project name ? : target-type multiple ? : property-set * : sour local type = [ $(t).type ] ; assert.nonempty-variable type ; assert.nonempty-variable target-type ; - if $(type) = $(target-type) || [ type.is-derived $(type) $(target-type) ] + + # Return only targets of the requested type, unless 'return-all' + # is specified. If we don't do this, then all targets calling + # 'construct' will get unused target returned, which will break + # checking for unused sources a bit harder. + if $(return-all) || + $(type) = $(target-type) || [ type.is-derived $(type) $(target-type) ] { result2 += $(t) ; } diff --git a/new/property-set.jam b/new/property-set.jam index 718cd7c8e..283f3191c 100644 --- a/new/property-set.jam +++ b/new/property-set.jam @@ -163,6 +163,12 @@ local rule property-set ( raw-properties * ) return $(self.added.$(ps)) ; } + rule add-raw ( properties * ) + { + return [ add [ property-set.create $(properties) ] ] ; + } + + # Returns all values of 'feature'. rule get ( feature ) { diff --git a/new/targets.jam b/new/targets.jam index 8d967d3a1..2f9a22e7d 100644 --- a/new/targets.jam +++ b/new/targets.jam @@ -819,7 +819,7 @@ rule basic-target ( name : project # Given the set of generated targets, and refined build # properties, determines and sets appripriate usage requirements # on those targets. - local rule compute-usage-requirements ( rproperties ) + rule compute-usage-requirements ( rproperties ) { xusage-requirements = [ $(self.usage-requirements).evaluate-conditionals $(rproperties) ] ; @@ -855,8 +855,11 @@ rule basic-target ( name : project } - # Check that 'result' makes use of all the 'sources'. If not, - # issues a warning. Note that 'result' *can* be empty. For + # Check that 'result' makes use of all the 'sources', i.e. Specifically, + # that for all specified sources, at least one virtual target is either + # present in result directly, or as dependency of some returned virtual + # target. If this is not the case, issues a warning. + # Note that 'result' *can* be empty. For # example, in this use case: # alias platform-sources ; # alias platform-sources : a.cpp : NT ; @@ -866,7 +869,7 @@ rule basic-target ( name : project local used-sources ; for local r in $(result) { - used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ; #: includes-sources ] ; + used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ; } local group ; @@ -878,6 +881,7 @@ rule basic-target ( name : project } else { + # We've collected a group of targets that originates from single # dependency main target, and must check that at least one of them # is used. If no targets were created for dependency, it's OK. diff --git a/test/searched_lib.py b/test/searched_lib.py index f8e9a0b16..51d00cd57 100644 --- a/test/searched_lib.py +++ b/test/searched_lib.py @@ -30,6 +30,19 @@ else: t.copy("lib/bin/$toolset/debug/test_lib.dll", "lib/libtest_lib.dll") +# Test that the simplest usage of searched library works. +t.write('project-root.jam', '') +t.write('Jamfile', """ +exe main : main.cpp test_lib ; +lib test_lib : : test_lib lib ; +""") +t.write("main.cpp", """ +void foo(); +int main() { foo(); return 0; } +""") +t.run_build_system() +t.expect_addition("bin/$toolset/debug/main-target-main/main.exe") +t.rm("bin/$toolset/debug/main-target-main/main.exe") # A regression test: property referring to # searched-lib was mishandled. As the result, we were @@ -41,13 +54,7 @@ else: # This problem shows up when searched libs are in usage # requirements. -t.write('project-root.jam', 'import gcc ;') t.write('Jamfile', 'exe main : main.cpp d/d2/a ;') -t.write("main.cpp", """ -void foo(); -int main() { foo(); return 0; } -""") - t.write('d/d2/Jamfile', """ lib test_lib : : test_lib ../../lib ; lib a : a.cpp : : : test_lib ; diff --git a/test/unused/project-root.jam b/test/unused/project-root.jam index 148715306..8994c99d2 100644 --- a/test/unused/project-root.jam +++ b/test/unused/project-root.jam @@ -16,10 +16,10 @@ rule test-target-class ( name : project ) rule construct ( source-targets * : property-set ) { - if [ modules.peek : GENERATE_NOTHING ] - { - return ; - } + if [ modules.peek : GENERATE_NOTHING ] + { + return ; + } else if [ modules.peek : GENERATE_ONLY_UNUSABLE ] { return [ virtual-target.from-file b.x : $(self.project) ] @@ -28,14 +28,14 @@ rule test-target-class ( name : project ) else { return [ virtual-target.from-file b.x : $(self.project) ] - [ virtual-target.from-file b.cpp : $(self.project) ] - ; + [ virtual-target.from-file b.cpp : $(self.project) ] + ; } } rule compute-usage-requirements ( rproperties ) { - return [ property-set.create FOO ] ; + return [ property-set.create FOO ] ; } } class test-target-class : basic-target ; diff --git a/v2/build/generators.jam b/v2/build/generators.jam index 4d4a66ab3..af7700415 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -970,7 +970,8 @@ local rule construct-without-caching ( # 'construct' in stack, returns only targets of requested 'target-type', # otherwise, returns also unused sources and additionally generated # targets. -rule construct ( project name ? : target-type multiple ? : property-set * : sources * ) +rule construct ( project name ? : target-type multiple ? : property-set * : sources * + : return-all ? ) { if (.construct-stack) { @@ -1014,7 +1015,13 @@ rule construct ( project name ? : target-type multiple ? : property-set * : sour local type = [ $(t).type ] ; assert.nonempty-variable type ; assert.nonempty-variable target-type ; - if $(type) = $(target-type) || [ type.is-derived $(type) $(target-type) ] + + # Return only targets of the requested type, unless 'return-all' + # is specified. If we don't do this, then all targets calling + # 'construct' will get unused target returned, which will break + # checking for unused sources a bit harder. + if $(return-all) || + $(type) = $(target-type) || [ type.is-derived $(type) $(target-type) ] { result2 += $(t) ; } diff --git a/v2/build/property-set.jam b/v2/build/property-set.jam index 718cd7c8e..283f3191c 100644 --- a/v2/build/property-set.jam +++ b/v2/build/property-set.jam @@ -163,6 +163,12 @@ local rule property-set ( raw-properties * ) return $(self.added.$(ps)) ; } + rule add-raw ( properties * ) + { + return [ add [ property-set.create $(properties) ] ] ; + } + + # Returns all values of 'feature'. rule get ( feature ) { diff --git a/v2/build/targets.jam b/v2/build/targets.jam index 8d967d3a1..2f9a22e7d 100644 --- a/v2/build/targets.jam +++ b/v2/build/targets.jam @@ -819,7 +819,7 @@ rule basic-target ( name : project # Given the set of generated targets, and refined build # properties, determines and sets appripriate usage requirements # on those targets. - local rule compute-usage-requirements ( rproperties ) + rule compute-usage-requirements ( rproperties ) { xusage-requirements = [ $(self.usage-requirements).evaluate-conditionals $(rproperties) ] ; @@ -855,8 +855,11 @@ rule basic-target ( name : project } - # Check that 'result' makes use of all the 'sources'. If not, - # issues a warning. Note that 'result' *can* be empty. For + # Check that 'result' makes use of all the 'sources', i.e. Specifically, + # that for all specified sources, at least one virtual target is either + # present in result directly, or as dependency of some returned virtual + # target. If this is not the case, issues a warning. + # Note that 'result' *can* be empty. For # example, in this use case: # alias platform-sources ; # alias platform-sources : a.cpp : NT ; @@ -866,7 +869,7 @@ rule basic-target ( name : project local used-sources ; for local r in $(result) { - used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ; #: includes-sources ] ; + used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ; } local group ; @@ -878,6 +881,7 @@ rule basic-target ( name : project } else { + # We've collected a group of targets that originates from single # dependency main target, and must check that at least one of them # is used. If no targets were created for dependency, it's OK. diff --git a/v2/test/searched_lib.py b/v2/test/searched_lib.py index f8e9a0b16..51d00cd57 100644 --- a/v2/test/searched_lib.py +++ b/v2/test/searched_lib.py @@ -30,6 +30,19 @@ else: t.copy("lib/bin/$toolset/debug/test_lib.dll", "lib/libtest_lib.dll") +# Test that the simplest usage of searched library works. +t.write('project-root.jam', '') +t.write('Jamfile', """ +exe main : main.cpp test_lib ; +lib test_lib : : test_lib lib ; +""") +t.write("main.cpp", """ +void foo(); +int main() { foo(); return 0; } +""") +t.run_build_system() +t.expect_addition("bin/$toolset/debug/main-target-main/main.exe") +t.rm("bin/$toolset/debug/main-target-main/main.exe") # A regression test: property referring to # searched-lib was mishandled. As the result, we were @@ -41,13 +54,7 @@ else: # This problem shows up when searched libs are in usage # requirements. -t.write('project-root.jam', 'import gcc ;') t.write('Jamfile', 'exe main : main.cpp d/d2/a ;') -t.write("main.cpp", """ -void foo(); -int main() { foo(); return 0; } -""") - t.write('d/d2/Jamfile', """ lib test_lib : : test_lib ../../lib ; lib a : a.cpp : : : test_lib ; diff --git a/v2/test/unused/project-root.jam b/v2/test/unused/project-root.jam index 148715306..8994c99d2 100644 --- a/v2/test/unused/project-root.jam +++ b/v2/test/unused/project-root.jam @@ -16,10 +16,10 @@ rule test-target-class ( name : project ) rule construct ( source-targets * : property-set ) { - if [ modules.peek : GENERATE_NOTHING ] - { - return ; - } + if [ modules.peek : GENERATE_NOTHING ] + { + return ; + } else if [ modules.peek : GENERATE_ONLY_UNUSABLE ] { return [ virtual-target.from-file b.x : $(self.project) ] @@ -28,14 +28,14 @@ rule test-target-class ( name : project ) else { return [ virtual-target.from-file b.x : $(self.project) ] - [ virtual-target.from-file b.cpp : $(self.project) ] - ; + [ virtual-target.from-file b.cpp : $(self.project) ] + ; } } rule compute-usage-requirements ( rproperties ) { - return [ property-set.create FOO ] ; + return [ property-set.create FOO ] ; } } class test-target-class : basic-target ; diff --git a/v2/tools/builtin.jam b/v2/tools/builtin.jam index bd2c0ad6f..41a92f497 100644 --- a/v2/tools/builtin.jam +++ b/v2/tools/builtin.jam @@ -377,15 +377,16 @@ type.set-scanner CPP : c-scanner ; type.register H : h hpp ; type.register C : c ; -# Generator for 'lib' target, which relays to generators for either -# "STATIC_LIB" or "SHARED_LIB", depending on the value of -# "shared" feature. -rule lib-generator ( ) -{ - generator.__init__ lib-generator true : : LIB : LIB ; +rule lib-target-class ( name : project + : sources * : requirements * : default-build * : usage-requirements * ) +{ + basic-target.__init__ $(name) : $(project) + : $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ; - rule run ( project name ? : property-set : sources * ) - { + IMPORT builtin : generators.construct : $(__name__) : generators.construct ; + + rule construct ( source-targets * : property-set ) + { local properties = [ $(property-set).raw ] ; # Determine the needed target type local actual-type ; @@ -401,15 +402,43 @@ rule lib-generator ( ) { actual-type = STATIC_LIB ; } + property-set = [ $(property-set).add-raw LIB ] ; # Construct the target. - return [ generators.construct $(project) $(name) : $(actual-type) - : $(property-set) : $(sources) ] ; + return [ generators.construct $(self.project) $(self.name) : $(actual-type) + : $(property-set) : $(source-targets) : return-all ] ; + } + + rule compute-usage-requirements ( rproperties ) + { + local result = [ basic-target.compute-usage-requirements $(rproperties) ] ; + local search = [ $(rproperties).get ] ; + if $(search) + { + result = [ $(result).add [ + property-set.create $(search:G=) ] ] ; + } + return $(result) ; } + } +class lib-target-class : basic-target ; -class lib-generator : generator ; - -generators.register [ new lib-generator ] ; +rule lib ( name : sources * : requirements * : default-build * + : usage-requirements * ) +{ + local project = [ CALLER_MODULE ] ; + + # This is a circular module dependency, so it must be imported here + import targets ; + targets.main-target-alternative + [ new lib-target-class $(name) : $(project) + : $(sources) + : [ targets.main-target-requirements $(requirements) : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ] + ] ; +} +IMPORT $(__name__) : lib : : lib ; rule searched-lib-generator ( ) { @@ -555,7 +584,6 @@ rule link-action ( targets + : sources * : action-name : properties * ) if [ class.is-a $(s) : searched-lib-target ] { local name = [ $(s).real-name ] ; - local search = [ $(s).search ] ; if [ $(s).shared ] { properties2 += $(name) ; @@ -568,7 +596,6 @@ rule link-action ( targets + : sources * : action-name : properties * ) { properties2 += $(name) ; } - properties2 += $(search) ; } else { @@ -585,7 +612,7 @@ rule link-action ( targets + : sources * : action-name : properties * ) { rpaths = [ sequence.unique $(rpaths) ] ; properties2 += $(rpaths) ; - } + } return $(properties2) ; }