| # Copyright 2002 Dave Abrahams |
| # 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 sequence ; |
| import set ; |
| import regex ; |
| import feature ; |
| import property ; |
| import container ; |
| import string ; |
| |
| |
| # Transform property-set by applying f to each component property. |
| # |
| local rule apply-to-property-set ( f property-set ) |
| { |
| local properties = [ feature.split $(property-set) ] ; |
| return [ string.join [ $(f) $(properties) ] : / ] ; |
| } |
| |
| |
| # Expand the given build request by combining all property-sets which do not |
| # specify conflicting non-free features. Expects all the project files to |
| # already be loaded. |
| # |
| rule expand-no-defaults ( property-sets * ) |
| { |
| # First make all features and subfeatures explicit. |
| local expanded-property-sets = [ sequence.transform apply-to-property-set |
| feature.expand-subfeatures : $(property-sets) ] ; |
| |
| # Now combine all of the expanded property-sets |
| local product = [ x-product $(expanded-property-sets) : $(feature-space) ] ; |
| |
| return $(product) ; |
| } |
| |
| |
| # Implementation of x-product, below. Expects all the project files to already |
| # be loaded. |
| # |
| local rule x-product-aux ( property-sets + ) |
| { |
| local result ; |
| local p = [ feature.split $(property-sets[1]) ] ; |
| local f = [ set.difference $(p:G) : [ feature.free-features ] ] ; |
| local seen ; |
| # No conflict with things used at a higher level? |
| if ! [ set.intersection $(f) : $(x-product-used) ] |
| { |
| local x-product-seen ; |
| { |
| # Do not mix in any conflicting features. |
| local x-product-used = $(x-product-used) $(f) ; |
| |
| if $(property-sets[2]) |
| { |
| local rest = [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; |
| result = $(property-sets[1])/$(rest) ; |
| } |
| |
| result ?= $(property-sets[1]) ; |
| } |
| |
| # If we did not encounter a conflicting feature lower down, do not |
| # recurse again. |
| if ! [ set.intersection $(f) : $(x-product-seen) ] |
| { |
| property-sets = ; |
| } |
| |
| seen = $(x-product-seen) ; |
| } |
| |
| if $(property-sets[2]) |
| { |
| result += [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; |
| } |
| |
| # Note that we have seen these features so that higher levels will recurse |
| # again without them set. |
| x-product-seen += $(f) $(seen) ; |
| return $(result) ; |
| } |
| |
| |
| # Return the cross-product of all elements of property-sets, less any that would |
| # contain conflicting values for single-valued features. Expects all the project |
| # files to already be loaded. |
| # |
| local rule x-product ( property-sets * ) |
| { |
| if $(property-sets).non-empty |
| { |
| # Prepare some "scoped globals" that can be used by the implementation |
| # function, x-product-aux. |
| local x-product-seen x-product-used ; |
| return [ x-product-aux $(property-sets) : $(feature-space) ] ; |
| } |
| # Otherwise return empty. |
| } |
| |
| |
| # Returns true if either 'v' or the part of 'v' before the first '-' symbol is |
| # an implicit value. Expects all the project files to already be loaded. |
| # |
| local rule looks-like-implicit-value ( v ) |
| { |
| if [ feature.is-implicit-value $(v) ] |
| { |
| return true ; |
| } |
| else |
| { |
| local split = [ regex.split $(v) - ] ; |
| if [ feature.is-implicit-value $(split[1]) ] |
| { |
| return true ; |
| } |
| } |
| } |
| |
| |
| # Takes the command line tokens (such as taken from the ARGV rule) and |
| # constructs a build request from them. Returns a vector of two vectors (where |
| # "vector" means container.jam's "vector"). First is the set of targets |
| # specified in the command line, and second is the set of requested build |
| # properties. Expects all the project files to already be loaded. |
| # |
| rule from-command-line ( command-line * ) |
| { |
| local targets ; |
| local properties ; |
| |
| command-line = $(command-line[2-]) ; |
| local skip-next = ; |
| for local e in $(command-line) |
| { |
| if $(skip-next) |
| { |
| skip-next = ; |
| } |
| else if ! [ MATCH "^(-).*" : $(e) ] |
| { |
| # Build request spec either has "=" in it or completely consists of |
| # implicit feature values. |
| local fs = feature-space ; |
| if [ MATCH "(.*=.*)" : $(e) ] |
| || [ looks-like-implicit-value $(e:D=) : $(feature-space) ] |
| { |
| properties += [ convert-command-line-element $(e) : |
| $(feature-space) ] ; |
| } |
| else |
| { |
| targets += $(e) ; |
| } |
| } |
| else if [ MATCH "^(-[-ldjfsto])$" : $(e) ] |
| { |
| skip-next = true ; |
| } |
| } |
| return [ new vector |
| [ new vector $(targets) ] |
| [ new vector $(properties) ] ] ; |
| } |
| |
| |
| # Converts one element of command line build request specification into internal |
| # form. Expects all the project files to already be loaded. |
| # |
| local rule convert-command-line-element ( e ) |
| { |
| local result ; |
| local parts = [ regex.split $(e) "/" ] ; |
| while $(parts) |
| { |
| local p = $(parts[1]) ; |
| local m = [ MATCH "([^=]*)=(.*)" : $(p) ] ; |
| local lresult ; |
| local feature ; |
| local values ; |
| if $(m) |
| { |
| feature = $(m[1]) ; |
| values = [ regex.split $(m[2]) "," ] ; |
| lresult = <$(feature)>$(values) ; |
| } |
| else |
| { |
| lresult = [ regex.split $(p) "," ] ; |
| } |
| |
| if $(feature) && free in [ feature.attributes $(feature) ] |
| { |
| # If we have free feature, then the value is everything |
| # until the end of the command line token. Slashes in |
| # the following string are not taked to mean separation |
| # of properties. Commas are also not interpreted specially. |
| values = $(values:J=,) ; |
| values = $(values) $(parts[2-]) ; |
| values = $(values:J=/) ; |
| lresult = <$(feature)>$(values) ; |
| parts = ; |
| } |
| |
| if ! [ MATCH (.*-.*) : $(p) ] |
| { |
| # property.validate cannot handle subfeatures, so we avoid the check |
| # here. |
| for local p in $(lresult) |
| { |
| property.validate $(p) : $(feature-space) ; |
| } |
| } |
| |
| if ! $(result) |
| { |
| result = $(lresult) ; |
| } |
| else |
| { |
| result = $(result)/$(lresult) ; |
| } |
| |
| parts = $(parts[2-]) ; |
| } |
| |
| return $(result) ; |
| } |
| |
| |
| rule __test__ ( ) |
| { |
| import assert ; |
| import feature ; |
| |
| feature.prepare-test build-request-test-temp ; |
| |
| import build-request ; |
| import build-request : expand-no-defaults : build-request.expand-no-defaults ; |
| import errors : try catch ; |
| import feature : feature subfeature ; |
| |
| feature toolset : gcc msvc borland : implicit ; |
| subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 |
| 3.0 3.0.1 3.0.2 : optional ; |
| |
| feature variant : debug release : implicit composite ; |
| feature inlining : on off ; |
| feature "include" : : free ; |
| |
| feature stdlib : native stlport : implicit ; |
| |
| feature runtime-link : dynamic static : symmetric ; |
| |
| # Empty build requests should expand to empty. |
| assert.result |
| : build-request.expand-no-defaults ; |
| |
| assert.result |
| <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug |
| <toolset>msvc/<stdlib>stlport/<variant>debug |
| <toolset>msvc/<variant>debug |
| : build-request.expand-no-defaults gcc-3.0.1/stlport msvc/stlport msvc debug ; |
| |
| assert.result |
| <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug |
| <toolset>msvc/<variant>debug |
| <variant>debug/<toolset>msvc/<stdlib>stlport |
| : build-request.expand-no-defaults gcc-3.0.1/stlport msvc debug msvc/stlport ; |
| |
| assert.result |
| <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug/<inlining>off |
| <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>release/<inlining>off |
| : build-request.expand-no-defaults gcc-3.0.1/stlport debug release <inlining>off ; |
| |
| assert.result |
| <include>a/b/c/<toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug/<include>x/y/z |
| <include>a/b/c/<toolset>msvc/<stdlib>stlport/<variant>debug/<include>x/y/z |
| <include>a/b/c/<toolset>msvc/<variant>debug/<include>x/y/z |
| : build-request.expand-no-defaults <include>a/b/c gcc-3.0.1/stlport msvc/stlport msvc debug <include>x/y/z ; |
| |
| local r ; |
| |
| r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ; |
| assert.equal [ $(r).get-at 1 ] : ; |
| assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ; |
| |
| try ; |
| { |
| build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ; |
| } |
| catch \"static\" is not a value of an implicit feature ; |
| |
| r = [ build-request.from-command-line bjam -d2 --debug debug target runtime-link=dynamic ] ; |
| assert.equal [ $(r).get-at 1 ] : target ; |
| assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ; |
| |
| r = [ build-request.from-command-line bjam debug runtime-link=dynamic,static ] ; |
| assert.equal [ $(r).get-at 1 ] : ; |
| assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic <runtime-link>static ; |
| |
| r = [ build-request.from-command-line bjam debug gcc/runtime-link=dynamic,static ] ; |
| assert.equal [ $(r).get-at 1 ] : ; |
| assert.equal [ $(r).get-at 2 ] : debug gcc/<runtime-link>dynamic |
| gcc/<runtime-link>static ; |
| |
| r = [ build-request.from-command-line bjam msvc gcc,borland/runtime-link=static ] ; |
| assert.equal [ $(r).get-at 1 ] : ; |
| assert.equal [ $(r).get-at 2 ] : msvc gcc/<runtime-link>static |
| borland/<runtime-link>static ; |
| |
| r = [ build-request.from-command-line bjam gcc-3.0 ] ; |
| assert.equal [ $(r).get-at 1 ] : ; |
| assert.equal [ $(r).get-at 2 ] : gcc-3.0 ; |
| |
| feature.finish-test build-request-test-temp ; |
| } |