1# Copyright 2003 Dave Abrahams 2# Copyright 2003, 2005 Vladimir Prus 3# Distributed under the Boost Software License, Version 1.0. 4# (See accompanying file LICENSE_1_0.txt or copy at 5# http://www.boost.org/LICENSE_1_0.txt) 6 7# Essentially an include guard; ensures that no module is loaded multiple times. 8.loaded ?= ; 9 10# A list of modules currently being loaded for error reporting of circular 11# dependencies. 12.loading ?= ; 13 14# A list of modules needing to be tested using their __test__ rule. 15.untested ?= ; 16 17# A list of modules which have been tested using their __test__ rule. 18.tested ?= ; 19 20 21# Runs internal Boost Build unit tests for the specified module. The module's 22# __test__ rule is executed in its own module to eliminate any inadvertent 23# effects of testing module dependencies (such as assert) on the module itself. 24# 25local rule run-module-test ( m ) 26{ 27 local tested-modules = [ modules.peek modules : .tested ] ; 28 29 if ( ! $(m) in $(tested-modules) ) # Avoid recursive test invocations. 30 && ( ( --debug in $(argv) ) || ( "--debug-module=$(m)" in $(argv) ) ) 31 { 32 modules.poke modules : .tested : $(tested-modules) $(m) ; 33 34 if ! ( __test__ in [ RULENAMES $(m) ] ) 35 { 36 local argv = [ peek : ARGV ] ; 37 if ! ( --quiet in $(argv) ) && ( --debug-tests in $(argv) ) 38 { 39 ECHO "warning:" no __test__ rule defined in module $(m) ; 40 } 41 } 42 else 43 { 44 if ! ( --quiet in $(argv) ) 45 { 46 ECHO testing module $(m)... ; 47 } 48 49 local test-module = __test-$(m)__ ; 50 IMPORT $(m) : [ RULENAMES $(m) ] : $(test-module) : [ RULENAMES $(m) 51 ] ; 52 IMPORT $(m) : __test__ : $(test-module) : __test__ : LOCALIZE ; 53 module $(test-module) 54 { 55 __test__ ; 56 } 57 } 58 } 59} 60 61 62# Return the binding of the given module. 63# 64rule binding ( module ) 65{ 66 return $($(module).__binding__) ; 67} 68 69 70# Sets the module-local value of a variable. This is the most reliable way to 71# set a module-local variable in a different module; it eliminates issues of 72# name shadowing due to dynamic scoping. 73# 74rule poke ( module-name ? : variables + : value * ) 75{ 76 module $(<) 77 { 78 $(>) = $(3) ; 79 } 80} 81 82 83# Returns the module-local value of a variable. This is the most reliable way to 84# examine a module-local variable in a different module; it eliminates issues of 85# name shadowing due to dynamic scoping. 86# 87rule peek ( module-name ? : variables + ) 88{ 89 module $(<) 90 { 91 return $($(>)) ; 92 } 93} 94 95 96# Call the given rule locally in the given module. Use this for rules accepting 97# rule names as arguments, so that the passed rule may be invoked in the context 98# of the rule's caller (for example, if the rule accesses module globals or is a 99# local rule). Note that rules called this way may accept at most 18 parameters. 100# 101rule call-in ( module-name ? : rule-name args * : * ) 102{ 103 module $(module-name) 104 { 105 return [ $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : 106 $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) : 107 $(19) ] ; 108 } 109} 110 111 112# Given a possibly qualified rule name and arguments, remove any initial module 113# qualification from the rule and invoke it in that module. If there is no 114# module qualification, the rule is invoked in the global module. Note that 115# rules called this way may accept at most 18 parameters. 116# 117rule call-locally ( qualified-rule-name args * : * ) 118{ 119 local module-rule = [ MATCH (.*)\\.(.*) : $(qualified-rule-name) ] ; 120 local rule-name = $(module-rule[2]) ; 121 rule-name ?= $(qualified-rule-name) ; 122 # We pass only 18 parameters here since Boost Jam allows at most 19 rule 123 # parameter positions and the call-in rule already uses up the initial 124 # position for the module name. 125 return [ call-in $(module-rule[1]) : $(rule-name) $(args) : $(2) : $(3) : 126 $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) 127 $(14) : $(15) : $(16) : $(17) : $(18) : $(19) ] ; 128} 129 130 131# Load the indicated module if it is not already loaded. 132# 133rule load ( 134 module-name # Name of module to load. Rules will be defined in this 135 # module. 136 : filename ? # (partial) path to file; Defaults to $(module-name).jam. 137 : search * # Directories in which to search for filename. Defaults to 138 # $(BOOST_BUILD_PATH). 139) 140{ 141 # Avoid loading modules twice. 142 if ! ( $(module-name) in $(.loaded) ) 143 { 144 filename ?= $(module-name).jam ; 145 146 # Mark the module loaded so we do not try to load it recursively. 147 .loaded += $(module-name:B) ; 148 149 # Suppress tests if any module loads are already in progress. 150 local suppress-test = $(.loading[1]) ; 151 152 # Push this module on the loading stack. 153 .loading += $(module-name) ; 154 155 # Remember that it is untested. 156 .untested += $(module-name) ; 157 158 # Insert the new module's __name__ and __file__ globals. 159 poke $(module-name) : __name__ : $(module-name) ; 160 poke $(module-name) : __file__ : $(filename) ; 161 162 module $(module-name) 163 { 164 # Add some grist so that the module will have a unique target name. 165 local module-target = $(__file__:G=module@) ; 166 167 local search = $(3) ; 168 search ?= [ modules.peek : BOOST_BUILD_PATH ] ; 169 SEARCH on $(module-target) = $(search) ; 170 BINDRULE on $(module-target) = modules.record-binding ; 171 172 include $(module-target) ; 173 174 # Allow the module to see its own names with full qualification. 175 local rules = [ RULENAMES $(__name__) ] ; 176 IMPORT $(__name__) : $(rules) : $(__name__) : $(__name__).$(rules) ; 177 } 178 179 if $(module-name) != modules && ! [ binding $(module-name) ] 180 { 181 import errors ; 182 errors.error "Could not find module" $(module-name) in $(search) ; 183 } 184 185 # Pop the loading stack. Must happen before testing or we will run into 186 # a circular loading dependency. 187 .loading = $(.loading[1--2]) ; 188 189 # Run any pending tests if this is an outer load. 190 if ! $(suppress-test) 191 { 192 local argv = [ peek : ARGV ] ; 193 for local m in $(.untested) 194 { 195 run-module-test $(m) ; 196 } 197 .untested = ; 198 } 199 } 200 else if $(module-name) in $(.loading) 201 { 202 import errors ; 203 errors.error loading \"$(module-name)\" 204 : circular module loading "dependency:" 205 : $(.loading)" ->" $(module-name) ; 206 } 207} 208 209 210# This helper is used by load (above) to record the binding (path) of each 211# loaded module. 212# 213rule record-binding ( module-target : binding ) 214{ 215 $(.loading[-1]).__binding__ = $(binding) ; 216} 217 218 219# Transform each path in the list, with all backslashes converted to forward 220# slashes and all detectable redundancy removed. Something like this is probably 221# needed in path.jam, but I am not sure of that, I do not understand it, and I 222# am not ready to move all of path.jam into the kernel. 223# 224local rule normalize-raw-paths ( paths * ) 225{ 226 local result ; 227 for p in $(paths:T) 228 { 229 result += [ NORMALIZE_PATH $(p) ] ; 230 } 231 return $(result) ; 232} 233 234 235.cwd = [ PWD ] ; 236 237 238# Load the indicated module and import rule names into the current module. Any 239# members of rules-opt will be available without qualification in the caller's 240# module. Any members of rename-opt will be taken as the names of the rules in 241# the caller's module, in place of the names they have in the imported module. 242# If rules-opt = '*', all rules from the indicated module are imported into the 243# caller's module. If rename-opt is supplied, it must have the same number of 244# elements as rules-opt. 245# 246rule import ( module-names + : rules-opt * : rename-opt * ) 247{ 248 if ( $(rules-opt) = * || ! $(rules-opt) ) && $(rename-opt) 249 { 250 import errors ; 251 errors.error "Rule aliasing is only available for explicit imports." ; 252 } 253 254 if $(module-names[2]) && ( $(rules-opt) || $(rename-opt) ) 255 { 256 import errors ; 257 errors.error "When loading multiple modules, no specific rules or" 258 "renaming is allowed" ; 259 } 260 261 local caller = [ CALLER_MODULE ] ; 262 263 # Import each specified module 264 for local m in $(module-names) 265 { 266 local module-name = $(m:B) ; 267 if ! $(module-name) in $(.loaded) 268 { 269 # If the importing module is not already in the BOOST_BUILD_PATH, 270 # prepend it to the path. We do not want to invert the search order 271 # of modules that are already there. 272 273 local caller-location ; 274 if $(caller) 275 { 276 caller-location = [ binding $(caller) ] ; 277 caller-location = $(caller-location:D) ; 278 caller-location = [ normalize-raw-paths 279 $(caller-location:R=$(.cwd)) ] ; 280 } 281 282 local search = [ peek : BOOST_BUILD_PATH ] ; 283 search = [ normalize-raw-paths $(search:R=$(.cwd)) ] ; 284 285 if $(caller-location) && ! $(caller-location) in $(search) 286 { 287 search = $(caller-location) $(search) ; 288 } 289 290 if $(m:D) 291 { 292 search = $(caller-location)/$(m:D) $(search)/$(m:D) $(search) ; 293 } 294 295 load $(module-name) : : $(search) ; 296 } 297 298 IMPORT_MODULE $(module-name) : $(caller) ; 299 300 if $(rules-opt) 301 { 302 local source-names ; 303 if $(rules-opt) = * 304 { 305 local all-rules = [ RULENAMES $(module-name) ] ; 306 source-names = $(all-rules) ; 307 } 308 else 309 { 310 source-names = $(rules-opt) ; 311 } 312 local target-names = $(rename-opt) ; 313 target-names ?= $(source-names) ; 314 IMPORT $(module-name) : $(source-names) : $(caller) : $(target-names) ; 315 } 316 } 317} 318 319 320# Define exported copies in $(target-module) of all rules exported from 321# $(source-module). Also make them available in the global module with 322# qualification, so that it is just as though the rules were defined originally 323# in $(target-module). 324# 325rule clone-rules ( source-module target-module ) 326{ 327 local r = [ RULENAMES $(source-module) ] ; 328 IMPORT $(source-module) : $(r) : $(target-module) : $(r) : LOCALIZE ; 329 EXPORT $(target-module) : $(r) ; 330 IMPORT $(target-module) : $(r) : : $(target-module).$(r) ; 331} 332 333 334# These rules need to be available in all modules to implement module loading 335# itself and other fundamental operations. 336local globalize = peek poke record-binding ; 337IMPORT modules : $(globalize) : : modules.$(globalize) ; 338 339 340rule __test__ ( ) 341{ 342 import assert ; 343 import modules : normalize-raw-paths ; 344 345 module modules.__test__ 346 { 347 foo = bar ; 348 } 349 350 assert.result bar : peek modules.__test__ : foo ; 351 352 poke modules.__test__ : foo : bar baz ; 353 assert.result bar baz : peek modules.__test__ : foo ; 354 355 assert.result c:/foo/bar : normalize-raw-paths c:/x/../foo/./xx/yy/../../bar ; 356 assert.result . : normalize-raw-paths . ; 357 assert.result .. : normalize-raw-paths .. ; 358 assert.result ../.. : normalize-raw-paths ../.. ; 359 assert.result .. : normalize-raw-paths ./.. ; 360 assert.result / / : normalize-raw-paths / \\ ; 361 assert.result a : normalize-raw-paths a ; 362 assert.result a : normalize-raw-paths a/ ; 363 assert.result /a : normalize-raw-paths /a/ ; 364 assert.result / : normalize-raw-paths /a/.. ; 365} 366