mirror of
https://github.com/boostorg/build.git
synced 2026-02-11 11:42:14 +00:00
Improve alternative selection.
* new/targets.jam (main-target.select-alternative): Rewrite. (main-target.generate-really): Issue error in all cases where best alternative could not be found. (basic-target.match): Renamed from 'match-rank' and changed. * boost_build_v2.html: Document new algorithm. * test/alternatives.py: More tests. [SVN r20331]
This commit is contained in:
@@ -22,12 +22,12 @@
|
||||
div.alert { color: red }
|
||||
table { align: center; border: thin; }
|
||||
|
||||
</style>
|
||||
</style>
|
||||
</head>
|
||||
<!-- Things yet to document:
|
||||
- build request, build request expansion and directly requested targets
|
||||
- conditional properties
|
||||
-->
|
||||
- build request, build request expansion and directly requested targets
|
||||
- conditional properties
|
||||
-->
|
||||
|
||||
<body>
|
||||
<p><a href="../../index.htm"><img class="banner" height="86" width="277"
|
||||
@@ -127,6 +127,13 @@
|
||||
|
||||
<dt><a href="#build_process">Build process</a></dt>
|
||||
|
||||
<dd>
|
||||
<dl class="page-index">
|
||||
<dt><a href="#alternative_selection">Alternative
|
||||
selection</a></dt>
|
||||
</dl>
|
||||
</dd>
|
||||
|
||||
<dt><a href="#generated_headers">Generated headers</a></dt>
|
||||
</dl>
|
||||
</dd>
|
||||
@@ -179,12 +186,12 @@
|
||||
<tt>bjam</tt> there. A simple application will be built. You can also
|
||||
play with other projects in <tt>examples-v2</tt>.
|
||||
<!-- This part should not go into intoduction docs, but we need to place
|
||||
it somewhere.
|
||||
|
||||
<p>It is slighly better way is to copy <tt>new/user-config.jam</tt>
|
||||
into one of the locations where it can be found (given in <a href=
|
||||
"#config_files_location">this table</a>). This prevent you from
|
||||
accidentally overwriting your config when updating.</p> -->
|
||||
it somewhere.
|
||||
|
||||
<p>It is slighly better way is to copy <tt>new/user-config.jam</tt>
|
||||
into one of the locations where it can be found (given in <a href=
|
||||
"#config_files_location">this table</a>). This prevent you from
|
||||
accidentally overwriting your config when updating.</p> -->
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
@@ -740,8 +747,8 @@ boost-build /path/to/boost.build ;
|
||||
Each call to the 'exe' rule defines a new <em>main target
|
||||
alternative</em> for the main target <tt>a</tt>. In this case, the first
|
||||
alternative will be used for the <tt>gcc</tt> toolset, while the second
|
||||
alternative will be used in other cases. TODO: document the exact
|
||||
selection method under "Build process" below.
|
||||
alternative will be used in other cases. See <a href=
|
||||
"#alternative_selection">below</a> for details.
|
||||
|
||||
<p>Sometime a main target is really needed only by some other main
|
||||
target. E.g. a rule that declared test-suite uses a main target that
|
||||
@@ -1029,17 +1036,17 @@ boost-build /path/to/boost.build ;
|
||||
|
||||
<li>It allows to have main target names with slashes.
|
||||
<!-- The motivation for which is:
|
||||
So, to summarize:
|
||||
1. The project which extract tarfile may extract all possible kinds of
|
||||
targets, and it's reasonable to use them directly from other project.
|
||||
2. The rule for unpacking tar is inplemented in terms of "patch-file", for
|
||||
maintainability, and therefore, must use main target name which contains
|
||||
slashes?
|
||||
3. Using sub-Jamfile in "foo" to declare extracted file "foo/b" is not an
|
||||
option, because you should not change existing tree
|
||||
So, to summarize:
|
||||
1. The project which extract tarfile may extract all possible kinds of
|
||||
targets, and it's reasonable to use them directly from other project.
|
||||
2. The rule for unpacking tar is inplemented in terms of "patch-file", for
|
||||
maintainability, and therefore, must use main target name which contains
|
||||
slashes?
|
||||
3. Using sub-Jamfile in "foo" to declare extracted file "foo/b" is not an
|
||||
option, because you should not change existing tree
|
||||
|
||||
That makes good rationale for why main target must contain names.
|
||||
-->
|
||||
That makes good rationale for why main target must contain names.
|
||||
-->
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1652,7 +1659,9 @@ borland/runtime-link=static,dynamic
|
||||
</li>
|
||||
|
||||
<li>
|
||||
For main target, steps are:
|
||||
For main target, with several alternatives, one of them is selected
|
||||
as described <a href="#alternative_selection">below</a>. If there's
|
||||
only one alternative, it's used unconditionally.
|
||||
|
||||
<ol>
|
||||
<li>All main target alternatives which requirements are satisfied
|
||||
@@ -1686,6 +1695,27 @@ borland/runtime-link=static,dynamic
|
||||
issued.</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="alternative_selection">Alternative selection</h4>
|
||||
|
||||
<p>When there are several alternatives, one of them must be selected. The
|
||||
process is as follows:</p>
|
||||
|
||||
<ol>
|
||||
<li>For each alternative <em>condition</em> is defined as the set of
|
||||
base properies in requirements. [Note: it might be better to explicitly
|
||||
specify the condition explicitly, as in conditional requirements].</li>
|
||||
|
||||
<li>An alternative is viable only if all properties in condition are
|
||||
present in build request.</li>
|
||||
|
||||
<li>If there's one viable alternative, it's choosen. Otherwise, an
|
||||
attempt is made to find one best alternative. An alternative a is
|
||||
better than another alternative b, iff set of properties in b's
|
||||
condition is stict subset of the set of properities of 'a's condition.
|
||||
If there's one viable alternative, which is better than all other, it's
|
||||
selected. Otherwise, an error is reported.</li>
|
||||
</ol>
|
||||
|
||||
<h3 id="generated_headers">Generated headers</h3>
|
||||
|
||||
<p>Usually, Boost.Build handles implicit dependendies completely
|
||||
|
||||
@@ -366,26 +366,65 @@ class main-target : abstract-target
|
||||
self.alternatives += $(target) ;
|
||||
}
|
||||
|
||||
# Returns the best viable alternative for this property-set
|
||||
# See the documentation for selection rules.
|
||||
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)
|
||||
# The algorithm: we keep the current best viable alternative.
|
||||
# When we've got new best viable alternative, we compare it
|
||||
# with the current one.
|
||||
|
||||
local best ;
|
||||
local best-properties ;
|
||||
|
||||
if $(self.alternatives[2-])
|
||||
{
|
||||
# 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)
|
||||
local bad ;
|
||||
local worklist = $(self.alternatives) ;
|
||||
while $(worklist) && ! $(bad)
|
||||
{
|
||||
viable += $(v) ;
|
||||
ranks += $(m) ;
|
||||
local v = $(worklist[1]) ;
|
||||
local properties = [ $(v).match $(property-set) ] ;
|
||||
|
||||
if $(properties) != no-match
|
||||
{
|
||||
if ! $(best)
|
||||
{
|
||||
best = $(v) ;
|
||||
best-properties = $(properties) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if $(properties) = $(best-properties)
|
||||
{
|
||||
bad = true ;
|
||||
}
|
||||
else if $(properties) in $(best-properties)
|
||||
{
|
||||
# Do nothing, this alternative is worse
|
||||
}
|
||||
else if $(best-properties) in $(properties)
|
||||
{
|
||||
best = $(v) ;
|
||||
best-properties = $(properties) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
bad = true ;
|
||||
}
|
||||
}
|
||||
}
|
||||
worklist = $(worklist[2-]) ;
|
||||
}
|
||||
if ! $(bad)
|
||||
{
|
||||
return $(best) ;
|
||||
}
|
||||
}
|
||||
|
||||
local best = [ sequence.select-highest-ranked $(viable) : $(ranks) ] ;
|
||||
return $(best) ;
|
||||
else
|
||||
{
|
||||
return $(self.alternatives) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -486,20 +525,13 @@ class main-target : abstract-target
|
||||
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 ]
|
||||
errors.error
|
||||
"skipped build of" [ full-name ]
|
||||
"with properties" [ $(property-set).raw ]
|
||||
"because no best-matching alternative could be found"
|
||||
;
|
||||
return [ property-set.empty ] ;
|
||||
}
|
||||
else if $(best-alternatives[2])
|
||||
{
|
||||
# TODO: again, a better error message in in order.
|
||||
errors.error "Ambiguous alternatives for main target" [ full-name ] ;
|
||||
}
|
||||
else
|
||||
{
|
||||
local result = [ $(best-alternatives).generate $(property-set) ] ;
|
||||
@@ -781,32 +813,20 @@ class basic-target : abstract-target
|
||||
return $(self.default-build) ;
|
||||
}
|
||||
|
||||
# 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. Return of empty value mean this target
|
||||
# can't be built with the given 'property-set'.
|
||||
rule match-rank ( property-set )
|
||||
# Returns the alternative condition for this alternative, if
|
||||
# the condition is satisfied by 'property-set'.
|
||||
rule match ( property-set )
|
||||
{
|
||||
# First check if our requirements can be satisfied.
|
||||
local rproperties = [ $(property-set).refine $(self.requirements) ] ;
|
||||
|
||||
# Kluge: previously, link-incompatible alternatives were skipped
|
||||
# completely. Until a better alternative selection algorithm is
|
||||
# in place, treat such alternatives as having rank of zero.
|
||||
# As another kluge, add 1 to all other ranks so that zero is worse that
|
||||
# anything.
|
||||
if [ $(rproperties).link-incompatible-with $(property-set) ]
|
||||
local condition = [ $(self.requirements).base ] ;
|
||||
# Weed out conditional properties.
|
||||
condition = [ MATCH ^([^:]*)\$ : $(condition) ] ;
|
||||
if $(condition) in [ $(property-set).raw ]
|
||||
{
|
||||
return 0 ;
|
||||
return $(condition) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Returns the number of properties common to requirements
|
||||
# and build request.
|
||||
return [ sequence.length "fake" [ set.intersection
|
||||
[ $(self.requirements).base ] :
|
||||
[ $(property-set).raw ] ] ] ;
|
||||
{
|
||||
return no-match ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,29 @@ t.write("a.cpp", "int main() { return 0; }\n")
|
||||
t.run_build_system("release")
|
||||
t.expect_addition("bin/$toolset/release/a.exe")
|
||||
|
||||
# Test that alternative selection works for ordinary
|
||||
# properties, in particular user-defined.
|
||||
t.write("project-root.jam", " ")
|
||||
t.write("Jamfile", """
|
||||
|
||||
import feature ;
|
||||
feature.feature X : off on : propagated ;
|
||||
|
||||
exe a : b.cpp ;
|
||||
exe a : a.cpp : <X>on ;
|
||||
""")
|
||||
t.write("b.cpp", "int main() { return 0; }\n")
|
||||
|
||||
t.rm("bin")
|
||||
|
||||
t.run_build_system()
|
||||
t.expect_addition("bin/$toolset/debug/b.obj")
|
||||
|
||||
t.run_build_system("X=on")
|
||||
t.expect_addition("bin/$toolset/debug/X-on/a.obj")
|
||||
|
||||
t.rm("bin")
|
||||
|
||||
# Test that everything works ok even with default
|
||||
# build.
|
||||
|
||||
@@ -31,7 +54,6 @@ exe a : a.cpp : <variant>debug ;
|
||||
t.run_build_system()
|
||||
t.expect_addition("bin/$toolset/debug/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
|
||||
@@ -42,7 +64,6 @@ 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")
|
||||
|
||||
@@ -63,11 +84,25 @@ t.write("Jamfile", """
|
||||
exe a : a_empty.cpp ;
|
||||
exe a : a.cpp ;
|
||||
""")
|
||||
expected="""error: Ambiguous alternatives for main target ./a
|
||||
expected="""error: skipped build of ./a with properties <debug-store>object <debug-symbols>on <exception-handling>on <hardcode-dll-paths>false <inlining>off <link-runtime>shared <link>shared <optimization>off <os>LINUX <profiling>off <rtti>on <runtime-debugging>on <stdlib>native <symlink-location>project-relative <threading>single <toolset>gcc <user-interface>console <variant>debug because no best-matching alternative could be found
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", status=1, stdout=expected)
|
||||
|
||||
# Another ambiguity test: two matches properties in one alternative are
|
||||
# neither better nor worse than a single one in another alternative.
|
||||
t.write("Jamfile", """
|
||||
exe a : a_empty.cpp : <optimization>off <profiling>off ;
|
||||
exe a : a.cpp : <debug-symbols>on ;
|
||||
""")
|
||||
expected="""error: skipped build of ./a with properties <debug-store>object <debug-symbols>on <exception-handling>on <hardcode-dll-paths>false <inlining>off <link-runtime>shared <link>shared <optimization>off <os>LINUX <profiling>off <rtti>on <runtime-debugging>on <stdlib>native <symlink-location>project-relative <threading>single <toolset>gcc <user-interface>console <variant>debug because no best-matching alternative could be found
|
||||
|
||||
"""
|
||||
t.run_build_system("--no-error-backtrace", status=1, stdout=expected)
|
||||
|
||||
|
||||
|
||||
|
||||
# Test that we can have alternative without sources
|
||||
t.write("Jamfile", """
|
||||
alias specific-sources ;
|
||||
|
||||
Reference in New Issue
Block a user