2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 00:52:16 +00:00
Files
build/boost-base.jam
Dave Abrahams bdc732fbd6 regex, threads, and python will all build from the top level. If you build the 'test' target from the top level, it will run all regressions.
Jamfile:
  subincludes for thread, python libs, and status for regression tests

Jamrules:
  Use the new path-global rule to establish BOOST_ROOT correctly for all subprojects

libs/regex/build/Jamfile
  Take advantage of correct BOOST_ROOT setting

libs/python/build/Jamfile
  Search for python executable; don't try to build anything if it can't be found.
  don't build tests by default
  improved comments, organization, and naming.

status/Jamfile
  Fixed references to config test files
  Failed tests now leave their stdout results in <testname>.error instead of removing it
  No test targets are dependencies of 'all' anymore
  Added comments
  Reorganized

tools/build/Jambase
  Meant to check this in long ago.

tools/build/allyourbase.jam
  Fixed SHELL_EXPORT setting, added SHELL_SET
  removed 'test' from the dependencies of 'all'; tests no longer run by default.
  Fixed the direction of slashes for Windows when ALL_LOCATE_TARGET is used.
  Added path-global rule for declaring path variables which may be relative
  rule in-invocation-subdir returns true if the current subproject is the one
     from which Jam was invoked
  rule protect-subdir is now used to protect subproject variables
  rule tokens-to-simple-path converts path tokens to a simplified path.

tools/build/boost-base.jam
  Fixed bugs

tools/build/jam_src/makedebugjam.bat
  Fixed a bug which prevented a final debug build

tools/build/jam_src/search.c
  Fixed a bug of mine which caused LOCATE to be ignored (!).


[SVN r11348]
2001-10-06 18:19:15 +00:00

1359 lines
39 KiB
Plaintext

# (C) Copyright David Abrahams and Carlos Pinto Coelho 2001. Permission to
# copy, use, modify, sell and distribute this software is granted provided this
# copyright notice appears in all copies. This software is provided "as is"
# without express or implied warranty, and with no claim as to its suitability
# for any purpose.
#
# Jamrules file by David Abrahams (abrahams@mediaone.net) and Carlos Pinto
# Coelho (cfspc@altrabroadband.com).
# Notes on the design of the system:
#
# This system is designed to support building the "same" targets with multiple
# build tool suites (e.g. msvc, gcc,...) and build variants (e.g. debug,
# release...). Although it currently varies across two dimensions, it should
# trivially support extension to three or more, e.g. in case of cross-platform
# builds. The word "same" is written in quotes above because internally,
# separate targets are generated for each unique toolset/build-variant
# combination.
#
# Specifics of build tool suites are specified in files with names of the form
# "<name>-tools.jam", where <name> is the name used to identify the tool suite.
# Workarounds for Jam limitations:
#
# 1. Jam supports something like the setting of attributes on targets using the
# syntax:
# <name> on <target> = <expression>
#
# This facility is used to control build actions for individual targets
# (target-specific variables take precedence over global ones when build
# actions # are executed). An obvious approach would be to use target
# attributes to hold # properties of build tools (e.g. where to find the
# standard includes). # Unfortunately, although you can write target
# attributes, there is no way to # read them. Instead, we take advantage of
# two properties of Jam:
#
# a. A variable name can be formed by evaluating an expression. For example,
# the following rule, appends FOOBAR to its first argument to form the
# name of a new variable, which is given the value "baz"
#
# rule X { $(1)FOOBAR = baz ; }
#
# b. Any character is allowed in a variable name. So, although they are
# actually global variables, we can form names like <name>.c++-flags thus:
#
# C++FLAGS = $($(1).c++-flags) # Get the c++-flags "attribute" from $(1)
#
# 2. There is no way to call a rule based on the value of a variable
# other than by using a switch statement. Because that approach requires
# intrusive changes to rules when the system is extended, we have avoided
# it. Instead, we have taken advantage of two "features" of Jam:
#
# a. The name of a file to be included can be computed from an
# expression. For example, the following includes a file whose name is
# formed by concatenating $(1) and "-tools.jam":
#
# include $(1)-tools.jam
#
# b. A rule can be redefined any number of times. Its latest definition is
# the one invoked. For example, the following prints "#2".
#
# rule X { ECHO #1 ; }
# rule X { ECHO #2 ; }
# X ;
#
# Together, these facts allow us to select tool-suite-specific actions for
# building specific target types by repeatedly redefining the generalized
# build actions in the various <build-tools>-tools.jam files
if $(NT)
{
TOOLS ?= borland gcc metrowerks msvc ;
}
else
{
TOOLS ?= gcc ;
}
# detect-build-tools <tools-name> : <detection-command>
#
# Declares a pseudotarget for the specified build tools which is built by
# the given <detection-command>.
#
# Although not currently implemented, the plan is to make compilation of
# tool-specific targets dependent on this pseudotarget. That way, we will never
# even attempt to build targets for tools that don't exist.
rule detect-build-tools
{
detection-command on $(<) = $($(<).bin-directory)$(>) ;
}
rule library-file
{
LibraryFromObjects $(<) : [ Objects $(>) ] ;
}
rule executable-file
{
type-DEPENDS exe : $(<) ;
main-from-objects $(<) : [ Objects $(>) ] : EXE ;
}
rule dll-files
{
type-DEPENDS dll : $(<) ;
# Set up the import library dependency on Windows
local f ;
for f in $(<[2-])
{
INCLUDES $(<) : $(f) ;
INCLUDES $(f) : $(<) ;
}
main-from-objects $(<) : [ Objects $(>) ] : DLL ;
}
# main-from-objects exe-target : obj-target... : ("EXE"|"DLL")
#
# generate instructions to build the given "main" target from the given object
# files given in the 2nd parameter. The 3rd parameter should be EXE for an
# executable, or DLL for a shared library.
rule main-from-objects
{
# make compiled sources a dependency of target
DEPENDS $(<) : $(>) ;
MakeLocate $(<) : $(LOCATE_TARGET) ;
Clean clean : $(<) ;
MODE on $(<) = $($(3)MODE) ;
local link-function = Link-$(3) ;
local ignored = [ $(link-function) $(<) : $(>) : $(3) ] ;
Chmod $(<[1]) ;
}
rule Link-EXE
{
# N.B. By the time this rule is invoked, we had better have gRUN_PATH completely set.
RUN_PATH on $(<) = [ join $(gRUN_PATH($(<))) $(RUN_PATH) : $(SPLITPATH) ] ;
Link-action $(<) : $(>) : EXE ;
}
rule Link-DLL
{
if $(<[2])
{
MODE on $(<[2]) = $(IMPMODE) ;
Chmod $(<[2]) ;
}
gRUN_PATH($(<)) += $(gLOCATE($(<[1]))) ;
Link-action $(<) : $(>) : DLL ;
}
# store the shell's PATH again, just in case someone uses PATH.
# This also allows the user to customize the base path for running built
# products from the command-line
RUN_PATH ?= $(PATH) ;
# A simple action to run an executable target
actions Run
{
$(SHELL_EXPORT)PATH=$(RUN_PATH)
$(<)
}
# bubble variable-name
#
# Helper function for sort, below
# Removes the greatest element from $(variable-name) and returns it.
rule bubble #
{
local result = ;
local last = $($(<)[1]) ;
for x in $($(<)[2-])
{
if $(last) <= $(x)
{
result += $(last) ;
last = $(x) ;
}
else
{
result += $(x) ;
}
}
$(<) = $(result) ;
return $(last) ;
}
# sort args
#
# return args sorted in lexicographic order.
rule sort
{
local _all = $(<) ;
local _result = ;
local _count ;
for _count in $(<)
{
_result = [ bubble _all ] $(_result) ;
}
return $(_result) ;
}
# min args
#
# return the lexicographic minimum element of args
rule min
{
local result = ;
local x ;
for x in $(<)
{
if ! $(result) || ( $(x) < $(result) )
{
result = $(x) ;
}
}
return $(result) ;
}
# difference listB : listA
# returns the elements of B that are not in A
rule difference
{
local result = ;
local element ;
for element in $(<)
{
if ! ( $(element) in $(>) )
{
result += $(element) ;
}
}
return $(result) ;
}
# replace list : old-value new-value
#
# returns list with occurrences of old-value replaced by new-value
rule replace
{
local result = ;
local x ;
for x in $(<)
{
if $(x) = $(>[1])
{
result += $(>[2]) ;
}
else
{
result += $(x) ;
}
}
return $(result) ;
}
# select-ungristed list...
#
# Returns the elements of list that have no grist
rule select-ungristed
{
local result x ;
for x in $(<)
{
if ! $(x:G)
{
result += $(x) ;
}
}
return $(result) ;
}
# Split a qualified property into 3 elements.
#
# Grammar description of qualified-property :
# [[<toolset>]<variant>]property
#
# returns <toolset> <variant> property
# missing <toolset> or <variant> are treated as <*>
rule split-qualified-property
{
local grist1 = $(<:G) ;
local ungrist1 = $(<:G=) ;
local grist2 = $(ungrist1:G) ;
local ungrist2 = $(ungrist1:G=) ;
local grist3 = $(ungrist2:G) ;
local ungrist3 = $(ungrist2:G=) ;
if $(grist3)
{
return $(grist1) $(grist2) $(grist3)$(ungrist3) ;
}
else if $(grist2)
{
return <*> $(grist1) $(grist2)$(ungrist2) ;
}
else
{
return <*> <*> $(<) ;
}
}
rule unique # list
{
local result = ;
local f ;
for f in $(<)
{
if ! $(f) in $(result)
{
result += $(f) ;
}
}
return $(result) ;
}
# get-properties features : properties
#
# Given a list of gristed features and a list of properties, returns the
# properties matching the given features.
rule get-properties
{
local result = ;
local property ;
for property in $(>)
{
if $(property:G) in $(<)
{
result += $(property) ;
}
}
return $(result) ;
}
# get-values features : properties
#
# Given a list of gristed feature names and a list of properties, returns the
# value(s) of the given features.
rule get-values
{
local _properties = [ get-properties $(<) : $(>) ] ;
return $(_properties:G=) ;
}
# normalize-properties properties
#
# Normalizes a set of (possibly qualified) properties by prepending <*> as many
# times as neccessary to ensure that each property has at least 3 gristed elements.
rule normalize-properties
{
local property ;
local result ;
for property in $(<)
{
switch $(property)
{
case <*><*><*>* : result += $(property) ;
case <*><*>* : result += <*>$(property) ;
case <*>* : result += <*><*>$(property) ;
case * : result += <*><*><*>$(property) ;
}
}
return $(result) ;
}
# intersection set1 : set2
#
# Removes from set1 any items which don't appear in set2 and returns the result.
rule intersection
{
local result v ;
for v in $(<)
{
if $(v) in $(>)
{
result += $(v) ;
}
}
return $(result) ;
}
# subset sub : super
#
# Returns true iff sub is a subset of super, empty otherwise
rule is-subset
{
if [ intersection $(<) : $(>) ] = $(<)
{
return true ;
}
}
# distribute-feature <feature>value1[/value2...]
#
# Distribute the given feature across the slash-separated set of values, i.e.
# returns <feature>value1[ <feature>/value2...]
rule distribute-feature
{
local g = $(<:G) ;
local result = [ split-path $(<:G=) ] ;
return $(g)$(result) ;
}
# set-insert variable-name : value... ;
#
# Appends the given values to the list designated by variable-name if they are
# not already present.
rule set-insert
{
local v ;
for v in $(>)
{
if ! ( $(v) in $($(<)) )
{
$(<) += $(v) ;
}
}
}
# equal-sets set1 : set2
#
# Returns true iff set1 contains the same elements as set2.
# Not sensitive to the same element appearing multiple times
rule equal-sets
{
if ( ! [ difference $(<) : $(>) ] ) && ( ! [ difference $(>) : $(<) ] )
{
return true ;
}
}
# feature name : [values...]
#
# Declares a feature with the given name, and the given allowed values.
rule feature
{
if $(<) in $(gFEATURES)
{
EXIT feature $(<) : $(gFEATURE_VALUES(<$(<)>) redeclared as $(<) : $(>) ;
}
gFEATURES += <$(<)> ;
gUNGRISTED(<$(<)>) = $(<) ;
gFEATURE_VALUES(<$(<)>) = $(>) ;
}
rule free-feature
{
feature $(<) : $(>) ;
gFREE_FEATURES += <$(<)> ;
if $(>)
{
gSINGLE_VALUED_FREE_FEATURES += <$(<)> ;
}
}
rule path-feature
{
free-feature $(<) : $(>) ;
gPATH_FEATURES += <$(<)> ;
}
rule dependency-feature
{
path-feature $(<) : $(>) ;
gDEPENDENCY_FEATURES += <$(<)> ;
}
# feature-default <feature>...
#
# return the default properties corresponding to the given feature(s)
rule feature-default
{
local result f ;
for f in $(<)
{
result += $(f)$(gFEATURE_VALUES($(f))[1]) ;
}
return $(result) ;
}
# flags tools-name variable-name condition [: value(s)]
#
# Declare command-line settings for a given toolset.
# toolset: the name of the toolset
# variable-name: the name of a global variable which can be used to carry
# information to a command-line
# condition: One of the following:
# 1. zero or more property-sets of the form:
# <feature>value[/<feature>value...]
# 2. one or more <feature>[/<feature>...]
#
# This rule appends to the specified variable, depending on a target's build
# configuration and the form of condition.
#
# 1. if any specified property-set is a subset of the target's build properties or if
# condition is empty, the values specified in $(3) will be appended once to
# /variable-name/.
#
# 2. The value of each specified feature that participates in the target's
# build properaties is appended to /variable-name/.
#
# The variable will be set "on" the target so it may be used in its build actions.
rule flags
{
local toolset = $(<[1]) ;
local variable = $(<[2]) ;
local condition = $(<[3-]) ;
# record the names of all variables used so they can be set on targets
if ! ( $(variable) in $(gTARGET_VARIABLES) )
{
gTARGET_VARIABLES += $(variable) ;
$(variable) = ;
}
local found = ;
local x ;
for x in $(condition)
{
x = [ split-path $(x) ] ;
# Add each feature to the set of features relevant to the toolset
gRELEVANT_FEATURES($(toolset)) += $(x:G) ;
# is it a property set?
if $(x:G=)
{
# if this property_set is a subset of the current build-properties
if ( ! $(found) ) && [ is-subset $(x) : $(gBUILD_PROPERTIES) ]
{
found = true ;
$(variable) += $(>) ;
}
}
else
{
$(variable) += [ get-values $(x) : $(gBUILD_PROPERTIES) ] ;
if $(x:G) in $(gDEPENDENCY_FEATURES)
{
gDEPENDENCY_VARIABLES($(toolset)) += $(variable) ;
}
}
}
if ! $(condition)
{
$(variable) += $(>) ;
}
}
# include-tools toolset
#
# Unconditionally process the specification file for the given toolset. It is
# neccessary to do this for each target built with that toolset, since the
# toolset will invoke the flags rule to set global variables based on the build
# properties of the target.
rule include-tools
{
gCURRENT_TOOLSET = $(<) ;
# clear any lingering target variables that may have been declared
$(gTARGET_VARIABLES) = ;
gTARGET_VARIABLES = ; # start over from the beginning
gRELEVANT_FEATURES($(<)) = ; # clear relevant feature set
gDEPENDENCY_VARIABLES($(<)) = ;
include $(BOOST_BUILD_INSTALLATION)$(SLASH)$(<)-tools.jam ;
# Always maintain the list of relevant features as unique
gRELEVANT_FEATURES($(<)) = [ unique $(gRELEVANT_FEATURES($(<))) ] ;
gINCLUDED($(BOOST_BUILD_INSTALLATION)$(SLASH)$(<)-tools.jam) = true ;
}
# extends-toolset toolset
#
# Used in a toolset definition file; Declares that the toolset currently being
# defined is an extension of the given toolset.
rule extends-toolset
{
{
local gCURRENT_TOOLSET ; # protect this from being clobbered
include-tools $(<) ;
}
# Add the relevant features from the base toolset
gRELEVANT_FEATURES($(gCURRENT_TOOLSET)) += $(gRELEVANT_FEATURES($(<))) ;
gDEPENDENCY_VARIABLES($(gCURRENT_TOOLSET)) += $(gDEPENDENCY_VARIABLES($(<))) ;
}
# relevant-features toolset
#
# Returns the set of unique features relevant to the given toolset; includes the
# toolset description file as a side-effect if neccessary.
rule relevant-features # name
{
if ! $(gRELEVANT_FEATURES($(<)))
{
include-tools $(<) ;
}
return $(gRELEVANT_FEATURES($(<))) ;
}
# variant name : [<toolset>]<feature>value...
#
# Delcare a build variant, whose configuration is given by the given (optionally
# toolset-qualified) properties.
rule variant
{
local toolset ;
for toolset in $(TOOLS)
{
# We hijack select-properties to do our dirty work here.
# Because properties in a variant declaration are only qualified with
# toolset and not variant, we specify the toolset where
# select-properties expects a variant name. The first toolset parameter
# is neccessary to get the relevant-features correctly set. We supply
# the variant name as the target name, so that error messages will look
# coherent.
gBASE_PROPERTIES($(toolset),$(<))
= [ sort [ select-properties $(toolset) $(toolset) $(<) : $(>) ] ] ;
}
}
# select-properties toolset variant target-name : qualified-properties...
#
# Returns
rule select-properties
{
local relevant_features = [ relevant-features $(<[1]) ] ;
local normalized = [ normalize-properties $(>) ] ;
# Classify properties by the specificity of their qualification.
# First, grab those that apply to this toolset, or all toolsets
local this_toolset = [ get-values <$(<[1])> : $(normalized) ] ;
local all_toolsets = [ get-values <*> : $(normalized) ] ;
local 0-stars = [ get-values <$(<[2])> : $(this_toolset) ] ;
local 1-stars = [ get-values <*> : $(this_toolset) ] [ get-values <$(<[2])> : $(all_toolsets) ] ;
local 2-stars = [ get-values <*> : $(all_toolsets) ] ;
# Select feature names from the features relevant to the toolset.
local features = [ intersection $(relevant_features)
: $(0-stars:G) $(1-stars:G) $(2-stars:G) ] ;
local result f ;
for f in $(features)
{
local is_free = [ intersection $(f) : $(gFREE_FEATURES) ] ;
# Go through the $(n-stars) lists from most- to least- specific;
# collect the best set of values of a simple feature, and /all/
# values of a free feature.
local r n ;
for n in 0 1 2
{
if ! $(r) || $(is_free)
{
r += [ get-values $(f) : $($(n)-stars) ] ;
}
}
r = [ unique $(r) ] ;
if $(r[2]) && ! $(is_free) # Check for conflicting simple-feature requests
{
EXIT "Error: Ambiguous properties requested for"
$(<[3]) <$(<[1])><$(<[2])> ":" $(f)$(r) ;
}
result += $(f)$(r) ;
}
return $(result) ;
}
# get toolset features
include $(BOOST_BUILD_INSTALLATION)$(SLASH)features.jam ;
# ungrist-properties properties...
#
# Transforms a list of properties of the form:
# <feature1>value1 [<feature2>value2... ]
# into a list of the form:
# feature1-value1 feature2-value2
# suitable for use as directory path elements
#
rule ungrist-properties
{
local property ;
local result = ;
for property in $(<)
{
result += $(gUNGRISTED($(property:G)))-$(property:G=) ;
}
return $(result) ;
}
# set-target-variables target
#
# attach global build tool settings to the given file-target, so that they can be
# used in build actions.
rule set-target-variables # file-target
{
local s ;
for s in $(gTARGET_VARIABLES)
{
$(s) on $(<) = $($(s)) ;
if $(s) in $(gDEPENDENCY_VARIABLES($(gCURRENT_TOOLSET)))
{
DEPENDS $(<) : $($(s)) ;
}
}
}
# fixup-path-properties properties...
#
# For path properties, add a relative path prefix to the value as neccessary to
# locate the path relative to the current subproject.
rule fixup-path-properties
{
local path-properties = [ get-properties $(gPATH_FEATURES) : $(<) ] ;
local non-path = [ difference $(<) : $(path-properties) ] ;
if $(RELATIVE_SUBDIR)
{
path-properties = [ root-paths $(path-properties) : $(RELATIVE_SUBDIR) ] ;
}
return $(path-properties) $(non-path) ;
}
# report-any-incompatible-properties requirements... : build-request... : target-name
#
# If any element of requirements has the same grist but a different ungristed
# part as any element of build-request, exits with an error report about target-name
rule report-any-incompatible-properties
{
local all-properties = [ unique $(<) $(>) ] ;
local all-features = $(all-properties:G) ;
local unique-features = [ unique $(all-features) ] ;
if $(all-features) != $(unique-features)
{
EXIT "Error:" "$(3):" "target requirements conflict for requested build {" $(<) $(>) "}" ;
}
}
if report-any-incompatible-properties in $(TEST)
{
report-any-incompatible-properties <foo>bar <baz>mumble : <b>c <foo>bar : my-target ;
report-any-incompatible-properties <foo>bat <baz>mumble <baz>buz <d>f : <b>c <foo>bar : my-target ;
}
# multiply-property-sets [<feature>value1[/value2...] ]...
#
# Expands a set of (possibly multi-valued) properties into all the combinations
# that include every feature in the set. Each combination is given as a path,
# with the slash separating the properties, sorted in feature order.
rule multiply-property-sets
{
local result p ;
for p in [ sort $(<) ]
{
# expand any multi-valued simple features from the default build
local multiple = [ distribute-feature $(p) ] ;
# concatenation produces a product, so the tree branches for each
# multi-valued simple feature.
result = $(result)/$(multiple) ;
result ?= $(multiple) ; # this trick allows us to get started
}
return $(result) ;
}
# make-path-property-sets base-path : common-properties : property-sets
#
# Returns a list of paths where the initial ungristed part of each element is a
# relative path to a subvariant directory from a target's build root and the
# rest of the element is a slash-separated property set describing the
# properties of the target to be built.
#
# Each path returned is base-path extended by one of the ungristed property-sets
# (or just the base-path if no property-sets are supplied). Each property set
# returned is formed by extending common-properties with one of the property-sets.
#
# For example,
#
# make-path-property-sets gcc/release : <p1>v1 : <p2>v2/<p3>v3
#
# returns this single-element list:
#
# gcc/release/p2-v2/p3-v3/<p1>v1/<p2>v2/<p3>v3
# |<-- subvariant path -->|<-- property-set -->|
rule make-path-property-sets
{
local result ;
local s ;
for s in $(3)
{
result += [ join
$(<) [ ungrist-properties [ split-path $(s) ] ] # directory components
$(>) $(s) : $(SLASH) ] ; # common properties + property set
}
# if there were no overrides, just add the base variant and properties
if ! $(result)
{
result = [ join $(<) $(>) : $(SLASH) ] ;
}
return $(result) ;
}
# segregate-overrides override-var base-var
#
# removes elements of $(base-var) from $(override-var), and removes elements
# whose grist is in $(override-var:G) from $(base-var).
rule segregate-overrides
{
$(<) = [ difference $($(<)) : $($(>)) ] ;
# Which features, and thus properties, of the base variant are we keeping?
local kept-features = [ difference $($(>):G) : $($(<):G) ] ;
$(>) = [ get-properties $(kept-features) : $($(>)) ] ;
}
# report-free-property-conflicts free-property... : target
#
# If any single-valued free-feature appears more than once in free-property...,
# exit with an appropriate error message.
rule report-free-property-conflicts
{
local p = [ get-properties $(gSINGLE_VALUED_FREE_FEATURES) $(<) ] ;
local f = [ unique $(p:G) ] ;
if $(p:G) != $(f)
{
EXIT $(>): multiple values for single-valued free feature(s)
[ difference $(p:G) $(f) ] requested ;
}
}
# expand-build-request
# toolset variant target-name : requirements : build-request
#
# Returns a list of path-property-sets (see make-path-property-sets above) for
# all build configurations based on the given toolset, requirements, and
# build-request. Target-name is just used for error reporting.
rule expand-build-request
{
local toolset = $(<[1]) ;
local variant = $(<[2]) ;
# grab the requirements and BUILD-request relevant to this toolset and variant
local requirements = [ select-properties $(toolset) $(variant) : $(>) ] ;
local build-request = [ select-properties $(toolset) $(variant) : $(3) ] ;
# Separate the free features (e.g. <define>, <undef>, <include>) from the others
local free-properties = [ segregate-free-properties requirements build-request ] ;
# Check for conflicts
report-free-property-conflicts $(free-properties) : $(<[3]) ;
report-any-incompatible-properties $(requirements) : $(build-request) : $(<[3]) ;
# Get the base variant for the toolset. Includes free features
local base-properties = $(gBASE_PROPERTIES($(toolset),$(variant))) ;
# Which properties will override settings in the base variant?
local override-properties = [ unique $(requirements) $(build-request) ] ;
segregate-overrides override-properties : base-properties ;
# Which features will pick up a default value because they are not in
# the base variant or in the overrides?
local relevant_features = [ relevant-features $(toolset) ] ;
local defaulted-features = [ difference $(relevant_features)
: $(override-properties:G) $(base-properties:G) ] ;
local defaulted-properties = [ feature-default $(defaulted-features) ] ;
# form override property sets of the form (property1[/property2...] )+,
# sorted in feature order. These represent the properties of subvariants
# that differ from the base variant
local override-sets
= [ multiply-property-sets $(override-properties) $(defaulted-properties) ] ;
# return path-property-sets corresponding to each (sub)variant build
# described.
return [ make-path-property-sets $(toolset)$(SLASH)$(variant)
: [ fixup-path-properties $(base-properties) $(free-properties) ]
: $(override-sets) ] ;
}
# split-path-at-grist path
#
# Breaks path at each $(SLASH) that is followed by grist. This can be used to
# break apart property sets, particularly where the <include> feature is used,
# since its value is typically a path.
rule split-path-at-grist
{
local full-split = [ split-path $(<) ] ;
local last ;
local result x ;
for x in $(full-split)
{
if $(x:G)
{
result += $(last) ;
last = $(x) ;
}
else
{
last = $(last)$(SLASH)$(x) ;
last ?= $(x) ;
}
}
return $(result) $(last) ;
}
# declare-local-target name : sources : requirements : local-BUILD : target-type
#
# declares a subproject-local target of the given name and target-type. This is
# all top-level rules which declare targets should eventually go through here.
rule declare-local-target
{
# We add SOURCE_GRIST the base target name here because we're referring the
# abstract target which generates all of the actual builds. We need a way to
# distinguish targets of the same name from different subprojects.
local target-id = [ FGristFiles $(<) ] ;
if ! $(5)
{
EXIT No target type given for "$(<)" ;
}
if ! $(gTARGET_TYPE($(target-id)))
{
gTARGET_TYPE($(target-id)) = $(5) ;
# Add the specified requirements to any requirements given by the target
# type, and the corresponding <target-type> property.
gTARGET_REQUIREMENTS($(target-id))
= $(3) $(gTARGET_TYPE_REQUIREMENTS($(5))) <target-type>$(5) ;
gTARGET_LIBS($(target-id)) = [ get-values <lib> : $(>) ] ;
gTARGET_SOURCES($(target-id))
= [ FGristFiles
[ difference $(>:G=) : $(gTARGET_LIBS($(target-id))) ] ] ;
}
else if $(gTARGET_TYPE($(target-id))) != $(5)
{
EXIT conflicting target types for "$(<)":
"$(gTARGET_TYPE($(target-id)))" "$(5)" ;
}
# Just gather information if we are including a library's Jamfile for a
# dependent target. Don't generate build instructions here.
if ! $(gIN_LIB_INCLUDE)
{
main-target $(target-id) : $(4) ;
}
return $(gTARGET_FILES($(target-id))) ;
}
# directory-of files...
#
# Returns a list of the directories containing each element of files
rule directory-of
{
local result d ;
for d in $(<:D)
{
if $(d) = ""
{
result += $(DOT) ;
}
else
{
result += $(d) ;
}
}
return $(result) ;
}
# top-relative-tokens path
#
# Returns a list of path elements which form the relative path from TOP to path,
# which is expected to be given relative to the current subproject.
rule top-relative-tokens
{
return [ simplify-path-tokens $(SUBDIR_TOKENS) [ split-path $(<) ] ] ;
}
# dependent-include target-path...
#
# For each target-path, ensure that the appropriate Jamfile has been
# included. Used when a target declares its dependency on another target.
rule dependent-include
{
local target ;
for target in $(<)
{
# compute the path to the Jamfile containing target. This path must be
# relative to the directory of Jam's invocation in order for the include
# rule to find it.
local jamfile-path
= [ tokens-to-simple-path
$(RELATIVE_SUBDIR_TOKENS) [ directory-of $(target) ] $(JAMFILE) ] ;
if ! $(gINCLUDED($(jamfile-path)))
{
# protect variables from being permanently set by SubDir invocations
# in included files.
local [ protect-subdir ] ;
# this stack allows us to avoid making dependee libraries part of
# the "type" targets, e.g. all, exe, obj. See rule type-DEPENDS.
local gIN_LIB_INCLUDE = 1 ;
include $(jamfile-path) ;
gINCLUDED($(jamfile-path)) = true ;
}
}
}
# segregate-free-properties variable1 variable2...
#
# returns the and removes the unique list of free properties from
# $(variable1) $(variable2)...
rule segregate-free-properties
{
local free-properties = [ unique [ get-properties $(gFREE_FEATURES) : $($(<)) ] ] ;
local v ;
for v in $(<)
{
$(v) = [ difference $($(v)) : $(free-properties) ] ;
}
return $(free-properties) ;
}
# is-link-compatible feature : value1 : value2
#
# return non-empty iff a library built with <feature>value1 can be linked into a
# target with <feature>value2, empty otherwise
rule is-link-compatible
{
return [ intersection
$(<) $(>:G=$(<)) $(>:G=$(<))$(SLASH)$(3:G=$(<)) : $(gLINK_COMPATIBLE) ] ;
}
# find-compatible-subvariant main-target : toolset variant : dependent-simple-properties
rule find-compatible-subvariant
{
local requirements = [ select-properties $(>)
: $(gTARGET_REQUIREMENTS($(<))) ] ;
local free-properties = [ segregate-free-properties requirements ] ;
# begin with the properties overridden by the target requirements
local override-properties = $(requirements) ;
# add properties from build-request, checking for compatibility
local p ;
for p in [ difference $(3) : $(requirements) ]
{
local f = $(p:G) ;
if $(f) in $(requirements:G)
{
local required-value
= [ get-values $(f) : $(override-properties) ] ;
if ! [ is-link-compatible $(f) : $(required-value) : $(p:G=) ]
{
EXIT $(<): required property $(f)$(required-value)
incompatible with requested $(p) ;
}
}
else
{
override-properties += $(p) ;
}
}
local base-properties = $(gBASE_PROPERTIES($(>[1]),$(>[2]))) ;
segregate-overrides override-properties : base-properties ;
# build the path identifying the subvariant
local subvariant-id
= [ join $(>) # toolset variant
[ ungrist-properties [ sort $(override-properties) ] ]
: $(SLASH) ] ;
return $(subvariant-id)
[ fixup-path-properties $(base-properties) $(free-properties) ]
$(override-properties) ;
}
# link-libraries libs... : toolset variant : dependent-simple-properties
#
# For each target specified in libs, generate build instructions
# for a subvariant that can be linked with a dependent target with
# dependent-properties, returning a list of the linkable targets.
rule link-libraries
{
local lib-path result ;
for lib-path in $(<)
{
local new-subdir = TOP [ top-relative-tokens [ directory-of $(lib-path) ] ] ;
# protect global variables from being permanently set by SubDir
local [ protect-subdir ] ;
# Enter the dependee subproject
SubDir $(new-subdir) ;
local lib-target = [ FGristFiles $(lib-path:D=) ] ;
local lib-subvariant = [ find-compatible-subvariant $(lib-target) : $(>) : $(3) ] ;
# Generate build instructions for the library target
local lib-files
= [ subvariant-target $(lib-target) : $(lib-subvariant) : $(>) ] ;
# Add the name of the linkable product to the result.
local type = $(gTARGET_TYPE($(lib-target))) ;
local index = $(gLINKABLE_PRODUCT_INDEX($(type))) ;
index ?= 1 ;
result += $(lib-files[$(index)]) ;
}
return $(result) ;
}
# Which configuration(s) to build if nothing is explicitly specified
DEFAULT_BUILD ?= debug ;
# get-BUILD [target-default-build]
#
# pick the first of ($(BUILD), $(>), $(DEFAULT_BUILD)) which is set. If it
# contains no variants, add variants from $(DEFAULT_BUILD).
rule get-BUILD
{
local build = $(BUILD) ;
build ?= $(<) ;
build ?= $(DEFAULT_BUILD) ;
local variants = [ select-ungristed $(build) ] ;
if ! $(variants)
{
build += [ select-ungristed $(DEFAULT_BUILD) ] ;
}
return $(build) ;
}
BIN_DIRECTORY ?= bin ;
# declare-fake-targets abstract-target : target-file
#
#
rule declare-fake-targets
{
# make a fake target so that it can be built without knowing the suffix
# Since executables under *NIX have no suffix, we'd better check
if $(>) != $(<)
{
DEPENDS $(<) : $(>) ;
NOTFILE $(<) ;
}
# The following checks that we're in the subdirectory of Jam's invocation
# so that we can arrange for ungristed target names to be built from the
# command-line.
if [ in-invocation-subdir ]
{
DEPENDS $(<:G=) : $(<) ; # allows $(<:G=) to be used to build all variants
NOTFILE $(<:G=) ;
}
}
# declare-target-type TYPE : [[<compiler>]<variant>]<feature>value...
rule declare-target-type
{
gTARGET_TYPE_REQUIREMENTS($(<)) = $(>) ;
}
declare-target-type DLL : <shared-linkable>true ;
if $(NT)
{
gIMPORT_SUFFIX(DLL) = .lib ;
gIMPORT_SUFFIX(LIB) = .lib ;
gEXPORT_SUFFIX(DLL) = .lib ;
}
else
{
gIMPORT_SUFFIX(DLL) = .so ;
gIMPORT_SUFFIX(LIB) = .a ;
}
# depend-on-libraries target-files : library-targets
rule depend-on-libraries
{
NEEDLIBS on $(<) += $(>) ;
DEPENDS $(<) : $(>) ;
# To run these targets, we need everything needed to run the libraries
gRUN_PATH($(<)) += $(gRUN_PATH($(>))) ;
}
# subvariant-target target : subvariant-id build-properties : toolset variant
#
# Given target, a main target name gristed with $(SOURCE_GRIST), generate build
# instructions for a subvariant target using the given toolset, variant, etc.
#
# RETURNS: the a list of target names for the files built by the subvariant. If
# the target is a library, the first filename is the one that should be linked
# into a dependent target.
rule subvariant-target
{
# SOURCE_GRIST identifies the subproject directory; TARGET_GRIST will identify
# the target and subvariant, since unique versions of files will be built for
# that combination.
local TARGET_GRIST = [ join-path $(SOURCE_GRIST) $(<:G=) $(>[1]) ] ;
local subvariant = $(<:G=$(TARGET_GRIST)) ;
# Do nothing if build instructions and dependencies for this target have
# already been generated.
if ! $(TARGET_GRIST) in $(gDECLARED_TARGETS)
{
gDECLARED_TARGETS += $(TARGET_GRIST) ;
local target-type = $(gTARGET_TYPE($(<))) ;
# LOCATE_TARGET affects where built targets are generated. We move it
# relative to the default location based on the subvariant
local LOCATE_TARGET
= [ join-path $(LOCATE_TARGET) $(BIN_DIRECTORY) $(<:G=) $(>[1]) ] ;
local target-files = [ FAppendSuffix $(subvariant) : $(SUF$(target-type)) ] ;
gTARGET_FILES($(subvariant)) = $(target-files) ;
gTARGET_FILES($(<)) += $(target-files) ;
# Remember the path from the build root to the subvariant directory
gSUBVARIANT_PATH($(subvariant)) = $(>[1]) ;
declare-fake-targets $(<) : $(target-files) ;
# set up gBUILD_PROPERTIES for include-tools (below)
local gBUILD_PROPERTIES = $(>[2-]) ;
# Include the toolset specification. This will set up the global flags
# variables in a way appropriate to this build.
include-tools $(3[1]) ;
# headers should be identified specific to the target, since search paths
# may differ for different subvariants. The same header name or relative
# path may refer to different files.
local HDRGRIST = [ join $(SOURCE_GRIST) $(HDRS) $(STDHDRS) : "#" ] ;
# transfer target variables to the target file.
set-target-variables $(target-files) ;
if $(gTARGET_LIBS($(<)))
{
# Protect target variables against modification while lib dependencies
# are built. They will be made empty here, and restored when this scope exits
local $(gTARGET_VARIABLES) ;
# extract the simple properties from dependent-properties
local simple-properties = $(gBUILD_PROPERTIES) ;
segregate-free-properties simple-properties ;
# generate library build instructions
local libs = [ link-libraries $(gTARGET_LIBS($(<))) : $(3) : $(simple-properties) ] ;
depend-on-libraries $(target-files) : $(libs) ;
}
# dispatch to the appropriate declaration function. Here we are using an
# FTJam-only feature (thanks, David Turner!)
local ignored = [ $(gGENERATOR_FUNCTION($(target-type))) $(target-files)
: $(gTARGET_SOURCES($(<))) ] ;
$(gTARGET_VARIABLES) = ; # Be sure that we don't mask bugs with lingering target variables
}
return $(gTARGET_FILES($(subvariant))) ;
}
# main-target target : local-build
#
# Generates requested subvariant build instructions for the given main target
rule main-target
{
local BUILD = [ get-BUILD $(>) ] ;
local variants = [ select-ungristed $(BUILD) ] ;
local build-request = [ difference $(BUILD) : $(variants) ] ;
# include each jamfile describing a dependee target.
dependent-include $(gTARGET_LIBS($(<))) ;
local toolset ;
for toolset in $(TOOLS)
{
local v ;
for v in $(variants)
{
local expanded
= [ expand-build-request $(toolset) $(v) $(<)
: $(gTARGET_REQUIREMENTS($(<))) : $(build-request) ] ;
local x ;
for x in $(expanded)
{
subvariant-target $(<)
: [ split-path-at-grist $(x) ] : $(toolset) $(v) ;
}
}
}
}
gGENERATOR_FUNCTION(EXE) = executable-file ;
# exe target : sources : requirements : local-build
#
# Declare an executable target.
rule exe
{
declare-local-target $(<) : $(2) : $(3) : $(4) : EXE ;
}
gGENERATOR_FUNCTION(DLL) = dll-files ;
# dll target : sources : requirements : local-build
#
# Declare a shared library target.
rule dll
{
declare-local-target $(<) : $(2) : $(3) : $(4) : DLL ;
}
gGENERATOR_FUNCTION(LIB) = library-file ;
# lib target : sources : requirements : local-build
#
# Declare a statically-linked library target.
rule lib
{
declare-local-target $(<) : $(2) : $(3) : $(4) : LIB ;
}
# unit-test target : sources : requirements : local-build
#
# Declare an executable target, to be run by tests.
rule unit-test
{
local files = [ exe $(<) : $(2) : $(3) : $(4) : EXE ] ;
type-DEPENDS test : $(files) ;
local file ;
for file in $(files)
{
Run $(file) ;
}
}