1# Copyright 2002 Dave Abrahams 2# Distributed under the Boost Software License, Version 1.0. 3# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 4 5import "class" : new ; 6import sequence ; 7import set ; 8import regex ; 9import feature ; 10import property ; 11import container ; 12import string ; 13 14 15# Transform property-set by applying f to each component property. 16# 17local rule apply-to-property-set ( f property-set ) 18{ 19 local properties = [ feature.split $(property-set) ] ; 20 return [ string.join [ $(f) $(properties) ] : / ] ; 21} 22 23 24# Expand the given build request by combining all property-sets which do not 25# specify conflicting non-free features. Expects all the project files to 26# already be loaded. 27# 28rule expand-no-defaults ( property-sets * ) 29{ 30 # First make all features and subfeatures explicit. 31 local expanded-property-sets = [ sequence.transform apply-to-property-set 32 feature.expand-subfeatures : $(property-sets) ] ; 33 34 # Now combine all of the expanded property-sets 35 local product = [ x-product $(expanded-property-sets) : $(feature-space) ] ; 36 37 return $(product) ; 38} 39 40 41# Implementation of x-product, below. Expects all the project files to already 42# be loaded. 43# 44local rule x-product-aux ( property-sets + ) 45{ 46 local result ; 47 local p = [ feature.split $(property-sets[1]) ] ; 48 local f = [ set.difference $(p:G) : [ feature.free-features ] ] ; 49 local seen ; 50 # No conflict with things used at a higher level? 51 if ! [ set.intersection $(f) : $(x-product-used) ] 52 { 53 local x-product-seen ; 54 { 55 # Do not mix in any conflicting features. 56 local x-product-used = $(x-product-used) $(f) ; 57 58 if $(property-sets[2]) 59 { 60 local rest = [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; 61 result = $(property-sets[1])/$(rest) ; 62 } 63 64 result ?= $(property-sets[1]) ; 65 } 66 67 # If we did not encounter a conflicting feature lower down, do not 68 # recurse again. 69 if ! [ set.intersection $(f) : $(x-product-seen) ] 70 { 71 property-sets = ; 72 } 73 74 seen = $(x-product-seen) ; 75 } 76 77 if $(property-sets[2]) 78 { 79 result += [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; 80 } 81 82 # Note that we have seen these features so that higher levels will recurse 83 # again without them set. 84 x-product-seen += $(f) $(seen) ; 85 return $(result) ; 86} 87 88 89# Return the cross-product of all elements of property-sets, less any that would 90# contain conflicting values for single-valued features. Expects all the project 91# files to already be loaded. 92# 93local rule x-product ( property-sets * ) 94{ 95 if $(property-sets).non-empty 96 { 97 # Prepare some "scoped globals" that can be used by the implementation 98 # function, x-product-aux. 99 local x-product-seen x-product-used ; 100 return [ x-product-aux $(property-sets) : $(feature-space) ] ; 101 } 102 # Otherwise return empty. 103} 104 105 106# Returns true if either 'v' or the part of 'v' before the first '-' symbol is 107# an implicit value. Expects all the project files to already be loaded. 108# 109local rule looks-like-implicit-value ( v ) 110{ 111 if [ feature.is-implicit-value $(v) ] 112 { 113 return true ; 114 } 115 else 116 { 117 local split = [ regex.split $(v) - ] ; 118 if [ feature.is-implicit-value $(split[1]) ] 119 { 120 return true ; 121 } 122 } 123} 124 125 126# Takes the command line tokens (such as taken from the ARGV rule) and 127# constructs a build request from them. Returns a vector of two vectors (where 128# "vector" means container.jam's "vector"). First is the set of targets 129# specified in the command line, and second is the set of requested build 130# properties. Expects all the project files to already be loaded. 131# 132rule from-command-line ( command-line * ) 133{ 134 local targets ; 135 local properties ; 136 137 command-line = $(command-line[2-]) ; 138 local skip-next = ; 139 for local e in $(command-line) 140 { 141 if $(skip-next) 142 { 143 skip-next = ; 144 } 145 else if ! [ MATCH ^(-) : $(e) ] 146 { 147 # Build request spec either has "=" in it or completely consists of 148 # implicit feature values. 149 local fs = feature-space ; 150 if [ MATCH "(.*=.*)" : $(e) ] 151 || [ looks-like-implicit-value $(e:D=) : $(feature-space) ] 152 { 153 properties += [ convert-command-line-element $(e) : 154 $(feature-space) ] ; 155 } 156 else if $(e) 157 { 158 targets += $(e) ; 159 } 160 } 161 else if [ MATCH "^(-[-ldjfsto])$" : $(e) ] 162 { 163 skip-next = true ; 164 } 165 } 166 return [ new vector 167 [ new vector $(targets) ] 168 [ new vector $(properties) ] ] ; 169} 170 171 172# Converts one element of command line build request specification into internal 173# form. Expects all the project files to already be loaded. 174# 175local rule convert-command-line-element ( e ) 176{ 177 local result ; 178 local parts = [ regex.split $(e) "/" ] ; 179 while $(parts) 180 { 181 local p = $(parts[1]) ; 182 local m = [ MATCH "([^=]*)=(.*)" : $(p) ] ; 183 local lresult ; 184 local feature ; 185 local values ; 186 if $(m) 187 { 188 feature = $(m[1]) ; 189 values = [ regex.split $(m[2]) "," ] ; 190 lresult = <$(feature)>$(values) ; 191 } 192 else 193 { 194 lresult = [ regex.split $(p) "," ] ; 195 } 196 197 if $(feature) && free in [ feature.attributes <$(feature)> ] 198 { 199 # If we have free feature, then the value is everything 200 # until the end of the command line token. Slashes in 201 # the following string are not taked to mean separation 202 # of properties. Commas are also not interpreted specially. 203 values = $(values:J=,) ; 204 values = $(values) $(parts[2-]) ; 205 values = $(values:J=/) ; 206 lresult = <$(feature)>$(values) ; 207 parts = ; 208 } 209 210 if ! [ MATCH (.*-.*) : $(p) ] 211 { 212 # property.validate cannot handle subfeatures, so we avoid the check 213 # here. 214 for local p in $(lresult) 215 { 216 property.validate $(p) : $(feature-space) ; 217 } 218 } 219 220 if ! $(result) 221 { 222 result = $(lresult) ; 223 } 224 else 225 { 226 result = $(result)/$(lresult) ; 227 } 228 229 parts = $(parts[2-]) ; 230 } 231 232 return $(result) ; 233} 234 235 236rule __test__ ( ) 237{ 238 import assert ; 239 import feature ; 240 241 feature.prepare-test build-request-test-temp ; 242 243 import build-request ; 244 import build-request : expand-no-defaults : build-request.expand-no-defaults ; 245 import errors : try catch ; 246 import feature : feature subfeature ; 247 248 feature toolset : gcc msvc borland : implicit ; 249 subfeature toolset gcc : version : 2.95.2 2.95.3 2.95.4 250 3.0 3.0.1 3.0.2 : optional ; 251 252 feature variant : debug release : implicit composite ; 253 feature inlining : on off ; 254 feature "include" : : free ; 255 256 feature stdlib : native stlport : implicit ; 257 258 feature runtime-link : dynamic static : symmetric ; 259 260 # Empty build requests should expand to empty. 261 assert.result 262 : build-request.expand-no-defaults ; 263 264 assert.result 265 <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug 266 <toolset>msvc/<stdlib>stlport/<variant>debug 267 <toolset>msvc/<variant>debug 268 : build-request.expand-no-defaults gcc-3.0.1/stlport msvc/stlport msvc debug ; 269 270 assert.result 271 <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug 272 <toolset>msvc/<variant>debug 273 <variant>debug/<toolset>msvc/<stdlib>stlport 274 : build-request.expand-no-defaults gcc-3.0.1/stlport msvc debug msvc/stlport ; 275 276 assert.result 277 <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug/<inlining>off 278 <toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>release/<inlining>off 279 : build-request.expand-no-defaults gcc-3.0.1/stlport debug release <inlining>off ; 280 281 assert.result 282 <include>a/b/c/<toolset>gcc/<toolset-gcc:version>3.0.1/<stdlib>stlport/<variant>debug/<include>x/y/z 283 <include>a/b/c/<toolset>msvc/<stdlib>stlport/<variant>debug/<include>x/y/z 284 <include>a/b/c/<toolset>msvc/<variant>debug/<include>x/y/z 285 : build-request.expand-no-defaults <include>a/b/c gcc-3.0.1/stlport msvc/stlport msvc debug <include>x/y/z ; 286 287 local r ; 288 289 r = [ build-request.from-command-line bjam debug runtime-link=dynamic ] ; 290 assert.equal [ $(r).get-at 1 ] : ; 291 assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ; 292 293 try ; 294 { 295 build-request.from-command-line bjam gcc/debug runtime-link=dynamic/static ; 296 } 297 catch \"static\" is not an implicit feature value ; 298 299 r = [ build-request.from-command-line bjam -d2 --debug debug target runtime-link=dynamic ] ; 300 assert.equal [ $(r).get-at 1 ] : target ; 301 assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic ; 302 303 r = [ build-request.from-command-line bjam debug runtime-link=dynamic,static ] ; 304 assert.equal [ $(r).get-at 1 ] : ; 305 assert.equal [ $(r).get-at 2 ] : debug <runtime-link>dynamic <runtime-link>static ; 306 307 r = [ build-request.from-command-line bjam debug gcc/runtime-link=dynamic,static ] ; 308 assert.equal [ $(r).get-at 1 ] : ; 309 assert.equal [ $(r).get-at 2 ] : debug gcc/<runtime-link>dynamic 310 gcc/<runtime-link>static ; 311 312 r = [ build-request.from-command-line bjam msvc gcc,borland/runtime-link=static ] ; 313 assert.equal [ $(r).get-at 1 ] : ; 314 assert.equal [ $(r).get-at 2 ] : msvc gcc/<runtime-link>static 315 borland/<runtime-link>static ; 316 317 r = [ build-request.from-command-line bjam gcc-3.0 ] ; 318 assert.equal [ $(r).get-at 1 ] : ; 319 assert.equal [ $(r).get-at 2 ] : gcc-3.0 ; 320 321 feature.finish-test build-request-test-temp ; 322} 323