2
0
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:
Vladimir Prus
2003-10-10 12:14:59 +00:00
parent 914fcb5194
commit 503da68bc4
3 changed files with 156 additions and 71 deletions

View File

@@ -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

View File

@@ -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 ;
}
}

View File

@@ -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 ;