2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-02 20:52:13 +00:00

Improve the algorithm for computing build properties.

[SVN r22542]
This commit is contained in:
Vladimir Prus
2004-03-22 08:10:42 +00:00
parent 38079ac203
commit 2dd280cbb6
5 changed files with 211 additions and 42 deletions

View File

@@ -63,6 +63,16 @@ class property-set
{
self.non-dependency += $(p) ;
}
if [ MATCH (:) : $(p:G=) ]
{
self.conditional += $(p) ;
}
else
{
self.non-conditional += $(p) ;
}
if propagated in $(att)
{
@@ -106,8 +116,17 @@ class property-set
{
return $(self.non-dependency) ;
}
rule conditional ( )
{
return $(self.conditional) ;
}
rule non-conditional ( )
{
return $(self.non-conditional) ;
}
# Returns incidental properties
rule incidental ( )
{
@@ -131,6 +150,16 @@ class property-set
return $(self.refined.$(ps)) ;
}
rule expand ( )
{
if ! $(self.expanded)
{
self.expanded = [ property-set.create [ feature.expand $(self.raw) ] ] ;
}
return $(self.expanded) ;
}
rule expand-composites ( )
{
if ! $(self.composites)

View File

@@ -343,6 +343,13 @@ class main-target : abstract-target
# See the documentation for selection rules.
local rule select-alternatives ( property-set )
{
# When selecting alternatives we have to consider defaults,
# for example:
# lib l : l.cpp : <variant>debug ;
# lib l : l_opt.cpp : <variant>release ;
# won't work unless we add default value <variant>debug.
property-set = [ $(p).add-defaults ] ;
# The algorithm: we keep the current best viable alternative.
# When we've got new best viable alternative, we compare it
# with the current one.
@@ -445,12 +452,13 @@ class main-target : abstract-target
[ feature.compress-subproperties $(raw) ]
$(defaults-to-apply)
] ;
if $(properties)
{
for local p in $(properties)
{
result += [ property-set.create [ feature.split $(p) ] ] ;
result += [ property-set.create
[ feature.expand [ feature.split $(p) ] ] ] ;
}
}
else
@@ -473,13 +481,16 @@ class main-target : abstract-target
rule generate ( property-set )
{
start-building $(__name__) ;
# We want composite properties in build request act as if
# all the properties it expands too are explicitly specified.
property-set = [ $(property-set).expand ] ;
local all-property-sets = [ apply-default-build $(property-set) ] ;
local usage-requirements = [ property-set.empty ] ;
local result ;
for local p in $(all-property-sets)
{
p = [ $(p).expand-composites ] ;
p = [ $(p).add-defaults ] ;
local r = [ generate-really $(p) ] ;
usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
result += $(r[2-]) ;
@@ -494,7 +505,7 @@ class main-target : abstract-target
# generated virtual target in other elements. It's possible
# that no targets are generated.
local rule generate-really ( property-set )
{
{
local best-alternatives = [ select-alternatives $(property-set) ] ;
if ! $(best-alternatives)
{
@@ -556,15 +567,6 @@ class file-reference : abstract-target
}
# Helper for 'find', below.
local rule remove-trailing-slash ( string )
{
local stripped = [ MATCH (.*)/$ : $(string) ] ;
stripped ?= $(string) ;
return $(stripped) ;
}
if "--quiet" in [ modules.peek : ARGV ]
{
.quiet = true ;
@@ -703,6 +705,92 @@ rule generate-from-reference
return [ $(target).generate $(rproperties) ] ;
}
# Given build request and requirements, return properties
# common to dependency build request and target build
# properties
rule common-properties ( build-request requirements )
{
# For optimization, we add free requirements directly,
# without using complex algorithsm.
# This gives the complex algorithm better chance of caching results.
local free = [ $(requirements).free ] ;
local non-free = [ property-set.create
[ $(requirements).base ] [ $(requirements).incidental ] ] ;
local key = .rp.$(build-request)-$(non-free) ;
if ! $($(key))
{
$(key) = [ common-properties2 $(build-request) $(non-free) ] ;
}
result = [ $($(key)).add-raw $(free) ] ;
}
rule common-properties2 ( build-request requirements )
{
# This guaranteed that default properties are present
# in result, unless they are overrided by some requirement.
# FIXME: There is possibility that we've added <foo>bar, which is composite
# and expands to <foo2>bar2, but default value of <foo2> is not bar2,
# in which case it's not clear what to do.
#
build-request = [ $(build-request).add-defaults ] ;
# Featured added by 'add-default' can be composite and expand
# to features without default values -- so they are not added yet.
# It could be clearer/faster to expand only newly added properties
# but that's not critical.
build-request = [ $(build-request).expand ] ;
# Apply non-conditional requirements.
# There's a slight bug here: it's possible that conditional
# requirement change a value set by non-conditional requirements. This
# should be error, but we don't detect it yet.
local raw = [ $(build-request).raw ] ;
raw = [ property.refine $(raw) :
[ feature.expand [ $(requirements).non-conditional ] ] ] ;
# We've collected properties that surely must be present in common
# properties. We now try to figure out what other properties
# should be added in order to satisfy rules (4)-(6) from the docs.
local conditionals = [ $(requirements).conditional ] ;
local count = $(conditionals) and-once-more ;
local prev ;
local current = $(raw) ;
local ok ;
while $(count)
{
# Evaluate conditionals in context of current properties
local e = [ property.evaluate-conditionals-in-context $(conditionals)
: $(current) ] ;
# ECHO "Got now: " $(e) ;
if $(e) = $(prev)
{
# If we got the same result, we've found final properties.
count = ;
ok = true ;
}
else
{
# Oops, results of evaluation of conditionals has changes
# Also 'current' contains leftover from previous evaluation.
# Recompute 'current' using initial properties and conditional
# requirements.
prev = $(e) ;
current = [ property.refine $(raw) : [ feature.expand $(e) ] ] ;
}
count = $(count[2-]) ;
}
if ! $(ok)
{
errors.error "Can't evaluate conditional properties " $(conditionals) ;
}
return [ property-set.create $(current) ] ;
}
# Implements the most standard way of constructing main target
# alternative from sources. Allows sources to be either file or
# other main target and handles generation of those dependency
@@ -714,6 +802,7 @@ class basic-target : abstract-target
import property-set ;
import set sequence errors ;
import "class" : new ;
import property feature ;
rule __init__ ( name : project
: sources * : requirements * :
@@ -772,17 +861,8 @@ class basic-target : abstract-target
rule match ( property-set )
{
local condition = [ $(self.requirements).base ] ;
local condition2 ;
# Weed out conditional properties.
for local c in $(condition)
{
if [ MATCH ^([^:]*)\$ : $(c:G=) ]
{
condition2 += $(c) ;
}
}
condition = $(condition2) ;
condition = [ MATCH ^([^:]*)\$ : $(condition) ] ;
if $(condition) in [ $(property-set).raw ]
{
return $(condition) ;
@@ -793,21 +873,6 @@ class basic-target : abstract-target
}
}
# Determine and return properties which should be used for
# building when given 'build-request'. This includes refining
# build request with requirements, evaluating conditionals,
# generating depenendecies and running actions for features.
local rule refined-properties ( build-request )
{
local erequirements = [ $(self.requirements).evaluate-conditionals
$(build-request) ] ;
erequirements = [ $(erequirements).expand-composites ] ;
local rproperties = [ $(build-request).refine $(erequirements) ] ;
return $(rproperties) ;
}
#
# Allows the user to tag the name of the target, according to properties.
#
@@ -861,7 +926,9 @@ class basic-target : abstract-target
{
if ! $(self.generated.$(property-set))
{
local rproperties = [ refined-properties $(property-set) ] ;
local rproperties = [ targets.common-properties $(property-set)
$(self.requirements) ] ;
if $(rproperties[1]) != "@error"
{
local source-targets ;

View File

@@ -96,7 +96,7 @@ class stlport-target-class : basic-target
# manually.
property-set = [ property-set.create [ difference
[ $(property-set).raw ] : <library>/stlport//stlport ] ] ;
[ $(property-set).raw ] : <library>/stlport//stlport <stdlib>stlport ] ] ;
return [ basic-target.generate $(property-set) ] ;
}

72
test/expansion.py Normal file
View File

@@ -0,0 +1,72 @@
#!/usr/bin/python
# Copyright (C) Vladimir Prus 2003. Permission to copy, use, modify, sell and
# distribute this software is granted provided this copyright notice appears in
# all copies. This software is provided "as is" without express or implied
# warranty, and with no claim as to its suitability for any purpose.
# This file is template for Boost.Build tests. It creates a simple
# project that builds one exe from one source, and checks that the exe
# is really created.
from BoostBuild import Tester, List
t = Tester()
t.write("a.cpp", """
#ifdef CF_IS_OFF
int main() { return 0; }
#endif
""")
t.write("b.cpp", """
#ifdef CF_1
int main() { return 0; }
#endif
""")
t.write("c.cpp", """
#ifdef FOO
int main() { return 0; }
#endif
""")
t.write("Jamfile", """
# See if default value of composite feature 'cf'
# will be expanded to <define>CF_IS_OFF
exe a : a.cpp ;
# See if subfeature in requirements in expanded.
exe b : b.cpp : <cf>on-1 ;
# See if conditional requirements are recursively expanded.
exe c : c.cpp : <toolset>$toolset:<variant>release <variant>release:<define>FOO ;
""")
t.write("project-root.jam", """
import feature ;
feature.feature cf : off on : composite incidental ;
feature.compose <cf>off : <define>CF_IS_OFF ;
feature.subfeature cf on : version : 1 2 : composite optional incidental ;
feature.compose <cf-on:version>1 : <define>CF_1 ;
""")
t.expand_toolset("Jamfile")
t.run_build_system()
t.expect_addition(["bin/$toolset/debug/a.exe",
"bin/$toolset/debug/b.exe",
"bin/$toolset/release/c.exe",
])
t.cleanup()

View File

@@ -122,6 +122,7 @@ tests = [ "project_test1",
"unit_test",
"standalone",
"library_order",
"expansion",
#"ordered_properties",
]