⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 generators.jam

📁 C++的一个好库。。。现在很流行
💻 JAM
📖 第 1 页 / 共 3 页
字号:
#  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.

#  Manages 'generators' --- objects which can do transformation between different
#  target types and contain algorithm for finding transformation from sources
#  to targets.
#
#  The main entry point to this module is generators.construct rule. It is given
#  a list of source targets, desired target type and a set of properties.
#  It starts by selecting 'viable generators', which have any chances of producing
#  the desired target type with the required properties. Generators are ranked and
#  a set of most specific ones is selected.
# 
#  The most specific generators have their 'run' methods called, with the properties
#  and list of sources. Each one selects target which can be directly consumed, and
#  tries to convert the remaining ones to the types it can consume. This is done
#  by recursively calling 'construct' with all consumable types.
#
#  If the generator has collected all the targets it needs, it creates targets 
#  corresponding to result, and returns it. When all generators have been run,
#  results of one of them are selected and returned as result.
#
#  It's quite possible that 'construct' returns more targets that it was asked for.
#  For example, it was asked to target type EXE, but the only found generators produces
#  both EXE and TDS (file with debug) information. The extra target will be returned.
#
#  Likewise, when generator tries to convert sources to consumable types, it can get
#  more targets that it was asked for. The question is what to do with extra targets.
#  Boost.Build attempts to convert them to requested types, and attempts as early as
#  possible. Specifically, this is done after invoking each generator. (Later I'll 
#  document the rationale for trying extra target conversion at that point).
#
#  That early conversion is not always desirable. Suppose a generator got a source of
#  type Y and must consume one target of type X_1 and one target of type X_2.
#  When converting Y to X_1 extra target of type Y_2 is created. We should not try to
#  convert it to type X_1, because if we do so, the generator will get two targets
#  of type X_1, and will be at loss as to which one to use. Because of that, the
#  'construct' rule has a parameter, telling if multiple targets can be returned. If
#  the parameter is false, conversion of extra targets is not performed.

import "class" : is-a new ;
import container ;
import utility : str equal ;
import set sequence ;
import assert ;
import virtual-target ;
import property-set ;

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

# Outputs a debug message if generators debugging is on.
# Each element of 'message' is checked to see if it's class instance.
# If so, instead of the value, the result of 'str' call is output.
local rule generators.dout ( message * )
{
    if $(.debug)
    {                
        ECHO [ sequence.transform utility.str : $(message) ] ;
    }    
}


local rule indent ( )
{
    return $(.indent:J="") ;
}

local rule increase-indent ( )
{
    .indent += "    " ;
}

local rule decrease-indent ( )
{
    .indent = $(.indent[2-]) ;
}

# Takes a vector of 'virtual-target' instances and makes a normalized
# representation, which is the same for given set of targets,
# regardless of their order.
rule normalize-target-list ( targets )
{
    local v = [ $(targets).get ] ;
    $(targets).set $(v[1]) [ sequence.insertion-sort $(v[2-]) : utility.less ] ;
}

# Creates a generator
class generator 
{
    import generators ;
    import assert ;
    import generators : indent increase-indent decrease-indent generators.dout ;
    import generators ;
    import set ;
    import utility : equal ;
    import feature ;
    import errors : error ;
    import sequence ;
    import type ;
    import virtual-target ;
    import "class" : new ;
    import property ;
   
    EXPORT class@generator : indent increase-indent decrease-indent generators.dout ;
    
    rule __init__ (  
      id # identifies the generator - should be name of the rule which
         # sets up build actions
      composing ? # whether generator processes each source target in
                  # turn, converting it to required types.
                  # Ordinary generators pass all sources together to
                  # recusrive generators.construct-types call.
    
    : source-types *  # types that this generator can handle.  If
                      # empty, the generator can consume anything.
    
    : target-types-and-names +   
      # types the generator will create and, optionally, names for
      # created targets. Each element should have the form
      #    type["(" name-pattern ")"]
      # for example, obj(%_x). Name of generated target will be found
      # by replacing % with the name of source, provided explicit name
      # was not specified.
    
    : requirements *
                )
    {                    
        self.id = $(id) ;
        self.composing = $(composing) ;
        self.source-types = $(source-types) ;
        self.target-types-and-names = $(target-types-and-names) ;
        self.requirements = $(requirements) ;
        
        for local e in $(target-types-and-names)
        {        
            # Create three parallel lists: one with the list of target types,
            # and two other with prefixes and postfixes to be added to target 
            # name. We use parallel lists for prefix and postfix (as opposed
            # to mapping), because given target type might occur several times,
            # for example "H H(%_symbols)".
            local m = [ MATCH ([^\\(]*)(\\((.*)%(.*)\\))? : $(e) ] ;
            self.target-types += $(m[1]) ;
            self.name-prefix += $(m[3]:E="") ;
            self.name-postfix += $(m[4]:E="") ;
        }
                                    
        # Note that 'transform' here, is the same as 'for_each'.
        sequence.transform type.validate : $(self.source-types) ;
        sequence.transform type.validate : $(self.target-types) ;
    }
                            
    ############## End of constructor #################
    
    rule id ( )
    {
        return $(self.id) ;
    }

    # Returns the list of target type the generator accepts.
    rule source-types ( )
    {
        return $(self.source-types) ;
    }

    # Returns the list of target types that this generator produces.
    # It is assumed to be always the same -- i.e. it cannot change depending
    # list of sources.    
    rule target-types ( )
    {
        return $(self.target-types) ;
    }
    
    # Returns the required properties for this generator. Properties
    # in returned set must be present in build properties if this 
    # generator is to be used. If result has grist-only element,
    # that build properties must include some value of that feature.
    # XXX: remove this method?
    rule requirements ( )
    {
        return $(self.requirements) ;
    }
    
    # Returns a true value if the generator can be run with the specified 
    # properties.
    rule match-rank ( property-set-to-match )
    {
        # See if generator's requirements are satisfied by
        # 'properties'.  Treat a feature name in requirements
        # (i.e. grist-only element), as matching any value of the
        # feature.
        local all-requirements = [ requirements ] ;
        
        local property-requirements feature-requirements ;
        for local r in $(all-requirements)
        {
            if $(r:G=)
            {
                property-requirements += $(r) ;
            }
            else
            {
                feature-requirements += $(r) ;
            }                      
        }

        local properties-to-match = [ $(property-set-to-match).raw ] ;
        if $(property-requirements) in $(properties-to-match) 
           && $(feature-requirements) in $(properties-to-match:G)
        {
            return true ;
        }
        else
        {
            return ;
        }
    }
        
    # Returns another generator which differers from $(self) in
    # - id
    # - value to <toolset> feature in properties
    rule clone ( new-id : new-toolset-properties + )
    {
        return [ new $(__class__) $(new-id) $(self.composing)
                 : $(self.source-types)
                 : $(self.target-types-and-names) 
                 # Note: this does not remove any subfeatures of <toolset>
                 # which might cause problems
                 : [ property.change $(self.requirements) : <toolset> ]
                   $(new-toolset-properties)
               ] ;
    }
    
    # Creates another generator that is the same as $(self), except that
    # if 'base' is in target types of $(self), 'type' will in target types
    # of the new generator.
    rule clone-and-change-target-type ( base : type )
    {
        local target-types ;
        for local t in $(self.target-types-and-names)
        {
            local m = [ MATCH ([^\\(]*)(\\(.*\\))? : $(t) ] ;
            if $(m) = $(base)
            {
                target-types += $(type)$(m[2]:E="") ;
            }
            else
            {
                target-types += $(t) ;
            }                                        
        }
                
        return [ new $(__class__) $(self.id) $(self.composing)
                 : $(self.source-types)
                 : $(target-types) 
                 : $(self.requirements)
               ] ;
    }

    
    # Tries to invoke this generator on the given sources. Returns a
    # list of generated targets (instances of 'virtual-target').
    rule run ( project  # Project for which the targets are generated
               name ?   # Determines the name of 'name' attribute for 
                        # all generated targets. See 'generated-targets' method.
               : property-set # Desired properties for generated targets.
               : sources +  # Source targets.
            )
    {       
        generators.dout [ indent ] "  generator" $(self.id) ;
        generators.dout [ indent ] "  multiple:" $(mutliple) ;
        generators.dout [ indent ] "  composing:" $(self.composing) ;        
        
        if ! $(self.composing) && $(sources[2]) && $(self.source-types[2])
        {
            errors.error "Unsupported source/source-type combination" ;
        }
               
        # We don't run composing generators if no name is specified. The reason
        # is that composing generator combines several targets, which can have
        # different names, and it cannot decide which name to give for produced
        # target. Therefore, the name must be passed.
        #
        # This in effect, means that composing generators are runnable only
        # at top-level of transofrmation graph, or if name is passed explicitly.
        # Thus, we dissallow composing generators in the middle. For example, the
        # transofrmation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE won't be allowed 
        # (the OBJ -> STATIC_LIB generator is composing)
        if ! $(self.composing) || $(name)
        {            
            run-really $(project) $(name) : $(property-set) : $(sources) ;
        }        
    }
    
    
    rule run-really ( project name ? : property-set : sources + )
    {
        # Targets that this generator will consume directly.
        local consumed = ;
        # Targets that can't be consumed and will be returned as-is.
        local bypassed = ;
        
        if $(self.composing)
        {
            convert-multiple-sources-to-consumable-types $(project)
              : $(property-set) : $(sources) : consumed bypassed ;            
        }
        else
        {               
            convert-to-consumable-types $(project) $(name) : 
              $(property-set) : $(sources) 
                :
                : consumed bypassed ;
        }
                
        local result ;
        if $(consumed)  
        {            
            result = [ construct-result $(consumed) : $(project) $(name) 
                     : $(property-set) ] ;
        }
                            
                
        if $(result)
        {
           generators.dout [ indent ] "  SUCCESS: " $(result) ;
        }
        else
        {
            generators.dout [ indent ] "  FAILURE" ;
        }
        generators.dout ;
        return $(result) ;        
    }

    # Constructs the dependency graph that will be returned by this 
    # generator
    rule construct-result ( 
        consumed + # Already prepared list of consumable targets
                   # If generator requires several source files will contain 
                   # exactly len $(self.source-types) targets with matching types
                   # Otherwise, might contain several targets with the type of 
                   # $(self.source-types[1])                               
        : project name ? 
        : property-set  # Properties to be used for all actions create here
    )
    {
        local result ;
        # If this is 1->1 transformation, apply it to all consumed targets in order.
        if ! $(self.source-types[2]) && ! $(self.composing)
        {
            generators.dout [ indent ] "alt1" ;
            for local r in $(consumed)
            {                
                result += [ generated-targets $(r) : $(property-set) : $(project) $(name) ] ; #(targets) ;
            }
        }
        else
        {
            generators.dout [ indent ] "alt2 : consumed is" $(consumed) ;
            if $(consumed) 
            {
                result += [ generated-targets $(consumed) : $(property-set) 
                            : $(project) $(name) ] ;
            }                        
        }
        return $(result) ;
    }   
    
    # Constructs targets that are created after consuming 'sources'.
    # The result will be the list of virtual-target, which the same length
    # as 'target-types' attribute and with corresponding types.
    # 
    # When 'name' is empty, all source targets must have the same value of 
    # the 'name' attribute, which will be used instead of the 'name' argument.
    #
    # The value of 'name' attribute for each generated target will be equal to
    # the 'name' parameter if there's no name pattern for this type. Otherwise,
    # the '%' symbol in the name pattern will be replaced with the 'name' parameter 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -