Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for an old version of boost. Click here for the latest Boost documentation.

tools/build/v2/build/targets.jam

#  Copyright (C) Vladimir Prus 2002. 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.


#   Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
#
#   Abstract targets are represented by classes derived from 'abstract-target' class. 
#   The first abstract target is 'project-target', which is created for each
#   Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
#   (see project.jam). 
#
#   Project targets keep a list of 'main-target' instances.
#   A main target is what the user explicitly defines in a Jamfile. It is
#   possible to have several definitions for a main target, for example to have
#   different lists of sources for different platforms. So, main targets
#   keep a list of alternatives.
#
#   Each alternative is an instance of 'abstract-target'. When a main target
#   subvariant is defined by some rule, that rule will decide what class to
#   use, create an instance of that class and add it to the list of alternatives
#   for the main target.
#
#   Rules supplied by the build system will use only targets derived
#   from 'basic-target' class, which will provide some default behaviour.
#   There will be two classes derived from it, 'make-target', created by the
#   'make' rule, and 'typed-target', created by rules such as 'exe' and 'dll'.

#
#                         +------------------------+
#                         |abstract-target         |
#                         +========================+
#                         |name                    |
#                         |project                 |                                   
#                         |                        |                                   
#                         |generate(properties) = 0|                                   
#                         +-----------+------------+                                   
#                                     |                                                
#                                     ^                                                
#                                    / \                                               
#                                   +-+-+                                              
#                                     |                                                
#                                     |                                                
#            +------------------------+------+------------------------------+          
#            |                               |                              |          
#            |                               |                              |          
# +----------+-----------+            +------+------+                +------+-------+  
# | project-target       |            | main-target |                | basic-target |  
# +======================+ 1        * +=============+  alternatives  +==============+  
# | generate(properties) |o-----------+ generate    |<>------------->| generate     |  
# | main-target          |            +-------------+                | construct = 0|
# +----------------------+                                           +--------------+  
#                                                                           |          
#                                                                           ^          
#                                                                          / \         
#                                                                         +-+-+        
#                                                                           |          
#                                                                           |          
#                 ...--+----------------+------------------+----------------+---+      
#                      |                |                  |                    |      
#                      |                |                  |                    |      
#               ... ---+-----+   +------+-------+   +------+------+    +--------+-----+
#                            |   | typed-target |   | make-target |    | stage-target |
#                            .   +==============+   +=============+    +==============+
#                            .   | construct    |   | construct   |    | construct    |
#                                +--------------+   +-------------+    +--------------+

import "class" : new ;
import sequence ;
import regex ;
import property ;
import errors ;
import common ;
import property-set ;
import project ;
import feature ;
import virtual-target ;
import path ;
import set ;

# Base class for all abstract targets.
class abstract-target 
{
    import project ;    
    
    rule __init__ ( name      # name of the target in Jamfile
        : project # the project module where the target is declared
    )
    {        
        # Note: it might seem that we don't need either name or project at all.
        # However, there are places where we really need it. One example is error
        # messages which should name problematic targets. Another is setting correct
        # paths for sources and generated files.
        
        self.name = $(name) ;
        self.project = $(project) ;
    }    
    
    # Returns the name of this target.
    rule name ( )
    {
        return $(self.name) ;
    }
    
    # Returns the project for this target.
    rule project ( )
    {
        return $(self.project) ;
    }
        
    # Returns a user-readable name for this target.
    rule full-name ( )
    {
        local location = [ project.attribute $(self.project) location ] ;
        return $(location)/$(self.name) ;
    }
        
    # Takes properties in split form ("<feature1>foo <feature2>bar").
    # Generates virtual targets for this abstract target, using the specified properties,
    # unless a different value of some feature is required by the target. The properties
    # on returned virtual targets should be link-compatible with the requested ones.
    #
    # On success, returns: 
    # - a property-set with the usage requirements to be applied to dependents
    # - a list of produced virtual targets, which may be empty.
    #
    # If 'properties' are empty, performs default build of this target, in a way specific
    # to derived class.
    rule generate ( property-set )
    {
        errors.error "method should be defined in derived classes" ;
    }
    
    rule rename ( new-name )
    {
        self.name = $(new-name) ;
    }        
}

#  Project target class (derived from 'abstract-target')
#
#  This class these responsibilities:
#  - maintaining a list of main target in this project and
#    building it
#
#  Main targets are constructed in two stages:
#  - When Jamfile is read, a number of calls to 'add-alternative' is made.
#    At that time, alternatives can also be renamed to account for inline
#    targets.
#  - The first time 'main-target' or 'has-main-target' rule is called,
#    all alternatives are enumerated an main targets are created.
class project-target : abstract-target 
{
    import project targets ;
    import path ;
    import print ;
    import property-set ;
    import set : difference : set.difference ;
    import sequence ;
    import "class" : new ;
        
    rule __init__ ( name : project : requirements * : default-build * )
    {    
        abstract-target.__init__ $(name) : $(project) ;
        
        self.requirements = $(requirements) ;
        self.default-build = $(default-build) ;
    }
    
    # Generates all possible targets contained in this project.
    rule generate ( property-set * )
    {
        local usage-requirements = [ property-set.empty ] ;
        local targets ;
        
        for local t in [ targets-to-build ] 
        {
            local g = [ $(t).generate $(property-set) ] ;
            usage-requirements = [ $(usage-requirements).add $(g[1]) ] ;
            targets += $(g[2-]) ;
        }
        return $(usage-requirements) [ sequence.unique $(targets) ] ;        
    }
        
    # Computes and returns a list of abstract-target instances which
    # must be built when this project is built.
    rule targets-to-build ( )
    {
        local result ;
        
        if ! $(self.built-main-targets)
        {
            build-main-targets ;
        }
        
        # Collect all main targets here, except for "explicit" ones.
        for local t in $(self.main-targets)
        {
            if ! [ $(t).name ] in $(self.explicit-targets)
            {                    
                result += $(t) ;
            }
        }

        # Collect all projects referenced via "projects-to-build" attribute.
        local self-location = [ project.attribute $(self.project) location ] ;
        for local pn in [ project.attribute $(self.project) projects-to-build ]
        {
            local p = [ project.module-name [ path.join $(self-location) $(pn) ] ] ;
            result += [ project.target $(p) ] ;
        }
                        
        return $(result) ;
    }
    
    # Add 'target' to the list of targets in this project that should be build
    # only by explicit request
    rule mark-target-as-explicit ( target-name )
    {
        # Record the name of the target, not instance, since this
        # rule is called before main target instaces are created.
        self.explicit-targets += $(target-name) ;
    }
    
    # Add new target alternative
    rule add-alternative ( target-instance )
    {
        if $(self.built-main-targets)
        {
            errors.error "add-alternative called when main targets are already created." ;
        }        
        self.alternatives += $(target-instance) ;
    }
    
            
    # Returns a 'main-target' class instance corresponding to the 'name'.
    rule main-target ( name )
    {
        if ! $(self.built-main-targets)
        {
            build-main-targets ;
        }
                        
        return $(self.main-target.$(name)) ;
    }

    # Tells if a main target with the specified name exists.
    rule has-main-target ( name )
    {
        if ! $(self.built-main-targets)
        {
            build-main-targets ;
        }
        
        if $(self.main-target.$(name)) 
        {
            return true ;
        } 
    }
    
    rule build-main-targets ( )
    {
        self.built-main-targets = true ;
        for local a in $(self.alternatives)
        {
            local name = [ $(a).name ] ;
            local target = $(self.main-target.$(name)) ;
            if ! $(target)
            {
                local t = [ new main-target $(name) : $(self.project) ] ;
                self.main-target.$(name) = $(t) ;
                self.main-targets += $(t) ;
                target = $(self.main-target.$(name)) ;
            }
            
            $(target).add-alternative $(a) ;            
        }    
    }                            
}


# Helper rules to detect cycles in main target references
local rule start-building ( main-target-instance )
{
    if $(main-target-instance) in $(.targets-being-built)
    {
        local names ;
        for local t in $(.targets-being-built) $(main-target-instance)
        {
            names += [ $(t).full-name ] ;
        }
        
        errors.error "Recursion in main target references" 
          : "the following target are being built currently:"
          : $(names) ;
    }
    .targets-being-built += $(main-target-instance) ;    
}

local rule end-building ( main-target-instance )
{
    .targets-being-built = $(.targets-being-built[1--2]) ;
}


# A named top-level target in Jamfile
class main-target : abstract-target
{
    import errors : error ;
    import assert ;
    import sequence ;    
    import print ;
    import build-request feature property-set ;
    import targets : start-building end-building ;
    import "class" : is-a ;
        
    rule __init__ ( name : project )
    {
        abstract-target.__init__ $(name) : $(project) ;
    }
    
        
    # Add a new alternative for this target
    rule add-alternative ( target )
    {
        local d = [ $(target).default-build ] ;
        if $(self.alternatives) && ( $(self.default-build) != $(d) )
        {
            errors.error "default build must be identical in all alternatives"
              : "main target is" [ full-name ]
              : "with" [ $(d).raw ]
              : "differing from previous default build" [ $(self.default-build).raw ] ;
        }
        else
        {
            self.default-build = $(d) ;
        }        
        self.alternatives += $(target) ;
    }

    # Returns the best viable alternative for this property-set
    # See the documentation for selection rules.
    local rule select-alternatives ( property-set )
    {
        # The algorithm: we keep the current best viable alternative.
        # When we've got new best viable alternative, we compare it
        # with the current one. 
        
        local best ;
        local best-properties ;
                        
        if $(self.alternatives[2-])
        {
            local bad ;
            local worklist = $(self.alternatives) ;    
            while $(worklist) && ! $(bad)
            {
                local v = $(worklist[1]) ; 
                local properties = [ $(v).match $(property-set) ] ;
                           
                if $(properties) != no-match
                {                                    
                    if ! $(best)
                    {
                        best = $(v) ;
                        best-properties = $(properties) ;
                    }
                    else
                    {                                                   
                        if $(properties) = $(best-properties)
                        {
                            bad = true ;
                        }
                        else if $(properties) in $(best-properties) 
                        {
                            # Do nothing, this alternative is worse
                        }
                        else if $(best-properties) in $(properties)
                        {
                            best = $(v) ;
                            best-properties = $(properties) ;
                        }
                        else 
                        {
                            bad = true ;
                        }                                                
                    }
                }
                worklist = $(worklist[2-]) ;                
            }
            if ! $(bad)
            {
                return $(best) ;
            }                                    
        }
        else
        {
            return $(self.alternatives) ;
        }        
    }
    
    
    rule apply-default-build ( property-set )
    {           
        # 1. First, see what properties from default-build
        # are already present in property-set. 
        
        local raw = [ $(property-set).raw ] ;
        local specified-features = $(raw:G) ;
        
        local defaults-to-apply ;
        for local d in [ $(self.default-build).raw ] 
        {
            if ! $(d:G) in $(specified-features)
            {
                defaults-to-apply += $(d) ; 
            }            
        }
        
        # 2. If there's any defaults to be applied, form the new
        # build request. Pass it throw 'expand-no-defaults', since
        # default-build might contain "release debug", which will
        # result in two property-sets.
        local result ;
        if $(defaults-to-apply)
        {
            properties = [ 
              build-request.expand-no-defaults 
                
                # We have to compress subproperties here to prevent
                # property lists like:
                #
                #    <toolset>msvc <toolset-msvc:version>7.1 <threading>multi
                #
                # from being expanded into:
                #
                #    <toolset-msvc:version>7.1/<threading>multi
                #    <toolset>msvc/<toolset-msvc:version>7.1/<threading>multi
                #
                # due to cross-product property combination.  That may
                # be an indication that
                # build-request.expand-no-defaults is the wrong rule
                # to use here.
                [ feature.compress-subproperties $(raw) ] 
                  $(defaults-to-apply)
            ] ;
            
            if $(properties)
            {                
                for local p in $(properties)
                {
                    result += [ property-set.create [ feature.split $(p) ] ] ;
                }
            }
            else
            {
                result = [ property-set.empty ] ;
            }            
            
        }
        else
        {
            result = $(property-set) ;
        }
        return $(result) ;
    }
        
    # Select an alternative for this main target, by finding all alternatives
    # which requirements are satisfied by 'properties' and picking the one with
    # longest requirements set.
    # Returns the result of calling 'generate' on that alternative.
    rule generate ( property-set )
    {
        start-building $(__name__) ;
        local all-property-sets = [ apply-default-build $(property-set) ] ;
        local usage-requirements = [ property-set.empty ] ;
        local result ;
        for local p in $(all-property-sets)
        {
            p = [ $(p).expand-composites ] ;
            p = [ $(p).add-defaults ] ;
            local r = [ generate-really $(p) ] ;
            usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
            result += $(r[2-]) ;
        }
        end-building $(__name__) ;
        return $(usage-requirements) [ sequence.unique $(result) ] ;                
    }
        
    # Generates the main target with the given property set
    # and returns a list which first element is property-set object
    # containing usage-requirements of generated target and with
    # generated virtual target in other elements. It's possible
    # that no targets are generated.
    local rule generate-really ( property-set )
    {    
        local best-alternatives = [ select-alternatives $(property-set) ] ;
        if ! $(best-alternatives)
        {
            errors.error
                "failed to build" [ full-name ]
                "with properties" [ $(property-set).raw ]
                  "because no best-matching alternative could be found"
                  ; 
            return [ property-set.empty ] ;
        }
        else
        {
            local result = [ $(best-alternatives).generate $(property-set) ] ;
                        
            # Now return virtual targets for the only alternative
            return $(result) ;
        }        
    }
    
    rule rename ( new-name )
    {
        abstract-target.rename $(new-name) ;
        for local a in $(self.alternatives)
        {
            $(a).rename $(new-name) ;
        }
        
    }
    
}

# Abstract target which refers to a source file.
# This is artificial creature; it's usefull so that sources to 
# a target can be represented as list of abstract target instances.
class file-reference : abstract-target 
{
    import virtual-target ;
    import property-set ;
    import path ;
    
    rule __init__ ( file : project )
    {
        abstract-target.__init__ $(file) : $(project) ;
    }
    
    rule generate ( properties )
    {
         return [ property-set.empty ] 
                [ virtual-target.from-file $(self.name) : $(self.project) ] ;        
    }    

    # Returns true if the referred file really exists;
    rule exists ( )
    {
        local location = [ path.root $(self.name)
          [ project.attribute $(self.project) source-location ] ] ;        
        return [ path.exists $(location) ] ;
    }    
}



# Helper for 'find', below.
local rule remove-trailing-slash ( string )
{
    local stripped = [ MATCH (.*)/$ : $(string) ] ;
    stripped ?= $(string) ;
    return $(stripped) ;
}

if "--quiet" in [ modules.peek : ARGV ]
{
    .quiet = true ;
}


rule find ( id : project )
{
    local current-location = [ project.attribute $(project) location ] ;
    local target ;    
    
    local split = [ MATCH (.*)//(.*) : $(id) ] ;
    
    local project-part = $(split[1]) ;
    local target-part = $(split[2]) ;
    if ! $(split)
    {
        target-part = $(id) ;
    }
        
    # Make a more convenient name
    local have-project-reference = $(split) ;
    
    # The project used for finding main targets and for providing base directory
    # for file paths.
    local base-project ;
    if $(project-part) 
    {
        base-project = [ project.find $(project-part) : $(current-location) ] ;
    }
    else
    {
        # No project part in id. Resolve references relatively to the referring
        # project.
        base-project = $(project) ;
    }
    
    
    # Interpret target-part as project-id
    if ! $(have-project-reference)
    {   
        local project-module = [ project.find $(target-part) : $(current-location) ] ;
        if $(project-module)
        {
            target = [ project.target $(project-module) ] ;
        }        
    }
    
    # Interpret target-name as name of main target
    if ! $(target) && $(base-project)
    {
        local project-target = [ project.target $(base-project) ] ;        
        if [ $(project-target).has-main-target $(target-part) ] 
        {
            target = [ $(project-target).main-target $(target-part) ] ;
        }
    }
    
    if ! $(target) && ! $(have-project-reference)
    {
        target = [ new file-reference $(target-part) : $(project) ] ;        
        if ! [ $(target).exists ]
        {
            # File actually does not exist.
            # Reset 'target' so that an error is issued.
            target = ;
        }                
    }
        
    if ! $(target)
    {
        errors.error "Unable to resolve target-id $(id)" : 
          "Reference was made from project at '$(current-location)'" ;
    }        
    return $(target) ;
}

# Given a target-reference, made in context of 'project',
# returns the abstract-target instance that is referred to, as well
# as properties explicitly specified for this reference.
rule resolve-reference ( target-reference : project )
{
    # Separate target name from properties override
    local split = [ MATCH "^([^<]*)(/(<.*))?$" : $(target-reference) ] ;
    local id = $(split[1]) ;
    local sproperties = ;
    if $(split[3])
    {
        sproperties = [ property.make [ feature.split $(split[3]) ] ] ;
        sproperties = [ feature.expand-composites $(sproperties) ] ;
    }

    # Find the target
    local target = 
      [ find $(id) :  $(project) ] ;
    
    # Do a sanity check
    if $(sproperties) && [ class.is-a $(target) : file-reference ] 
    {
        errors.error 
          "error: target reference '$(target-reference)' contains properties," :
          "error: but refers to a file" ;
    }
    return $(target) [ property-set.create $(sproperties) ] ;
}



# Attempts to generate the target given by target reference, which
# can refer both to a main target or to a file.
# Returns a list consisting of
# - usage requirements
# - generated virtual targets, if any
rule generate-from-reference 
   ( target-reference            # Target reference
    : project                    # Project where the reference is made
    : property-set               # Properties of the main target that 
                                 # makes the reference
   )
{
    local r = [ resolve-reference $(target-reference) : $(project) ] ;
    local target = $(r[1]) ;
    local sproperties = $(r[2]) ;
    
    # Take properties which should be propagated and refine them
    # with source-specific requirements.
    local propagated = [ $(property-set).propagated ] ;
    local rproperties = [ $(propagated).refine $(sproperties) ] ;
    if $(rproperties[1]) = "@error"
    {
        errors.error
          "When building" [ full-name ] " with properties " $(properties) :
            "Invalid properties specified for " $(source) ":"
              $(rproperties[2-]) ;
    }
    return [ $(target).generate $(rproperties) ] ;
}

# Returns two property sets.
# The second one inclues all properties from
# 'property-set', except that all dependency properties are
# generated with 'generation-ps', and the obtained virtual targets
# are added as the values of original features.
# For example, <library>a/b might become <library>object(virtual-target)@1
#
# The first one include all usage requirements propagated from generated
# targets
rule generate-dependencies ( property-set : project : generation-ps )
{
    local usage-requirements = [ property-set.empty ] ;
    local xproperties ;    
    for local p in [ $(property-set).dependency ] 
    {
        local g = [ targets.generate-from-reference $(p:TG=) : $(project) : $(generation-ps) ] ;
        usage-requirements = [ $(usage-requirements).add $(g[1]) ] ;
        xproperties += $(p:G)$(g[2-]) ;
    }
    local r = [ property-set.create 
        [ $(property-set).base ] 
        [ $(property-set).free ] 
          $(xproperties)
        [ $(property-set).incidental ] ] ;
    return $(usage-requirements) $(r) ;
}



# Implements the most standard way of constructing main target
# alternative from sources. Allows sources to be either file or
# other main target and handles generation of those dependency
# targets.
class basic-target : abstract-target
{
    import build-request ;
    import virtual-target targets ;
    import property-set ;
    import set sequence errors ;
    import "class" : new ;
        
    rule __init__ ( name : project
        : sources * : requirements * : 
        default-build * : usage-requirements * )
    {        
        abstract-target.__init__ $(name) : $(project) ;
    
        self.sources = $(sources) ;
        if ! $(requirements) {
            requirements = [ property-set.empty ] ;
        }    
        self.requirements = $(requirements) ;
        if ! $(default-build) 
        {
            default-build = [ property-set.empty ] ;
        }    
        self.default-build = $(default-build) ;
        if ! $(usage-requirements)
        {
            usage-requirements = [ property-set.empty ] ;
        }    
        self.usage-requirements = $(usage-requirements) ;
        
        if $(sources:G)
        {
            errors.error "gristed element in sources for" [ full-name ] ;
        }
    }
    
    # Returns the list of abstract-targets which are used as sources.
    # The extra properties specified for sources are not represented.
    rule sources ( )
    {
        if ! $(self.source-targets) {
            for local s in $(self.sources)
            {
                self.source-targets += 
                  [ targets.resolve-reference $(s) : $(self.project) ] ;
            }            
        }    
        return $(self.source-targets) ;
    }
    
    rule requirements ( )
    {
        return $(self.requirements) ;
    }
                        
    rule default-build ( )
    {
        return $(self.default-build) ;
    }
    
    # Returns the alternative condition for this alternative, if
    # the condition is satisfied by 'property-set'.
    rule match ( property-set )
    {    
        local condition = [ $(self.requirements).base ] ;
        # Weed out conditional properties.
        condition = [ MATCH ^([^:]*)\$ : $(condition) ] ;
        if $(condition) in [ $(property-set).raw ] 
        {
            return $(condition) ;
        }
        else
        {
            return no-match ;
        }        
    }

    # Determine and return properties which should be used for
    # building when given 'build-request'. This includes refining
    # build request with requirements, evaluating conditionals,
    # generating depenendecies and running actions for features.
    local rule refined-properties ( build-request )
    {
        local erequirements = [ $(self.requirements).evaluate-conditionals
          $(build-request) ] ;
        erequirements = [ $(erequirements).expand-composites ] ;

        local rproperties = [ $(build-request).refine $(erequirements) ] ;

        if $(rproperties[1]) != "@error"                    
        {
            # TODO: issue a warning when requirements change properties, but
            # link-compatibility is still not broken.
            rproperties = [ $(rproperties).run-actions ] ;                
        }
        return $(rproperties) ;
    }

    # Generate all sources for this target. Returns property-set with 
    # usage requirements, followed by the list of virtual targets.
    local rule generate-sources ( property-set )
    {      
        local usage-requirements = [ property-set.empty ] ;
        local source-targets ;
        for local s in $(self.sources)
        {

            # Try treating this source as reference to main target
            local more-targets = 
              [ targets.generate-from-reference $(s) : $(self.project) 
                : $(property-set) ] ;
            check-for-link-compatibility $(more-targets[2-]) : $(property-set) ;
            
                        
            usage-requirements = [ $(usage-requirements).add $(more-targets[1]) ] ;
            source-targets += $(more-targets[2-]) ;            
        } 
        return $(usage-requirements) $(source-targets) ;
    }

    #
    # Allows the user to tag the name of the target, according to properties.
    #
    rule tag-name ( name : property-set )
    {
        local properties = [ $(property-set).raw ] ;

        local tagged-name = $(name) ;

        if <tag> in $(properties:G)
        {
            local tags = [ $(property-set).get <tag> ] ;
            for local tag in $(tags)
            {
                tagged-name = $(tagged-name)$(tag) ;
            }
        }

        return $(tagged-name) ;
    }
    
    # Determines final build properties, generates sources,
    # and calls 'construct'. This method should not be
    # overridden.
    rule generate ( property-set )
    {
        if ! $(self.generated.$(property-set)) 
        {           
            local rproperties = [ refined-properties $(property-set) ] ;            
            if $(rproperties[1]) != "@error"                    
            {
                local usage-requirements = [ property-set.empty ] ;
                
                rproperties = 
                  [ targets.generate-dependencies $(rproperties) : $(self.project) 
                    : $(rproperties) ] ;                                
                usage-requirements = [ $(usage-requirements).add $(rproperties[1]) ] ;
                rproperties = $(rproperties[2-]) ;
                local deps = [ $(rproperties).dependency ] ;
                check-for-link-compatibility $(deps:G=) : $(property-set) ;
                                            
                local source-targets = [ generate-sources $(rproperties) ] ;
                
                usage-requirements = 
                  [ $(usage-requirements).add $(source-targets[1]) ] ;
                source-targets = $(source-targets[2-]) ;
                                
                rproperties = [ $(rproperties).add $(usage-requirements) ] ;

                local tagged-name = [ tag-name $(self.name) : $(rproperties) ] ;

                local original-name = $(self.name) ;
                self.name = $(tagged-name) ;
                
                local result = 
                  [ construct $(source-targets) : $(rproperties) ] ;
                check-for-unused-sources 
                  $(result) : $(source-targets) ;

                local s = [ create-subvariant $(result) : $(property-set) : $(source-targets)
                  : $(rproperties) : $(usage-requirements) ] ;

                local ur = [ compute-usage-requirements $(s) ] ;
                $(s).set-usage-requirements $(ur) ;
                self.generated.$(property-set) = $(ur) $(result) ;

                self.name = $(original-name) ;
            } 
            else
            {
                self.generated.$(property-set) = $(rproperties) ;
            }       
        }                
        return $(self.generated.$(property-set)) ;
    }

    # Given the set of generated targets, and refined build 
    # properties, determines and sets appripriate usage requirements
    # on those targets.
    rule compute-usage-requirements ( subvariant )
    {
        local rproperties = [ $(subvariant).build-properties ] ;
        xusage-requirements = [ $(self.usage-requirements).evaluate-conditionals 
          $(rproperties) ] ;                
        
        local xusage-requirements = 
          [ targets.generate-dependencies 
              $(xusage-requirements) 
                : $(self.project) : $(rproperties) ] ;
        local recursive-usage-requirements = $(xusage-requirements[1]) ;
        local result = [ $(recursive-usage-requirements).add $(xusage-requirements[2-]) ] ;        
        
        return $(result) ;
    }
    
    # Creates a new subvariant-dg instances for 'targets'
    local rule create-subvariant ( targets * : build-request : sources * :
        rproperties 
        : usage-requirements )
    {
        for local e in $(targets)                    
        {
            $(e).root true ;
        }                    
        
        # Process all vtargets that will be created if this main target
        # is created.
        local all-targets = 
          [ sequence.transform virtual-target.traverse : $(targets) ] ; 
        local s = [ new subvariant $(__name__) : $(build-request) : $(sources)
          : $(rproperties) : $(usage-requirements) : $(all-targets) ] ;
        for local v in $(all-targets)          
        {
            if ! [ $(v).creating-subvariant ]
            {                
                $(v).creating-subvariant $(s) ;            
            }            
        }                        
        return $(s) ;
    }
    
        
    # Check that each source virtual target is either directly present in the
    # result, or is in dependency graph of some returned virtual target. 
    # If this is not the case, issues a warning. 
    # Note that 'result' *can* be empty. For
    # example, in this use case:
    #    alias platform-sources ;
    #    alias platform-sources : a.cpp : <os>NT ;
    # result will be empty in the first case.
    local rule check-for-unused-sources ( result * : sources * )
    {
        local used-sources ;
        for local r in $(result)
        {
            used-sources += [ virtual-target.traverse $(r) : include-roots : 1 ] ;
        }
        
        local unused = [ set.difference $(sources) : $(used-sources) ] ;
        if $(unused)
        {
            for local u in $(unused)
            {               
                errors.warning "Unused source" [ $(u).str ] "in main target" [ full-name ] ;                    
            }                            
        }                                                       
    }
    
    # Checks if 'targets' are link-compatible with 'build-request' and
    # issues a warning if that's not the case.
    local rule check-for-link-compatibility ( targets * : build-request )
    {
        local checked ;
        for local t in $(targets)
        {
            local a = [ $(t).action ] ;
            if $(a)
            {
                local p = [ $(a).properties ] ;
                if ! $(p) in $(cheched)
                {
                    if [ $(p).link-incompatible-with $(build-request) ]
                    {
                        local s = [ $(t).creating-subvariant ] ;
                        local other-mt = [ $(s).main-target ] ;
                        ECHO "warning: targets produced from" [ $(other-mt).name ] 
                            "are link incompatible" ;
                        ECHO "warning: with main target" [ name ] ;
                    }                    
                }                
                checked += $(p) ;
            }            
        }
        
    }
    
    
        
    # Constructs the virtual targets for this abstract targets and
    # the dependecy graph. Returns the list of virtual targets.
    # Should be overrided in derived classes.
    rule construct ( source-targets * : properties * )
    {
        errors.error "method should be defined in derived classes" ;
    }
}

class typed-target : basic-target
{
    import generators ;    
    
    rule __init__ ( name : project : type 
    : sources * : requirements * : default-build * : usage-requirements * )
    {
        basic-target.__init__ $(name) : $(project) 
          : $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ;
        
        self.type = $(type) ;
    }
    
    rule type ( )
    {
        return $(self.type) ;
    }
            
    rule construct ( source-targets * : property-set )
    {
        local r = [ generators.construct $(self.project) $(self.name) : $(self.type) 
          : [ property-set.create [ $(property-set).raw ] # [ feature.expand
              <main-target-type>$(self.type) ]
          # ]
            : $(source-targets) ] ;
        if ! $(r)
        {
            errors.error "unable to construct" [ full-name ] ;
        }
        
        return $(r) ;
    }            
}

# Return the list of sources to use, if main target rule is invoked
# with 'sources'. If there are any objects in 'sources', they are treated
# as main target instances, and WRITEME.
rule main-target-sources ( sources * : main-target-name )
{
    local result ;
    for local t in $(sources)
    {
        if [ class.is-instance $(t) ]
        {
            local name = [ $(t).name ] ;
            # NOTE: on windows, this won't work if 'main-target-name'
            # if single letter. But other alternatives are even worse.
            local new-name = $(main-target-name)..$(name) ;
            $(t).rename $(new-name) ;
            #local p = [ $(t).project ] ;            
            #local pt = [ project.target $(p) ] ;
            #$(pt).rename-main-target $(name) : $(new-name) ;
            result += $(new-name) ;
        }
        else
        {
            result += $(t) ;
        }                
    }    
    return $(result) ;
}


# Returns the requirement to use when declaring a main target,
# which are obtained by
# - translating all specified property paths, and
# - refining project requirements with the one specified for the target
rule main-target-requirements ( 
  specification * # Properties explicitly specified for a main target
  : project       # Project where the main target is to be declared
      )
{
    local loc = [ project.attribute $(project) location ] ;
    local requirements = [ property.translate-paths $(specification) : $(loc) ] ; 
    local requirements = [ property-set.create $(requirements) ] ;
    local project-requirements = [ project.attribute $(project) requirements ] ;
    requirements = [ $(project-requirements).refine $(requirements) ] ;    
    if $(requirements[1]) = "@error" 
    {
        errors.error "Conflicting requirements for target:" $(requirements) ;
    }
    return $(requirements) ;
}

# Returns the use requirement to use when declaraing a main target,
# which are obtained by
# - translating all specified property paths, and
# - adding project's usage requirements
rule main-target-usage-requirements (
  specification * # Use-properties explicitly specified for a main target
  : project       # Project where the main target is to be declared
     )
{
    local loc = [ project.attribute $(project) location ] ;         
    local project-usage-requirements = [ project.attribute $(project) usage-requirements ] ;
        
    local usage-requirements = [ property-set.create 
        [ property.translate-paths $(specification) : $(loc) ] ] ;
    
    return [ $(project-usage-requirements).add $(usage-requirements) ] ;
}

# Return the default build value to use when declaring a main target,
# which is obtained by using specified value if not empty and parent's
# default build attribute otherwise.
rule main-target-default-build (
  specification * # Default build explicitly specified for a main target
  : project       # Project where the main target is to be declared
     )
{
    local result ;
    if $(specification)
    {
        result = $(specification) ;
    }
    else
    {
        result = [ project.attribute $(project) default-build ] ;
    }    
    return [ property-set.create-with-validation $(result) ] ;
}         

# Registers the specified target as a main target alternatives.
# Returns 'target'.
rule main-target-alternative ( target ) 
{               
    local ptarget = [ project.target [ $(target).project ] ] ;
    
    $(ptarget).add-alternative $(target) ;
    return $(target) ;
}

# Creates a typed-target with the specified properties.
# The 'name', 'sources', 'requirements', 'default-build' and
# 'usage-requirements' are assumed to be in the form specified
# by the user in Jamfile corresponding to 'project'.
rule create-typed-target ( type : project : 
    name : sources * : requirements * : default-build * 
    : usage-requirements * )
{
    return [
      targets.main-target-alternative
      [ new typed-target $(name) : $(project) : $(type)
        : [ targets.main-target-sources $(sources) : $(name) ] 
        : [ targets.main-target-requirements $(requirements) : $(project) ] 
        : [ targets.main-target-default-build $(default-build) : $(project) ]
        : [ targets.main-target-usage-requirements $(usage-requirements) : $(project) ]
      ] ] ;

}