1# Copyright 2003 Dave Abrahams 2# Copyright 2005 Rene Rivera 3# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or copy at 6# http://www.boost.org/LICENSE_1_0.txt) 7 8# Support for toolset definition. 9 10import errors ; 11import feature ; 12import generators ; 13import numbers ; 14import path ; 15import property ; 16import regex ; 17import sequence ; 18import set ; 19import property-set ; 20import order ; 21import "class" : new ; 22 23 24.flag-no = 1 ; 25 26.ignore-requirements = ; 27 28# This is used only for testing, to make sure we do not get random extra 29# elements in paths. 30if --ignore-toolset-requirements in [ modules.peek : ARGV ] 31{ 32 .ignore-requirements = 1 ; 33} 34 35 36# Initializes an additional toolset-like module. First load the 'toolset-module' 37# and then calls its 'init' rule with trailing arguments. 38# 39rule using ( toolset-module : * ) 40{ 41 import $(toolset-module) ; 42 $(toolset-module).init $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) 43 ; 44} 45 46 47# Expands subfeatures in each property sets, e.g. '<toolset>gcc-3.2' will be 48# converted to '<toolset>gcc/<toolset-version>3.2'. 49# 50local rule normalize-condition ( property-sets * ) 51{ 52 local result ; 53 for local p in $(property-sets) 54 { 55 local split = [ feature.split $(p) ] ; 56 local expanded = [ feature.expand-subfeatures [ feature.split $(p) ] ] ; 57 result += $(expanded:J=/) ; 58 } 59 return $(result) ; 60} 61 62 63# Specifies if the 'flags' rule should check that the invoking module is the 64# same as the module we are setting the flag for. 'v' can be either 'checked' or 65# 'unchecked'. Subsequent call to 'pop-checking-for-flags-module' will restore 66# the setting that was in effect before calling this rule. 67# 68rule push-checking-for-flags-module ( v ) 69{ 70 .flags-module-checking = $(v) $(.flags-module-checking) ; 71} 72 73rule pop-checking-for-flags-module ( ) 74{ 75 .flags-module-checking = $(.flags-module-checking[2-]) ; 76} 77 78 79# Specifies the flags (variables) that must be set on targets under certain 80# conditions, described by arguments. 81# 82rule flags ( 83 rule-or-module # If contains a dot, should be a rule name. The flags will 84 # be applied when that rule is used to set up build 85 # actions. 86 # 87 # If does not contain dot, should be a module name. The 88 # flag will be applied for all rules in that module. If 89 # module for rule is different from the calling module, an 90 # error is issued. 91 92 variable-name # Variable that should be set on target. 93 condition * : # A condition when this flag should be applied. Should be a 94 # set of property sets. If one of those property sets is 95 # contained in the build properties, the flag will be used. 96 # Implied values are not allowed: "<toolset>gcc" should be 97 # used, not just "gcc". Subfeatures, like in 98 # "<toolset>gcc-3.2" are allowed. If left empty, the flag 99 # will be used unconditionally. 100 # 101 # Propery sets may use value-less properties ('<a>' vs. 102 # '<a>value') to match absent properties. This allows to 103 # separately match: 104 # 105 # <architecture>/<address-model>64 106 # <architecture>ia64/<address-model> 107 # 108 # Where both features are optional. Without this syntax 109 # we would be forced to define "default" values. 110 111 values * : # The value to add to variable. If <feature> is specified, 112 # then the value of 'feature' will be added. 113 unchecked ? # If value 'unchecked' is passed, will not test that flags 114 # are set for the calling module. 115 : hack-hack ? # For 116 # flags rule OPTIONS <cxx-abi> : -model ansi 117 # Treat <cxx-abi> as condition 118 # FIXME: ugly hack. 119) 120{ 121 local caller = [ CALLER_MODULE ] ; 122 if ! [ MATCH ".*([.]).*" : $(rule-or-module) ] 123 && [ MATCH "(Jamfile<.*)" : $(caller) ] 124 { 125 # Unqualified rule name, used inside Jamfile. Most likely used with 126 # 'make' or 'notfile' rules. This prevents setting flags on the entire 127 # Jamfile module (this will be considered as rule), but who cares? 128 # Probably, 'flags' rule should be split into 'flags' and 129 # 'flags-on-module'. 130 rule-or-module = $(caller).$(rule-or-module) ; 131 } 132 else 133 { 134 local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ; 135 if $(unchecked) != unchecked 136 && $(.flags-module-checking[1]) != unchecked 137 && $(module_) != $(caller) 138 { 139 errors.error "Module $(caller) attempted to set flags for module $(module_)" ; 140 } 141 } 142 143 if $(condition) && ! $(condition:G=) && ! $(hack-hack) 144 { 145 # We have condition in the form '<feature>', that is, without value. 146 # That is an older syntax: 147 # flags gcc.link RPATH <dll-path> ; 148 # for compatibility, convert it to 149 # flags gcc.link RPATH : <dll-path> ; 150 values = $(condition) ; 151 condition = ; 152 } 153 154 if $(condition) 155 { 156 property.validate-property-sets $(condition) ; 157 condition = [ normalize-condition $(condition) ] ; 158 } 159 160 add-flag $(rule-or-module) : $(variable-name) : $(condition) : $(values) ; 161} 162 163 164# Adds a new flag setting with the specified values. Does no checking. 165# 166local rule add-flag ( rule-or-module : variable-name : condition * : values * ) 167{ 168 .$(rule-or-module).flags += $(.flag-no) ; 169 170 # Store all flags for a module. 171 local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ; 172 .module-flags.$(module_) += $(.flag-no) ; 173 # Store flag-no -> rule-or-module mapping. 174 .rule-or-module.$(.flag-no) = $(rule-or-module) ; 175 176 .$(rule-or-module).variable.$(.flag-no) += $(variable-name) ; 177 .$(rule-or-module).values.$(.flag-no) += $(values) ; 178 .$(rule-or-module).condition.$(.flag-no) += $(condition) ; 179 180 .flag-no = [ numbers.increment $(.flag-no) ] ; 181} 182 183 184# Returns the first element of 'property-sets' which is a subset of 185# 'properties' or an empty list if no such element exists. 186# 187rule find-property-subset ( property-sets * : properties * ) 188{ 189 # Cut property values off. 190 local prop-keys = $(properties:G) ; 191 192 local result ; 193 for local s in $(property-sets) 194 { 195 if ! $(result) 196 { 197 # Handle value-less properties like '<architecture>' (compare with 198 # '<architecture>x86'). 199 200 local set = [ feature.split $(s) ] ; 201 202 # Find the set of features that 203 # - have no property specified in required property set 204 # - are omitted in the build property set. 205 local default-props ; 206 for local i in $(set) 207 { 208 # If $(i) is a value-less property it should match default value 209 # of an optional property. See the first line in the example 210 # below: 211 # 212 # property set properties result 213 # <a> <b>foo <b>foo match 214 # <a> <b>foo <a>foo <b>foo no match 215 # <a>foo <b>foo <b>foo no match 216 # <a>foo <b>foo <a>foo <b>foo match 217 if ! ( $(i:G=) || ( $(i:G) in $(prop-keys) ) ) 218 { 219 default-props += $(i) ; 220 } 221 } 222 223 if $(set) in $(properties) $(default-props) 224 { 225 result = $(s) ; 226 } 227 } 228 } 229 return $(result) ; 230} 231 232 233# Returns a value to be added to some flag for some target based on the flag's 234# value definition and the given target's property set. 235# 236rule handle-flag-value ( value * : properties * ) 237{ 238 local result ; 239 if $(value:G) 240 { 241 local matches = [ property.select $(value) : $(properties) ] ; 242 local order ; 243 for local p in $(matches) 244 { 245 local att = [ feature.attributes $(p:G) ] ; 246 if dependency in $(att) 247 { 248 # The value of a dependency feature is a target and needs to be 249 # actualized. 250 result += [ $(p:G=).actualize ] ; 251 } 252 else if path in $(att) || free in $(att) 253 { 254 local values ; 255 # Treat features with && in the value specially -- each 256 # &&-separated element is considered a separate value. This is 257 # needed to handle searched libraries or include paths, which 258 # may need to be in a specific order. 259 if ! [ MATCH (&&) : $(p:G=) ] 260 { 261 values = $(p:G=) ; 262 } 263 else 264 { 265 values = [ regex.split $(p:G=) "&&" ] ; 266 } 267 if path in $(att) 268 { 269 values = [ sequence.transform path.native : $(values) ] ; 270 } 271 result += $(values) ; 272 if $(values[2]) 273 { 274 if ! $(order) 275 { 276 order = [ new order ] ; 277 } 278 local prev ; 279 for local v in $(values) 280 { 281 if $(prev) 282 { 283 $(order).add-pair $(prev) $(v) ; 284 } 285 prev = $(v) ; 286 } 287 } 288 } 289 else 290 { 291 result += $(p:G=) ; 292 } 293 } 294 if $(order) 295 { 296 result = [ $(order).order [ sequence.unique $(result) : stable ] ] ; 297 DELETE_MODULE $(order) ; 298 } 299 } 300 else 301 { 302 result += $(value) ; 303 } 304 return $(result) ; 305} 306 307 308# Given a rule name and a property set, returns a list of interleaved variables 309# names and values which must be set on targets for that rule/property-set 310# combination. 311# 312rule set-target-variables-aux ( rule-or-module : property-set ) 313{ 314 local result ; 315 properties = [ $(property-set).raw ] ; 316 for local f in $(.$(rule-or-module).flags) 317 { 318 local variable = $(.$(rule-or-module).variable.$(f)) ; 319 local condition = $(.$(rule-or-module).condition.$(f)) ; 320 local values = $(.$(rule-or-module).values.$(f)) ; 321 322 if ! $(condition) || 323 [ find-property-subset $(condition) : $(properties) ] 324 { 325 local processed ; 326 for local v in $(values) 327 { 328 # The value might be <feature-name> so needs special treatment. 329 processed += [ handle-flag-value $(v) : $(properties) ] ; 330 } 331 for local r in $(processed) 332 { 333 result += $(variable) $(r) ; 334 } 335 } 336 } 337 338 # Strip away last dot separated part and recurse. 339 local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ; 340 if $(next) 341 { 342 result += [ set-target-variables-aux $(next[1]) : $(property-set) ] ; 343 } 344 return $(result) ; 345} 346 347rule relevant-features ( rule-or-module ) 348{ 349 local result ; 350 if ! $(.relevant-features.$(rule-or-module)) 351 { 352 for local f in $(.$(rule-or-module).flags) 353 { 354 local condition = $(.$(rule-or-module).condition.$(f)) ; 355 local values = $(.$(rule-or-module).values.$(f)) ; 356 357 for local c in $(condition) 358 { 359 for local p in [ feature.split $(c) ] 360 { 361 if $(p:G) 362 { 363 result += $(p:G) ; 364 } 365 else 366 { 367 local temp = [ feature.expand-subfeatures $(p) ] ; 368 result += $(temp:G) ; 369 } 370 } 371 } 372 373 for local v in $(values) 374 { 375 if $(v:G) 376 { 377 result += $(v:G) ; 378 } 379 } 380 } 381 382 # Strip away last dot separated part and recurse. 383 local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ; 384 if $(next) 385 { 386 result += [ relevant-features $(next[1]) ] ; 387 } 388 result = [ sequence.unique $(result) ] ; 389 if $(result[1]) = "" 390 { 391 result = $(result) ; 392 } 393 .relevant-features.$(rule-or-module) = $(result) ; 394 return $(result) ; 395 } 396 else 397 { 398 return $(.relevant-features.$(rule-or-module)) ; 399 } 400} 401 402rule filter-property-set ( rule-or-module : property-set ) 403{ 404 local key = .filtered.property-set.$(rule-or-module).$(property-set) ; 405 if ! $($(key)) 406 { 407 local relevant = [ relevant-features $(rule-or-module) ] ; 408 local result ; 409 for local p in [ $(property-set).raw ] 410 { 411 if $(p:G) in $(relevant) 412 { 413 result += $(p) ; 414 } 415 } 416 $(key) = [ property-set.create $(result) ] ; 417 } 418 return $($(key)) ; 419} 420 421rule set-target-variables ( rule-or-module targets + : property-set ) 422{ 423 property-set = [ filter-property-set $(rule-or-module) : $(property-set) ] ; 424 local key = .stv.$(rule-or-module).$(property-set) ; 425 local settings = $($(key)) ; 426 if ! $(settings) 427 { 428 settings = [ set-target-variables-aux $(rule-or-module) : 429 $(property-set) ] ; 430 431 if ! $(settings) 432 { 433 settings = none ; 434 } 435 $(key) = $(settings) ; 436 } 437 438 if $(settings) != none 439 { 440 local var-name = ; 441 for local name-or-value in $(settings) 442 { 443 if $(var-name) 444 { 445 $(var-name) on $(targets) += $(name-or-value) ; 446 var-name = ; 447 } 448 else 449 { 450 var-name = $(name-or-value) ; 451 } 452 } 453 } 454} 455 456 457# Make toolset 'toolset', defined in a module of the same name, inherit from 458# 'base'. 459# 1. The 'init' rule from 'base' is imported into 'toolset' with full name. 460# Another 'init' is called, which forwards to the base one. 461# 2. All generators from 'base' are cloned. The ids are adjusted and <toolset> 462# property in requires is adjusted too. 463# 3. All flags are inherited. 464# 4. All rules are imported. 465# 466rule inherit ( toolset : base ) 467{ 468 import $(base) ; 469 inherit-generators $(toolset) : $(base) ; 470 inherit-flags $(toolset) : $(base) ; 471 inherit-rules $(toolset) : $(base) ; 472} 473 474 475rule inherit-generators ( toolset properties * : base : generators-to-ignore * ) 476{ 477 properties ?= <toolset>$(toolset) ; 478 local base-generators = [ generators.generators-for-toolset $(base) ] ; 479 for local g in $(base-generators) 480 { 481 local id = [ $(g).id ] ; 482 483 if ! $(id) in $(generators-to-ignore) 484 { 485 # Some generator names have multiple periods in their name, so 486 # $(id:B=$(toolset)) does not generate the right new-id name. E.g. 487 # if id = gcc.compile.c++ then $(id:B=darwin) = darwin.c++, which is 488 # not what we want. Manually parse the base and suffix. If there is 489 # a better way to do this, I would love to see it. See also the 490 # register() rule in the generators module. 491 local base = $(id) ; 492 local suffix = "" ; 493 while $(base:S) 494 { 495 suffix = $(base:S)$(suffix) ; 496 base = $(base:B) ; 497 } 498 local new-id = $(toolset)$(suffix) ; 499 500 generators.register [ $(g).clone $(new-id) : $(properties) ] ; 501 } 502 } 503} 504 505 506# Brings all flag definitions from the 'base' toolset into the 'toolset' 507# toolset. Flag definitions whose conditions make use of properties in 508# 'prohibited-properties' are ignored. Do not confuse property and feature, for 509# example <debug-symbols>on and <debug-symbols>off, so blocking one of them does 510# not block the other one. 511# 512# The flag conditions are not altered at all, so if a condition includes a name, 513# or version of a base toolset, it will not ever match the inheriting toolset. 514# When such flag settings must be inherited, define a rule in base toolset 515# module and call it as needed. 516# 517rule inherit-flags ( toolset : base : prohibited-properties * : prohibited-vars * ) 518{ 519 for local f in $(.module-flags.$(base)) 520 { 521 local rule-or-module = $(.rule-or-module.$(f)) ; 522 if ( [ set.difference 523 $(.$(rule-or-module).condition.$(f)) : 524 $(prohibited-properties) ] 525 || ! $(.$(rule-or-module).condition.$(f)) 526 ) && ( ! $(.$(rule-or-module).variable.$(f)) in $(prohibited-vars) ) 527 { 528 local rule_ = [ MATCH "[^.]*\.(.*)" : $(rule-or-module) ] ; 529 local new-rule-or-module ; 530 if $(rule_) 531 { 532 new-rule-or-module = $(toolset).$(rule_) ; 533 } 534 else 535 { 536 new-rule-or-module = $(toolset) ; 537 } 538 539 add-flag 540 $(new-rule-or-module) 541 : $(.$(rule-or-module).variable.$(f)) 542 : $(.$(rule-or-module).condition.$(f)) 543 : $(.$(rule-or-module).values.$(f)) ; 544 } 545 } 546} 547 548 549rule inherit-rules ( toolset : base : localize ? ) 550{ 551 # It appears that "action" creates a local rule. 552 local base-generators = [ generators.generators-for-toolset $(base) ] ; 553 local rules ; 554 for local g in $(base-generators) 555 { 556 rules += [ MATCH "[^.]*\.(.*)" : [ $(g).rule-name ] ] ; 557 } 558 rules = [ sequence.unique $(rules) ] ; 559 IMPORT $(base) : $(rules) : $(toolset) : $(rules) : $(localize) ; 560 IMPORT $(toolset) : $(rules) : : $(toolset).$(rules) ; 561} 562 563.requirements = [ property-set.empty ] ; 564 565# Return the list of global 'toolset requirements'. Those requirements will be 566# automatically added to the requirements of any main target. 567# 568rule requirements ( ) 569{ 570 return $(.requirements) ; 571} 572 573 574# Adds elements to the list of global 'toolset requirements'. The requirements 575# will be automatically added to the requirements for all main targets, as if 576# they were specified literally. For best results, all requirements added should 577# be conditional or indirect conditional. 578# 579rule add-requirements ( requirements * ) 580{ 581 if ! $(.ignore-requirements) 582 { 583 requirements = [ property.translate-indirect $(requirements) : [ CALLER_MODULE ] ] ; 584 requirements = [ property.expand-subfeatures-in-conditions $(requirements) ] ; 585 requirements = [ property.make $(requirements) ] ; 586 .requirements = [ $(.requirements).add-raw $(requirements) ] ; 587 } 588} 589 590 591rule __test__ ( ) 592{ 593 import assert ; 594 local p = <b>0 <c>1 <d>2 <e>3 <f>4 ; 595 assert.result <c>1/<d>2/<e>3 : find-property-subset <c>1/<d>2/<e>3 <a>0/<b>0/<c>1 <d>2/<e>5 <a>9 : $(p) ; 596 assert.result : find-property-subset <a>0/<b>0/<c>9/<d>9/<e>5 <a>9 : $(p) ; 597 598 local p-set = <a>/<b> <a>0/<b> <a>/<b>1 <a>0/<b>1 ; 599 assert.result <a>/<b> : find-property-subset $(p-set) : ; 600 assert.result <a>0/<b> : find-property-subset $(p-set) : <a>0 <c>2 ; 601 assert.result <a>/<b>1 : find-property-subset $(p-set) : <b>1 <c>2 ; 602 assert.result <a>0/<b>1 : find-property-subset $(p-set) : <a>0 <b>1 ; 603} 604