# 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 gcc/3.0.1/stlport/debug msvc/stlport/debug msvc/debug : build-request.expand-no-defaults gcc-3.0.1/stlport msvc/stlport msvc debug ; assert.result gcc/3.0.1/stlport/debug msvc/debug debug/msvc/stlport : build-request.expand-no-defaults gcc-3.0.1/stlport msvc debug msvc/stlport ; assert.result gcc/3.0.1/stlport/debug/off gcc/3.0.1/stlport/release/off : build-request.expand-no-defaults gcc-3.0.1/stlport debug release off ; assert.result a/b/c/gcc/3.0.1/stlport/debug/x/y/z a/b/c/msvc/stlport/debug/x/y/z a/b/c/msvc/debug/x/y/z : build-request.expand-no-defaults a/b/c gcc-3.0.1/stlport msvc/stlport msvc debug 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 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 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 dynamic 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/dynamic gcc/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/static borland/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 ; }