]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2002, 2003 Dave Abrahams |
2 | # Copyright 2002, 2005, 2006 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 | # Implements project representation and loading. Each project is represented by: | |
9 | # - a module where all the Jamfile content lives. | |
10 | # - an instance of 'project-attributes' class. | |
11 | # (given a module name, can be obtained using the 'attributes' rule) | |
12 | # - an instance of 'project-target' class (from targets.jam) | |
13 | # (given a module name, can be obtained using the 'target' rule) | |
14 | # | |
15 | # Typically, projects are created as result of loading a Jamfile, which is done | |
16 | # by rules 'load' and 'initialize', below. First, a module is prepared and a new | |
17 | # project-attributes instance is created. Some rules necessary for all projects | |
18 | # are added to the module (see the 'project-rules' module). Default project | |
19 | # attributes are set (inheriting parent project attributes, if it exists). After | |
20 | # that the Jamfile is read. It can declare its own attributes using the | |
21 | # 'project' rule which will be combined with any already set. | |
22 | # | |
23 | # The 'project' rule can also declare a project id which will be associated with | |
24 | # the project module. | |
25 | # | |
26 | # Besides Jamfile projects, we also support 'standalone' projects created by | |
27 | # calling 'initialize' in an arbitrary module and not specifying the project's | |
28 | # location. After the call, the module can call the 'project' rule, declare main | |
29 | # targets and behave as a regular project except that, since it is not | |
30 | # associated with any location, it should only declare prebuilt targets. | |
31 | # | |
32 | # The list of all loaded Jamfiles is stored in the .project-locations variable. | |
33 | # It is possible to obtain a module name for a location using the 'module-name' | |
34 | # rule. Standalone projects are not recorded and can only be referenced using | |
35 | # their project id. | |
36 | ||
37 | import "class" : new ; | |
38 | import modules ; | |
39 | import path ; | |
40 | import print ; | |
41 | import property-set ; | |
42 | import sequence ; | |
43 | ||
44 | ||
45 | .debug-loading = [ MATCH ^(--debug-loading)$ : [ modules.peek : ARGV ] ] ; | |
46 | ||
47 | ||
48 | # Loads the Jamfile at the given location. After loading, project global file | |
49 | # and Jamfiles needed by the requested one will be loaded recursively. If the | |
50 | # Jamfile at that location is loaded already, does nothing. Returns the project | |
51 | # module for the Jamfile. | |
52 | # | |
53 | rule load ( jamfile-location : synthesize ? ) | |
54 | { | |
55 | local module-name = [ module-name $(jamfile-location) ] ; | |
56 | # If Jamfile is already loaded, do not try again. | |
57 | if ! $(module-name) in $(.jamfile-modules) | |
58 | { | |
59 | if $(.debug-loading) | |
60 | { | |
61 | ECHO Loading Jamfile at '$(jamfile-location)' ; | |
62 | } | |
63 | ||
64 | load-jamfile $(jamfile-location) : $(module-name) : $(synthesize) ; | |
65 | ||
66 | # We want to make sure that child project are loaded only after parent | |
67 | # projects. In particular, because parent projects define attributes | |
68 | # which are then inherited by children, and we do not want children to | |
69 | # be loaded before parent has defined everything. | |
70 | # | |
71 | # While "build-project" and "use-project" can potentially refer to child | |
72 | # projects from parent projects, we do not immediately load child | |
73 | # projects when seeing those attributes. Instead, we record the minimal | |
74 | # information to be used only later. | |
75 | load-used-projects $(module-name) ; | |
76 | } | |
77 | return $(module-name) ; | |
78 | } | |
79 | ||
80 | ||
81 | rule load-used-projects ( module-name ) | |
82 | { | |
83 | local used = [ modules.peek $(module-name) : .used-projects ] ; | |
84 | local location = [ attribute $(module-name) location ] ; | |
85 | while $(used) | |
86 | { | |
87 | local id = $(used[1]) ; | |
88 | local where = [ path.make $(used[2]) ] ; | |
89 | register-id $(id) : [ load [ path.root $(where) $(location) ] ] ; | |
90 | used = $(used[3-]) ; | |
91 | } | |
92 | } | |
93 | ||
94 | ||
95 | # Note the use of character groups, as opposed to listing 'Jamroot' and | |
96 | # 'jamroot'. With the latter, we would get duplicate matches on Windows and | |
97 | # would have to eliminate duplicates. | |
98 | JAMROOT ?= [ modules.peek : JAMROOT ] ; | |
11fdf7f2 | 99 | JAMROOT ?= project-root.jam "[Jj]amroot" "[Jj]amroot." "[Jj]amroot.jam" ; |
7c673cae FG |
100 | |
101 | ||
102 | # Loads parent of Jamfile at 'location'. Issues an error if nothing is found. | |
103 | # | |
104 | rule load-parent ( location ) | |
105 | { | |
106 | local found = [ path.glob-in-parents $(location) : $(JAMROOT) $(JAMFILE) ] ; | |
92f5a8d4 | 107 | if $(found) |
7c673cae | 108 | { |
92f5a8d4 | 109 | return [ load $(found[1]:D) ] ; |
7c673cae | 110 | } |
7c673cae FG |
111 | } |
112 | ||
113 | ||
114 | # Returns the project module corresponding to the given project-id or plain | |
115 | # directory name. Returns nothing if such a project can not be found. | |
116 | # | |
117 | rule find ( name : current-location ) | |
118 | { | |
119 | local project-module ; | |
120 | ||
121 | # Try interpreting name as project id. | |
122 | if [ path.is-rooted $(name) ] | |
123 | { | |
124 | project-module = $($(name).jamfile-module) ; | |
125 | } | |
126 | ||
127 | if ! $(project-module) | |
128 | { | |
129 | local location = [ path.root [ path.make $(name) ] $(current-location) ] | |
130 | ; | |
131 | ||
132 | # If no project is registered for the given location, try to load it. | |
133 | # First see if we have a Jamfile. If not, then see if we might have a | |
134 | # project root willing to act as a Jamfile. In that case, project root | |
135 | # must be placed in the directory referred to by id. | |
136 | ||
137 | project-module = [ module-name $(location) ] ; | |
138 | if ! $(project-module) in $(.jamfile-modules) | |
139 | { | |
140 | if [ path.glob $(location) : $(JAMROOT) $(JAMFILE) ] | |
141 | { | |
142 | project-module = [ load $(location) ] ; | |
143 | } | |
144 | else | |
145 | { | |
146 | project-module = ; | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
151 | return $(project-module) ; | |
152 | } | |
153 | ||
154 | ||
155 | # Returns the name of the module corresponding to 'jamfile-location'. If no | |
156 | # module corresponds to that location yet, associates the default module name | |
157 | # with that location. | |
158 | # | |
159 | rule module-name ( jamfile-location ) | |
160 | { | |
161 | if ! $(.module.$(jamfile-location)) | |
162 | { | |
163 | # Root the path, so that locations are always unambiguous. Without this, | |
164 | # we can not decide if '../../exe/program1' and '.' are the same paths. | |
165 | local normalized = [ path.root $(jamfile-location) [ path.pwd ] ] ; | |
166 | ||
167 | # Quick & dirty fix to get the same module name when we supply two | |
168 | # equivalent location paths, e.g. 'd:\Foo' & 'D:\fOo\bar\..' on Windows. | |
169 | # Note that our current implementation will not work correctly if the | |
170 | # given location references an empty folder, but in that case any later | |
171 | # attempt to load a Jamfile from this location will fail anyway. | |
172 | # FIXME: Implement this cleanly. Support for this type of path | |
173 | # normalization already exists internally in Boost Jam and the current | |
174 | # fix relies on the GLOB builtin rule using that support. Most likely we | |
175 | # just need to add a new builtin rule to do this explicitly. | |
176 | normalized = [ NORMALIZE_PATH $(normalized) ] ; | |
177 | local glob-result = [ GLOB [ path.native $(normalized) ] : * ] ; | |
178 | if $(glob-result) | |
179 | { | |
180 | normalized = $(glob-result[1]:D) ; | |
181 | } | |
182 | .module.$(jamfile-location) = Jamfile<$(normalized)> ; | |
183 | } | |
184 | return $(.module.$(jamfile-location)) ; | |
185 | } | |
186 | ||
187 | ||
188 | # Default patterns to search for the Jamfiles to use for build declarations. | |
189 | # | |
190 | JAMFILE = [ modules.peek : JAMFILE ] ; | |
11fdf7f2 | 191 | JAMFILE ?= "[Bb]uild.jam" "[Jj]amfile.v2" "[Jj]amfile" "[Jj]amfile." "[Jj]amfile.jam" ; |
7c673cae FG |
192 | |
193 | ||
194 | # Find the Jamfile at the given location. This returns the exact names of all | |
195 | # the Jamfiles in the given directory. The optional parent-root argument causes | |
196 | # this to search not the given directory but the ones above it up to the | |
197 | # parent-root directory. | |
198 | # | |
199 | rule find-jamfile ( | |
200 | dir # The directory(s) to look for a Jamfile. | |
201 | parent-root ? # Optional flag indicating to search for the parent Jamfile. | |
202 | : no-errors ? | |
203 | ) | |
204 | { | |
205 | # Glob for all the possible Jamfiles according to the match pattern. | |
206 | # | |
207 | local jamfile-glob = ; | |
208 | if $(parent-root) | |
209 | { | |
210 | if ! $(.parent-jamfile.$(dir)) | |
211 | { | |
212 | .parent-jamfile.$(dir) = [ path.glob-in-parents $(dir) : $(JAMFILE) | |
213 | ] ; | |
214 | } | |
215 | jamfile-glob = $(.parent-jamfile.$(dir)) ; | |
216 | } | |
217 | else | |
218 | { | |
219 | if ! $(.jamfile.$(dir)) | |
220 | { | |
221 | .jamfile.$(dir) = [ path.glob $(dir) : $(JAMFILE) ] ; | |
222 | } | |
223 | jamfile-glob = $(.jamfile.$(dir)) ; | |
224 | ||
225 | } | |
226 | ||
227 | local jamfile-to-load = $(jamfile-glob) ; | |
228 | # Multiple Jamfiles found in the same place. Warn about this and ensure we | |
229 | # use only one of them. As a temporary convenience measure, if there is | |
230 | # Jamfile.v2 among found files, suppress the warning and use it. | |
231 | # | |
232 | if $(jamfile-to-load[2-]) | |
233 | { | |
11fdf7f2 | 234 | local v2-jamfiles = [ MATCH "^(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)$" : |
7c673cae FG |
235 | $(jamfile-to-load) ] ; |
236 | ||
237 | if $(v2-jamfiles) && ! $(v2-jamfiles[2]) | |
238 | { | |
239 | jamfile-to-load = $(v2-jamfiles) ; | |
240 | } | |
241 | else | |
242 | { | |
243 | local jamfile = [ path.basename $(jamfile-to-load[1]) ] ; | |
244 | ECHO "warning: Found multiple Jamfiles at '"$(dir)"'!" | |
245 | "Loading the first one: '$(jamfile)'." ; | |
246 | } | |
247 | ||
248 | jamfile-to-load = $(jamfile-to-load[1]) ; | |
249 | } | |
250 | ||
251 | # Could not find it, error. | |
252 | # | |
253 | if ! $(no-errors) && ! $(jamfile-to-load) | |
254 | { | |
255 | import errors ; | |
256 | errors.error Unable to load Jamfile. | |
257 | : Could not find a Jamfile in directory '$(dir)'. | |
258 | : Attempted to find it with pattern '$(JAMFILE:J=" ")'. | |
11fdf7f2 | 259 | : Please consult the documentation at "'http://www.boost.org'." ; |
7c673cae FG |
260 | } |
261 | ||
262 | return $(jamfile-to-load) ; | |
263 | } | |
264 | ||
265 | ||
92f5a8d4 TL |
266 | # Default patterns to search for auto-include of package manager build declarations. |
267 | # | |
268 | PACKAGE_MANAGER_BUILD_INFO(CONAN) = "conanbuildinfo.jam" ; | |
269 | ||
270 | # Default to using the package manager build info in this priority order: | |
271 | # 1. Configuration, user, project, etc. | |
272 | # 2. Command line argument "--use-package-manager=<name>". | |
273 | # 3. Environment variable "PACKAGE_MANAGER_BUILD_INFO". | |
274 | # 4. Conan, others. | |
275 | # | |
276 | local .use-package-manager = [ MATCH "^--use-package-manager=(.*)$" : [ modules.peek : ARGV ] ] ; | |
277 | PACKAGE_MANAGER_BUILD_INFO ?= $(PACKAGE_MANAGER_BUILD_INFO($(.use-package-manager:U))) ; | |
278 | PACKAGE_MANAGER_BUILD_INFO ?= [ modules.peek : PACKAGE_MANAGER_BUILD_INFO ] ; | |
279 | PACKAGE_MANAGER_BUILD_INFO ?= $(PACKAGE_MANAGER_BUILD_INFO(CONAN)) ; | |
280 | ||
281 | ||
282 | # Load the configured package manager build information file. | |
283 | # | |
284 | rule load-package-manager-build-info ( ) | |
285 | { | |
286 | # This first variable is the one from the configuration (user, project, etc). | |
287 | local package-manager-build-info = [ modules.peek [ CALLER_MODULE ] : PACKAGE_MANAGER_BUILD_INFO ] ; | |
288 | # And this is the rest as it takes it from the settings in the "project" module. | |
289 | # I.e. the variable assignments above. | |
290 | package-manager-build-info ?= $(PACKAGE_MANAGER_BUILD_INFO) ; | |
291 | if $(package-manager-build-info) | |
292 | { | |
293 | local pm = [ path.glob $(dir) : $(package-manager-build-info) ] ; | |
294 | pm = $(pm[1]) ; | |
295 | local cm = [ CALLER_MODULE ] ; | |
296 | local pm-tag = "$(cm)<$(pm:B)>" ; | |
297 | if $(pm) && ! ( $(pm-tag) in $(.package-manager-build-info) ) | |
298 | { | |
299 | .package-manager-build-info += $(pm-tag) ; | |
300 | # We found a matching builf info to load, but we have to be careful | |
301 | # as the loading can affect the current project since it can define | |
302 | # sub-projects. Hence we save and restore the current project. | |
303 | local saved-project = $(.current-project) ; | |
304 | modules.load $(cm) : $(pm) ; | |
305 | .current-project = $(saved-project) ; | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | ||
7c673cae FG |
311 | # Load a Jamfile at the given directory. Returns nothing. Will attempt to load |
312 | # the file as indicated by the JAMFILE patterns. Effect of calling this rule | |
313 | # twice with the same 'dir' is undefined. | |
314 | # | |
315 | local rule load-jamfile ( dir : jamfile-module : synthesize ? ) | |
316 | { | |
317 | # See if the Jamfile is where it should be. | |
318 | # | |
319 | local jamfile-to-load = [ path.glob $(dir) : $(JAMROOT) ] ; | |
320 | if ! $(jamfile-to-load) | |
321 | { | |
322 | jamfile-to-load = [ find-jamfile $(dir) : $(synthesize) ] ; | |
323 | } | |
324 | ||
325 | if $(jamfile-to-load[2]) | |
326 | { | |
327 | import errors ; | |
328 | errors.error "Multiple Jamfiles found at '$(dir)'" : | |
329 | "Filenames are: " $(jamfile-to-load:D=) ; | |
330 | } | |
331 | ||
332 | if ! $(jamfile-to-load) && $(synthesize) | |
333 | { | |
334 | jamfile-to-load = $(dir)/@ ; | |
335 | } | |
336 | ||
337 | # Now load the Jamfile in its own context. | |
338 | # The call to 'initialize' may load the parent Jamfile, which might contain | |
339 | # a 'use-project' or a 'project.load' call, causing a second attempt to load | |
340 | # the same project we are loading now. Checking inside .jamfile-modules | |
341 | # prevents that second attempt from messing things up. | |
342 | if ! $(jamfile-module) in $(.jamfile-modules) | |
343 | { | |
344 | local previous-project = $(.current-project) ; | |
345 | ||
346 | # Initialize the Jamfile module before loading. | |
347 | initialize $(jamfile-module) : [ path.parent $(jamfile-to-load) ] : | |
348 | $(jamfile-to-load:BS) ; | |
349 | ||
92f5a8d4 TL |
350 | # Auto-load package manager(s) build information. |
351 | IMPORT project : load-package-manager-build-info | |
352 | : $(jamfile-module) : project.load-package-manager-build-info ; | |
353 | modules.call-in $(jamfile-module) : project.load-package-manager-build-info ; | |
354 | ||
7c673cae FG |
355 | if ! $(jamfile-module) in $(.jamfile-modules) |
356 | { | |
357 | .jamfile-modules += $(jamfile-module) ; | |
358 | ||
359 | local saved-project = $(.current-project) ; | |
360 | ||
361 | mark-as-user $(jamfile-module) ; | |
362 | if $(jamfile-to-load:B) = "@" | |
363 | { | |
364 | # Not a real jamfile to load. Synthsize the load. | |
365 | modules.poke $(jamfile-module) : __name__ : $(jamfile-module) ; | |
366 | modules.poke $(jamfile-module) : __file__ : [ path.native $(jamfile-to-load) ] ; | |
367 | modules.poke $(jamfile-module) : __binding__ : [ path.native $(jamfile-to-load) ] ; | |
368 | } | |
369 | else | |
370 | { | |
371 | modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ] | |
372 | : . ; | |
373 | if [ MATCH ^($(JAMROOT))$ : $(jamfile-to-load:BS) ] | |
374 | { | |
375 | jamfile = [ find-jamfile $(dir) : no-errors ] ; | |
376 | if $(jamfile) | |
377 | { | |
378 | load-aux $(jamfile-module) : [ path.native $(jamfile) ] ; | |
379 | } | |
380 | } | |
381 | } | |
382 | ||
383 | # Now do some checks. | |
384 | if $(.current-project) != $(saved-project) | |
385 | { | |
386 | import errors ; | |
387 | errors.error | |
388 | The value of the .current-project variable has magically | |
389 | : changed after loading a Jamfile. This means some of the | |
390 | : targets might be defined in the wrong project. | |
391 | : after loading $(jamfile-module) | |
392 | : expected value $(saved-project) | |
393 | : actual value $(.current-project) ; | |
394 | } | |
395 | ||
396 | end-load $(previous-project) ; | |
397 | ||
398 | if $(.global-build-dir) | |
399 | { | |
400 | if [ attribute $(jamfile-module) location ] && ! [ attribute | |
401 | $(jamfile-module) id ] | |
402 | { | |
403 | local project-root = [ attribute $(jamfile-module) | |
404 | project-root ] ; | |
405 | if $(project-root) = $(dir) | |
406 | { | |
407 | ECHO "warning: the --build-dir option was specified" ; | |
408 | ECHO "warning: but Jamroot at '$(dir)'" ; | |
409 | ECHO "warning: specified no project id" ; | |
410 | ECHO "warning: the --build-dir option will be ignored" ; | |
411 | } | |
412 | } | |
413 | } | |
414 | } | |
415 | } | |
416 | } | |
417 | ||
418 | ||
419 | # Called when done loading a project module. Restores the current project to its | |
420 | # previous value and does some additional checking to make sure our 'currently | |
421 | # loaded project' identifier does not get left with an invalid value. | |
422 | # | |
423 | rule end-load ( previous-project ? ) | |
424 | { | |
425 | if ! $(.current-project) | |
426 | { | |
427 | import errors ; | |
428 | errors.error Ending project loading requested when there was no project | |
429 | currently being loaded. ; | |
430 | } | |
431 | ||
432 | if ! $(previous-project) && $(.saved-current-project) | |
433 | { | |
434 | import errors ; | |
435 | errors.error Ending project loading requested with no 'previous project' | |
436 | when there were other projects still marked as being loaded | |
437 | recursively. ; | |
438 | } | |
439 | ||
440 | .current-project = $(previous-project) ; | |
441 | } | |
442 | ||
443 | ||
444 | rule mark-as-user ( module-name ) | |
445 | { | |
446 | if USER_MODULE in [ RULENAMES ] | |
447 | { | |
448 | USER_MODULE $(module-name) ; | |
449 | } | |
450 | } | |
451 | ||
452 | ||
453 | rule load-aux ( module-name : file ) | |
454 | { | |
455 | mark-as-user $(module-name) ; | |
456 | ||
457 | module $(module-name) | |
458 | { | |
459 | include $(2) ; | |
460 | local rules = [ RULENAMES $(1) ] ; | |
461 | IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ; | |
462 | } | |
463 | } | |
464 | ||
465 | ||
466 | .global-build-dir = [ MATCH ^--build-dir=(.*)$ : [ modules.peek : ARGV ] ] ; | |
467 | if $(.global-build-dir) | |
468 | { | |
469 | # If the option is specified several times, take the last value. | |
470 | .global-build-dir = [ path.make $(.global-build-dir[-1]) ] ; | |
471 | } | |
472 | ||
473 | ||
474 | # Initialize the module for a project. | |
475 | # | |
476 | rule initialize ( | |
477 | module-name # The name of the project module. | |
478 | : location ? # The location (directory) of the project to initialize. If | |
479 | # not specified, a standalone project will be initialized. | |
480 | : basename ? | |
481 | ) | |
482 | { | |
483 | if $(.debug-loading) | |
484 | { | |
485 | ECHO "Initializing project '$(module-name)'" ; | |
486 | } | |
487 | ||
488 | local jamroot ; | |
489 | ||
490 | local parent-module ; | |
11fdf7f2 | 491 | if $(module-name) in test-config all-config |
7c673cae FG |
492 | { |
493 | # No parent. | |
494 | } | |
495 | else if $(module-name) = site-config | |
496 | { | |
497 | parent-module = test-config ; | |
498 | } | |
499 | else if $(module-name) = user-config | |
500 | { | |
501 | parent-module = site-config ; | |
502 | } | |
503 | else if $(module-name) = project-config | |
504 | { | |
505 | parent-module = user-config ; | |
506 | } | |
7c673cae FG |
507 | else if $(location) |
508 | { | |
92f5a8d4 | 509 | if ! [ MATCH ^($(JAMROOT))$ : $(basename) ] |
7c673cae | 510 | { |
92f5a8d4 TL |
511 | # We search for parent/jamroot only if this is a jamfile project, i.e. |
512 | # if is not a standalone or a jamroot project. | |
513 | parent-module = [ load-parent $(location) ] ; | |
7c673cae | 514 | } |
92f5a8d4 | 515 | if ! $(parent-module) |
7c673cae | 516 | { |
92f5a8d4 TL |
517 | # We have a jamroot project, or a jamfile project |
518 | # without a parent that becomes a jamroot. Inherit from | |
519 | # user-config (or project-config | |
520 | # if it exists). | |
521 | if $(project-config.attributes) | |
522 | { | |
523 | parent-module = project-config ; | |
524 | } | |
525 | else | |
526 | { | |
527 | parent-module = user-config ; | |
528 | } | |
529 | jamroot = true ; | |
7c673cae | 530 | } |
7c673cae FG |
531 | } |
532 | ||
533 | # TODO: need to consider if standalone projects can do anything but define | |
534 | # prebuilt targets. If so, we need to give them a more sensible "location", | |
535 | # so that source paths are correct. | |
536 | location ?= "" ; | |
537 | # Create the module for the Jamfile first. | |
538 | module $(module-name) | |
539 | { | |
540 | } | |
541 | ||
542 | # load-parent can end up loading this module again. Make sure this is not | |
543 | # duplicated. | |
544 | if ! $($(module-name).attributes) | |
545 | { | |
546 | $(module-name).attributes = [ new project-attributes $(location) | |
547 | $(module-name) ] ; | |
548 | local attributes = $($(module-name).attributes) ; | |
549 | ||
550 | if $(location) | |
551 | { | |
552 | $(attributes).set source-location : [ path.make $(location) ] : | |
553 | exact ; | |
554 | } | |
555 | else | |
556 | { | |
11fdf7f2 | 557 | local cfgs = project site test user all ; |
7c673cae FG |
558 | if ! $(module-name) in $(cfgs)-config |
559 | { | |
560 | # This is a standalone project with known location. Set its | |
561 | # source location so it can declare targets. This is needed so | |
562 | # you can put a .jam file with your sources and use it via | |
563 | # 'using'. Standard modules (in the 'tools' subdir) may not | |
564 | # assume source dir is set. | |
565 | local s = [ modules.binding $(module-name) ] ; | |
566 | if ! $(s) | |
567 | { | |
568 | import errors ; | |
569 | errors.error Could not determine project location | |
570 | $(module-name) ; | |
571 | } | |
572 | $(attributes).set source-location : $(s:D) : exact ; | |
573 | } | |
574 | } | |
575 | ||
576 | $(attributes).set requirements : [ property-set.empty ] : exact ; | |
577 | $(attributes).set usage-requirements : [ property-set.empty ] : exact ; | |
578 | ||
579 | # Import rules common to all project modules from project-rules module, | |
580 | # defined at the end of this file. | |
581 | local rules = [ RULENAMES project-rules ] ; | |
582 | IMPORT project-rules : $(rules) : $(module-name) : $(rules) ; | |
583 | ||
584 | if $(parent-module) | |
585 | { | |
586 | inherit-attributes $(module-name) : $(parent-module) ; | |
587 | $(attributes).set parent-module : $(parent-module) : exact ; | |
588 | } | |
589 | ||
590 | if $(jamroot) | |
591 | { | |
592 | $(attributes).set project-root : $(location) : exact ; | |
593 | if ! $(.first-project-root) | |
594 | { | |
595 | .first-project-root = $(module-name) ; | |
596 | } | |
597 | } | |
598 | ||
599 | local parent ; | |
600 | if $(parent-module) | |
601 | { | |
602 | parent = [ target $(parent-module) ] ; | |
603 | } | |
604 | ||
605 | if ! $(.target.$(module-name)) | |
606 | { | |
607 | local requirements = [ attribute $(module-name) requirements ] ; | |
608 | .target.$(module-name) = [ new project-target $(module-name) : | |
609 | $(module-name) $(parent) : $(requirements) ] ; | |
610 | ||
611 | if $(.debug-loading) | |
612 | { | |
613 | ECHO Assigned project target $(.target.$(module-name)) to | |
614 | '$(module-name)' ; | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
619 | .current-project = [ target $(module-name) ] ; | |
620 | } | |
621 | ||
622 | ||
623 | # Make 'project-module' inherit attributes of project root and parent module. | |
624 | # | |
625 | rule inherit-attributes ( project-module : parent-module ) | |
626 | { | |
627 | local attributes = $($(project-module).attributes) ; | |
628 | local pattributes = [ attributes $(parent-module) ] ; | |
629 | # Parent module might be locationless configuration module. | |
630 | if [ modules.binding $(parent-module) ] | |
631 | { | |
632 | $(attributes).set parent : | |
633 | [ path.parent [ path.make [ modules.binding $(parent-module) ] ] ] ; | |
634 | } | |
635 | $(attributes).set project-root : | |
636 | [ $(pattributes).get project-root ] : exact ; | |
637 | $(attributes).set default-build : | |
638 | [ $(pattributes).get default-build ] ; | |
639 | $(attributes).set requirements : | |
640 | [ $(pattributes).get requirements ] : exact ; | |
641 | $(attributes).set usage-requirements : | |
642 | [ $(pattributes).get usage-requirements ] : exact ; | |
643 | ||
644 | local parent-build-dir = [ $(pattributes).get build-dir ] ; | |
645 | if $(parent-build-dir) | |
646 | { | |
647 | # Have to compute relative path from parent dir to our dir. Convert both | |
648 | # paths to absolute, since we cannot find relative path from ".." to | |
649 | # ".". | |
650 | ||
651 | local location = [ attribute $(project-module) location ] ; | |
652 | local parent-location = [ attribute $(parent-module) location ] ; | |
653 | ||
654 | local pwd = [ path.pwd ] ; | |
655 | local parent-dir = [ path.root $(parent-location) $(pwd) ] ; | |
656 | local our-dir = [ path.root $(location) $(pwd) ] ; | |
657 | $(attributes).set build-dir : [ path.join $(parent-build-dir) | |
658 | [ path.relative $(our-dir) $(parent-dir) ] ] : exact ; | |
659 | } | |
660 | } | |
661 | ||
662 | ||
663 | # Returns whether the given string is a valid registered project id. | |
664 | # | |
665 | rule is-registered-id ( id ) | |
666 | { | |
667 | return $($(id).jamfile-module) ; | |
668 | } | |
669 | ||
670 | ||
671 | # Associate the given id with the given project module. Returns the possibly | |
672 | # corrected project id. | |
673 | # | |
674 | rule register-id ( id : module ) | |
675 | { | |
676 | id = [ path.root $(id) / ] ; | |
677 | ||
678 | if [ MATCH (//) : $(id) ] | |
679 | { | |
680 | import errors ; | |
681 | errors.user-error Project id may not contain two consecutive slash | |
11fdf7f2 | 682 | characters (project "id:" '$(id)'). ; |
7c673cae FG |
683 | } |
684 | ||
685 | local orig-module = $($(id).jamfile-module) ; | |
686 | if $(orig-module) && $(orig-module) != $(module) | |
687 | { | |
688 | local new-file = [ modules.peek $(module) : __file__ ] ; | |
689 | local new-location = [ project.attribute $(module) location ] ; | |
690 | ||
691 | local orig-file = [ modules.peek $(orig-module) : __file__ ] ; | |
692 | local orig-main-id = [ project.attribute $(orig-module) id ] ; | |
693 | local orig-location = [ project.attribute $(orig-module) location ] ; | |
694 | local orig-project = [ target $(orig-module) ] ; | |
695 | local orig-name = [ $(orig-project).name ] ; | |
696 | ||
697 | import errors ; | |
698 | errors.user-error Attempt to redeclare already registered project id | |
699 | '$(id)'. | |
11fdf7f2 TL |
700 | : Original "project:" |
701 | : " " "Name:" $(orig-name:E=---) | |
702 | : " " "Module:" $(orig-module) | |
703 | : " " "Main id: "$(orig-main-id:E=---) | |
704 | : " " "File:" $(orig-file:E=---) | |
705 | : " " "Location:" $(orig-location:E=---) | |
706 | : New "project:" | |
707 | : " " "Module:" $(module) | |
708 | : " " "File:" $(new-file:E=---) | |
709 | : " " "Location:" $(new-location:E=---) ; | |
7c673cae FG |
710 | } |
711 | ||
712 | $(id).jamfile-module = $(module) ; | |
713 | return $(id) ; | |
714 | } | |
715 | ||
716 | ||
717 | # Class keeping all the attributes of a project. | |
718 | # | |
719 | # The standard attributes are "id", "location", "project-root", "parent" | |
720 | # "requirements", "default-build", "source-location" and "projects-to-build". | |
721 | # | |
722 | class project-attributes | |
723 | { | |
724 | import path ; | |
725 | import print ; | |
726 | import project ; | |
727 | import property ; | |
728 | import property-set ; | |
729 | import sequence ; | |
730 | ||
731 | rule __init__ ( location project-module ) | |
732 | { | |
733 | self.location = $(location) ; | |
734 | self.project-module = $(project-module) ; | |
735 | } | |
736 | ||
737 | # Set the named attribute from the specification given by the user. The | |
738 | # value actually set may be different. | |
739 | # | |
740 | rule set ( attribute : specification * | |
741 | : exact ? # Sets value from 'specification' without any processing. | |
742 | ) | |
743 | { | |
744 | if $(exact) | |
745 | { | |
746 | self.$(attribute) = $(specification) ; | |
747 | } | |
748 | else if $(attribute) = "requirements" | |
749 | { | |
750 | local result = [ property-set.refine-from-user-input | |
751 | $(self.requirements) : $(specification) | |
752 | : $(self.project-module) : $(self.location) ] ; | |
753 | ||
754 | if $(result[1]) = "@error" | |
755 | { | |
756 | import errors : error : errors.error ; | |
757 | errors.error Requirements for project at '$(self.location)' | |
11fdf7f2 | 758 | conflict with parent's. : "Explanation:" $(result[2-]) ; |
7c673cae FG |
759 | } |
760 | ||
761 | self.requirements = $(result) ; | |
762 | } | |
763 | else if $(attribute) = "usage-requirements" | |
764 | { | |
765 | local unconditional ; | |
766 | for local p in $(specification) | |
767 | { | |
768 | local split = [ property.split-conditional $(p) ] ; | |
769 | split ?= nothing $(p) ; | |
770 | unconditional += $(split[2]) ; | |
771 | } | |
772 | ||
773 | local non-free = [ property.remove free : $(unconditional) ] ; | |
774 | if $(non-free) | |
775 | { | |
776 | import errors : error : errors.error ; | |
777 | errors.error usage-requirements $(specification) have non-free | |
778 | properties $(non-free) ; | |
779 | } | |
780 | local t = [ property.translate-paths $(specification) : | |
781 | $(self.location) ] ; | |
782 | if $(self.usage-requirements) | |
783 | { | |
784 | self.usage-requirements = [ property-set.create | |
785 | [ $(self.usage-requirements).raw ] $(t) ] ; | |
786 | } | |
787 | else | |
788 | { | |
789 | self.usage-requirements = [ property-set.create $(t) ] ; | |
790 | } | |
791 | } | |
792 | else if $(attribute) = "default-build" | |
793 | { | |
794 | self.default-build = [ property.make $(specification) ] ; | |
795 | } | |
796 | else if $(attribute) = "source-location" | |
797 | { | |
798 | self.source-location = ; | |
799 | for local src-path in $(specification) | |
800 | { | |
801 | self.source-location += [ path.root [ path.make $(src-path) ] | |
802 | $(self.location) ] ; | |
803 | } | |
804 | } | |
805 | else if $(attribute) = "build-dir" | |
806 | { | |
807 | self.build-dir = [ path.root [ path.make $(specification) ] | |
808 | $(self.location) ] ; | |
809 | } | |
810 | else if $(attribute) = "id" | |
811 | { | |
812 | self.id = [ project.register-id $(specification) : | |
813 | $(self.project-module) ] ; | |
814 | } | |
815 | else if ! $(attribute) in "default-build" "location" "parent" | |
816 | "projects-to-build" "project-root" "source-location" | |
817 | { | |
818 | import errors : error : errors.error ; | |
819 | errors.error Invalid project attribute '$(attribute)' specified for | |
820 | project at '$(self.location)' ; | |
821 | } | |
822 | else | |
823 | { | |
824 | self.$(attribute) = $(specification) ; | |
825 | } | |
826 | } | |
827 | ||
828 | # Returns the value of the given attribute. | |
829 | # | |
830 | rule get ( attribute ) | |
831 | { | |
832 | return $(self.$(attribute)) ; | |
833 | } | |
834 | ||
835 | # Returns whether these attributes belong to a Jamroot project module. | |
836 | # | |
837 | rule is-jamroot ( ) | |
838 | { | |
839 | if $(self.location) && $(self.project-root) = $(self.location) | |
840 | { | |
841 | return true ; | |
842 | } | |
843 | } | |
844 | ||
845 | # Prints the project attributes. | |
846 | # | |
847 | rule print ( ) | |
848 | { | |
849 | local id = '$(self.id)' ; | |
850 | print.section $(id:E=(none)) ; | |
851 | print.list-start ; | |
852 | print.list-item "Parent project:" $(self.parent:E=(none)) ; | |
853 | print.list-item "Requirements:" [ $(self.requirements).raw ] ; | |
854 | print.list-item "Default build:" $(self.default-build) ; | |
855 | print.list-item "Source location:" $(self.source-location) ; | |
856 | print.list-item "Projects to build:" [ sequence.insertion-sort | |
857 | $(self.projects-to-build) ] ; | |
858 | print.list-end ; | |
859 | } | |
860 | } | |
861 | ||
862 | ||
863 | # Returns the build directory for standalone projects | |
864 | # | |
865 | rule standalone-build-dir ( ) | |
866 | { | |
867 | project = [ target $(.first-project-root) ] ; | |
868 | return [ path.join [ $(project).build-dir ] standalone ] ; | |
869 | } | |
870 | ||
871 | # Returns the project which is currently being loaded. | |
872 | # | |
873 | rule current ( ) | |
874 | { | |
875 | if ! $(.current-project) | |
876 | { | |
877 | import errors ; | |
878 | errors.error Reference to the project currently being loaded requested | |
879 | when there was no project module being loaded. ; | |
880 | } | |
881 | return $(.current-project) ; | |
882 | } | |
883 | ||
884 | ||
885 | # Temporarily changes the current project to 'project'. Should be followed by | |
886 | # 'pop-current'. | |
887 | # | |
11fdf7f2 | 888 | rule push-current ( project ? ) |
7c673cae FG |
889 | { |
890 | .saved-current-project += $(.current-project) ; | |
891 | .current-project = $(project) ; | |
892 | } | |
893 | ||
894 | ||
895 | rule pop-current ( ) | |
896 | { | |
897 | .current-project = $(.saved-current-project[-1]) ; | |
898 | .saved-current-project = $(.saved-current-project[1--2]) ; | |
899 | } | |
900 | ||
901 | ||
902 | # Returns the project-attribute instance for the specified Jamfile module. | |
903 | # | |
904 | rule attributes ( project ) | |
905 | { | |
906 | return $($(project).attributes) ; | |
907 | } | |
908 | ||
909 | ||
910 | # Returns the value of the specified attribute in the specified Jamfile module. | |
911 | # | |
912 | rule attribute ( project attribute ) | |
913 | { | |
914 | return [ $($(project).attributes).get $(attribute) ] ; | |
915 | } | |
916 | ||
917 | ||
918 | # Returns whether a project module is one of Boost Build's configuration | |
919 | # modules. | |
920 | # | |
921 | rule is-config-module ( project ) | |
922 | { | |
923 | local cfgs = project site test user ; | |
924 | if $(project) in $(cfgs)-config | |
925 | { | |
926 | return true ; | |
927 | } | |
928 | } | |
929 | ||
930 | ||
931 | # Returns whether a project module is a Jamroot project module. | |
932 | # | |
933 | rule is-jamroot-module ( project ) | |
934 | { | |
935 | return [ $($(project).attributes).is-jamroot ] ; | |
936 | } | |
937 | ||
938 | ||
939 | # Returns a project's parent jamroot module. Returns nothing if there is no such | |
940 | # module, i.e. if this is a standalone project or one of the internal Boost | |
941 | # Build configuration projects. | |
942 | # | |
943 | rule get-jamroot-module ( project ) | |
944 | { | |
945 | local jamroot-location = [ attribute $(project) project-root ] ; | |
946 | if $(jamroot-location) | |
947 | { | |
948 | return [ module-name $(jamroot-location) ] ; | |
949 | } | |
950 | } | |
951 | ||
952 | ||
953 | # Returns the project target corresponding to the 'project-module'. | |
954 | # | |
11fdf7f2 | 955 | rule target ( project-module : allow-missing ? ) |
7c673cae | 956 | { |
11fdf7f2 | 957 | if ! $(.target.$(project-module)) && ! $(allow-missing) |
7c673cae FG |
958 | { |
959 | import errors ; | |
960 | errors.user-error Project target requested but not yet assigned for | |
961 | module '$(project-module)'. ; | |
962 | } | |
963 | return $(.target.$(project-module)) ; | |
964 | } | |
965 | ||
966 | ||
967 | # Defines a Boost.Build extension project. Such extensions usually contain | |
968 | # library targets and features that can be used by many people. Even though | |
969 | # extensions are really projects, they can be initialized as a module would be | |
970 | # with the "using" (project.project-rules.using) mechanism. | |
971 | # | |
972 | rule extension ( id space ? : options * : * ) | |
973 | { | |
974 | # The caller is a standalone module for the extension. | |
975 | local mod = [ CALLER_MODULE ] ; | |
976 | ||
977 | # We need to do the rest within the extension module. | |
978 | module $(mod) | |
979 | { | |
980 | import path ; | |
981 | ||
982 | # Find the root project. | |
983 | local root-project = [ project.current ] ; | |
984 | root-project = [ $(root-project).project-module ] ; | |
985 | while | |
986 | [ project.attribute $(root-project) parent-module ] && | |
987 | [ project.attribute $(root-project) parent-module ] != user-config | |
988 | { | |
989 | root-project = [ project.attribute $(root-project) parent-module ] ; | |
990 | } | |
991 | ||
992 | # Default to creating extensions in /ext/.. project space. | |
993 | local id = $(1[1]) ; | |
994 | local space = $(1[2]) ; | |
995 | space ?= ext ; | |
996 | ||
997 | # Create the project data, and bring in the project rules into the | |
998 | # module. | |
999 | project.initialize $(__name__) : [ path.join [ project.attribute | |
1000 | $(root-project) location ] $(space:L) $(id:L) ] ; | |
1001 | ||
1002 | # Create the project itself, i.e. the attributes. | |
1003 | project /$(space:L)/$(id:L) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : | |
1004 | $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) | |
1005 | : $(18) : $(19) ; | |
1006 | local attributes = [ project.attributes $(__name__) ] ; | |
1007 | ||
1008 | # Inherit from the root project of whomever is defining us. | |
1009 | project.inherit-attributes $(__name__) : $(root-project) ; | |
1010 | $(attributes).set parent-module : $(root-project) : exact ; | |
1011 | } | |
1012 | } | |
1013 | ||
1014 | ||
1015 | rule glob-internal ( project : wildcards + : excludes * : rule-name ) | |
1016 | { | |
1017 | local location = [ $(project).get source-location ] ; | |
1018 | ||
1019 | local result ; | |
1020 | local paths = [ path.$(rule-name) $(location) : | |
1021 | [ sequence.transform path.make : $(wildcards) ] : | |
1022 | [ sequence.transform path.make : $(excludes) ] ] ; | |
1023 | if $(wildcards:D) || $(rule-name) != glob | |
1024 | { | |
1025 | # The paths we have found are relative to the current directory, but the | |
1026 | # names specified in the sources list are assumed to be relative to the | |
1027 | # source directory of the corresponding project. So, just make the names | |
1028 | # absolute. | |
1029 | for local p in $(paths) | |
1030 | { | |
1031 | # If the path is below source location, use relative path. | |
1032 | # Otherwise, use full path just to avoid any ambiguities. | |
1033 | local rel = [ path.relative $(p) $(location) : no-error ] ; | |
1034 | if $(rel) = not-a-child | |
1035 | { | |
1036 | result += [ path.root $(p) [ path.pwd ] ] ; | |
1037 | } | |
1038 | else | |
1039 | { | |
1040 | result += $(rel) ; | |
1041 | } | |
1042 | } | |
1043 | } | |
1044 | else | |
1045 | { | |
1046 | # There were no wildcards in the directory path, so the files are all in | |
1047 | # the source directory of the project. Just drop the directory, instead | |
1048 | # of making paths absolute. | |
1049 | result = $(paths:D="") ; | |
1050 | } | |
1051 | ||
1052 | return $(result) ; | |
1053 | } | |
1054 | ||
1055 | ||
1056 | rule glob-path-root ( root path ) | |
1057 | { | |
1058 | return [ path.root $(path) $(root) ] ; | |
1059 | } | |
1060 | ||
1061 | rule glob-internal-ex ( project : paths + : wildcards + : excludes * : rule-name ) | |
1062 | { | |
1063 | # Make the paths we search in absolute, if they aren't already absolute. | |
1064 | # If the given paths are relative, they will be relative to the source | |
1065 | # directory. So that's what we root against. | |
1066 | local source-location | |
1067 | = [ path.root [ $(project).get source-location ] [ path.pwd ] ] ; | |
1068 | local search-paths | |
1069 | = [ sequence.transform project.glob-path-root $(source-location) : $(paths) ] ; | |
1070 | paths | |
1071 | = [ path.$(rule-name) $(search-paths) : $(wildcards) : $(excludes) ] ; | |
1072 | # The paths we have found are absolute, but the names specified in the | |
1073 | # sources list are assumed to be relative to the source directory of the | |
1074 | # corresponding project. Make the results relative to the source again. | |
1075 | local result | |
1076 | = [ sequence.transform path.relative-to $(source-location) : $(paths) ] ; | |
1077 | ||
1078 | return $(result) ; | |
1079 | } | |
1080 | ||
1081 | ||
1082 | # This module defines rules common to all projects. | |
1083 | # | |
1084 | module project-rules | |
1085 | { | |
1086 | import modules ; | |
1087 | ||
1088 | rule using ( toolset-module : * ) | |
1089 | { | |
1090 | import toolset ; | |
1091 | ||
1092 | local saved-project = [ modules.peek project : .current-project ] ; | |
1093 | ||
1094 | # Temporarily change the search path so the module referred to by | |
1095 | # 'using' can be placed in the same directory as Jamfile. User will | |
1096 | # expect the module to be found even though the directory is not in | |
1097 | # BOOST_BUILD_PATH. | |
1098 | local x = [ modules.peek : BOOST_BUILD_PATH ] ; | |
1099 | local caller = [ CALLER_MODULE ] ; | |
1100 | local caller-location = [ modules.binding $(caller) ] ; | |
1101 | modules.poke : BOOST_BUILD_PATH : $(caller-location:D) $(x) ; | |
1102 | toolset.using $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : | |
1103 | $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) | |
1104 | : $(18) : $(19) ; | |
1105 | modules.poke : BOOST_BUILD_PATH : $(x) ; | |
1106 | ||
1107 | # The above might have clobbered .current-project in case it caused a | |
1108 | # new project instance to be created (which would then automatically | |
1109 | # get set as the 'current' project). Restore the correct value so any | |
1110 | # main targets declared after this do not get mapped to the loaded | |
1111 | # module's project. | |
1112 | modules.poke project : .current-project : $(saved-project) ; | |
1113 | } | |
1114 | ||
1115 | rule import ( * : * : * ) | |
1116 | { | |
1117 | local caller = [ CALLER_MODULE ] ; | |
1118 | local saved-project = [ modules.peek project : .current-project ] ; | |
1119 | module $(caller) | |
1120 | { | |
1121 | modules.import $(1) : $(2) : $(3) ; | |
1122 | } | |
1123 | ||
1124 | # The above might have clobbered .current-project in case it caused a | |
1125 | # new project instance to be created (which would then automatically | |
1126 | # get set as the 'current' project). Restore the correct value so any | |
1127 | # main targets declared after this do not get mapped to the loaded | |
1128 | # module's project. | |
1129 | modules.poke project : .current-project : $(saved-project) ; | |
1130 | } | |
1131 | ||
1132 | rule project ( id ? : options * : * ) | |
1133 | { | |
1134 | import path ; | |
1135 | import project ; | |
1136 | ||
1137 | local caller = [ CALLER_MODULE ] ; | |
1138 | local attributes = [ project.attributes $(caller) ] ; | |
1139 | if $(id) | |
1140 | { | |
1141 | $(attributes).set id : $(id) ; | |
1142 | } | |
1143 | ||
1144 | local explicit-build-dir ; | |
1145 | ||
1146 | for n in 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
1147 | { | |
1148 | local option = $($(n)) ; | |
1149 | if $(option) | |
1150 | { | |
1151 | $(attributes).set $(option[1]) : $(option[2-]) ; | |
1152 | } | |
1153 | if $(option[1]) = "build-dir" | |
1154 | { | |
1155 | explicit-build-dir = [ path.make $(option[2-]) ] ; | |
1156 | } | |
1157 | } | |
1158 | ||
1159 | # If '--build-dir' is specified, change the build dir for the project. | |
1160 | local global-build-dir = [ modules.peek project : .global-build-dir ] ; | |
1161 | ||
1162 | if $(global-build-dir) | |
1163 | { | |
1164 | local location = [ $(attributes).get location ] ; | |
1165 | # Project with an empty location is a 'standalone' project such as | |
1166 | # user-config or qt. It has no build dir. If we try to set build dir | |
1167 | # for user-config, we shall then try to inherit it, with either | |
1168 | # weird or wrong consequences. | |
1169 | if $(location) && $(location) = [ $(attributes).get project-root ] | |
1170 | { | |
1171 | # Re-read the project id, since it might have been modified a | |
1172 | # bit when setting the project's id attribute, e.g. might have | |
1173 | # been prefixed by a slash if it was not already. | |
1174 | id = [ $(attributes).get id ] ; | |
1175 | # This is Jamroot. | |
1176 | if $(id) | |
1177 | { | |
1178 | if $(explicit-build-dir) && | |
1179 | [ path.is-rooted $(explicit-build-dir) ] | |
1180 | { | |
1181 | import errors ; | |
1182 | errors.user-error Absolute directory specified via | |
1183 | 'build-dir' project attribute : Do not know how to | |
1184 | combine that with the --build-dir option. ; | |
1185 | } | |
1186 | # Strip the leading slash from id. | |
1187 | local rid = [ MATCH ^/(.*) : $(id) ] ; | |
1188 | local p = [ path.join $(global-build-dir) $(rid) | |
1189 | $(explicit-build-dir) ] ; | |
1190 | ||
1191 | $(attributes).set build-dir : $(p) : exact ; | |
1192 | } | |
1193 | } | |
1194 | else | |
1195 | { | |
1196 | # Not Jamroot. | |
1197 | if $(explicit-build-dir) | |
1198 | { | |
1199 | import errors ; | |
1200 | errors.user-error When --build-dir is specified, the | |
1201 | 'build-dir' project : attribute is allowed only for | |
1202 | top-level 'project' invocations ; | |
1203 | } | |
1204 | } | |
1205 | } | |
1206 | } | |
1207 | ||
1208 | # Declare and set a project global constant. Project global constants are | |
1209 | # normal variables but should not be changed. They are applied to every | |
1210 | # child Jamfile. | |
1211 | # | |
1212 | rule constant ( name : value + ) | |
1213 | { | |
1214 | import project ; | |
1215 | local caller = [ CALLER_MODULE ] ; | |
1216 | local p = [ project.target $(caller) ] ; | |
1217 | $(p).add-constant $(name) : $(value) ; | |
1218 | } | |
1219 | ||
1220 | # Declare and set a project global constant, whose value is a path. The path | |
1221 | # is adjusted to be relative to the invocation directory. The given value | |
1222 | # path is taken to be either absolute, or relative to this project root. | |
1223 | # | |
1224 | rule path-constant ( name : value + ) | |
1225 | { | |
1226 | import project ; | |
1227 | local caller = [ CALLER_MODULE ] ; | |
1228 | local p = [ project.target $(caller) ] ; | |
1229 | $(p).add-constant $(name) : $(value) : path ; | |
1230 | } | |
1231 | ||
1232 | rule use-project ( id : where ) | |
1233 | { | |
1234 | # See comment in 'load' for explanation. | |
1235 | local caller = [ CALLER_MODULE ] ; | |
1236 | modules.poke $(caller) : .used-projects : [ modules.peek $(caller) : | |
1237 | .used-projects ] $(id) $(where) ; | |
1238 | } | |
1239 | ||
1240 | rule build-project ( dir ) | |
1241 | { | |
1242 | import project ; | |
1243 | local caller = [ CALLER_MODULE ] ; | |
1244 | local attributes = [ project.attributes $(caller) ] ; | |
1245 | local now = [ $(attributes).get projects-to-build ] ; | |
1246 | $(attributes).set projects-to-build : $(now) $(dir) ; | |
1247 | } | |
1248 | ||
1249 | rule explicit ( target-names * ) | |
1250 | { | |
1251 | import project ; | |
1252 | # If 'explicit' is used in a helper rule defined in Jamroot and | |
1253 | # inherited by children, then most of the time we want 'explicit' to | |
1254 | # operate on the Jamfile where the helper rule is invoked. | |
1255 | local t = [ project.current ] ; | |
1256 | for local n in $(target-names) | |
1257 | { | |
1258 | $(t).mark-target-as-explicit $(n) ; | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | rule always ( target-names * ) | |
1263 | { | |
1264 | import project ; | |
1265 | local t = [ project.current ] ; | |
1266 | for local n in $(target-names) | |
1267 | { | |
1268 | $(t).mark-target-as-always $(n) ; | |
1269 | } | |
1270 | } | |
1271 | ||
1272 | rule glob ( wildcards + : excludes * ) | |
1273 | { | |
1274 | import project ; | |
1275 | return [ project.glob-internal [ project.current ] : $(wildcards) : | |
1276 | $(excludes) : glob ] ; | |
1277 | } | |
1278 | ||
1279 | rule glob-tree ( wildcards + : excludes * ) | |
1280 | { | |
1281 | import project ; | |
1282 | if $(wildcards:D) || $(excludes:D) | |
1283 | { | |
1284 | import errors ; | |
1285 | errors.user-error The patterns to 'glob-tree' may not include | |
1286 | directory ; | |
1287 | } | |
1288 | return [ project.glob-internal [ project.current ] : $(wildcards) : | |
1289 | $(excludes) : glob-tree ] ; | |
1290 | } | |
1291 | ||
1292 | rule glob-ex ( paths + : wildcards + : excludes * ) | |
1293 | { | |
1294 | import project ; | |
1295 | return [ project.glob-internal-ex [ project.current ] | |
1296 | : $(paths) : $(wildcards) : $(excludes) : glob ] ; | |
1297 | } | |
1298 | ||
1299 | rule glob-tree-ex ( paths + : wildcards + : excludes * ) | |
1300 | { | |
1301 | import project ; | |
1302 | return [ project.glob-internal-ex [ project.current ] | |
1303 | : $(paths) : $(wildcards) : $(excludes) : glob-tree ] ; | |
1304 | } | |
1305 | ||
1306 | # Calculates conditional requirements for multiple requirements at once. | |
1307 | # This is a shorthand to reduce duplication and to keep an inline | |
1308 | # declarative syntax. For example: | |
1309 | # | |
1310 | # lib x : x.cpp : [ conditional <toolset>gcc <variant>debug : | |
1311 | # <define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ; | |
1312 | # | |
1313 | rule conditional ( condition + : requirements * ) | |
1314 | { | |
1315 | local condition = $(condition:J=,) ; | |
11fdf7f2 | 1316 | if [ MATCH "(:)" : $(condition) ] |
7c673cae FG |
1317 | { |
1318 | return $(condition)$(requirements) ; | |
1319 | } | |
1320 | else | |
1321 | { | |
11fdf7f2 | 1322 | return "$(condition):$(requirements)" ; |
7c673cae FG |
1323 | } |
1324 | } | |
1325 | ||
1326 | rule option ( name : value ) | |
1327 | { | |
1328 | local m = [ CALLER_MODULE ] ; | |
1329 | local cfgs = project site test user ; | |
1330 | if ! $(m) in $(cfgs)-config | |
1331 | { | |
1332 | import errors ; | |
1333 | errors.error The 'option' rule may only be used "in" Boost Build | |
1334 | configuration files. ; | |
1335 | } | |
1336 | import option ; | |
1337 | option.set $(name) : $(value) ; | |
1338 | } | |
92f5a8d4 TL |
1339 | |
1340 | # This allows one to manually import a package manager build information file. | |
1341 | # The argument can be either a symbolic name of a supported package manager or | |
1342 | # the a glob pattern to load a b2 jam file. | |
1343 | # | |
1344 | rule use-packages ( name-or-glob-pattern ? ) | |
1345 | { | |
1346 | local m = [ CALLER_MODULE ] ; | |
1347 | local glob-pattern = $(name-or-glob-pattern) ; | |
1348 | local glob-for-name = [ modules.peek project : PACKAGE_MANAGER_BUILD_INFO($(name-or-glob-pattern:U)) ] ; | |
1349 | if $(glob-for-name) | |
1350 | { | |
1351 | glob-pattern = $(glob-for-name) ; | |
1352 | } | |
1353 | modules.call-in $(m) : constant PACKAGE_MANAGER_BUILD_INFO : $(glob-pattern) ; | |
1354 | IMPORT project : load-package-manager-build-info : $(m) : project.load-package-manager-build-info ; | |
1355 | modules.call-in $(m) : project.load-package-manager-build-info ; | |
1356 | } | |
7c673cae | 1357 | } |