# 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. 'gcc-3.2' will be # converted to 'gcc/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: "gcc" should be # used, not just "gcc". Subfeatures, like in # "gcc-3.2" are allowed. If left empty, the flag # will be used unconditionally. # # Propery sets may use value-less properties ('' vs. # 'value') to match absent properties. This allows to # separately match: # # /64 # ia64/ # # Where both features are optional. Without this syntax # we would be forced to define "default" values. values * : # The value to add to variable. If 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 : -model ansi # Treat 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 '', that is, without value. # That is an older syntax: # flags gcc.link RPATH ; # for compatibility, convert it to # flags gcc.link RPATH : ; 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 '' (compare with # '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 # foo foo match # foo foo foo no match # foo foo foo no match # foo foo foo 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 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 # 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) ; 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 on and 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 = 0 1 2 3 4 ; assert.result 1/2/3 : find-property-subset 1/2/3 0/0/1 2/5 9 : $(p) ; assert.result : find-property-subset 0/0/9/9/5 9 : $(p) ; local p-set = / 0/ /1 0/1 ; assert.result / : find-property-subset $(p-set) : ; assert.result 0/ : find-property-subset $(p-set) : 0 2 ; assert.result /1 : find-property-subset $(p-set) : 1 2 ; assert.result 0/1 : find-property-subset $(p-set) : 0 1 ; }