| # Copyright 2003 Dave Abrahams |
| # Copyright 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) |
| |
| import "class" : new ; |
| import feature ; |
| import path ; |
| import project ; |
| import property ; |
| import sequence ; |
| import set ; |
| import option ; |
| |
| # Class for storing a set of properties. |
| # |
| # There is 1<->1 correspondence between identity and value. No two instances |
| # of the class are equal. To maintain this property, the 'property-set.create' |
| # rule should be used to create new instances. Instances are immutable. |
| # |
| # Each property is classified with regard to its effect on build results. |
| # Incidental properties have no effect on build results, from Boost.Build's |
| # point of view. Others are either free, or non-free and we refer to non-free |
| # ones as 'base'. Each property belongs to exactly one of those categories. |
| # |
| # It is possible to get a list of properties belonging to each category as |
| # well as a list of properties with a specific attribute. |
| # |
| # Several operations, like and refine and as-path are provided. They all use |
| # caching whenever possible. |
| # |
| class property-set |
| { |
| import errors ; |
| import feature ; |
| import path ; |
| import property ; |
| import property-set ; |
| import set ; |
| |
| rule __init__ ( raw-properties * ) |
| { |
| self.raw = $(raw-properties) ; |
| |
| for local p in $(raw-properties) |
| { |
| if ! $(p:G) |
| { |
| errors.error "Invalid property: '$(p)'" ; |
| } |
| |
| local att = [ feature.attributes $(p:G) ] ; |
| # A feature can be both incidental and free, in which case we add it |
| # to incidental. |
| if incidental in $(att) |
| { |
| self.incidental += $(p) ; |
| } |
| else if free in $(att) |
| { |
| self.free += $(p) ; |
| } |
| else |
| { |
| self.base += $(p) ; |
| } |
| |
| if dependency in $(att) |
| { |
| self.dependency += $(p) ; |
| } |
| else |
| { |
| self.non-dependency += $(p) ; |
| } |
| |
| if [ MATCH (:) : $(p:G=) ] |
| { |
| self.conditional += $(p) ; |
| } |
| else |
| { |
| self.non-conditional += $(p) ; |
| } |
| |
| if propagated in $(att) |
| { |
| self.propagated += $(p) ; |
| } |
| if link-incompatible in $(att) |
| { |
| self.link-incompatible += $(p) ; |
| } |
| } |
| } |
| |
| # Returns Jam list of stored properties. |
| # |
| rule raw ( ) |
| { |
| return $(self.raw) ; |
| } |
| |
| rule str ( ) |
| { |
| return "[" $(self.raw) "]" ; |
| } |
| |
| # Returns properties that are neither incidental nor free. |
| # |
| rule base ( ) |
| { |
| return $(self.base) ; |
| } |
| |
| # Returns free properties which are not incidental. |
| # |
| rule free ( ) |
| { |
| return $(self.free) ; |
| } |
| |
| # Returns dependency properties. |
| # |
| rule dependency ( ) |
| { |
| return $(self.dependency) ; |
| } |
| |
| rule non-dependency ( ) |
| { |
| return $(self.non-dependency) ; |
| } |
| |
| rule conditional ( ) |
| { |
| return $(self.conditional) ; |
| } |
| |
| rule non-conditional ( ) |
| { |
| return $(self.non-conditional) ; |
| } |
| |
| # Returns incidental properties. |
| # |
| rule incidental ( ) |
| { |
| return $(self.incidental) ; |
| } |
| |
| rule refine ( ps ) |
| { |
| if ! $(self.refined.$(ps)) |
| { |
| local r = [ property.refine $(self.raw) : [ $(ps).raw ] ] ; |
| if $(r[1]) != "@error" |
| { |
| self.refined.$(ps) = [ property-set.create $(r) ] ; |
| } |
| else |
| { |
| self.refined.$(ps) = $(r) ; |
| } |
| } |
| 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) |
| { |
| self.composites = [ property-set.create |
| [ feature.expand-composites $(self.raw) ] ] ; |
| } |
| return $(self.composites) ; |
| } |
| |
| rule evaluate-conditionals ( context ? ) |
| { |
| context ?= $(__name__) ; |
| if ! $(self.evaluated.$(context)) |
| { |
| self.evaluated.$(context) = [ property-set.create |
| [ property.evaluate-conditionals-in-context $(self.raw) : [ $(context).raw ] ] ] ; |
| } |
| return $(self.evaluated.$(context)) ; |
| } |
| |
| rule propagated ( ) |
| { |
| if ! $(self.propagated-ps) |
| { |
| self.propagated-ps = [ property-set.create $(self.propagated) ] ; |
| } |
| return $(self.propagated-ps) ; |
| } |
| |
| rule link-incompatible ( ) |
| { |
| if ! $(self.link-incompatible-ps) |
| { |
| self.link-incompatible-ps = |
| [ property-set.create $(self.link-incompatible) ] ; |
| } |
| return $(self.link-incompatible-ps) ; |
| } |
| |
| rule run-actions ( ) |
| { |
| if ! $(self.run) |
| { |
| self.run = [ property-set.create [ feature.run-actions $(self.raw) ] ] ; |
| } |
| return $(self.run) ; |
| } |
| |
| rule add-defaults ( ) |
| { |
| if ! $(self.defaults) |
| { |
| self.defaults = [ property-set.create |
| [ feature.add-defaults $(self.raw) ] ] ; |
| } |
| return $(self.defaults) ; |
| } |
| |
| rule as-path ( ) |
| { |
| if ! $(self.as-path) |
| { |
| self.as-path = [ property.as-path $(self.base) ] ; |
| } |
| return $(self.as-path) ; |
| } |
| |
| # Computes the path to be used for a target with the given properties. |
| # Returns a list of |
| # - the computed path |
| # - if the path is relative to the build directory, a value of 'true'. |
| # |
| rule target-path ( ) |
| { |
| if ! $(self.target-path) |
| { |
| # The <location> feature can be used to explicitly change the |
| # location of generated targets. |
| local l = [ get <location> ] ; |
| if $(l) |
| { |
| self.target-path = $(l) ; |
| } |
| else |
| { |
| local p = [ as-path ] ; |
| p = [ property-set.hash-maybe $(p) ] ; |
| |
| # A real ugly hack. Boost regression test system requires |
| # specific target paths, and it seems that changing it to handle |
| # other directory layout is really hard. For that reason, we |
| # teach V2 to do the things regression system requires. The |
| # value of '<location-prefix>' is prepended to the path. |
| local prefix = [ get <location-prefix> ] ; |
| if $(prefix) |
| { |
| self.target-path = [ path.join $(prefix) $(p) ] ; |
| } |
| else |
| { |
| self.target-path = $(p) ; |
| } |
| if ! $(self.target-path) |
| { |
| self.target-path = . ; |
| } |
| # The path is relative to build dir. |
| self.target-path += true ; |
| } |
| } |
| return $(self.target-path) ; |
| } |
| |
| rule add ( ps ) |
| { |
| if ! $(self.added.$(ps)) |
| { |
| self.added.$(ps) = [ property-set.create $(self.raw) [ $(ps).raw ] ] ; |
| } |
| return $(self.added.$(ps)) ; |
| } |
| |
| rule add-raw ( properties * ) |
| { |
| return [ add [ property-set.create $(properties) ] ] ; |
| } |
| |
| rule link-incompatible-with ( ps ) |
| { |
| if ! $(.li.$(ps)) |
| { |
| local li1 = [ $(__name__).link-incompatible ] ; |
| local li2 = [ $(ps).link-incompatible ] ; |
| if [ set.equal $(li1) : $(li2) ] |
| { |
| .li.$(ps) = false ; |
| } |
| else |
| { |
| .li.$(ps) = true ; |
| } |
| } |
| if $(.li.$(ps)) = true |
| { |
| return true ; |
| } |
| else |
| { |
| return ; |
| } |
| } |
| |
| # Returns all values of 'feature'. |
| # |
| rule get ( feature ) |
| { |
| if ! $(self.map-built) |
| { |
| # For each feature, create a member var and assign all values to it. |
| # Since all regular member vars start with 'self', there will be no |
| # conflicts between names. |
| self.map-built = true ; |
| for local v in $(self.raw) |
| { |
| $(v:G) += $(v:G=) ; |
| } |
| } |
| return $($(feature)) ; |
| } |
| } |
| |
| |
| # Creates a new 'property-set' instance for the given raw properties or returns |
| # an already existing ones. |
| # |
| rule create ( raw-properties * ) |
| { |
| raw-properties = [ sequence.unique |
| [ sequence.insertion-sort $(raw-properties) ] ] ; |
| |
| local key = $(raw-properties:J=-:E=) ; |
| |
| if ! $(.ps.$(key)) |
| { |
| .ps.$(key) = [ new property-set $(raw-properties) ] ; |
| } |
| return $(.ps.$(key)) ; |
| } |
| NATIVE_RULE property-set : create ; |
| |
| |
| # Creates a new 'property-set' instance after checking that all properties are |
| # valid and converting incidental properties into gristed form. |
| # |
| rule create-with-validation ( raw-properties * ) |
| { |
| property.validate $(raw-properties) ; |
| return [ create [ property.make $(raw-properties) ] ] ; |
| } |
| |
| |
| # Creates a property-set from the input given by the user, in the context of |
| # 'jamfile-module' at 'location'. |
| # |
| rule create-from-user-input ( raw-properties * : jamfile-module location ) |
| { |
| local specification = [ property.translate-paths $(raw-properties) |
| : $(location) ] ; |
| specification = [ property.translate-indirect $(specification) |
| : $(jamfile-module) ] ; |
| local project-id = [ project.attribute $(jamfile-module) id ] ; |
| project-id ?= [ path.root $(location) [ path.pwd ] ] ; |
| specification = [ property.translate-dependencies |
| $(specification) : $(project-id) : $(location) ] ; |
| specification = |
| [ property.expand-subfeatures-in-conditions $(specification) ] ; |
| specification = [ property.make $(specification) ] ; |
| return [ property-set.create $(specification) ] ; |
| } |
| |
| |
| # Refines requirements with requirements provided by the user. Specially handles |
| # "-<property>value" syntax in specification to remove given requirements. |
| # - parent-requirements -- property-set object with requirements to refine. |
| # - specification -- string list of requirements provided by the user. |
| # - project-module -- module to which context indirect features will be |
| # bound. |
| # - location -- path to which path features are relative. |
| # |
| rule refine-from-user-input ( parent-requirements : specification * : |
| project-module : location ) |
| { |
| if ! $(specification) |
| { |
| return $(parent-requirements) ; |
| } |
| else |
| { |
| local add-requirements ; |
| local remove-requirements ; |
| |
| for local r in $(specification) |
| { |
| local m = [ MATCH "^-(.*)" : $(r) ] ; |
| if $(m) |
| { |
| remove-requirements += $(m) ; |
| } |
| else |
| { |
| add-requirements += $(r) ; |
| } |
| } |
| |
| if $(remove-requirements) |
| { |
| # Need to create a property set, so that path features and indirect |
| # features are translated just like they are in project |
| # requirements. |
| local ps = [ property-set.create-from-user-input |
| $(remove-requirements) : $(project-module) $(location) ] ; |
| |
| parent-requirements = [ property-set.create |
| [ set.difference [ $(parent-requirements).raw ] |
| : [ $(ps).raw ] ] ] ; |
| specification = $(add-requirements) ; |
| } |
| |
| local requirements = [ property-set.create-from-user-input |
| $(specification) : $(project-module) $(location) ] ; |
| |
| return [ $(parent-requirements).refine $(requirements) ] ; |
| } |
| } |
| |
| |
| # Returns a property-set with an empty set of properties. |
| # |
| rule empty ( ) |
| { |
| if ! $(.empty) |
| { |
| .empty = [ create ] ; |
| } |
| return $(.empty) ; |
| } |
| |
| if [ option.get hash : : yes ] = yes |
| { |
| rule hash-maybe ( path ? ) |
| { |
| path ?= "" ; |
| return [ MD5 $(path) ] ; |
| } |
| } |
| else |
| { |
| rule hash-maybe ( path ? ) |
| { |
| return $(path) ; |
| } |
| } |
| |