#  (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
    if $(<[2])
    {
        INCLUDES $(<[1]) : $(<[2-]) ;
        INCLUDES $(<[2-]) : $(<[1]) ;
    }
    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

    MakeLocate $(<) : $(LOCATE_TARGET) ;

    Clean clean : $(<) ;

    MODE on $(<) = $($(3)MODE) ;
    local link-function = Link-$(3) ;
    local ignored = [ $(link-function) $(<) : $(>) : $(3) ] ;
    Chmod $(<[1]) ;
    DEPENDS $(<) : $(>) ;
}

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_SET)PATH=$(RUN_PATH)
    $(SHELL_EXPORT)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 = NEEDLIBS ; # 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)) ;

        # set up dependencies if the target is a "top-level" target
        if ( $(s) in $(gDEPENDENCY_VARIABLES($(gCURRENT_TOOLSET))) ) && $(gTARGET_TYPE($(<)))
        {
            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) ] ;
    # VP: defaulted-properties have the form <feature>value and there's 1 value.
    # Hence, each element of defaulted-properties will be part of each
    # component of override-sets and will be a part of each property-set 
    # returned. By segregating them, the result is changed in only one
    # way: free properties does not show up in target path.
    local defaulted-free-properties = [ segregate-free-properties defaulted-properties ] ;


    # 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) 
                  $(defaulted-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 $(<:G) && [ 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 += $(>) ;
    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($(<)))
    {
        local 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 instructionsn
            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) ;
    }
}

# Used to build command files from a list of sources.
rule build-command-file ( command : sources * )
{
    DEPENDS $(command) : $(sources) ;
    # First empty the file
    command-file-clear $(command) : $(sources) ;
    # Then fill it up piecemeal
    command-file-dump $(command) : $(sources) ;
}

# command-file-clear: silently remove the target if it exists
if $(NT)
{
    # NT needs special handling because DEL always barks if the file isn't found
    actions quietly command-file-clear
    {
        IF EXIST "$(<)" $(RM) "$(<)"
    }
}
else
{
    actions quietly command-file-clear
    {
        $(RM) "$(<)"
    }
}

# command-file-dump: dump the source paths into the target
actions quietly piecemeal command-file-dump
{
    echo "$(>)" >> "$(<)"
}

# Clean up the temporary COMMAND-FILE used to build TARGETS.
rule remove-command-file ( targets + : command-file )
{
    TEMPORARY $(command-file) ;
    Clean clean : $(command-file) ; # Mark the file for removal via clean
}
actions quietly piecemeal together remove-command-file
{
    $(RM) $(>)
}

# build TARGETS from SOURCES using a command-file, where RULE-NAME is
# used to generate the build instructions from the command-file to
# TARGETS
rule with-command-file ( rule-name targets * : sources * )
{
    # create a command-file target and place it where the first target
    # will be built
    local command-file = $(<[2]:S=.CMD) ;
    LOCATE on $(command-file) = $(gLOCATE($(targets[1]))) ;
    build-command-file $(command-file) : $(sources) ;

    # Build the targets from the command-file instead of the sources
    DEPENDS $(targets) : $(command-file) ;
    local result = [ $(rule-name) $(targets) : $(command-file) ] ;

    # clean up afterwards
    remove-command-file $(targets) : $(command-file) ;
    return result ;
}
