]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/build/project.jam
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / tools / build / src / build / project.jam
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 ] ;
99 JAMROOT ?= project-root.jam "[Jj]amroot" "[Jj]amroot." "[Jj]amroot.jam" ;
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 return [ load $(found[1]:D) ] ;
110 }
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 ] ;
191 JAMFILE ?= "[Bb]uild.jam" "[Jj]amfile.v2" "[Jj]amfile" "[Jj]amfile." "[Jj]amfile.jam" ;
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 {
234 local v2-jamfiles = [ MATCH "^(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)$" :
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=" ")'.
259 : Please consult the documentation at "'http://www.boost.org'." ;
260 }
261
262 return $(jamfile-to-load) ;
263 }
264
265
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
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
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
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 ;
491 if $(module-name) in test-config all-config
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 }
507 else if $(location)
508 {
509 if ! [ MATCH ^($(JAMROOT))$ : $(basename) ]
510 {
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) ] ;
514 }
515 if ! $(parent-module)
516 {
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 ;
530 }
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 {
557 local cfgs = project site test user all ;
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
682 characters (project "id:" '$(id)'). ;
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)'.
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=---) ;
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)'
758 conflict with parent's. : "Explanation:" $(result[2-]) ;
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 #
888 rule push-current ( project ? )
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 #
955 rule target ( project-module : allow-missing ? )
956 {
957 if ! $(.target.$(project-module)) && ! $(allow-missing)
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=,) ;
1316 if [ MATCH "(:)" : $(condition) ]
1317 {
1318 return $(condition)$(requirements) ;
1319 }
1320 else
1321 {
1322 return "$(condition):$(requirements)" ;
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 }
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 }
1357 }