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

📄 generators.jam

📁 boost库提供标准的C++ API 配合dev 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 ;

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 )
{
    $(targets).sort ;                   
}

# Creates a generator
class generator 
{
    import generators ;
    import assert ;
    import generators : indent increase-indent decrease-indent generators.dout ;
    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
    
    : 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) ;
        if $(self.target-types) != *
        {        
            sequence.transform type.validate : $(self.target-types) ;
        }
        
        
        # Add the bases of the target types to our optional properties.
        # Since optional properties improve a generator's match-rank, a
        # generator which requires only a base target type will not be as
        # good a match as a generator which requires one of its derived
        # target types (and thus has the base type as an optional
        # property).
        self.optional-properties
          = [ feature.expand <base-target-type>$(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 the list of properties which increase this generator's 
    # specificity for the given target-type.
    # TODO: comment is out of date.
    rule optional-properties ( )
    {
        return $(self.optional-properties) ;
    }
    
    # Returns a number telling how well generator's properties match
    # the passed properties, or an empty list if the generator can't
    # be run at all.
    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)
        {
            # We only count matched optional properties to achive better
            # orthogonality:
            # - required properties are what is needed to generators to run
            # - optional properties are used to resolve ambiguities
            #
            # For example, we have two generators
            # - CPP->OBJ, with required property <toolset>msvc
            # - RC->OBJ, without required properties
            #
            # If we could required properties, then the first one will be
            # considered more specific, and the second one will never be
            # tried.
            return 
              [ sequence.length
                  [ set.intersection 
                      [ optional-properties ] 
                    : $(properties-to-match)
                  ]
              ] ;
        }            
    }
        
    # Returns another generator which differers from $(self) in
    # - id
    # - value to <toolset> feature in properties
    rule clone ( new-id : new-toolset-name )
    {
        return [ new $(__class__) $(new-id)
                 : $(self.source-types)
                 : $(self.target-types-and-names) 
                 : [ property.change $(self.requirements) 
                     : <toolset> $(new-toolset-name)
                   ]
               ] ;
    }
    
    # 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.
               : multiple ? # Allows the rule to run generator several times and return
                          # multiple targets of the same type. When this argument is not
                          # given, 'run' will return the list of targets, which is equal
                          # in size to the list of target types, and where type of
                          # each target is the same as the corresponding element of
                          # target type list. Non-empty value allows to return several
                          # such target lists, joined together.
             )
    {
        # multiple = true ; # The tests seem to tolerate this; will
                          # remove the parameter altogether in the
                          # next revision to see what I learn -- DWA 2003/5/6
        
        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" ;
        }

        if $(self.source-types[2])
        {
            multiple = ;
        }
        
        # 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) : $(multiple) ;
        }        
    }
    
    
    rule run-really ( project name ? : property-set : sources + : multiple ? )
    {
                
        # 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) : $(multiple)
                :
                : consumed bypassed ;
        }
                
        local result ;
        if $(consumed)  
        {            
            result = [ construct-result $(consumed) : $(project) $(name) 
                     : $(property-set) ] ;
            result += $(bypassed) ;
        }
                            
                
        if $(result)
        {
           generators.dout [ indent ] "  SUCCESS: " $(result) ;
        }
        else
        {

⌨️ 快捷键说明

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