2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-21 02:52:12 +00:00
Files
build/new/property.jam
Vladimir Prus fa173e4cb5 Allow customization of suffixes used for generated targets.
* 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]
2003-01-22 06:33:05 +00:00

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