mirror of
https://github.com/boostorg/build.git
synced 2026-02-21 02:52:12 +00:00
* new/property.jam
(property-map.find-replace): New method
(property-map.find): Implement of terms of the above.
* new/qt.jam: Don't set any suffix on UIC_H.
* new/type.jam
(change-generated-target-suffix): New rule.
(generated-target-suffix): Process base types if no
suffix is found.
[SVN r16986]
533 lines
14 KiB
Plaintext
533 lines
14 KiB
Plaintext
# Copyright (C) Vladimir Prus 2002. 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.
|
|
|
|
import feature : grist ;
|
|
import utility : ungrist ;
|
|
import sequence : unique ;
|
|
import errors : error ;
|
|
import class : class ;
|
|
|
|
|
|
# Refines 'properties' by overriding any elements for which a different
|
|
# value is specified in 'requirements'. If the resulting property set
|
|
# will be link-incompatible with 'properties', it is an error.
|
|
# Conditional requirements are just added without modification.
|
|
# On success, returns properties. On error, returns a list which first
|
|
# element is "@error" and the other elements compose the explanation
|
|
# string.
|
|
rule refine ( properties * : requirements * : feature-space ? )
|
|
{
|
|
feature-space ?= feature ;
|
|
|
|
local result ;
|
|
local error ;
|
|
|
|
# All the elements of requirements should be present in the result
|
|
# Record them so that we can handle 'properties'.
|
|
for local r in $(requirements)
|
|
{
|
|
if ! [ MATCH (:) : $(r) ]
|
|
{
|
|
# Note: cannot use local here, so take an ugly name
|
|
__require__$(r:G) = $(r:G=) ;
|
|
}
|
|
}
|
|
|
|
for local p in $(properties)
|
|
{
|
|
# No processing for free properties
|
|
if free in [ $(feature-space).attributes $(p:G) ]
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
else
|
|
{
|
|
local required-value = $(__require__$(p:G)) ;
|
|
if $(required-value)
|
|
{
|
|
local value = $(p:G=) ;
|
|
if $(value) != $(required-value)
|
|
{
|
|
if link-incompatible in [ $(feature-space).attributes $(p:G) ]
|
|
{
|
|
error = @error link-incompatible properties $(p) and
|
|
$(p:G)$(required-value) ;
|
|
# Cannot break, so just iterate again
|
|
}
|
|
else
|
|
{
|
|
result += $(p:G)$(required-value) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Unset our ugly map.
|
|
for local r in $(requirements)
|
|
{
|
|
__require__$(r:G) = ;
|
|
}
|
|
|
|
if $(error)
|
|
{
|
|
return $(error) ;
|
|
}
|
|
else
|
|
{
|
|
return [ unique $(result) $(requirements) ] ;
|
|
}
|
|
}
|
|
|
|
# Removes all conditional properties which conditions are not met
|
|
# For those with met conditions, removes the condition.
|
|
rule evaluate-conditionals ( properties * )
|
|
{
|
|
local base ;
|
|
local conditionals ;
|
|
for local p in $(properties)
|
|
{
|
|
if [ MATCH (:<) : $(p) ]
|
|
{
|
|
conditionals += $(p) ;
|
|
}
|
|
else
|
|
{
|
|
base += $(p) ;
|
|
}
|
|
}
|
|
|
|
local result = $(base) ;
|
|
for local p in $(conditionals)
|
|
{
|
|
# Separate condition and property
|
|
local s = [ MATCH (.*):(.*) : $(p) ] ;
|
|
# Split condition into individual properties
|
|
local c = [ regex.split $(s[1]) "," ] ;
|
|
# Evaluate condition
|
|
if $(c) in $(base)
|
|
{
|
|
result += $(s[2]) ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
# Helper for as-path, below. Orders properties with the implicit ones
|
|
# first, and within the two sections in alphabetical order of feature
|
|
# name.
|
|
local rule path-order ( feature-space x y )
|
|
{
|
|
if $(y:G) && ! $(x:G)
|
|
{
|
|
return true ;
|
|
}
|
|
else if $(x:G) && ! $(y:G)
|
|
{
|
|
return ;
|
|
}
|
|
else
|
|
{
|
|
if ! $(x:G)
|
|
{
|
|
x = [ $(feature-space).expand-subfeatures $(x) ] ;
|
|
y = [ $(feature-space).expand-subfeatures $(y) ] ;
|
|
}
|
|
|
|
if $(x[0]) < $(y[0])
|
|
{
|
|
return true ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Returns a path which represents the given expanded property set.
|
|
rule as-path ( properties * : feature-space ? )
|
|
{
|
|
feature-space ?= feature ;
|
|
|
|
local entry = .result.$(properties:J=-).$(feature-space) ;
|
|
|
|
if ! $($(entry))
|
|
{
|
|
# trim redundancy
|
|
properties = [ $(feature-space).minimize $(properties) ] ;
|
|
|
|
# sort according to path-order
|
|
properties = [ sequence.insertion-sort $(properties) : path-order $(feature-space) ] ;
|
|
|
|
local components ;
|
|
for local p in $(properties)
|
|
{
|
|
if $(p:G)
|
|
{
|
|
local f = [ ungrist $(p:G) ] ;
|
|
components += $(f)-$(p:G=) ;
|
|
}
|
|
else
|
|
{
|
|
components += $(p) ;
|
|
}
|
|
}
|
|
|
|
$(entry) = $(components:J=/) ;
|
|
}
|
|
|
|
return $($(entry)) ;
|
|
}
|
|
|
|
# Exit with error if property is not valid.
|
|
rule validate ( property : feature-space ? )
|
|
{
|
|
feature-space ?= feature ;
|
|
local msg ;
|
|
if $(property:G)
|
|
{
|
|
local feature = [ ungrist $(property:G) ] ; # Ungrist for better error messages
|
|
local value = $(property:G=) ;
|
|
|
|
if ! [ $(feature-space).valid $(feature) ]
|
|
{
|
|
msg = "unknown feature '$(feature)'" ;
|
|
}
|
|
else if $(value) && ! free in [ $(feature-space).attributes $(feature) ]
|
|
{
|
|
$(feature-space).validate-value-string $(feature) $(value) ;
|
|
}
|
|
else if ! $(value)
|
|
{
|
|
msg = "No value specified for feature '$(feature)'" ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
local feature = [ $(feature-space).implied-feature $(property) ] ;
|
|
$(feature-space).validate-value-string $(feature) $(property) ;
|
|
}
|
|
if $(msg)
|
|
{
|
|
error "Invalid property "'$(property:J=" ")'": "$(msg:J=" "). ;
|
|
}
|
|
}
|
|
|
|
rule validate-property-sets ( property-sets * : feature-space ? )
|
|
{
|
|
for local s in $(property-sets)
|
|
{
|
|
for local p in [ feature.split $(s) ]
|
|
{
|
|
validate $(p) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Makes a property set from 'specification', converting implicit values into
|
|
# full properties.
|
|
# TODO: Might want to use 'feature-space' here as well as in other rules.
|
|
rule make ( specification * )
|
|
{
|
|
local result ;
|
|
for local e in $(specification)
|
|
{
|
|
if $(e:G)
|
|
{
|
|
result += $(e) ;
|
|
}
|
|
else if [ feature.is-implicit-value $(e) ]
|
|
{
|
|
local feature = [ feature.implied-feature $(e) ] ;
|
|
result += $(feature)$(e) ;
|
|
}
|
|
else
|
|
{
|
|
error "'$(e)' is not a valid for property specification" ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Returns a property sets which include all the elements in 'properties' that
|
|
# do not have attributes listed in 'attributes'.
|
|
rule remove ( attributes + : properties * : feature-space ? )
|
|
{
|
|
feature-space ?= feature ;
|
|
local result ;
|
|
for local e in $(properties)
|
|
{
|
|
if ! [ set.intersection $(attributes) : [ $(feature-space).attributes $(e:G) ] ]
|
|
{
|
|
result += $(e) ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Returns a property set which include all properties in 'properties' that have
|
|
# any of 'attributes'.
|
|
rule take ( attributes + : properties * : feature-space ? )
|
|
{
|
|
feature-space ?= feature ;
|
|
local result ;
|
|
for local e in $(properties)
|
|
{
|
|
if [ set.intersection $(attributes) : [ $(feature-space).attributes $(e:G) ] ]
|
|
{
|
|
result += $(e) ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Selects properties which correspond to any of the given features.
|
|
rule select ( features * : properties * )
|
|
{
|
|
local result ;
|
|
|
|
# add any missing angle brackets
|
|
local empty = "" ;
|
|
features = $(empty:G=$(features)) ;
|
|
|
|
for local p in $(properties)
|
|
{
|
|
if $(p:G) in $(features)
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Changes the value of 'feature' to 'value', or adds it.
|
|
rule change ( properties * : feature value )
|
|
{
|
|
local result ;
|
|
feature = [ grist $(feature) ] ;
|
|
for local p in $(properties)
|
|
{
|
|
if $(p:G) = $(feature)
|
|
{
|
|
result += $(value:G=$(feature)) ;
|
|
}
|
|
else
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
|
|
# Interpret all path properties in 'properties' as relative to 'path'
|
|
rule translate-paths ( properties * : path )
|
|
{
|
|
local result ;
|
|
for local p in $(properties)
|
|
{
|
|
if path in [ feature.attributes $(p:G) ]
|
|
{
|
|
local t = [ path.root $(p:TG=) $(path) ] ;
|
|
result += $(t:TG=$(p:G)) ;
|
|
}
|
|
else
|
|
{
|
|
result += $(p) ;
|
|
}
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
# Class which maintains a property set -> string
|
|
# mapping
|
|
rule property-map ( )
|
|
{
|
|
self.next-flag = 1 ;
|
|
|
|
# Associate 'value' with 'properties'
|
|
rule insert ( properties + : value )
|
|
{
|
|
self.all-flags += $(self.next-flag) ;
|
|
self.properties.$(self.next-flag) = $(properties) ;
|
|
self.value.$(self.next-flag) = $(value) ;
|
|
|
|
self.next-flag = [ numbers.increment $(self.next-flag) ] ;
|
|
}
|
|
|
|
# Return the value associated with 'properties'
|
|
# or any subset of it. If more than one
|
|
# subset has value assigned to it, return the
|
|
# value for the longest subset, if it's unique.
|
|
rule find ( properties + )
|
|
{
|
|
return [ find-replace $(properties) ] ;
|
|
}
|
|
|
|
# Find the value associated with 'properties'.
|
|
# If 'value' parameter is given, replaces the found value
|
|
# Returns the value that were stored originally.
|
|
rule find-replace ( properties + : value ? )
|
|
{
|
|
# First find all matches
|
|
local matches ;
|
|
local match-ranks ;
|
|
for local i in $(self.all-flags)
|
|
{
|
|
if $(self.properties.$(i)) in $(properties)
|
|
{
|
|
matches += $(i) ;
|
|
match-ranks += [ sequence.length
|
|
$(self.properties.$(i)) ] ;
|
|
}
|
|
}
|
|
local best = [ sequence.select-highest-ranked
|
|
$(matches) : $(match-ranks) ] ;
|
|
if $(best[2])
|
|
{
|
|
errors.error "Ambiguous key" ;
|
|
}
|
|
local original = $(self.value.$(best)) ;
|
|
if $(value)
|
|
{
|
|
self.value.$(best) = $(value) ;
|
|
}
|
|
return $(original) ;
|
|
}
|
|
}
|
|
|
|
class property-map ;
|
|
|
|
|
|
local rule __test__ ( )
|
|
{
|
|
import class : new ;
|
|
import errors : try catch ;
|
|
|
|
local test-space = [ new feature-space ] ;
|
|
|
|
module $(test-space)
|
|
{
|
|
import assert ;
|
|
|
|
feature toolset : gcc : implicit ;
|
|
subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4
|
|
3.0 3.0.1 3.0.2 : optional ;
|
|
feature define : : free ;
|
|
feature runtime-link : dynamic static : symmetric link-incompatible ;
|
|
feature optimization : on off ;
|
|
feature variant : debug release : implicit composite ;
|
|
feature rtti : on off : link-incompatible ;
|
|
|
|
compose <variant>debug : <define>_DEBUG <optimization>off ;
|
|
compose <variant>release : <define>NDEBUG <optimization>on ;
|
|
|
|
}
|
|
|
|
validate gcc : $(test-space) ;
|
|
validate gcc-3.0.1 : $(test-space) ;
|
|
validate <toolset>gcc : $(test-space) ;
|
|
validate <toolset>gcc-3.0.1 : $(test-space) ;
|
|
|
|
assert.result <toolset>gcc <rtti>off <define>FOO
|
|
: refine <toolset>gcc <rtti>off
|
|
: <define>FOO
|
|
: $(test-space)
|
|
;
|
|
|
|
assert.result <toolset>gcc <optimization>on
|
|
: refine <toolset>gcc <optimization>off
|
|
: <optimization>on
|
|
: $(test-space)
|
|
;
|
|
|
|
assert.result <toolset>gcc <rtti>off
|
|
: refine <toolset>gcc : <rtti>off : $(test-space)
|
|
;
|
|
|
|
assert.result <toolset>gcc <rtti>off <rtti>off:<define>FOO
|
|
: refine <toolset>gcc : <rtti>off <rtti>off:<define>FOO
|
|
: $(test-space)
|
|
;
|
|
|
|
assert.result <toolset>gcc <variant>release <rtti>off <define>MY_RELEASE
|
|
: evaluate-conditionals <toolset>gcc <variant>release <rtti>off
|
|
<variant>release,<rtti>off:<define>MY_RELEASE
|
|
;
|
|
|
|
assert.result debug
|
|
: as-path <optimization>off <variant>debug
|
|
: $(test-space)
|
|
;
|
|
|
|
assert.result gcc/debug/rtti-off
|
|
: as-path <toolset>gcc <optimization>off <rtti>off <variant>debug
|
|
: $(test-space)
|
|
;
|
|
|
|
r = [ refine <toolset>gcc <rtti>off
|
|
: <rtti>on
|
|
: $(test-space) ] ;
|
|
assert.equal $(r[1]) : "@error" ;
|
|
|
|
try ;
|
|
validate <feature>value : $(test-space) ;
|
|
catch "Invalid property '<feature>value': unknown feature 'feature'." ;
|
|
|
|
try ;
|
|
validate <rtti>default : $(test-space) ;
|
|
catch \"default\" is not a known value of feature <rtti> ;
|
|
|
|
validate <define>WHATEVER : $(test-space) ;
|
|
|
|
try ;
|
|
validate <rtti> : $(test-space) ;
|
|
catch "Invalid property '<rtti>': No value specified for feature 'rtti'." ;
|
|
|
|
try ;
|
|
validate value : $(test-space) ;
|
|
catch "value" is not a value of an implicit feature ;
|
|
|
|
|
|
assert.result <rtti>on
|
|
: remove free implicit : <toolset>gcc <define>foo <rtti>on : $(test-space) ;
|
|
|
|
assert.result <include>a
|
|
: select include : <include>a <toolset>gcc ;
|
|
|
|
assert.result <include>a
|
|
: select include bar : <include>a <toolset>gcc ;
|
|
|
|
assert.result <include>a <toolset>gcc
|
|
: select include <bar> <toolset> : <include>a <toolset>gcc ;
|
|
|
|
assert.result <toolset>kylix <include>a
|
|
: change <toolset>gcc <include>a : toolset kylix ;
|
|
|
|
pm = [ new property-map ] ;
|
|
$(pm).insert <toolset>gcc : o ;
|
|
$(pm).insert <toolset>gcc <os>NT : obj ;
|
|
$(pm).insert <toolset>gcc <os>CYGWIN : obj ;
|
|
|
|
assert.equal o
|
|
: [ $(pm).find <toolset>gcc ] ;
|
|
|
|
assert.equal obj
|
|
: [ $(pm).find <toolset>gcc <os>NT ] ;
|
|
|
|
try ;
|
|
$(pm).find <toolset>gcc <os>NT <os>CYGWIN ;
|
|
catch "Ambiguous key" ;
|
|
|
|
}
|
|
|