# Copyright 2002, 2003 Dave Abrahams # Copyright 2002, 2005, 2006 Rene Rivera # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) # Implements project representation and loading. Each project is represented by: # - a module where all the Jamfile content live. # - an instance of 'project-attributes' class. # (given a module name, can be obtained using the 'attributes' rule) # - an instance of 'project-target' class (from targets.jam) # (given a module name, can be obtained using the 'target' rule) # # Typically, projects are created as result of loading a Jamfile, which is done # by rules 'load' and 'initialize', below. First, module for Jamfile is loaded # and new project-attributes instance is created. Some rules necessary for # project are added to the module (see 'project-rules' module) at the bottom of # this file. Default project attributes are set (inheriting attributes of parent # project, if it exists). After that the Jamfile is read. It can declare its own # attributes using the 'project' rule which will be combined with any already # set attributes. # # The 'project' rule can also declare a project id which will be associated with # the project module. # # There can also be 'standalone' projects. They are created by calling # 'initialize' on an arbitrary module and not specifying their location. After # the call, the module can call the 'project' rule, declare main targets and # behave as a regular project except that, since it is not associated with any # location, it should not declare targets that are not prebuilt. # # The list of all loaded Jamfile is stored in the .project-locations variable. # It is possible to obtain a module name for a location using the 'module-name' # rule. Standalone projects are not recorded and can only be referenced using # their project id. import "class" : new ; import errors ; import modules ; import path ; import print ; import property-set ; import sequence ; # Loads the Jamfile at the given location. After loading, project global file # and Jamfiles needed by the requested one will be loaded recursively. If the # Jamfile at that location is loaded already, does nothing. Returns the project # module for the Jamfile. # rule load ( jamfile-location ) { if --debug-loading in [ modules.peek : ARGV ] { ECHO "Loading Jamfile at" '$(jamfile-location)' ; } local module-name = [ module-name $(jamfile-location) ] ; # If Jamfile is already loaded, don't try again. if ! $(module-name) in $(.jamfile-modules) { load-jamfile $(jamfile-location) : $(module-name) ; # We want to make sure that child project are loaded only after parent # projects. In particular, because parent projects define attributes # which are inherited by children, and we don't want children to be # loaded before parent has defined everything. # # While "build-project" and "use-project" can potentially refer to child # projects from parent projects, we don't immediately load child # projects when seeing those attributes. Instead, we record the minimal # information to be used only later. load-used-projects $(module-name) ; } return $(module-name) ; } rule load-used-projects ( module-name ) { local used = [ modules.peek $(module-name) : .used-projects ] ; local location = [ attribute $(module-name) location ] ; import project ; while $(used) { local id = $(used[1]) ; local where = $(used[2]) ; project.use $(id) : [ path.root [ path.make $(where) ] $(location) ] ; used = $(used[3-]) ; } } # Note the use of character groups, as opposed to listing 'Jamroot' and # 'jamroot'. With the latter, we would get duplicate matches on Windows and # would have to eliminate duplicates. JAMROOT ?= [ modules.peek : JAMROOT ] ; JAMROOT ?= project-root.jam [Jj]amroot [Jj]amroot.jam ; # Loads parent of Jamfile at 'location'. Issues an error if nothing is found. # rule load-parent ( location ) { local found = [ path.glob-in-parents $(location) : $(JAMROOT) $(JAMFILE) ] ; if ! $(found) { ECHO error: Could not find parent for project at '$(location)' ; EXIT error: Did not find Jamfile.jam or Jamroot.jam in any parent directory. ; } return [ load $(found[1]:D) ] ; } # Makes the specified 'module' act as if it were a regularly loaded Jamfile at # 'location'. Reports an error if a Jamfile has already been loaded for that # location. # rule act-as-jamfile ( module : location ) { if [ module-name $(location) ] in $(.jamfile-modules) { errors.error "Jamfile was already loaded for '$(location)'" ; } # Set up non-default mapping from location to module. .module.$(location) = $(module) ; # Add the location to the list of project locations so that we don't try to # reload the same Jamfile in the future. .jamfile-modules += [ module-name $(location) ] ; initialize $(module) : $(location) ; } # Returns the project module corresponding to the given project-id or plain # directory name. Returns nothing if such a project can not be found. # rule find ( name : current-location ) { local project-module ; # Try interpreting name as project id. if [ path.is-rooted $(name) ] { project-module = $($(name).jamfile-module) ; } if ! $(project-module) { local location = [ path.root [ path.make $(name) ] $(current-location) ] ; # If no project is registered for the given location, try to load it. # First see if we have a Jamfile. If not, then see if we might have a # project root willing to act as a Jamfile. In that case, project root # must be placed in the directory referred by id. project-module = [ module-name $(location) ] ; if ! $(project-module) in $(.jamfile-modules) { if [ path.glob $(location) : $(JAMROOT) $(JAMFILE) ] { project-module = [ load $(location) ] ; } else { project-module = ; } } } return $(project-module) ; } # Returns the name of the module corresponding to 'jamfile-location'. If no # module corresponds to that location yet, associates the default module name # with that location. # rule module-name ( jamfile-location ) { if ! $(.module.$(jamfile-location)) { # Root the path, so that locations are always unambiguous. Without this, # we can't decide if '../../exe/program1' and '.' are the same paths. jamfile-location = [ path.root $(jamfile-location) [ path.pwd ] ] ; .module.$(jamfile-location) = Jamfile<$(jamfile-location)> ; } return $(.module.$(jamfile-location)) ; } # Default patterns to search for the Jamfiles to use for build declarations. # JAMFILE = [ modules.peek : JAMFILE ] ; JAMFILE ?= [Bb]uild.jam [Jj]amfile.v2 [Jj]amfile [Jj]amfile.jam ; # Find the Jamfile at the given location. This returns the exact names of all # the Jamfiles in the given directory. The optional parent-root argument causes # this to search not the given directory but the ones above it up to the # directory given in it. # rule find-jamfile ( dir # The directory(s) to look for a Jamfile. parent-root ? # Optional flag indicating to search for the parent Jamfile. : no-errors ? ) { # Glob for all the possible Jamfiles according to the match pattern. # local jamfile-glob = ; if $(parent-root) { if ! $(.parent-jamfile.$(dir)) { .parent-jamfile.$(dir) = [ path.glob-in-parents $(dir) : $(JAMFILE) ] ; } jamfile-glob = $(.parent-jamfile.$(dir)) ; } else { if ! $(.jamfile.$(dir)) { .jamfile.$(dir) = [ path.glob $(dir) : $(JAMFILE) ] ; } jamfile-glob = $(.jamfile.$(dir)) ; } local jamfile-to-load = $(jamfile-glob) ; # Multiple Jamfiles found in the same place. Warn about this and ensure we # use only one of them. As a temporary convenience measure, if there is # Jamfile.v2 among found files, suppress the warning and use it. # if $(jamfile-to-load[2-]) { local v2-jamfiles = [ MATCH (.*[Jj]amfile\\.v2)|(.*[Bb]uild\\.jam) : $(jamfile-to-load) ] ; if $(v2-jamfiles) && ! $(v2-jamfiles[2]) { jamfile-to-load = $(v2-jamfiles) ; } else { local jamfile = [ path.basename $(jamfile-to-load[1]) ] ; ECHO "warning: Found multiple Jamfiles at '"$(dir)"'!" "Loading the first one: '$(jamfile)'." ; } jamfile-to-load = $(jamfile-to-load[1]) ; } # Could not find it, error. # if ! $(no-errors) && ! $(jamfile-to-load) { errors.error Unable to load Jamfile. : Could not find a Jamfile in directory '$(dir)'. : Attempted to find it with pattern '"$(JAMFILE:J=" ")"'. : Please consult the documentation at 'http://www.boost.org'. ; } return $(jamfile-to-load) ; } # Load a Jamfile at the given directory. Returns nothing. Will attempt to load # the file as indicated by the JAMFILE patterns. Effect of calling this rule # twice with the same 'dir' is undefined. # local rule load-jamfile ( dir # The directory of the project Jamfile. : jamfile-module ) { # See if the Jamfile is where it should be. # local jamfile-to-load = [ path.glob $(dir) : $(JAMROOT) ] ; if ! $(jamfile-to-load) { jamfile-to-load = [ find-jamfile $(dir) ] ; } if $(jamfile-to-load[2]) { errors.error "Multiple Jamfiles found at '$(dir)'" : "Filenames are: " $(jamfile-to-load:D=) ; } # Now load the Jamfile in it's own context. # The call to 'initialize' may load parent Jamfile, which might have # 'use-project' statement that causes a second attempt to load the # same project we're loading now. Checking inside .jamfile-modules # prevents that second attempt from messing up. if ! $(jamfile-module) in $(.jamfile-modules) { .jamfile-modules += $(jamfile-module) ; # Initialize the Jamfile module before loading. # initialize $(jamfile-module) : [ path.parent $(jamfile-to-load) ] : $(jamfile-to-load:BS) ; local saved-project = $(.current-project) ; mark-as-user $(jamfile-module) ; modules.load $(jamfile-module) : [ path.native $(jamfile-to-load) ] : . ; if [ MATCH ($(JAMROOT)) : $(jamfile-to-load:BS) ] { jamfile = [ find-jamfile $(dir) : no-errors ] ; if $(jamfile) { load-aux $(jamfile-module) : [ path.native $(jamfile) ] ; } } # Now do some checks. if $(.current-project) != $(saved-project) { errors.error "The value of the .current-project variable has magically" : "changed after loading a Jamfile. This means some of the targets" : "might be defined in the wrong project." : "after loading" $(jamfile-module) : "expected value" $(saved-project) : "actual value" $(.current-project) ; } if $(.global-build-dir) { local id = [ attribute $(jamfile-module) id ] ; local project-root = [ attribute $(jamfile-module) project-root ] ; local location = [ attribute $(jamfile-module) location ] ; if $(location) && $(project-root) = $(dir) { # This is Jamroot. if ! $(id) { ECHO "warning: the --build-dir option was specified" ; ECHO "warning: but Jamroot at '$(dir)'" ; ECHO "warning: specified no project id" ; ECHO "warning: the --build-dir option will be ignored" ; } } } } } rule mark-as-user ( module-name ) { if USER_MODULE in [ RULENAMES ] { USER_MODULE $(module-name) ; } } rule load-aux ( module-name : file ) { mark-as-user $(module-name) ; module $(module-name) { include $(2) ; local rules = [ RULENAMES $(1) ] ; IMPORT $(1) : $(rules) : $(1) : $(1).$(rules) ; } } .global-build-dir = [ MATCH --build-dir=(.*) : [ modules.peek : ARGV ] ] ; if $(.global-build-dir) { # If the option is specified several times, take the last value. .global-build-dir = [ path.make $(.global-build-dir[-1]) ] ; } # Initialize the module for a project. # rule initialize ( module-name # The name of the project module. : location ? # The location (directory) of the project to initialize. If # not specified, a standalone project will be initialized. : basename ? ) { if --debug-loading in [ modules.peek : ARGV ] { ECHO "Initializing project '$(module-name)'" ; } # TODO: need to consider if standalone projects can do anything but define # prebuilt targets. If so, we need to give it a more sensible "location", so # that source paths are correct. location ?= "" ; # Create the module for the Jamfile first. module $(module-name) { } $(module-name).attributes = [ new project-attributes $(location) $(module-name) ] ; local attributes = $($(module-name).attributes) ; if $(location) { $(attributes).set source-location : [ path.make $(location) ] : exact ; } else if ! $(module-name) in test-config site-config user-config project-config { # This is a standalone project with known location. Set source location # so that it can declare targets. This is intended so that you can put # a .jam file in your sources and use it via 'using'. Standard modules # (in 'tools' subdir) may not assume source dir is set. local s = [ modules.binding $(module-name) ] ; if ! $(s) { errors.error "Could not determine project location $(module-name)" ; } $(attributes).set source-location : $(s:D) : exact ; } $(attributes).set requirements : [ property-set.empty ] : exact ; $(attributes).set usage-requirements : [ property-set.empty ] : exact ; # Import rules common to all project modules from project-rules module, # defined at the end of this file. local rules = [ RULENAMES project-rules ] ; IMPORT project-rules : $(rules) : $(module-name) : $(rules) ; local jamroot ; local parent-module ; if $(module-name) = test-config { # No parent. } else if $(module-name) = site-config { parent-module = test-config ; } else if $(module-name) = user-config { parent-module = site-config ; } else if $(module-name) = project-config { parent-module = user-config ; } else { # We search for parent/project-root only if Jamfile was specified, i.e. # if the project is not standalone. if $(location) && ! [ MATCH ($(JAMROOT)) : $(basename) ] { parent-module = [ load-parent $(location) ] ; } else { # It's either jamroot or standalone project. If it's jamroot, # inherit from user-config. if $(location) { # If project-config module exist, inherit from it. if $(project-config.attributes) { parent-module = project-config ; } else { parent-module = user-config ; } jamroot = true ; } } } if $(parent-module) { inherit-attributes $(module-name) : $(parent-module) ; $(attributes).set parent-module : $(parent-module) : exact ; } if $(jamroot) { $(attributes).set project-root : $(location) : exact ; } local parent ; if $(parent-module) { parent = [ target $(parent-module) ] ; } if ! $(.target.$(module-name)) { .target.$(module-name) = [ new project-target $(module-name) : $(module-name) $(parent) : [ attribute $(module-name) requirements ] ] ; if --debug-loading in [ modules.peek : ARGV ] { ECHO "Assigned project target" $(.target.$(module-name)) "to '$(module-name)'" ; } } .current-project = [ target $(module-name) ] ; } # Make 'project-module' inherit attributes of project root and parent module. # rule inherit-attributes ( project-module : parent-module ) { local attributes = $($(project-module).attributes) ; local pattributes = [ attributes $(parent-module) ] ; # Parent module might be locationless configuration module. if [ modules.binding $(parent-module) ] { $(attributes).set parent : [ path.parent [ path.make [ modules.binding $(parent-module) ] ] ] ; } local v = [ $(pattributes).get project-root ] ; $(attributes).set project-root : $(v) : exact ; $(attributes).set default-build : [ $(pattributes).get default-build ] ; $(attributes).set requirements : [ $(pattributes).get requirements ] : exact ; $(attributes).set usage-requirements : [ $(pattributes).get usage-requirements ] : exact ; local parent-build-dir = [ $(pattributes).get build-dir ] ; if $(parent-build-dir) { # Have to compute relative path from parent dir to our dir. Convert both # paths to absolute, since we cannot find relative path from ".." to # ".". local location = [ attribute $(project-module) location ] ; local parent-location = [ attribute $(parent-module) location ] ; local pwd = [ path.pwd ] ; local parent-dir = [ path.root $(parent-location) $(pwd) ] ; local our-dir = [ path.root $(location) $(pwd) ] ; $(attributes).set build-dir : [ path.join $(parent-build-dir) [ path.relative $(our-dir) $(parent-dir) ] ] : exact ; } } # Associate the given id with the given project module. # rule register-id ( id : module ) { $(id).jamfile-module = $(module) ; } # Class keeping all the attributes of a project. # # The standard attributes are "id", "location", "project-root", "parent" # "requirements", "default-build", "source-location" and "projects-to-build". # class project-attributes { import property ; import property-set ; import errors ; import path ; import print ; import sequence ; import project ; rule __init__ ( location project-module ) { self.location = $(location) ; self.project-module = $(project-module) ; } # Set the named attribute from the specification given by the user. The # value actually set may be different. # rule set ( attribute : specification * : exact ? # Sets value from 'specification' without any processing. ) { if $(exact) { self.$(attribute) = $(specification) ; } else if $(attribute) = "requirements" { local result = [ property-set.refine-from-user-input $(self.requirements) : $(specification) : $(self.project-module) : $(self.location) ] ; if $(result[1]) = "@error" { errors.error Requirements for project at '$(self.location)' conflict with parent's. : Explanation: $(result[2-]) ; } else { self.requirements = $(result) ; } } else if $(attribute) = "usage-requirements" { local unconditional ; for local p in $(specification) { local split = [ property.split-conditional $(p) ] ; split ?= nothing $(p) ; unconditional += $(split[2]) ; } local non-free = [ property.remove free : $(unconditional) ] ; if $(non-free) { errors.error usage-requirements $(specification) have non-free properties $(non-free) ; } local t = [ property.translate-paths $(specification) : $(self.location) ] ; if $(self.usage-requirements) { self.usage-requirements = [ property-set.create [ $(self.usage-requirements).raw ] $(t) ] ; } else { self.usage-requirements = [ property-set.create $(t) ] ; } } else if $(attribute) = "default-build" { self.default-build = [ property.make $(specification) ] ; } else if $(attribute) = "source-location" { self.source-location = ; for local src-path in $(specification) { self.source-location += [ path.root [ path.make $(src-path) ] $(self.location) ] ; } } else if $(attribute) = "build-dir" { self.build-dir = [ path.root [ path.make $(specification) ] $(self.location) ] ; } else if $(attribute) = "id" { id = [ path.root $(specification) / ] ; project.register-id $(id) : $(self.project-module) ; self.id = $(id) ; } else if ! $(attribute) in "default-build" "location" "parent" "projects-to-build" "project-root" "source-location" { errors.error Invalid project attribute '$(attribute)' specified for project at '$(self.location)' ; } else { self.$(attribute) = $(specification) ; } } # Returns the value of the given attribute. # rule get ( attribute ) { return $(self.$(attribute)) ; } # Prints the project attributes. # rule print ( ) { local id = $(self.id) ; id ?= (none) ; local parent = $(self.parent) ; parent ?= (none) ; print.section "'"$(id)"'" ; print.list-start ; print.list-item "Parent project:" $(parent) ; print.list-item "Requirements:" [ $(self.requirements).raw ] ; print.list-item "Default build:" $(self.default-build) ; print.list-item "Source location:" $(self.source-location) ; print.list-item "Projects to build:" [ sequence.insertion-sort $(self.projects-to-build) ] ; print.list-end ; } } # Returns the project which is currently being loaded. # rule current ( ) { return $(.current-project) ; } # Temporarily changes the current project to 'project'. Should be followed by # 'pop-current'. # rule push-current ( project ) { .saved-current-project += $(.current-project) ; .current-project = $(project) ; } rule pop-current ( ) { .current-project = $(.saved-current-project[-1]) ; .saved-current-project = $(.saved-current-project[1--2]) ; } # Returns the project-attribute instance for the specified Jamfile module. # rule attributes ( project ) { return $($(project).attributes) ; } # Returns the value of the specified attribute in the specified Jamfile module. # rule attribute ( project attribute ) { return [ $($(project).attributes).get $(attribute) ] ; } # Returns the project target corresponding to the 'project-module'. # rule target ( project-module ) { if ! $(.target.$(project-module)) { .target.$(project-module) = [ new project-target $(project-module) : $(project-module) : [ attribute $(project-module) requirements ] ] ; } return $(.target.$(project-module)) ; } # Use/load a project. # rule use ( id : location ) { local saved-project = $(.current-project) ; local project-module = [ project.load $(location) ] ; local declared-id = [ project.attribute $(project-module) id ] ; if ! $(declared-id) || $(declared-id) != $(id) { # The project at 'location' either has no id or that id is not equal to # the 'id' parameter. if $($(id).jamfile-module) && ( $($(id).jamfile-module) != $(project-module) ) { errors.user-error Attempt to redeclare already existing project id '$(id)' location '$(location)' ; } $(id).jamfile-module = $(project-module) ; } .current-project = $(saved-project) ; } # Defines a Boost.Build extension project. Such extensions usually contain # library targets and features that can be used by many people. Even though # extensions are really projects, they can be initialized as a module would be # with the "using" (project.project-rules.using) mechanism. # rule extension ( id : options * : * ) { # The caller is a standalone module for the extension. local mod = [ CALLER_MODULE ] ; # We need to do the rest within the extension module. module $(mod) { import path ; # Find the root project. local root-project = [ project.current ] ; root-project = [ $(root-project).project-module ] ; while [ project.attribute $(root-project) parent-module ] && [ project.attribute $(root-project) parent-module ] != user-config { root-project = [ project.attribute $(root-project) parent-module ] ; } # Create the project data, and bring in the project rules into the # module. project.initialize $(__name__) : [ path.join [ project.attribute $(root-project) location ] ext $(1:L) ] ; # Create the project itself, i.e. the attributes. All extensions are # created in the "/ext" project space. project /ext/$(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; local attributes = [ project.attributes $(__name__) ] ; # Inherit from the root project of whomever is defining us. project.inherit-attributes $(__name__) : $(root-project) ; $(attributes).set parent-module : $(root-project) : exact ; } } rule glob-internal ( project : wildcards + : excludes * : rule-name ) { local location = [ $(project).get source-location ] ; local result ; local paths = [ path.$(rule-name) $(location) : [ sequence.transform path.make : $(wildcards) ] : [ sequence.transform path.make : $(excludes) ] ] ; if $(wildcards:D) || $(rule-name) != glob { # The paths we have found are relative to the current directory, but the # names specified in the sources list are assumed to be relative to the # source directory of the corresponding project. So, just make the names # absolute. for local p in $(paths) { # If the path is below source location, use relative path. # Otherwise, use full path just to avoid any ambiguities. local rel = [ path.relative $(p) $(location) : no-error ] ; if $(rel) = not-a-child { result += [ path.root $(p) [ path.pwd ] ] ; } else { result += $(rel) ; } } } else { # There were no wildcards in the directory path, so the files are all in # the source directory of the project. Just drop the directory, instead # of making paths absolute. result = $(paths:D="") ; } return $(result) ; } # This module defines rules common to all projects. # module project-rules { rule using ( toolset-module : * ) { import toolset ; import modules ; import project ; # Temporarily change the search path so the module referred to by # 'using' can be placed in the same directory as Jamfile. User will # expect the module to be found even though the directory is not in # BOOST_BUILD_PATH. local x = [ modules.peek : BOOST_BUILD_PATH ] ; local caller = [ CALLER_MODULE ] ; local caller-location = [ modules.binding $(caller) ] ; modules.poke : BOOST_BUILD_PATH : $(caller-location:D) $(x) ; toolset.using $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; modules.poke : BOOST_BUILD_PATH : $(x) ; # The above might have clobbered .current-project. Restore the correct # value. modules.poke project : .current-project : [ project.target $(caller) ] ; } import modules ; rule import ( * : * : * ) { modules.import project ; local caller = [ CALLER_MODULE ] ; local saved = [ modules.peek project : .current-project ] ; module $(caller) { modules.import $(1) : $(2) : $(3) ; } modules.poke project : .current-project : $(saved) ; } rule project ( id ? : options * : * ) { import errors ; import path ; import project ; local caller = [ CALLER_MODULE ] ; local attributes = [ project.attributes $(caller) ] ; if $(id) { $(attributes).set id : $(id) ; } local explicit-build-dir ; for n in 2 3 4 5 6 7 8 9 { local option = $($(n)) ; if $(option) { $(attributes).set $(option[1]) : $(option[2-]) ; } if $(option[1]) = "build-dir" { explicit-build-dir = [ path.make $(option[2-]) ] ; } } # If '--build-dir' is specified, change the build dir for the project. local global-build-dir = [ modules.peek project : .global-build-dir ] ; if $(global-build-dir) { local location = [ $(attributes).get location ] ; # Project with an empty location is a 'standalone' project such as # user-config or qt. It has no build dir. If we try to set build dir # for user-config, we shall then try to inherit it, with either # weird or wrong consequences. if $(location) && $(location) = [ $(attributes).get project-root ] { # Re-read the project id, since it might have been changed in # the project's attributes. id = [ $(attributes).get id ] ; # This is Jamroot. if $(id) { if $(explicit-build-dir) && [ path.is-rooted $(explicit-build-dir) ] { errors.user-error Absolute directory specified via 'build-dir' project attribute : Do not know how to combine that with the --build-dir option. ; } # Strip the leading slash from id. local rid = [ MATCH /(.*) : $(id) ] ; local p = [ path.join $(global-build-dir) $(rid) $(explicit-build-dir) ] ; $(attributes).set build-dir : $(p) : exact ; } } else { # Not Jamroot. if $(explicit-build-dir) { errors.user-error When --build-dir is specified, the 'build-dir' project : attribute is allowed only for top-level 'project' invocations ; } } } } # Declare and set a project global constant. Project global constants are # normal variables but should not be changed. They are applied to every # child Jamfile. # rule constant ( name # Variable name of the constant. : value + # Value of the constant. ) { import project ; local caller = [ CALLER_MODULE ] ; local p = [ project.target $(caller) ] ; $(p).add-constant $(name) : $(value) ; } # Declare and set a project global constant, whose value is a path. The path # is adjusted to be relative to the invocation directory. The given value # path is taken to be either absolute, or relative to this project root. # rule path-constant ( name # Variable name of the constant. : value + # Value of the constant. ) { import project ; local caller = [ CALLER_MODULE ] ; local p = [ project.target $(caller) ] ; $(p).add-constant $(name) : $(value) : path ; } rule use-project ( id : where ) { import modules ; # See comment in 'load' for explanation. local caller = [ CALLER_MODULE ] ; modules.poke $(caller) : .used-projects : [ modules.peek $(caller) : .used-projects ] $(id) $(where) ; } rule build-project ( dir ) { import project ; local caller = [ CALLER_MODULE ] ; local attributes = [ project.attributes $(caller) ] ; local now = [ $(attributes).get projects-to-build ] ; $(attributes).set projects-to-build : $(now) $(dir) ; } rule explicit ( target-names * ) { import project ; # If 'explicit' is used in a helper rule defined in Jamroot and # inherited by children, then most of the time we want 'explicit' to # operate on the Jamfile where the helper rule is invoked. local t = [ project.current ] ; for local n in $(target-names) { $(t).mark-target-as-explicit $(n) ; } } rule always ( target-names * ) { import project ; local t = [ project.current ] ; for local n in $(target-names) { $(t).mark-target-as-always $(n) ; } } rule glob ( wildcards + : excludes * ) { import project ; return [ project.glob-internal [ project.current ] : $(wildcards) : $(excludes) : glob ] ; } rule glob-tree ( wildcards + : excludes * ) { import project ; if $(wildcards:D) || $(excludes:D) { errors.user-error The patterns to 'glob-tree' may not include directory ; } return [ project.glob-internal [ project.current ] : $(wildcards) : $(excludes) : glob-tree ] ; } # Calculates conditional requirements for multiple requirements at once. # This is a shorthand to reduce duplication and to keep an inline # declarative syntax. For example: # # lib x : x.cpp : [ conditional gcc debug : # DEBUG_EXCEPTION DEBUG_TRACE ] ; # rule conditional ( condition + : requirements * ) { local condition = $(condition:J=,) ; if [ MATCH (:) : $(condition) ] { return $(condition)$(requirements) ; } else { return $(condition):$(requirements) ; } } rule option ( name : value ) { if $(__name__) != site-config && $(__name__) != user-config && $(__name__) != project-config { import errors ; errors.error "The 'option' rule may be used only in site-config or user-config" ; } import option ; option.set $(name) : $(value) ; } }