]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/build/project.jam
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / tools / build / src / build / project.jam
CommitLineData
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
37import "class" : new ;
38import modules ;
39import path ;
40import print ;
41import property-set ;
42import 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#
53rule 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
81rule 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.
98JAMROOT ?= [ modules.peek : JAMROOT ] ;
11fdf7f2 99JAMROOT ?= 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#
104rule 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#
120rule 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#
162rule 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#
193JAMFILE = [ modules.peek : JAMFILE ] ;
11fdf7f2 194JAMFILE ?= "[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#
202rule 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#
273local 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#
376rule 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
397rule mark-as-user ( module-name )
398{
399 if USER_MODULE in [ RULENAMES ]
400 {
401 USER_MODULE $(module-name) ;
402 }
403}
404
405
406rule 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 ] ] ;
420if $(.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#
429rule 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#
573rule 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#
613rule 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#
622rule 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#
670class 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#
813rule 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#
821rule 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 836rule push-current ( project ? )
7c673cae
FG
837{
838 .saved-current-project += $(.current-project) ;
839 .current-project = $(project) ;
840}
841
842
843rule 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#
852rule attributes ( project )
853{
854 return $($(project).attributes) ;
855}
856
857
858# Returns the value of the specified attribute in the specified Jamfile module.
859#
860rule 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#
869rule 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#
881rule 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#
891rule 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 903rule 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#
920rule 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
963rule 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
1004rule glob-path-root ( root path )
1005{
1006 return [ path.root $(path) $(root) ] ;
1007}
1008
1009rule 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#
1032module 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}