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/virtual-target.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.

#  Implements virtual targets, which correspond to actual files created during
#  build, but are not yet targets in Jam sense. They are needed, for example,
#  when searching for possible transormation sequences, when it's not known
#  if particular target should be created at all.

import "class" : new ;
import path property-set utility sequence errors ;

#                       +--------------------------+
#                       | virtual-target           |
#                       +==========================+
#                       | actualize                |
#                       +--------------------------+
#                       | actualize-action() = 0   |
#                       | actualize-location() = 0 |
#                       +----------------+---------+
#                                        |
#                                        ^
#                                       / \
#                                      +-+-+
#                                        |
#    +---------------------+     +-------+--------------+
#    | action              |     | abstract-file-target |
#    +=====================|   * +======================+
#    | action-name         |  +--+ action               |
#    | properties          |  |  +----------------------+
#    +---------------------+--+  | actualize-action()   |
#    | actualize()         |0..1 +-----------+----------+
#    | path()              |                 |
#    | adjust-properties() | sources         |
#    | actualize-sources() | targets         |
#    +------+--------------+                 ^
#           |                               / \
#           ^                              +-+-+
#          / \                               |
#         +-+-+                +-------------+-------------+
#           |                  |                           |
#           |           +------+---------------+  +--------+-------------+
#           |           | file-target          |  | searched-lib-target  |
#           |           +======================+  +======================+
#           |           | actualize-location() |  | actualize-location() |
#           |           +----------------------+  +----------------------+
#           |
#         +-+------------------------------+
#         |                                |
#    +----+----------------+     +---------+-----------+
#    | compile-action      |     | link-action         |
#    +=====================+     +=====================+
#    | adjust-properties() |     | adjust-properties() |
#    +---------------------+     | actualize-sources() |
#                                +---------------------+
#
# The 'compile-action' and 'link-action' classes are defined not here,
# but in builtin.jam modules. They are shown in the diagram to give
# the big picture.

# Potential target. It can be converted into jam target and used in
# building, if needed. However, it can be also dropped, which allows
# to search for different transformation and select only one.
#
class virtual-target 
{
    import virtual-target utility scanner ;    
    
    rule __init__ ( name  # Name of this target -- specifies the name of
        : project # Project to which this target belongs
    )
    {    
        self.name = $(name) ;
        self.project = $(project) ;
        self.dependencies = ;
    }
    
    # Name of this target.
    rule name ( ) { return $(self.name) ; }

    # Project of this target.
    rule project ( ) { return $(self.project) ; }

    # Adds additional instances of 'virtual-target' that this
    # one depends on.
    rule depends ( d + )
    {
        self.dependencies = [ sequence.merge $(self.dependencies)
                               : [ sequence.insertion-sort $(d) ] ] ;
    }

    rule dependencies ( )
    {
        return $(self.dependencies) ;
    }

    # Generates all the actual targets and sets up build actions for
    # this target.
    #
    # If 'scanner' is specified, creates an additional target
    # with the same location as actual target, which will depend on the
    # actual target and be associated with 'scanner'. That additional
    # target is returned. See the docs (#dependency_scanning) for rationale.
    # Target must correspond to a file if 'scanner' is specified.
    #
    # If scanner is not specified, then actual target is returned.
    rule actualize ( scanner ? )
    {
        local actual-name = [ actualize-no-scanner ] ;

        if ! $(scanner)
        {
            return $(actual-name) ;
        }
        else
        {
            # Add the scanner instance to the grist for name.
            local g = [ sequence.join
                [ utility.ungrist $(actual-name:G) ] $(scanner) : - ] ;
            local name = $(actual-name:G=$(g)) ;

            if ! $(self.made.$(name)) {
                self.made.$(name) = true ;

                DEPENDS $(name) : $(actual-name) ;

                actualize-location $(name) ;

                scanner.install $(scanner) : $(name) $(__name__) ;
            }
            return $(name) ;
        }

    }

# private: (overridables)

    # Sets up build actions for 'target'. Should call appropriate rules
    # and set target variables.
    rule actualize-action ( target )
    {
        errors.error "method should be defined in derived classes" ;
    }

    # Sets up variables on 'target' which specify its location.
    rule actualize-location ( target )
    {
        errors.error "method should be defined in derived classes" ;
    }
    
    # If the target is generated one, returns the path where it will be
    # generated. Otherwise, returns empty list.
    rule path ( )
    {
        errors.error "method should be defined in derived classes" ;        
    }
    
    # Return that actual target name that should be used
    # (for the case where no scanner is involved)
    rule actual-name ( )
    {
        errors.error "method should be defined in derived classes" ;
    }

# implementation
    rule actualize-no-scanner ( )
    {
        local name = [ actual-name ] ;

        # Do anything only on the first invocation
        if ! $(self.made.$(name)) {
            self.made.$(name) = true ;
            
            virtual-target.register-actual-name $(name) : $(__name__) ;            

            for local i in $(self.dependencies) {
                DEPENDS $(name) : [ $(i).actualize ] ;
            }

            actualize-location $(name) ;
            actualize-action $(name) ;
        }
        return $(name) ;
    }


}


# Target which correspond to a file. The exact mapping for file
# is not yet specified in this class. (TODO: Actually, the class name
# could be better...)
#
# May be a source file (when no action is specified), or
# derived file (otherwise).
#
# The target's grist is concatenation of project's location,
# properties of action (for derived files), and, optionally,
# value identifying the main target.
class abstract-file-target : virtual-target
{
    import project regex sequence path type ;
    
    rule __init__ ( name
        : type ?  # Optional type for this target
                  : project
    )
    {
        virtual-target.__init__ $(name) : $(project) ;
            
        self.type = $(type) ;
        self.action = ;
    }
    
    rule type ( ) { return $(self.type) ; }
    rule set-type ( type )
    {
        self.type = $(type) ;
    }

    # Sets the suffix. When generating target name, it will be used in preference to
    # the suffix that is associated with 'type'
    rule suffix ( suffix ? )
    {
        if $(suffix)
        {
            self.suffix = $(suffix) ;
        }
        return $(self.suffix) ;
    }

    # Sets the path. When generating target name, it will override any path
    # computation from properties.
    rule set-path ( path )
    {
        self.path = [ path.native $(path) ] ;
    }

    # If 'a' is supplied, sets action to 'a'.
    # Returns the action currently set.
    rule action ( a ? )
    {
        if $(a)
        {
            self.action = $(a) ;
        }
        return $(self.action) ;
    }

    # Sets/gets the 'root' flag. Target is root is it directly correspods to some
    # variant of a main target.
    rule root ( set ? )
    {
        if $(set)
        {
            self.root = true ;
        }
        return $(self.root) ;
    }

    # Gets or sets the subvariant which created this target. Subvariant
    # is set when target is brought into existance, and is never changed
    # after that. In particual, if target is shared by subvariant, only 
    # the first is stored.    
    rule creating-subvariant ( s ? # If specified, specified the value to set,
                                   # which should be instance of 'subvariant'
                                   # class.
                             )
    {
        if $(s) && ( ! $(self.creating-subvariant) && ! $(overwrite) )
        {
            if $(self.creating-subvariant)
            {
                errors.error "Attempt to change 'dg'" ;
            }
            else
            {                
                self.creatin-subvariant = $(s) ;
            }            
        }
        return $(self.creatin-subvariant) ;
    }

    rule actualize-action ( target )
    {
        if $(self.action)
        {
            $(self.action).actualize ;
        }
    }
          
    # Return a human-readable representation of this target
    #
    # If this target has an action, that's:
    #
    #    { <action-name>-<self.name>.<self.type> <action-sources>... }
    #
    # otherwise, it's:
    #
    #    { <self.name>.<self.type> }
    #
    rule str ( )
    {
        local action = [ action ] ;
        
        local name-dot-type = [ sequence.join $(self.name) "."  $(self.type) ] ;
        
        if $(action)
        {
            local sources = [ $(action).sources ] ;
            
            local action-name =  [ $(action).action-name ] ;            

            local ss ;            
            for local s in $(sources)
            {
                ss += [ $(s).str ] ;
            }
            
            return "{" $(action-name)-$(name-dot-type) $(ss) "}" ;
        }
        else
        {
            return "{" $(name-dot-type) "}" ;
        }
    }

    rule less ( a )
    {
        if [ str ] < [ $(a).str ]
        {
            return true ;
        }
    }

    rule equal ( a )
    {
        if [ str ] = [ $(a).str ]
        {
            return true ;
        }
    }

# private:
    rule actual-name ( )
    {
        if ! $(self.actual-name)
        {            
            local grist = [ grist ] ;
            
            local basename = [ actual-basename ] ;
            self.actual-name = <$(grist)>$(basename) ;
            
        }
        return $(self.actual-name) ;
    }
        
    # Helper to 'actual-name', above. Compute unique prefix used to distinguish
    # this target from other targets with the same name which create different
    # file.
    rule grist ( )
    {
        # Depending on target, there may be different approaches to generating
        # unique prefixes. We'll generate prefixes in the form 
        # <one letter approach code> <the actual prefix>
        local path = [ path ] ;
        if $(path)
        {
            # The target will be generated to a know path. Just use the path
            # for identification, since path is as unique as it can get.
            return p$(path) ;            
        }
        else
        {
            # File is either source, which will be searched for, or is not a file at
            # all. Use the location of project for distinguishing.
            local project-location = [ project.attribute $(self.project) location ] ;
            local location-grist =
              [ sequence.join [ regex.split $(project-location) "/" ] : "!" ] ;
            
            if $(self.action)
            {
                local ps = [ $(self.action).properties ] ;     
                local property-grist = [ $(ps).as-path ] ;
                location-grist = $(location-grist)/$(property-grist) ;
            }            
                        
            return l$(location-grist) ;
        }                
    }
        
    # Helper to actual-name, above. Compute the 'basename' of the filename
    # of the actual created file.
    rule actual-basename ( )
    {
        local name = [ path.native $(self.name) ] ;
        if $(self.suffix)
        {
            name = $(name).$(self.suffix) ;
        }
        else if $(self.type)
        {
            local properties ;
            if $(self.action)
            {
                local ps = [ $(self.action).properties ] ;
                properties = [ $(ps).raw ] ;
            }                        
            local suffix = [ type.generated-target-suffix $(self.type) :
              $(properties) ] ;
            if $(suffix)
            {                
                name = $(name).$(suffix) ;
            }            
        }
        return $(name) ;        
    }
    

}

# File target with explicitly known location.
#
# The file path is determined as
#    - value passed to the 'set-path' method, if any
#    - for derived files, project's build dir, joined with components
#      that describe action's properties. If the free properties
#      are not equal to the project's reference properties
#      an element with name of main target is added.
#    - for source files, project's source dir
#
# The file suffix is
#     - the value passed to the 'suffix' method, if any, or
#     - the suffix which correspond to the target's type.
#
class file-target : abstract-file-target 
{
    import common ;    
    import errors ;
    
    rule __init__ (
      name
        : type ?  # Optional type for this target
        : project
    )
    {
        abstract-file-target.__init__ $(name) : $(type) : $(project) ;   
    }
        
    rule actualize-location ( target )
    {
        if $(self.action)
        {
            # This is a derived file.
            local path = [ path ] ;
            LOCATE on $(target) = $(path) ;                        

            # Make sure the path exists.
            DEPENDS $(target) : $(path) ;
            common.MkDir $(path) ;
        }
        else
        {
            # This is a source file.
            SEARCH on $(target) =
              [ path.native [ project.attribute $(self.project) source-location ] ] ;
        }        
    }
    
    # Returns the directory for this target
    rule path ( )
    {
        if ! $(self.path)
        {
            if $(self.action)
            {                
                local build-dir = [ project.attribute $(self.project) build-dir ] ;
                if ! $(build-dir)
                {
                    build-dir = [ path.join 
                        [ project.attribute $(self.project) location ]
                        bin
                    ] ;
                }
                
                local path = [ path.join
                    $(build-dir)
                      [ $(self.action).path ]
                ] ;
                
                # Store the computed path, so that it's not recomputed
                # any more
                self.path = [ path.native $(path) ] ;
            }            
        }
        return $(self.path) ;
     }

}

class notfile-target : abstract-file-target
{
    rule __init__ ( name : project )
    {
        abstract-file-target.__init__ $(name) : : $(project) ;
    }
    
            
    rule actualize-location ( target )
    {
        NOTFILE $(target) ;
        ALWAYS $(target) ;
    }    
}    

# Class which represents an action.
# Both 'targets' and 'sources' should list instances of 'virtual-target'.
# Action name should name a rule with this prototype
#     rule action-name ( targets + : sources * : properties * )
# Targets and sources are passed as actual jam targets. The rule may
# not establish dependency relationship, but should do everything else.
class action 
{
    import type toolset property-set indirect class path ;
    
    rule __init__ ( targets + : sources * : action-name + : property-set ? )
    {        
        self.targets = $(targets) ;
        self.sources = $(sources) ;
    
        self.action-name = [ indirect.make-qualified $(action-name) ] ;
        
        if ! $(property-set) 
        {
            property-set = [ property-set.empty ] ;
        }
        
        if ! [ class.is-instance $(property-set) ]
        {        
            errors.error "Property set instance required" ;
        }
        
        self.properties = $(property-set) ;
    }        
    
    rule targets ( )
    {
        return $(self.targets) ;
    }

    rule sources ( )
    {
        return $(self.sources) ;
    }

    rule action-name ( )
    {
        return $(self.action-name) ;
    }

    rule properties ( )
    {
        return $(self.properties) ;
    }

    # Generates actual build instructions.
    rule actualize ( )
    {
        if ! $(self.actualized)
        {
            self.actualized = true ;

            local ps = [ properties ] ;
            local properties = [ adjust-properties [ $(ps).raw ] ] ;


            local actual-targets ;
            for local i in [ targets ]
            {
                actual-targets += [ $(i).actualize ] ;
            }

            actualize-sources [ sources ] ;

            DEPENDS $(actual-targets) : $(self.actual-sources) $(self.dependency-only-sources) ;

            # Action name can include additional argument to rule, which should not
            # be passed to 'set-target-variables'
            toolset.set-target-variables
              [ indirect.get-rule $(self.action-name[1]) ] $(actual-targets)
                : $(properties) ;
            
            indirect.call $(self.action-name) 
              $(actual-targets) : $(self.actual-sources) : $(properties)
                ;
            
            # Since we set up creating action here, we also set up
            # action for cleaning up
            common.Clean clean : $(actual-targets) ;
        }
    }

    # Helper for 'actualize-sources'.
    # For each passed source, actualizes it with the appropriate scanner.
    # Returns the actualized virtual targets.
    rule actualize-source-type ( sources * )
    {
        local result = ;
        for local i in $(sources)
        {
            local scanner ;
            if [ $(i).type ]
            {
                scanner =
                  [ type.get-scanner [ $(i).type ] : $(properties) ] ;
            }
            result += [ $(i).actualize $(scanner) ] ;
        }
        
        return $(result) ;
    }
    
    # Creates actual jam targets for sources. Initialized two member
    # variables:.
    # 'self.actual-sources' -- sources which are passed to updating action
    # 'self.dependency-only-sources' -- sources which are made dependencies, but
    # are not used otherwise.
    #
    # New values will be *appended* to the variables. They may be non-empty,
    # if caller wants it.
    rule actualize-sources ( sources * )
    {
        local dependencies = [ $(self.properties).get <dependency> ] ;
                
        self.dependency-only-sources += [ actualize-source-type $(dependencies) ] ;
        self.actual-sources += [ actualize-source-type $(sources) ] ;
    }

    rule path ( )
    {
        local p = [ $(self.properties).as-path ] ;
        # Really, an ugly hack. Boost regression test system requires
        # specific target paths, and it seems that changing it to handle
        # other directory layout is really hard. For that reason,
        # we teach V2 to do the things regression system requires.
        # The value o '<location-prefix>' is predended to the path.
        local prefix = [ $(self.properties).get <location-prefix> ] ;
        if $(prefix)
        {
            p = [ path.join $(prefix) $(p) ] ;
        }        
        return $(p) ;
    }

    # Determined real properties when trying building with 'properties'.
    # This is last chance to fix properties, for example to adjust includes
    # to get generated headers correctly. Default implementation returns
    # its argument.
    rule adjust-properties ( properties * )
    {
        return $(properties) ;
    }


    rule set-targets ( targets * )
    {
        self.targets = $(targets) ;
    }
}

# Action class which does nothing --- it produces the targets with
# specific properties out of nowhere. It's needed to distinguish virtual
# targets with different properties that are known to exist, and have no 
# actions which create them.
class null-action : action 
{
    rule __init__ ( targets + : property-set ? )
    {
        action.__init__ $(targets) : : .no-action : $(property-set) ;
    }
        
    rule actualize ( )
    {
        if ! $(self.actualized)
        {
            self.actualized = true ;

            for local i in [ targets ]
            {
                $(i).actualize ;
            }
        }        
    }
}


# Creates a virtual target with approariate name and type from 'file'.
# If a target with that name in that project was already created, returns that already
# created target.
# FIXME: more correct way would be to compute path to the file, based on name and source location
# for the project, and use that path to determine if the target was already created.
# TODO: passing project with all virtual targets starts to be annoying.
rule from-file ( file : project )
{
    import type ; # had to do this here to break a circular dependency
    
    if $(.files.$(file).$(project))
    {
        return $(.files.$(file).$(project)) ;
    }
    else
    {
        local name = [ path.make $(file:S=) ] ;
        local type = [ type.type $(file) ] ;
        local result ;
        if ! $(type)
        {
            # warning "cannot determine type for file $(file)" ;
            result = [ new file-target $(file) :  : $(project) ] ;
        }
        else
        {
            local v = [ new file-target $(name) : $(type) : $(project) ] ;
            $(v).suffix [ MATCH ^.(.*)$ : $(file:S) ] ;
            result = $(v) ;
        }
        .files.$(file).$(project) = $(result) ;
        return $(result) ;
    }
}

# Registers a new virtual target. Checks if there's already registered target, with the same
# name, type, project and subvariant properties, and also with the same sources
# and equal action. If such target is found it is retured and 'target' is not registers.
# Otherwise, 'target' is registered and returned.
rule register ( target )
{
    local signature = [ sequence.join
        [ $(target).project ] [ $(target).name ] [ $(target).type ] : - ] ;
    local result ;
    for local t in $(.cache.$(signature))
    {
        local a1 = [ $(t).action ] ;
        local a2 = [ $(target).action ] ;
        
        if ! $(result)
        {
            if ! $(a1) && ! $(a2)
            {
                result = $(t) ;
            }
            else 
            {
                if $(a1) && $(a2) && [ $(a1).action-name ] = [ $(a2).action-name ] && 
                  [ $(a1).sources ] = [ $(a2).sources ]
                {
                    local ps1 = [ $(a1).properties ] ;
                    local ps2 = [ $(a2).properties ] ;
                    local p1 = [ $(ps1).base ] [ $(ps1).free ] [ $(ps1).dependency ] ;
                    local p2 = [ $(ps2).base ] [ $(ps2).free ] [ $(ps2).dependency ] ;
                    if $(p1) = $(p2) 
                    {                        
                        result = $(t) ;
                    }                    
                }
            }                        
        }
    }
    if ! $(result)
    {
        .cache.$(signature) += $(target) ;
        result = $(target) ;
    }

    return $(result) ;
}

rule register-actual-name ( actual-name : virtual-target )
{
    if $(.actual.$(actual-name))
    {
        errors.error "Duplicate name of actual target:" $(actual-name) 
          : "previous virtual target" [ $(.actual.$(actual-name)).str ] 
          : "another virtual target" [ $(virtual-target).str ] ;
    }
    else
    {
        .actual.$(actual-name) = $(virtual-target) ;
    }        
}


# Traverses the dependency graph of 'target' and return all targets that will
# be created before this one is created. If root of some dependency graph is
# found during traversal, it's either included or not, dependencing of the
# value of 'include-roots'. In either case, sources of root are not traversed.
rule traverse ( target : include-roots ? : include-sources ? )
{
    local result ;
    if [ $(target).action ]
    {
        local action = [ $(target).action ] ;
        # This includes 'target' as well
        result += [ $(action).targets ] ;

        for local t in [ $(action).sources ]
        {
            if ! [ $(t).root ]
            {
                result += [ traverse $(t) : $(include-roots) : $(include-sources) ] ;
            }
            else if $(include-roots)
            {
                result += $(t) ;
            }            
        }
    }
    else if $(include-sources)
    {
        result = $(target) ;
    }    
    return $(result) ;
}

# Takes an 'action' instances and creates new instance of it
# and all produced target. The rule-name and properties are set
# to 'new-rule-name' and 'new-properties', if those are specified.
# Returns the cloned action.
rule clone-action ( action : new-project : new-action-name ? : new-properties ? )
{
    if ! $(new-action-name)
    {
        new-action-name = [ $(action).action-name ] ;
    }
    if ! $(new-properties)
    {
        new-properties = [ $(action).properties ] ;
    }
                    
    local cloned-targets ;
    for local target in [ $(action).targets ]
    {
        local n = [ $(target).name ] ;
        local cloned-target = [ class.new file-target $(n:D=) : [ $(target).type ] 
          : $(new-project) ] ;
        local d = [ $(target).dependencies ] ;
        if $(d)
        {            
            $(cloned-target).depends $(d) ;
        }                    
        $(cloned-target).root [ $(target).root ] ;
        $(cloned-target).creating-subvariant [ $(target).creating-subvariant ] ;
        
        cloned-targets += $(cloned-target) ;
    }        
        
    local action-class = [ modules.peek $(action) : __class__ ] ;
    
    local cloned-action = [ class.new $(action-class) $(cloned-targets) : 
      [ $(action).sources ] : $(new-action-name) : $(new-properties) ] ;
    
    for local cloned-target in $(cloned-targets)
    {        
        $(cloned-target).action $(cloned-action) ;
    }
    
    
    return $(cloned-action) ;        
}




# Clones a dependency graph template, given one of its root,
# and a new source target to instantinate the template with.
#
# If 'target's name is "%" and type is equal to 'new-source's
# return 'new-source', otherwise created a new target:
#  - if there "%" in target's name, its replaced with 'new-target's
#  - project for new target is the same as for 'new-target'
#  - other attributes are copied
#
# If 'dont-recurse' is not set, clones action, which results in
# cloning of other targets, and, ultimately, cloning of the
# entire dependency graph.
#
# The 'new-project' parameter tells what project should be assigned
# for newly created non-source targets.
rule clone-template ( target dont-recurse ? : new-source : new-project )
{
    local name = [ $(new-source).name ] ;
    local old-name = [ $(target).name ] ;
    local new-name = $(old-name) ;
    local m = [ MATCH (.*)(%)(.*) : $(old-name) ] ;
    if $(m)
    {
        if [ $(target).action ]
        {
            new-name = [ sequence.join $(m[1]) $(name:D=) $(m[3]) ] ;
        }
        else
        {
            new-name = [ sequence.join $(m[1]) $(name) $(m[3]) ] ;
        }
    }

    if $(old-name) = % && [ $(target).type ] = [ $(new-source).type ]
    {
        return $(new-source) ;
    }
    else
    {
        local cloned = [ new file-target $(new-name) : [ $(target).type ] :
          $(new-project) ] ;

        if ! $(dont-recurse) && [ $(target).action ]
        {
            local cloned-action = [ clone-action-template
                [ $(target).action ] $(target) $(cloned) : $(new-source) : $(new-project) ] ;

            cloned-targets = $(cloned) ;
            for t in [ $(cloned-action).targets ]
            {
                if $(t) != $(target)
                {
                    cloned-targets +=
                      [ clone-template $(t) dont-recurse : $(new-source) : $(new-project) ] ;
                }
            }
            local cloned-targets2 ;
            for local t in $(cloned-targets)
            {
                $(t).action $(cloned-action) ;

                cloned-targets2 += [ register $(t) ] ;

            }
            $(cloned-action).set-targets $(cloned-targets2) ;
            cloned = $(cloned-targets2[1]) ;
        }
        else
        {
            cloned = [ register $(cloned) ] ;
        }
        return $(cloned) ;
    }
}

# Clones an action template: helper for clone-template above.
local rule clone-action-template ( action from cloned-from : new-source : new-project )
{
    local targets ;
    local sources ;

    for local t in [ $(action).sources ]
    {
        sources += [ clone-template $(t) : $(new-source) : $(new-project) ] ;
    }

    local action-class = [ modules.peek $(action) : __class__ ] ;

    local ps = [ $(action).properties ] ;
    local cloned = [ new $(action-class) [ $(action).targets ] : $(sources)
                     : [ $(action).action-name ] : $(ps) ] ;

    return $(cloned) ;
}

class subvariant
{
    import sequence ;    
    import type ;
    
    rule __init__ ( main-target # The instance of main-target class
        : property-set                     # Properties requested for this target
        : sources *
        : build-properties                 # Actually used properties
        : sources-usage-requirements       # Properties propagated from sources
        : created-targets * )              # Top-level created targets
    {        
        self.main-target = $(main-target) ;        
        self.properties = $(property-set) ;
        self.sources = $(sources) ;
        self.build-properties = $(build-properties) ;
        self.sources-usage-requirements = $(sources-usage-requirements) ;
        self.created-targets = $(created-targets) ;

        # Pre-compose the list of other dependency graphs, on which this one
        # depends
        local deps = [ $(build-properties).get <implicit-dependency> ] ;
        for local d in $(deps)
        {
            self.other-dg += [ $(d:G=).creating-subvariant ] ;
        }
        
        self.other-dg = [ sequence.unique $(self.other-dg) ] ;
    }
    
               
    rule main-target ( )
    {
        return $(self.main-target) ;
    }
    
    rule created-targets ( ) 
    {
        return $(self.created-targets) ;
    }
    
    rule requested-properties ( )
    {
        return $(self.properties) ;
    }
    
    rule build-properties ( )
    {
        return $(self.build-properties) ;
    }
        
    rule sources-usage-requirements ( )
    {
        return $(self.sources-usage-requirements) ;
    }
    
    rule set-usage-requirements ( usage-requirements )
    {
        self.usage-requirements = $(usage-requirements) ;
    }
    
    rule usage-requirements ( )
    {
        return $(self.usage-requirements) ;
    }
            
    # Returns all targets referenced by this subvariant,
    # either directly or indirectly, and 
    # either as sources, or as dependency properties.
    rule all-referenced-targets ( )
    {
        # Find directly referenced targets.
        local deps = [ $(self.build-properties).dependency ] ;
        local all-targets = $(self.sources) $(deps:G=) ;
        
        # Find other subvariants.
        local r ;
        for local t in $(all-targets)
        {            
            r += [ $(t).creating-subvariant ] ;
        }
        r = [ sequence.unique $(r) ] ;
        for local s in $(r) 
        {
            if $(s) != $(__name__)
            {
                all-targets += [ $(s).all-referenced-targets ] ;
            }            
        }
        return $(all-targets) ;                        
    }
               
    # Returns the properties which specify implicit include paths to
    # generated headers. This traverses all targets in this subvariant,
    # and subvariants referred by <implcit-dependecy>properties.
    # For all targets which are of type 'target-type' (or for all targets,
    # if 'target-type' is not specified), the result will contain
    # <$(feature)>path-to-that-target.
    rule implicit-includes ( feature : target-type ? )
    {
        local key = ii$(feature)-$(target-type:E="") ;
        if ! $($(key))-is-nonempty
        {
            local target-paths = [ all-target-directories $(target-type) ] ;    
            target-paths = [ sequence.unique $(target-paths) ] ;
            local result = $(target-paths:G=$(feature)) ;
            if ! $(result)
            {
                result = "" ;
            }            
            $(key) = $(result) ;
        }
        if $($(key)) = ""
        {
            return ;
        }
        else
        {
            return $($(key)) ;
        }        
    }
        
    rule all-target-directories ( target-type ? )
    {
        if ! $(self.target-directories)
        {
            compute-target-directories $(target-type) ;
        }                
        return $(self.target-directories) ;
    }
    
    rule compute-target-directories ( target-type ? )
    {   
        local result ;
        for local t in $(self.created-targets)
        {
            if $(target-type) && ! [ type.is-derived [ $(t).type ] $(target-type) ] 
            {
                # Skip target which is of wrong type.
            }
            else
            {                
                result = [ sequence.merge $(result) : [ $(t).path ] ] ;
            }            
        }
        for local d in $(self.other-dg)
        {
            result += [ $(d).all-target-directories $(target-type) ] ;
        }
        self.target-directories = $(result) ;
    }   
}