mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
Refactorings.
* new/targets.jam
(main-target.select-alternatives): New rule.
(main-target.generate): Simplify, using the above.
* new/errors.jam
(error): Do EXIT if --no-error-backtrace is given.
* tests/alternatives.py: New test.
[SVN r17673]
This commit is contained in:
@@ -138,6 +138,7 @@ rule error ( messages * : * )
|
||||
}
|
||||
}
|
||||
}
|
||||
EXIT ;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
130
new/targets.jam
130
new/targets.jam
@@ -262,6 +262,28 @@ rule main-target ( name : project )
|
||||
self.direct-request.$(base:J=-) = $(property-set) ;
|
||||
}
|
||||
}
|
||||
|
||||
local rule select-alternatives ( property-set )
|
||||
{
|
||||
local viable ; # alternatives that may be used
|
||||
local ranks ; # ranks for viable alternatives
|
||||
for local v in $(self.alternatives)
|
||||
{
|
||||
# For now, alternative should be derived from 'basic-target'.
|
||||
# We'll see if this restriction if reasonable.
|
||||
assert.equal [ is-a $(v) : basic-target ] : true ;
|
||||
local m = [ $(v).match-rank $(property-set) ] ;
|
||||
|
||||
if $(m)
|
||||
{
|
||||
viable += $(v) ;
|
||||
ranks += $(m) ;
|
||||
}
|
||||
}
|
||||
|
||||
local best = [ sequence.select-highest-ranked $(viable) : $(ranks) ] ;
|
||||
return $(best) ;
|
||||
}
|
||||
|
||||
|
||||
# Select an alternative for this main target, by finding all alternatives
|
||||
@@ -277,83 +299,42 @@ rule main-target ( name : project )
|
||||
property-set = $(ep) ;
|
||||
}
|
||||
|
||||
# Try to generate all the alternatives.
|
||||
local alternatives = [ new vector ] ;
|
||||
|
||||
for local v in $(self.alternatives)
|
||||
{
|
||||
local vtargets = [ $(v).generate $(property-set) ] ;
|
||||
if $(vtargets) && $(vtargets[1]) != "@error"
|
||||
{
|
||||
$(alternatives).push-back [ new vector $(v) $(vtargets) ] ;
|
||||
}
|
||||
}
|
||||
if [ $(alternatives).empty ]
|
||||
local best-alternatives = [ select-alternatives $(property-set) ] ;
|
||||
if ! $(best-alternatives)
|
||||
{
|
||||
# TODO: probably, should explain, for each alternative,
|
||||
# why it can't be build.
|
||||
print.wrapped-text
|
||||
"warning: skipped build of" [ full-name ]
|
||||
"with properties" [ $(property-set).raw ] ;
|
||||
} else {
|
||||
local result ;
|
||||
if [ $(alternatives).size ] = 1
|
||||
{
|
||||
result = [ $(alternatives).get-at 1 ] ;
|
||||
result = $(result[2-]) ;
|
||||
"with properties" [ $(property-set).raw ] ;
|
||||
}
|
||||
else if $(best-alternatives[2])
|
||||
{
|
||||
# TODO: again, a better error message in in order.
|
||||
errors.error "Ambiguous alternatives for main target" [ full-name ] ;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Find the alternative with the longest set of non-free and
|
||||
# non-indicental requirements and are in 'properties'
|
||||
local r = [ $(alternatives).indices ] ;
|
||||
|
||||
# First compute the length of requirements sets
|
||||
local req-length ;
|
||||
for local p in $(r)
|
||||
local result = [ $(best-alternatives).generate $(property-set) ] ;
|
||||
|
||||
# Mark all targets in result as roots
|
||||
for local t in $(result)
|
||||
{
|
||||
local target = [ $(alternatives).at $(p) : 1 ] ;
|
||||
# FIXME: in general, 'abstract-target' derivatives might not
|
||||
# have 'requirements' method.
|
||||
# assert.equal [ is-a $(target) : basic-target ] : true ;
|
||||
local req = [ $(target).requirements ] ;
|
||||
req = [ $(req).base ] ;
|
||||
req-length += [ sequence.length
|
||||
[ set.intersection $(req) : [ $(property-set).raw ] ] ] ;
|
||||
$(t).root true ;
|
||||
}
|
||||
|
||||
local best = [ sequence.select-highest-ranked $(r) : $(req-length) ] ;
|
||||
|
||||
if ! $(best[2])
|
||||
{
|
||||
local index = $(best[1]) ;
|
||||
result = [ $(alternatives).get-at $(index) ] ;
|
||||
result = $(result[2-]) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
error "Ambiguous alternatives for main target" [ full-name ] ;
|
||||
}
|
||||
}
|
||||
|
||||
# Mark all targets in result as roots
|
||||
for local t in $(result)
|
||||
{
|
||||
$(t).root true ;
|
||||
}
|
||||
|
||||
# Process all vtargets that will be created if this main target
|
||||
# is created.
|
||||
local all-targets =
|
||||
[ sequence.transform virtual-target.traverse : $(result) ] ;
|
||||
local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ;
|
||||
for local v in $(all-targets)
|
||||
{
|
||||
$(v).dg $(dg) ;
|
||||
}
|
||||
# Process all vtargets that will be created if this main target
|
||||
# is created.
|
||||
local all-targets =
|
||||
[ sequence.transform virtual-target.traverse : $(result) ] ;
|
||||
local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ;
|
||||
for local v in $(all-targets)
|
||||
{
|
||||
$(v).dg $(dg) ;
|
||||
}
|
||||
|
||||
# Now return virtual targets for the only alternative
|
||||
return $(result) ;
|
||||
# Now return virtual targets for the only alternative
|
||||
return $(result) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,8 +442,25 @@ rule basic-target ( name : project
|
||||
{
|
||||
errors.error "gristed element in sources for" [ full-name ] ;
|
||||
}
|
||||
|
||||
|
||||
# Returns a number which estimates this targets's suitability for
|
||||
# building with the given 'property-set'. Among several alternatives
|
||||
# for a main target, the one with greatest match-rank will be used
|
||||
# to do actual generation
|
||||
rule match-rank ( property-set )
|
||||
{
|
||||
# First check if our requirements can be satisfied.
|
||||
local rproperties = [ $(property-set).refine $(self.requirements) ] ;
|
||||
if $(rproperties[1]) != "@error"
|
||||
{
|
||||
# Returns the number of properties common to requirements
|
||||
# and build request.
|
||||
return [ sequence.length [ set.intersection
|
||||
[ $(self.requirements).base ] :
|
||||
[ $(property-set).raw ] ] ] ;
|
||||
}
|
||||
}
|
||||
|
||||
# Applies default-build if 'properties' are empty or
|
||||
# have only single <toolset> element.
|
||||
# Generates sources. Calls 'construct'
|
||||
|
||||
59
test/alternatives.py
Normal file
59
test/alternatives.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Test main target alternatives.
|
||||
|
||||
from BoostBuild import Tester
|
||||
t = Tester()
|
||||
|
||||
|
||||
# Test that basic alternatives selection works.
|
||||
t.write("project-root.jam", " ")
|
||||
t.write("Jamfile", """
|
||||
|
||||
exe a : a_empty.cpp ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
t.write("a_empty.cpp", "")
|
||||
t.write("a.cpp", "int main() { return 0; }\n")
|
||||
|
||||
t.run_build_system("release")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that only properties which are in build request
|
||||
# matters when selection alternative. IOW, alternative
|
||||
# with <variant>release is better than one with
|
||||
# <variant>debug when building release version.
|
||||
t.write("Jamfile", """
|
||||
|
||||
exe a : a_empty.cpp : <variant>debug ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
|
||||
t.rm("bin/$toolset/release/a.exe")
|
||||
t.run_build_system("release")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that free properties do not matter. We really don't
|
||||
# want <cxxflags> property in build request to affect
|
||||
# alternative selection.
|
||||
t.write("Jamfile", """
|
||||
exe a : a_empty.cpp : <variant>debug <define>FOO <include>BAR ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
|
||||
t.rm("bin/$toolset/release/a.exe")
|
||||
t.run_build_system("release define=FOO")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that abibuity is reported correctly
|
||||
t.write("Jamfile", """
|
||||
exe a : a_empty.cpp ;
|
||||
exe a : a.cpp ;
|
||||
""")
|
||||
expected="""error: Ambiguous alternatives for main target ./a
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", status=1, stdout=expected)
|
||||
|
||||
|
||||
t.cleanup()
|
||||
@@ -40,6 +40,7 @@ t.copy("lib/Jamfile2", "lib/Jamfile")
|
||||
|
||||
expected="""error: Requirements for project at 'lib' conflict with parent's.
|
||||
Explanation: link-incompatible properties <threading>single and <threading>multi
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", stdout=expected, status=None)
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ tests = [ "project_test1",
|
||||
"build_dir",
|
||||
"searched_lib",
|
||||
"make_rule",
|
||||
"alias"]
|
||||
"alias",
|
||||
"alternatives",
|
||||
]
|
||||
|
||||
if os.name == 'posix':
|
||||
tests.append("symlink")
|
||||
|
||||
@@ -262,6 +262,28 @@ rule main-target ( name : project )
|
||||
self.direct-request.$(base:J=-) = $(property-set) ;
|
||||
}
|
||||
}
|
||||
|
||||
local rule select-alternatives ( property-set )
|
||||
{
|
||||
local viable ; # alternatives that may be used
|
||||
local ranks ; # ranks for viable alternatives
|
||||
for local v in $(self.alternatives)
|
||||
{
|
||||
# For now, alternative should be derived from 'basic-target'.
|
||||
# We'll see if this restriction if reasonable.
|
||||
assert.equal [ is-a $(v) : basic-target ] : true ;
|
||||
local m = [ $(v).match-rank $(property-set) ] ;
|
||||
|
||||
if $(m)
|
||||
{
|
||||
viable += $(v) ;
|
||||
ranks += $(m) ;
|
||||
}
|
||||
}
|
||||
|
||||
local best = [ sequence.select-highest-ranked $(viable) : $(ranks) ] ;
|
||||
return $(best) ;
|
||||
}
|
||||
|
||||
|
||||
# Select an alternative for this main target, by finding all alternatives
|
||||
@@ -277,83 +299,42 @@ rule main-target ( name : project )
|
||||
property-set = $(ep) ;
|
||||
}
|
||||
|
||||
# Try to generate all the alternatives.
|
||||
local alternatives = [ new vector ] ;
|
||||
|
||||
for local v in $(self.alternatives)
|
||||
{
|
||||
local vtargets = [ $(v).generate $(property-set) ] ;
|
||||
if $(vtargets) && $(vtargets[1]) != "@error"
|
||||
{
|
||||
$(alternatives).push-back [ new vector $(v) $(vtargets) ] ;
|
||||
}
|
||||
}
|
||||
if [ $(alternatives).empty ]
|
||||
local best-alternatives = [ select-alternatives $(property-set) ] ;
|
||||
if ! $(best-alternatives)
|
||||
{
|
||||
# TODO: probably, should explain, for each alternative,
|
||||
# why it can't be build.
|
||||
print.wrapped-text
|
||||
"warning: skipped build of" [ full-name ]
|
||||
"with properties" [ $(property-set).raw ] ;
|
||||
} else {
|
||||
local result ;
|
||||
if [ $(alternatives).size ] = 1
|
||||
{
|
||||
result = [ $(alternatives).get-at 1 ] ;
|
||||
result = $(result[2-]) ;
|
||||
"with properties" [ $(property-set).raw ] ;
|
||||
}
|
||||
else if $(best-alternatives[2])
|
||||
{
|
||||
# TODO: again, a better error message in in order.
|
||||
errors.error "Ambiguous alternatives for main target" [ full-name ] ;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Find the alternative with the longest set of non-free and
|
||||
# non-indicental requirements and are in 'properties'
|
||||
local r = [ $(alternatives).indices ] ;
|
||||
|
||||
# First compute the length of requirements sets
|
||||
local req-length ;
|
||||
for local p in $(r)
|
||||
local result = [ $(best-alternatives).generate $(property-set) ] ;
|
||||
|
||||
# Mark all targets in result as roots
|
||||
for local t in $(result)
|
||||
{
|
||||
local target = [ $(alternatives).at $(p) : 1 ] ;
|
||||
# FIXME: in general, 'abstract-target' derivatives might not
|
||||
# have 'requirements' method.
|
||||
# assert.equal [ is-a $(target) : basic-target ] : true ;
|
||||
local req = [ $(target).requirements ] ;
|
||||
req = [ $(req).base ] ;
|
||||
req-length += [ sequence.length
|
||||
[ set.intersection $(req) : [ $(property-set).raw ] ] ] ;
|
||||
$(t).root true ;
|
||||
}
|
||||
|
||||
local best = [ sequence.select-highest-ranked $(r) : $(req-length) ] ;
|
||||
|
||||
if ! $(best[2])
|
||||
{
|
||||
local index = $(best[1]) ;
|
||||
result = [ $(alternatives).get-at $(index) ] ;
|
||||
result = $(result[2-]) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
error "Ambiguous alternatives for main target" [ full-name ] ;
|
||||
}
|
||||
}
|
||||
|
||||
# Mark all targets in result as roots
|
||||
for local t in $(result)
|
||||
{
|
||||
$(t).root true ;
|
||||
}
|
||||
|
||||
# Process all vtargets that will be created if this main target
|
||||
# is created.
|
||||
local all-targets =
|
||||
[ sequence.transform virtual-target.traverse : $(result) ] ;
|
||||
local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ;
|
||||
for local v in $(all-targets)
|
||||
{
|
||||
$(v).dg $(dg) ;
|
||||
}
|
||||
# Process all vtargets that will be created if this main target
|
||||
# is created.
|
||||
local all-targets =
|
||||
[ sequence.transform virtual-target.traverse : $(result) ] ;
|
||||
local dg = [ new subvariant-dg $(__name__) : $(property-set) : $(all-targets) ] ;
|
||||
for local v in $(all-targets)
|
||||
{
|
||||
$(v).dg $(dg) ;
|
||||
}
|
||||
|
||||
# Now return virtual targets for the only alternative
|
||||
return $(result) ;
|
||||
# Now return virtual targets for the only alternative
|
||||
return $(result) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,8 +442,25 @@ rule basic-target ( name : project
|
||||
{
|
||||
errors.error "gristed element in sources for" [ full-name ] ;
|
||||
}
|
||||
|
||||
|
||||
# Returns a number which estimates this targets's suitability for
|
||||
# building with the given 'property-set'. Among several alternatives
|
||||
# for a main target, the one with greatest match-rank will be used
|
||||
# to do actual generation
|
||||
rule match-rank ( property-set )
|
||||
{
|
||||
# First check if our requirements can be satisfied.
|
||||
local rproperties = [ $(property-set).refine $(self.requirements) ] ;
|
||||
if $(rproperties[1]) != "@error"
|
||||
{
|
||||
# Returns the number of properties common to requirements
|
||||
# and build request.
|
||||
return [ sequence.length [ set.intersection
|
||||
[ $(self.requirements).base ] :
|
||||
[ $(property-set).raw ] ] ] ;
|
||||
}
|
||||
}
|
||||
|
||||
# Applies default-build if 'properties' are empty or
|
||||
# have only single <toolset> element.
|
||||
# Generates sources. Calls 'construct'
|
||||
|
||||
@@ -138,6 +138,7 @@ rule error ( messages * : * )
|
||||
}
|
||||
}
|
||||
}
|
||||
EXIT ;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
59
v2/test/alternatives.py
Normal file
59
v2/test/alternatives.py
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Test main target alternatives.
|
||||
|
||||
from BoostBuild import Tester
|
||||
t = Tester()
|
||||
|
||||
|
||||
# Test that basic alternatives selection works.
|
||||
t.write("project-root.jam", " ")
|
||||
t.write("Jamfile", """
|
||||
|
||||
exe a : a_empty.cpp ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
t.write("a_empty.cpp", "")
|
||||
t.write("a.cpp", "int main() { return 0; }\n")
|
||||
|
||||
t.run_build_system("release")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that only properties which are in build request
|
||||
# matters when selection alternative. IOW, alternative
|
||||
# with <variant>release is better than one with
|
||||
# <variant>debug when building release version.
|
||||
t.write("Jamfile", """
|
||||
|
||||
exe a : a_empty.cpp : <variant>debug ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
|
||||
t.rm("bin/$toolset/release/a.exe")
|
||||
t.run_build_system("release")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that free properties do not matter. We really don't
|
||||
# want <cxxflags> property in build request to affect
|
||||
# alternative selection.
|
||||
t.write("Jamfile", """
|
||||
exe a : a_empty.cpp : <variant>debug <define>FOO <include>BAR ;
|
||||
exe a : a.cpp : <variant>release ;
|
||||
""")
|
||||
|
||||
t.rm("bin/$toolset/release/a.exe")
|
||||
t.run_build_system("release define=FOO")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that abibuity is reported correctly
|
||||
t.write("Jamfile", """
|
||||
exe a : a_empty.cpp ;
|
||||
exe a : a.cpp ;
|
||||
""")
|
||||
expected="""error: Ambiguous alternatives for main target ./a
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", status=1, stdout=expected)
|
||||
|
||||
|
||||
t.cleanup()
|
||||
@@ -40,6 +40,7 @@ t.copy("lib/Jamfile2", "lib/Jamfile")
|
||||
|
||||
expected="""error: Requirements for project at 'lib' conflict with parent's.
|
||||
Explanation: link-incompatible properties <threading>single and <threading>multi
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", stdout=expected, status=None)
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ tests = [ "project_test1",
|
||||
"build_dir",
|
||||
"searched_lib",
|
||||
"make_rule",
|
||||
"alias"]
|
||||
"alias",
|
||||
"alternatives",
|
||||
]
|
||||
|
||||
if os.name == 'posix':
|
||||
tests.append("symlink")
|
||||
|
||||
Reference in New Issue
Block a user