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)
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)
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.
23 # The 'project' rule can also declare a project id which will be associated with
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.
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
37 import "class" : new ;
45 .debug-loading = [ MATCH ^(--debug-loading)$ : [ modules.peek : ARGV ] ] ;
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.
53 rule load ( jamfile-location : synthesize ? )
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)
61 ECHO Loading Jamfile at '$(jamfile-location)' ;
64 load-jamfile $(jamfile-location) : $(module-name) : $(synthesize) ;
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.
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) ;
77 return $(module-name) ;
81 rule load-used-projects ( module-name )
83 local used = [ modules.peek $(module-name) : .used-projects ] ;
84 local location = [ attribute $(module-name) location ] ;
87 local id = $(used[1]) ;
88 local where = [ path.make $(used[2]) ] ;
89 register-id $(id) : [ load [ path.root $(where) $(location) ] ] ;
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" ;
102 # Loads parent of Jamfile at 'location'. Issues an error if nothing is found.
104 rule load-parent ( location )
106 local found = [ path.glob-in-parents $(location) : $(JAMROOT) $(JAMFILE) ] ;
109 return [ load $(found[1]:D) ] ;
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.
117 rule find ( name : current-location )
119 local project-module ;
121 # Try interpreting name as project id.
122 if [ path.is-rooted $(name) ]
124 project-module = $($(name).jamfile-module) ;
127 if ! $(project-module)
129 local location = [ path.root [ path.make $(name) ] $(current-location) ]
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.
137 project-module = [ module-name $(location) ] ;
138 if ! $(project-module) in $(.jamfile-modules)
140 if [ path.glob $(location) : $(JAMROOT) $(JAMFILE) ]
142 project-module = [ load $(location) ] ;
151 return $(project-module) ;
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.
159 rule module-name ( jamfile-location )
161 if ! $(.module.$(jamfile-location))
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 ] ] ;
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) ] : * ] ;
180 normalized = $(glob-result[1]:D) ;
182 .module.$(jamfile-location) = Jamfile<$(normalized)> ;
184 return $(.module.$(jamfile-location)) ;
188 # Default patterns to search for the Jamfiles to use for build declarations.
190 JAMFILE = [ modules.peek : JAMFILE ] ;
191 JAMFILE ?= "[Bb]uild.jam" "[Jj]amfile.v2" "[Jj]amfile" "[Jj]amfile." "[Jj]amfile.jam" ;
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.
200 dir # The directory(s) to look for a Jamfile.
201 parent-root ? # Optional flag indicating to search for the parent Jamfile.
205 # Glob for all the possible Jamfiles according to the match pattern.
207 local jamfile-glob = ;
210 if ! $(.parent-jamfile.$(dir))
212 .parent-jamfile.$(dir) = [ path.glob-in-parents $(dir) : $(JAMFILE)
215 jamfile-glob = $(.parent-jamfile.$(dir)) ;
219 if ! $(.jamfile.$(dir))
221 .jamfile.$(dir) = [ path.glob $(dir) : $(JAMFILE) ] ;
223 jamfile-glob = $(.jamfile.$(dir)) ;
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.
232 if $(jamfile-to-load[2-])
234 local v2-jamfiles = [ MATCH "^(.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam)$" :
235 $(jamfile-to-load) ] ;
237 if $(v2-jamfiles) && ! $(v2-jamfiles[2])
239 jamfile-to-load = $(v2-jamfiles) ;
243 local jamfile = [ path.basename $(jamfile-to-load[1]) ] ;
244 ECHO "warning: Found multiple Jamfiles at '"$(dir)"'!"
245 "Loading the first one: '$(jamfile)'." ;
248 jamfile-to-load = $(jamfile-to-load[1]) ;
251 # Could not find it, error.
253 if ! $(no-errors) && ! $(jamfile-to-load)
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'." ;
262 return $(jamfile-to-load) ;
266 # Default patterns to search for auto-include of package manager build declarations.
268 PACKAGE_MANAGER_BUILD_INFO(CONAN) = "conanbuildinfo.jam" ;
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".
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)) ;
282 # Load the configured package manager build information file.
284 rule load-package-manager-build-info ( )
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)
293 local pm = [ path.glob $(dir) : $(package-manager-build-info) ] ;
295 local cm = [ CALLER_MODULE ] ;
296 local pm-tag = "$(cm)<$(pm:B)>" ;
297 if $(pm) && ! ( $(pm-tag) in $(.package-manager-build-info) )
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) ;
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.
315 local rule load-jamfile ( dir : jamfile-module : synthesize ? )
317 # See if the Jamfile is where it should be.
319 local jamfile-to-load = [ path.glob $(dir) : $(JAMROOT) ] ;
320 if ! $(jamfile-to-load)
322 jamfile-to-load = [ find-jamfile $(dir) : $(synthesize) ] ;
325 if $(jamfile-to-load[2])
328 errors.error "Multiple Jamfiles found at '$(dir)'" :
329 "Filenames are: " $(jamfile-to-load:D=) ;
332 if ! $(jamfile-to-load) && $(synthesize)
334 jamfile-to-load = $(dir)/@ ;
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)
344 local previous-project = $(.current-project) ;
346 # Initialize the Jamfile module before loading.
347 initialize $(jamfile-module) : [ path.parent $(jamfile-to-load) ] :
348 $(jamfile-to-load:BS) ;
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 ;
355 if ! $(jamfile-module) in $(.jamfile-modules)
357 .jamfile-modules += $(jamfile-module) ;
359 local saved-project = $(.current-project) ;
361 mark-as-user $(jamfile-module) ;
362 if $(jamfile-to-load:B) = "@"
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) ] ;
371 modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ]
373 if [ MATCH ^($(JAMROOT))$ : $(jamfile-to-load:BS) ]
375 jamfile = [ find-jamfile $(dir) : no-errors ] ;
378 load-aux $(jamfile-module) : [ path.native $(jamfile) ] ;
383 # Now do some checks.
384 if $(.current-project) != $(saved-project)
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) ;
396 end-load $(previous-project) ;
398 if $(.global-build-dir)
400 if [ attribute $(jamfile-module) location ] && ! [ attribute
401 $(jamfile-module) id ]
403 local project-root = [ attribute $(jamfile-module)
405 if $(project-root) = $(dir)
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" ;
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.
423 rule end-load ( previous-project ? )
425 if ! $(.current-project)
428 errors.error Ending project loading requested when there was no project
429 currently being loaded. ;
432 if ! $(previous-project) && $(.saved-current-project)
435 errors.error Ending project loading requested with no 'previous project'
436 when there were other projects still marked as being loaded
440 .current-project = $(previous-project) ;
444 rule mark-as-user ( module-name )
446 if USER_MODULE in [ RULENAMES ]
448 USER_MODULE $(module-name) ;
453 rule load-aux ( module-name : file )
455 mark-as-user $(module-name) ;
457 module $(module-name)
460 local rules = [ RULENAMES $(1) ] ;
461 IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ;
466 .global-build-dir = [ MATCH ^--build-dir=(.*)$ : [ modules.peek : ARGV ] ] ;
467 if $(.global-build-dir)
469 # If the option is specified several times, take the last value.
470 .global-build-dir = [ path.make $(.global-build-dir[-1]) ] ;
474 # Initialize the module for a project.
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.
485 ECHO "Initializing project '$(module-name)'" ;
490 local parent-module ;
491 if $(module-name) in test-config all-config
495 else if $(module-name) = site-config
497 parent-module = test-config ;
499 else if $(module-name) = user-config
501 parent-module = site-config ;
503 else if $(module-name) = project-config
505 parent-module = user-config ;
509 if ! [ MATCH ^($(JAMROOT))$ : $(basename) ]
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) ] ;
515 if ! $(parent-module)
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
521 if $(project-config.attributes)
523 parent-module = project-config ;
527 parent-module = user-config ;
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.
537 # Create the module for the Jamfile first.
538 module $(module-name)
542 # load-parent can end up loading this module again. Make sure this is not
544 if ! $($(module-name).attributes)
546 $(module-name).attributes = [ new project-attributes $(location)
548 local attributes = $($(module-name).attributes) ;
552 $(attributes).set source-location : [ path.make $(location) ] :
557 local cfgs = project site test user all ;
558 if ! $(module-name) in $(cfgs)-config
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) ] ;
569 errors.error Could not determine project location
572 $(attributes).set source-location : $(s:D) : exact ;
576 $(attributes).set requirements : [ property-set.empty ] : exact ;
577 $(attributes).set usage-requirements : [ property-set.empty ] : exact ;
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) ;
586 inherit-attributes $(module-name) : $(parent-module) ;
587 $(attributes).set parent-module : $(parent-module) : exact ;
592 $(attributes).set project-root : $(location) : exact ;
593 if ! $(.first-project-root)
595 .first-project-root = $(module-name) ;
602 parent = [ target $(parent-module) ] ;
605 if ! $(.target.$(module-name))
607 local requirements = [ attribute $(module-name) requirements ] ;
608 .target.$(module-name) = [ new project-target $(module-name) :
609 $(module-name) $(parent) : $(requirements) ] ;
613 ECHO Assigned project target $(.target.$(module-name)) to
619 .current-project = [ target $(module-name) ] ;
623 # Make 'project-module' inherit attributes of project root and parent module.
625 rule inherit-attributes ( project-module : parent-module )
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) ]
632 $(attributes).set parent :
633 [ path.parent [ path.make [ modules.binding $(parent-module) ] ] ] ;
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 ;
644 local parent-build-dir = [ $(pattributes).get build-dir ] ;
645 if $(parent-build-dir)
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
651 local location = [ attribute $(project-module) location ] ;
652 local parent-location = [ attribute $(parent-module) location ] ;
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 ;
663 # Returns whether the given string is a valid registered project id.
665 rule is-registered-id ( id )
667 return $($(id).jamfile-module) ;
671 # Associate the given id with the given project module. Returns the possibly
672 # corrected project id.
674 rule register-id ( id : module )
676 id = [ path.root $(id) / ] ;
678 if [ MATCH (//) : $(id) ]
681 errors.user-error Project id may not contain two consecutive slash
682 characters (project "id:" '$(id)'). ;
685 local orig-module = $($(id).jamfile-module) ;
686 if $(orig-module) && $(orig-module) != $(module)
688 local new-file = [ modules.peek $(module) : __file__ ] ;
689 local new-location = [ project.attribute $(module) location ] ;
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 ] ;
698 errors.user-error Attempt to redeclare already registered project 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=---)
707 : " " "Module:" $(module)
708 : " " "File:" $(new-file:E=---)
709 : " " "Location:" $(new-location:E=---) ;
712 $(id).jamfile-module = $(module) ;
717 # Class keeping all the attributes of a project.
719 # The standard attributes are "id", "location", "project-root", "parent"
720 # "requirements", "default-build", "source-location" and "projects-to-build".
722 class project-attributes
728 import property-set ;
731 rule __init__ ( location project-module )
733 self.location = $(location) ;
734 self.project-module = $(project-module) ;
737 # Set the named attribute from the specification given by the user. The
738 # value actually set may be different.
740 rule set ( attribute : specification *
741 : exact ? # Sets value from 'specification' without any processing.
746 self.$(attribute) = $(specification) ;
748 else if $(attribute) = "requirements"
750 local result = [ property-set.refine-from-user-input
751 $(self.requirements) : $(specification)
752 : $(self.project-module) : $(self.location) ] ;
754 if $(result[1]) = "@error"
756 import errors : error : errors.error ;
757 errors.error Requirements for project at '$(self.location)'
758 conflict with parent's. : "Explanation:" $(result[2-]) ;
761 self.requirements = $(result) ;
763 else if $(attribute) = "usage-requirements"
765 local unconditional ;
766 for local p in $(specification)
768 local split = [ property.split-conditional $(p) ] ;
769 split ?= nothing $(p) ;
770 unconditional += $(split[2]) ;
773 local non-free = [ property.remove free : $(unconditional) ] ;
776 import errors : error : errors.error ;
777 errors.error usage-requirements $(specification) have non-free
778 properties $(non-free) ;
780 local t = [ property.translate-paths $(specification) :
782 if $(self.usage-requirements)
784 self.usage-requirements = [ property-set.create
785 [ $(self.usage-requirements).raw ] $(t) ] ;
789 self.usage-requirements = [ property-set.create $(t) ] ;
792 else if $(attribute) = "default-build"
794 self.default-build = [ property.make $(specification) ] ;
796 else if $(attribute) = "source-location"
798 self.source-location = ;
799 for local src-path in $(specification)
801 self.source-location += [ path.root [ path.make $(src-path) ]
805 else if $(attribute) = "build-dir"
807 self.build-dir = [ path.root [ path.make $(specification) ]
810 else if $(attribute) = "id"
812 self.id = [ project.register-id $(specification) :
813 $(self.project-module) ] ;
815 else if ! $(attribute) in "default-build" "location" "parent"
816 "projects-to-build" "project-root" "source-location"
818 import errors : error : errors.error ;
819 errors.error Invalid project attribute '$(attribute)' specified for
820 project at '$(self.location)' ;
824 self.$(attribute) = $(specification) ;
828 # Returns the value of the given attribute.
830 rule get ( attribute )
832 return $(self.$(attribute)) ;
835 # Returns whether these attributes belong to a Jamroot project module.
839 if $(self.location) && $(self.project-root) = $(self.location)
845 # Prints the project attributes.
849 local id = '$(self.id)' ;
850 print.section $(id:E=(none)) ;
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) ] ;
863 # Returns the build directory for standalone projects
865 rule standalone-build-dir ( )
867 project = [ target $(.first-project-root) ] ;
868 return [ path.join [ $(project).build-dir ] standalone ] ;
871 # Returns the project which is currently being loaded.
875 if ! $(.current-project)
878 errors.error Reference to the project currently being loaded requested
879 when there was no project module being loaded. ;
881 return $(.current-project) ;
885 # Temporarily changes the current project to 'project'. Should be followed by
888 rule push-current ( project ? )
890 .saved-current-project += $(.current-project) ;
891 .current-project = $(project) ;
897 .current-project = $(.saved-current-project[-1]) ;
898 .saved-current-project = $(.saved-current-project[1--2]) ;
902 # Returns the project-attribute instance for the specified Jamfile module.
904 rule attributes ( project )
906 return $($(project).attributes) ;
910 # Returns the value of the specified attribute in the specified Jamfile module.
912 rule attribute ( project attribute )
914 return [ $($(project).attributes).get $(attribute) ] ;
918 # Returns whether a project module is one of Boost Build's configuration
921 rule is-config-module ( project )
923 local cfgs = project site test user ;
924 if $(project) in $(cfgs)-config
931 # Returns whether a project module is a Jamroot project module.
933 rule is-jamroot-module ( project )
935 return [ $($(project).attributes).is-jamroot ] ;
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.
943 rule get-jamroot-module ( project )
945 local jamroot-location = [ attribute $(project) project-root ] ;
946 if $(jamroot-location)
948 return [ module-name $(jamroot-location) ] ;
953 # Returns the project target corresponding to the 'project-module'.
955 rule target ( project-module : allow-missing ? )
957 if ! $(.target.$(project-module)) && ! $(allow-missing)
960 errors.user-error Project target requested but not yet assigned for
961 module '$(project-module)'. ;
963 return $(.target.$(project-module)) ;
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.
972 rule extension ( id space ? : options * : * )
974 # The caller is a standalone module for the extension.
975 local mod = [ CALLER_MODULE ] ;
977 # We need to do the rest within the extension module.
982 # Find the root project.
983 local root-project = [ project.current ] ;
984 root-project = [ $(root-project).project-module ] ;
986 [ project.attribute $(root-project) parent-module ] &&
987 [ project.attribute $(root-project) parent-module ] != user-config
989 root-project = [ project.attribute $(root-project) parent-module ] ;
992 # Default to creating extensions in /ext/.. project space.
994 local space = $(1[2]) ;
997 # Create the project data, and bring in the project rules into the
999 project.initialize $(__name__) : [ path.join [ project.attribute
1000 $(root-project) location ] $(space:L) $(id:L) ] ;
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)
1006 local attributes = [ project.attributes $(__name__) ] ;
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 ;
1015 rule glob-internal ( project : wildcards + : excludes * : rule-name )
1017 local location = [ $(project).get source-location ] ;
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
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
1029 for local p in $(paths)
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
1036 result += [ path.root $(p) [ path.pwd ] ] ;
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="") ;
1056 rule glob-path-root ( root path )
1058 return [ path.root $(path) $(root) ] ;
1061 rule glob-internal-ex ( project : paths + : wildcards + : excludes * : rule-name )
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 ] ] ;
1069 = [ sequence.transform project.glob-path-root $(source-location) : $(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.
1076 = [ sequence.transform path.relative-to $(source-location) : $(paths) ] ;
1082 # This module defines rules common to all projects.
1084 module project-rules
1088 rule using ( toolset-module : * )
1092 local saved-project = [ modules.peek project : .current-project ] ;
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
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)
1105 modules.poke : BOOST_BUILD_PATH : $(x) ;
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
1112 modules.poke project : .current-project : $(saved-project) ;
1115 rule import ( * : * : * )
1117 local caller = [ CALLER_MODULE ] ;
1118 local saved-project = [ modules.peek project : .current-project ] ;
1121 modules.import $(1) : $(2) : $(3) ;
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
1129 modules.poke project : .current-project : $(saved-project) ;
1132 rule project ( id ? : options * : * )
1137 local caller = [ CALLER_MODULE ] ;
1138 local attributes = [ project.attributes $(caller) ] ;
1141 $(attributes).set id : $(id) ;
1144 local explicit-build-dir ;
1146 for n in 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
1148 local option = $($(n)) ;
1151 $(attributes).set $(option[1]) : $(option[2-]) ;
1153 if $(option[1]) = "build-dir"
1155 explicit-build-dir = [ path.make $(option[2-]) ] ;
1159 # If '--build-dir' is specified, change the build dir for the project.
1160 local global-build-dir = [ modules.peek project : .global-build-dir ] ;
1162 if $(global-build-dir)
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 ]
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 ] ;
1178 if $(explicit-build-dir) &&
1179 [ path.is-rooted $(explicit-build-dir) ]
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. ;
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) ] ;
1191 $(attributes).set build-dir : $(p) : exact ;
1197 if $(explicit-build-dir)
1200 errors.user-error When --build-dir is specified, the
1201 'build-dir' project : attribute is allowed only for
1202 top-level 'project' invocations ;
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
1212 rule constant ( name : value + )
1215 local caller = [ CALLER_MODULE ] ;
1216 local p = [ project.target $(caller) ] ;
1217 $(p).add-constant $(name) : $(value) ;
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.
1224 rule path-constant ( name : value + )
1227 local caller = [ CALLER_MODULE ] ;
1228 local p = [ project.target $(caller) ] ;
1229 $(p).add-constant $(name) : $(value) : path ;
1232 rule use-project ( id : where )
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) ;
1240 rule build-project ( dir )
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) ;
1249 rule explicit ( target-names * )
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)
1258 $(t).mark-target-as-explicit $(n) ;
1262 rule always ( target-names * )
1265 local t = [ project.current ] ;
1266 for local n in $(target-names)
1268 $(t).mark-target-as-always $(n) ;
1272 rule glob ( wildcards + : excludes * )
1275 return [ project.glob-internal [ project.current ] : $(wildcards) :
1276 $(excludes) : glob ] ;
1279 rule glob-tree ( wildcards + : excludes * )
1282 if $(wildcards:D) || $(excludes:D)
1285 errors.user-error The patterns to 'glob-tree' may not include
1288 return [ project.glob-internal [ project.current ] : $(wildcards) :
1289 $(excludes) : glob-tree ] ;
1292 rule glob-ex ( paths + : wildcards + : excludes * )
1295 return [ project.glob-internal-ex [ project.current ]
1296 : $(paths) : $(wildcards) : $(excludes) : glob ] ;
1299 rule glob-tree-ex ( paths + : wildcards + : excludes * )
1302 return [ project.glob-internal-ex [ project.current ]
1303 : $(paths) : $(wildcards) : $(excludes) : glob-tree ] ;
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:
1310 # lib x : x.cpp : [ conditional <toolset>gcc <variant>debug :
1311 # <define>DEBUG_EXCEPTION <define>DEBUG_TRACE ] ;
1313 rule conditional ( condition + : requirements * )
1315 local condition = $(condition:J=,) ;
1316 if [ MATCH "(:)" : $(condition) ]
1318 return $(condition)$(requirements) ;
1322 return "$(condition):$(requirements)" ;
1326 rule option ( name : value )
1328 local m = [ CALLER_MODULE ] ;
1329 local cfgs = project site test user ;
1330 if ! $(m) in $(cfgs)-config
1333 errors.error The 'option' rule may only be used "in" Boost Build
1334 configuration files. ;
1337 option.set $(name) : $(value) ;
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.
1344 rule use-packages ( name-or-glob-pattern ? )
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)) ] ;
1351 glob-pattern = $(glob-for-name) ;
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 ;