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