blob: f2036d999cdf6600949e4fce230deb30cbb2c7c4 [file] [log] [blame]
# Copyright 2003 Dave Abrahams
# Copyright 2005 Rene Rivera
# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Support for toolset definition.
import errors ;
import feature ;
import generators ;
import numbers ;
import path ;
import property ;
import regex ;
import sequence ;
import set ;
.flag-no = 1 ;
.ignore-requirements = ;
# This is used only for testing, to make sure we do not get random extra
# elements in paths.
if --ignore-toolset-requirements in [ modules.peek : ARGV ]
{
.ignore-requirements = 1 ;
}
# Initializes an additional toolset-like module. First load the 'toolset-module'
# and then calls its 'init' rule with trailing arguments.
#
rule using ( toolset-module : * )
{
import $(toolset-module) ;
$(toolset-module).init $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ;
}
# Expands subfeatures in each property sets, e.g. '<toolset>gcc-3.2' will be
# converted to '<toolset>gcc/<toolset-version>3.2'.
#
local rule normalize-condition ( property-sets * )
{
local result ;
for local p in $(property-sets)
{
local split = [ feature.split $(p) ] ;
local expanded = [ feature.expand-subfeatures [ feature.split $(p) ] ] ;
result += $(expanded:J=/) ;
}
return $(result) ;
}
# Specifies if the 'flags' rule should check that the invoking module is the
# same as the module we are setting the flag for. 'v' can be either 'checked' or
# 'unchecked'. Subsequent call to 'pop-checking-for-flags-module' will restore
# the setting that was in effect before calling this rule.
#
rule push-checking-for-flags-module ( v )
{
.flags-module-checking = $(v) $(.flags-module-checking) ;
}
rule pop-checking-for-flags-module ( )
{
.flags-module-checking = $(.flags-module-checking[2-]) ;
}
# Specifies the flags (variables) that must be set on targets under certain
# conditions, described by arguments.
#
rule flags (
rule-or-module # If contains a dot, should be a rule name. The flags will
# be applied when that rule is used to set up build
# actions.
#
# If does not contain dot, should be a module name. The
# flag will be applied for all rules in that module. If
# module for rule is different from the calling module, an
# error is issued.
variable-name # Variable that should be set on target.
condition * : # A condition when this flag should be applied. Should be a
# set of property sets. If one of those property sets is
# contained in the build properties, the flag will be used.
# Implied values are not allowed: "<toolset>gcc" should be
# used, not just "gcc". Subfeatures, like in
# "<toolset>gcc-3.2" are allowed. If left empty, the flag
# will be used unconditionally.
#
# Propery sets may use value-less properties ('<a>' vs.
# '<a>value') to match absent properties. This allows to
# separately match:
#
# <architecture>/<address-model>64
# <architecture>ia64/<address-model>
#
# Where both features are optional. Without this syntax
# we would be forced to define "default" values.
values * : # The value to add to variable. If <feature> is specified,
# then the value of 'feature' will be added.
unchecked ? # If value 'unchecked' is passed, will not test that flags
# are set for the calling module.
: hack-hack ? # For
# flags rule OPTIONS <cxx-abi> : -model ansi
# Treat <cxx-abi> as condition
# FIXME: ugly hack.
)
{
local caller = [ CALLER_MODULE ] ;
if ! [ MATCH ".*([.]).*" : $(rule-or-module) ]
&& [ MATCH "(Jamfile<.*)" : $(caller) ]
{
# Unqualified rule name, used inside Jamfile. Most likely used with
# 'make' or 'notfile' rules. This prevents setting flags on the entire
# Jamfile module (this will be considered as rule), but who cares?
# Probably, 'flags' rule should be split into 'flags' and
# 'flags-on-module'.
rule-or-module = $(caller).$(rule-or-module) ;
}
else
{
local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ;
if $(unchecked) != unchecked
&& $(.flags-module-checking[1]) != unchecked
&& $(module_) != $(caller)
{
errors.error "Module $(caller) attempted to set flags for module $(module_)" ;
}
}
if $(condition) && ! $(condition:G=) && ! $(hack-hack)
{
# We have condition in the form '<feature>', that is, without value.
# That is an older syntax:
# flags gcc.link RPATH <dll-path> ;
# for compatibility, convert it to
# flags gcc.link RPATH : <dll-path> ;
values = $(condition) ;
condition = ;
}
if $(condition)
{
property.validate-property-sets $(condition) ;
condition = [ normalize-condition $(condition) ] ;
}
add-flag $(rule-or-module) : $(variable-name) : $(condition) : $(values) ;
}
# Adds a new flag setting with the specified values. Does no checking.
#
local rule add-flag ( rule-or-module : variable-name : condition * : values * )
{
.$(rule-or-module).flags += $(.flag-no) ;
# Store all flags for a module.
local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ;
.module-flags.$(module_) += $(.flag-no) ;
# Store flag-no -> rule-or-module mapping.
.rule-or-module.$(.flag-no) = $(rule-or-module) ;
.$(rule-or-module).variable.$(.flag-no) += $(variable-name) ;
.$(rule-or-module).values.$(.flag-no) += $(values) ;
.$(rule-or-module).condition.$(.flag-no) += $(condition) ;
.flag-no = [ numbers.increment $(.flag-no) ] ;
}
# Returns the first element of 'property-sets' which is a subset of
# 'properties' or an empty list if no such element exists.
#
rule find-property-subset ( property-sets * : properties * )
{
# Cut property values off.
local prop-keys = $(properties:G) ;
local result ;
for local s in $(property-sets)
{
if ! $(result)
{
# Handle value-less properties like '<architecture>' (compare with
# '<architecture>x86').
local set = [ feature.split $(s) ] ;
# Find the set of features that
# - have no property specified in required property set
# - are omitted in the build property set.
local default-props ;
for local i in $(set)
{
# If $(i) is a value-less property it should match default value
# of an optional property. See the first line in the example
# below:
#
# property set properties result
# <a> <b>foo <b>foo match
# <a> <b>foo <a>foo <b>foo no match
# <a>foo <b>foo <b>foo no match
# <a>foo <b>foo <a>foo <b>foo match
if ! ( $(i:G=) || ( $(i:G) in $(prop-keys) ) )
{
default-props += $(i) ;
}
}
if $(set) in $(properties) $(default-props)
{
result = $(s) ;
}
}
}
return $(result) ;
}
# Returns a value to be added to some flag for some target based on the flag's
# value definition and the given target's property set.
#
rule handle-flag-value ( value * : properties * )
{
local result ;
if $(value:G)
{
local matches = [ property.select $(value) : $(properties) ] ;
for local p in $(matches)
{
local att = [ feature.attributes $(p:G) ] ;
if dependency in $(att)
{
# The value of a dependency feature is a target and needs to be
# actualized.
result += [ $(p:G=).actualize ] ;
}
else if path in $(att) || free in $(att)
{
local values ;
# Treat features with && in the value specially -- each
# &&-separated element is considered a separate value. This is
# needed to handle searched libraries or include paths, which
# may need to be in a specific order.
if ! [ MATCH (&&) : $(p:G=) ]
{
values = $(p:G=) ;
}
else
{
values = [ regex.split $(p:G=) "&&" ] ;
}
if path in $(att)
{
result += [ sequence.transform path.native : $(values) ] ;
}
else
{
result += $(values) ;
}
}
else
{
result += $(p:G=) ;
}
}
}
else
{
result += $(value) ;
}
return $(result) ;
}
# Given a rule name and a property set, returns a list of interleaved variables
# names and values which must be set on targets for that rule/property-set
# combination.
#
rule set-target-variables-aux ( rule-or-module : property-set )
{
local result ;
properties = [ $(property-set).raw ] ;
for local f in $(.$(rule-or-module).flags)
{
local variable = $(.$(rule-or-module).variable.$(f)) ;
local condition = $(.$(rule-or-module).condition.$(f)) ;
local values = $(.$(rule-or-module).values.$(f)) ;
if ! $(condition) ||
[ find-property-subset $(condition) : $(properties) ]
{
local processed ;
for local v in $(values)
{
# The value might be <feature-name> so needs special treatment.
processed += [ handle-flag-value $(v) : $(properties) ] ;
}
for local r in $(processed)
{
result += $(variable) $(r) ;
}
}
}
# Strip away last dot separated part and recurse.
local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ;
if $(next)
{
result += [ set-target-variables-aux $(next[1]) : $(property-set) ] ;
}
return $(result) ;
}
rule set-target-variables ( rule-or-module targets + : property-set )
{
properties = [ $(property-set).raw ] ;
local key = $(rule-or-module).$(property-set) ;
local settings = $(.stv.$(key)) ;
if ! $(settings)
{
settings = [ set-target-variables-aux $(rule-or-module) :
$(property-set) ] ;
if ! $(settings)
{
settings = none ;
}
.stv.$(key) = $(settings) ;
}
if $(settings) != none
{
local var-name = ;
for local name-or-value in $(settings)
{
if $(var-name)
{
$(var-name) on $(targets) += $(name-or-value) ;
var-name = ;
}
else
{
var-name = $(name-or-value) ;
}
}
}
}
# Make toolset 'toolset', defined in a module of the same name, inherit from
# 'base'.
# 1. The 'init' rule from 'base' is imported into 'toolset' with full name.
# Another 'init' is called, which forwards to the base one.
# 2. All generators from 'base' are cloned. The ids are adjusted and <toolset>
# property in requires is adjusted too.
# 3. All flags are inherited.
# 4. All rules are imported.
#
rule inherit ( toolset : base )
{
import $(base) ;
inherit-generators $(toolset) : $(base) ;
inherit-flags $(toolset) : $(base) ;
inherit-rules $(toolset) : $(base) ;
}
rule inherit-generators ( toolset properties * : base : generators-to-ignore * )
{
properties ?= <toolset>$(toolset) ;
local base-generators = [ generators.generators-for-toolset $(base) ] ;
for local g in $(base-generators)
{
local id = [ $(g).id ] ;
if ! $(id) in $(generators-to-ignore)
{
# Some generator names have multiple periods in their name, so
# $(id:B=$(toolset)) does not generate the right new-id name. E.g.
# if id = gcc.compile.c++ then $(id:B=darwin) = darwin.c++, which is
# not what we want. Manually parse the base and suffix. If there is
# a better way to do this, I would love to see it. See also the
# register() rule in the generators module.
local base = $(id) ;
local suffix = "" ;
while $(base:S)
{
suffix = $(base:S)$(suffix) ;
base = $(base:B) ;
}
local new-id = $(toolset)$(suffix) ;
generators.register [ $(g).clone $(new-id) : $(properties) ] ;
}
}
}
# Brings all flag definitions from the 'base' toolset into the 'toolset'
# toolset. Flag definitions whose conditions make use of properties in
# 'prohibited-properties' are ignored. Do not confuse property and feature, for
# example <debug-symbols>on and <debug-symbols>off, so blocking one of them does
# not block the other one.
#
# The flag conditions are not altered at all, so if a condition includes a name,
# or version of a base toolset, it will not ever match the inheriting toolset.
# When such flag settings must be inherited, define a rule in base toolset
# module and call it as needed.
#
rule inherit-flags ( toolset : base : prohibited-properties * : prohibited-vars * )
{
for local f in $(.module-flags.$(base))
{
local rule-or-module = $(.rule-or-module.$(f)) ;
if ( [ set.difference
$(.$(rule-or-module).condition.$(f)) :
$(prohibited-properties) ]
|| ! $(.$(rule-or-module).condition.$(f))
) && ( ! $(.$(rule-or-module).variable.$(f)) in $(prohibited-vars) )
{
local rule_ = [ MATCH "[^.]*\.(.*)" : $(rule-or-module) ] ;
local new-rule-or-module ;
if $(rule_)
{
new-rule-or-module = $(toolset).$(rule_) ;
}
else
{
new-rule-or-module = $(toolset) ;
}
add-flag
$(new-rule-or-module)
: $(.$(rule-or-module).variable.$(f))
: $(.$(rule-or-module).condition.$(f))
: $(.$(rule-or-module).values.$(f)) ;
}
}
}
rule inherit-rules ( toolset : base : localize ? )
{
# It appears that "action" creates a local rule.
local base-generators = [ generators.generators-for-toolset $(base) ] ;
local rules ;
for local g in $(base-generators)
{
rules += [ MATCH "[^.]*\.(.*)" : [ $(g).rule-name ] ] ;
}
rules = [ sequence.unique $(rules) ] ;
IMPORT $(base) : $(rules) : $(toolset) : $(rules) : $(localize) ;
IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ;
}
# Return the list of global 'toolset requirements'. Those requirements will be
# automatically added to the requirements of any main target.
#
rule requirements ( )
{
return $(.requirements) ;
}
# Adds elements to the list of global 'toolset requirements'. The requirements
# will be automatically added to the requirements for all main targets, as if
# they were specified literally. For best results, all requirements added should
# be conditional or indirect conditional.
#
rule add-requirements ( requirements * )
{
if ! $(.ignore-requirements)
{
.requirements += $(requirements) ;
}
}
rule __test__ ( )
{
import assert ;
local p = <b>0 <c>1 <d>2 <e>3 <f>4 ;
assert.result <c>1/<d>2/<e>3 : find-property-subset <c>1/<d>2/<e>3 <a>0/<b>0/<c>1 <d>2/<e>5 <a>9 : $(p) ;
assert.result : find-property-subset <a>0/<b>0/<c>9/<d>9/<e>5 <a>9 : $(p) ;
local p-set = <a>/<b> <a>0/<b> <a>/<b>1 <a>0/<b>1 ;
assert.result <a>/<b> : find-property-subset $(p-set) : ;
assert.result <a>0/<b> : find-property-subset $(p-set) : <a>0 <c>2 ;
assert.result <a>/<b>1 : find-property-subset $(p-set) : <b>1 <c>2 ;
assert.result <a>0/<b>1 : find-property-subset $(p-set) : <a>0 <b>1 ;
}