Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/tools/build/v2/build/generators.jam @ 12

Last change on this file since 12 was 12, checked in by landauf, 18 years ago

added boost

File size: 38.8 KB
Line 
1#  Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
2#  distribute this software is granted provided this copyright notice appears in
3#  all copies. This software is provided "as is" without express or implied
4#  warranty, and with no claim as to its suitability for any purpose.
5
6#  Manages 'generators' --- objects which can do transformation between different
7#  target types and contain algorithm for finding transformation from sources
8#  to targets.
9#
10#  The main entry point to this module is generators.construct rule. It is given
11#  a list of source targets, desired target type and a set of properties.
12#  It starts by selecting 'viable generators', which have any chances of producing
13#  the desired target type with the required properties. Generators are ranked and
14#  a set of most specific ones is selected.
15#
16#  The most specific generators have their 'run' methods called, with the properties
17#  and list of sources. Each one selects target which can be directly consumed, and
18#  tries to convert the remaining ones to the types it can consume. This is done
19#  by recursively calling 'construct' with all consumable types.
20#
21#  If the generator has collected all the targets it needs, it creates targets
22#  corresponding to result, and returns it. When all generators have been run,
23#  results of one of them are selected and returned as result.
24#
25#  It's quite possible that 'construct' returns more targets that it was asked for.
26#  For example, it was asked to target type EXE, but the only found generators produces
27#  both EXE and TDS (file with debug) information. The extra target will be returned.
28#
29#  Likewise, when generator tries to convert sources to consumable types, it can get
30#  more targets that it was asked for. The question is what to do with extra targets.
31#  Boost.Build attempts to convert them to requested types, and attempts as early as
32#  possible. Specifically, this is done after invoking each generator. (Later I'll
33#  document the rationale for trying extra target conversion at that point).
34#
35#  That early conversion is not always desirable. Suppose a generator got a source of
36#  type Y and must consume one target of type X_1 and one target of type X_2.
37#  When converting Y to X_1 extra target of type Y_2 is created. We should not try to
38#  convert it to type X_1, because if we do so, the generator will get two targets
39#  of type X_1, and will be at loss as to which one to use. Because of that, the
40#  'construct' rule has a parameter, telling if multiple targets can be returned. If
41#  the parameter is false, conversion of extra targets is not performed.
42
43import "class" : is-a new ;
44import container ;
45import utility : str equal ;
46import set sequence ;
47import assert ;
48import virtual-target ;
49import property-set ;
50
51if "--debug-generators" in [ modules.peek : ARGV ]
52{   
53    .debug = true ;
54}
55
56# Outputs a debug message if generators debugging is on.
57# Each element of 'message' is checked to see if it's class instance.
58# If so, instead of the value, the result of 'str' call is output.
59local rule generators.dout ( message * )
60{
61    if $(.debug)
62    {               
63        ECHO [ sequence.transform utility.str : $(message) ] ;
64    }   
65}
66
67
68local rule indent ( )
69{
70    return $(.indent:J="") ;
71}
72
73local rule increase-indent ( )
74{
75    .indent += "    " ;
76}
77
78local rule decrease-indent ( )
79{
80    .indent = $(.indent[2-]) ;
81}
82
83# Takes a vector of 'virtual-target' instances and makes a normalized
84# representation, which is the same for given set of targets,
85# regardless of their order.
86rule normalize-target-list ( targets )
87{
88    local v = [ $(targets).get ] ;
89    $(targets).set $(v[1]) [ sequence.insertion-sort $(v[2-]) : utility.less ] ;
90}
91
92# Creates a generator
93class generator
94{
95    import generators ;
96    import assert ;
97    import generators : indent increase-indent decrease-indent generators.dout ;
98    import generators ;
99    import set ;
100    import utility : equal ;
101    import feature ;
102    import errors : error ;
103    import sequence ;
104    import type ;
105    import virtual-target ;
106    import "class" : new ;
107    import property ;
108   
109    EXPORT class@generator : indent increase-indent decrease-indent generators.dout ;
110   
111    rule __init__ ( 
112      id # identifies the generator - should be name of the rule which
113         # sets up build actions
114      composing ? # whether generator processes each source target in
115                  # turn, converting it to required types.
116                  # Ordinary generators pass all sources together to
117                  # recusrive generators.construct-types call.
118   
119    : source-types *  # types that this generator can handle.  If
120                      # empty, the generator can consume anything.
121   
122    : target-types-and-names +   
123      # types the generator will create and, optionally, names for
124      # created targets. Each element should have the form
125      #    type["(" name-pattern ")"]
126      # for example, obj(%_x). Name of generated target will be found
127      # by replacing % with the name of source, provided explicit name
128      # was not specified.
129   
130    : requirements *
131                )
132    {                   
133        self.id = $(id) ;
134        self.composing = $(composing) ;
135        self.source-types = $(source-types) ;
136        self.target-types-and-names = $(target-types-and-names) ;
137        self.requirements = $(requirements) ;
138       
139        for local e in $(target-types-and-names)
140        {       
141            # Create three parallel lists: one with the list of target types,
142            # and two other with prefixes and postfixes to be added to target
143            # name. We use parallel lists for prefix and postfix (as opposed
144            # to mapping), because given target type might occur several times,
145            # for example "H H(%_symbols)".
146            local m = [ MATCH ([^\\(]*)(\\((.*)%(.*)\\))? : $(e) ] ;
147            self.target-types += $(m[1]) ;
148            self.name-prefix += $(m[3]:E="") ;
149            self.name-postfix += $(m[4]:E="") ;
150        }
151                                   
152        # Note that 'transform' here, is the same as 'for_each'.
153        sequence.transform type.validate : $(self.source-types) ;
154        sequence.transform type.validate : $(self.target-types) ;
155    }
156                           
157    ############## End of constructor #################
158   
159    rule id ( )
160    {
161        return $(self.id) ;
162    }
163
164    # Returns the list of target type the generator accepts.
165    rule source-types ( )
166    {
167        return $(self.source-types) ;
168    }
169
170    # Returns the list of target types that this generator produces.
171    # It is assumed to be always the same -- i.e. it cannot change depending
172    # list of sources.   
173    rule target-types ( )
174    {
175        return $(self.target-types) ;
176    }
177   
178    # Returns the required properties for this generator. Properties
179    # in returned set must be present in build properties if this
180    # generator is to be used. If result has grist-only element,
181    # that build properties must include some value of that feature.
182    # XXX: remove this method?
183    rule requirements ( )
184    {
185        return $(self.requirements) ;
186    }
187   
188    # Returns a true value if the generator can be run with the specified
189    # properties.
190    rule match-rank ( property-set-to-match )
191    {
192        # See if generator's requirements are satisfied by
193        # 'properties'.  Treat a feature name in requirements
194        # (i.e. grist-only element), as matching any value of the
195        # feature.
196        local all-requirements = [ requirements ] ;
197       
198        local property-requirements feature-requirements ;
199        for local r in $(all-requirements)
200        {
201            if $(r:G=)
202            {
203                property-requirements += $(r) ;
204            }
205            else
206            {
207                feature-requirements += $(r) ;
208            }                     
209        }
210
211        local properties-to-match = [ $(property-set-to-match).raw ] ;
212        if $(property-requirements) in $(properties-to-match)
213           && $(feature-requirements) in $(properties-to-match:G)
214        {
215            return true ;
216        }
217        else
218        {
219            return ;
220        }
221    }
222       
223    # Returns another generator which differers from $(self) in
224    # - id
225    # - value to <toolset> feature in properties
226    rule clone ( new-id : new-toolset-properties + )
227    {
228        return [ new $(__class__) $(new-id) $(self.composing)
229                 : $(self.source-types)
230                 : $(self.target-types-and-names)
231                 # Note: this does not remove any subfeatures of <toolset>
232                 # which might cause problems
233                 : [ property.change $(self.requirements) : <toolset> ]
234                   $(new-toolset-properties)
235               ] ;
236    }
237   
238    # Creates another generator that is the same as $(self), except that
239    # if 'base' is in target types of $(self), 'type' will in target types
240    # of the new generator.
241    rule clone-and-change-target-type ( base : type )
242    {
243        local target-types ;
244        for local t in $(self.target-types-and-names)
245        {
246            local m = [ MATCH ([^\\(]*)(\\(.*\\))? : $(t) ] ;
247            if $(m) = $(base)
248            {
249                target-types += $(type)$(m[2]:E="") ;
250            }
251            else
252            {
253                target-types += $(t) ;
254            }                                       
255        }
256               
257        return [ new $(__class__) $(self.id) $(self.composing)
258                 : $(self.source-types)
259                 : $(target-types)
260                 : $(self.requirements)
261               ] ;
262    }
263
264   
265    # Tries to invoke this generator on the given sources. Returns a
266    # list of generated targets (instances of 'virtual-target').
267    rule run ( project  # Project for which the targets are generated
268               name ?   # Determines the name of 'name' attribute for
269                        # all generated targets. See 'generated-targets' method.
270               : property-set # Desired properties for generated targets.
271               : sources +  # Source targets.
272            )
273    {       
274        generators.dout [ indent ] "  generator" $(self.id) ;
275        generators.dout [ indent ] "  multiple:" $(mutliple) ;
276        generators.dout [ indent ] "  composing:" $(self.composing) ;       
277       
278        if ! $(self.composing) && $(sources[2]) && $(self.source-types[2])
279        {
280            errors.error "Unsupported source/source-type combination" ;
281        }
282               
283        # We don't run composing generators if no name is specified. The reason
284        # is that composing generator combines several targets, which can have
285        # different names, and it cannot decide which name to give for produced
286        # target. Therefore, the name must be passed.
287        #
288        # This in effect, means that composing generators are runnable only
289        # at top-level of transofrmation graph, or if name is passed explicitly.
290        # Thus, we dissallow composing generators in the middle. For example, the
291        # transofrmation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE won't be allowed
292        # (the OBJ -> STATIC_LIB generator is composing)
293        if ! $(self.composing) || $(name)
294        {           
295            run-really $(project) $(name) : $(property-set) : $(sources) ;
296        }       
297    }
298   
299   
300    rule run-really ( project name ? : property-set : sources + )
301    {
302        # Targets that this generator will consume directly.
303        local consumed = ;
304        # Targets that can't be consumed and will be returned as-is.
305        local bypassed = ;
306       
307        if $(self.composing)
308        {
309            convert-multiple-sources-to-consumable-types $(project)
310              : $(property-set) : $(sources) : consumed bypassed ;           
311        }
312        else
313        {               
314            convert-to-consumable-types $(project) $(name) :
315              $(property-set) : $(sources)
316                :
317                : consumed bypassed ;
318        }
319               
320        local result ;
321        if $(consumed) 
322        {           
323            result = [ construct-result $(consumed) : $(project) $(name)
324                     : $(property-set) ] ;
325        }
326                           
327               
328        if $(result)
329        {
330           generators.dout [ indent ] "  SUCCESS: " $(result) ;
331        }
332        else
333        {
334            generators.dout [ indent ] "  FAILURE" ;
335        }
336        generators.dout ;
337        return $(result) ;       
338    }
339
340    # Constructs the dependency graph that will be returned by this
341    # generator
342    rule construct-result (
343        consumed + # Already prepared list of consumable targets
344                   # If generator requires several source files will contain
345                   # exactly len $(self.source-types) targets with matching types
346                   # Otherwise, might contain several targets with the type of
347                   # $(self.source-types[1])                               
348        : project name ?
349        : property-set  # Properties to be used for all actions create here
350    )
351    {
352        local result ;
353        # If this is 1->1 transformation, apply it to all consumed targets in order.
354        if ! $(self.source-types[2]) && ! $(self.composing)
355        {
356            generators.dout [ indent ] "alt1" ;
357            for local r in $(consumed)
358            {               
359                result += [ generated-targets $(r) : $(property-set) : $(project) $(name) ] ; #(targets) ;
360            }
361        }
362        else
363        {
364            generators.dout [ indent ] "alt2 : consumed is" $(consumed) ;
365            if $(consumed)
366            {
367                result += [ generated-targets $(consumed) : $(property-set)
368                            : $(project) $(name) ] ;
369            }                       
370        }
371        return $(result) ;
372    }   
373   
374    # Constructs targets that are created after consuming 'sources'.
375    # The result will be the list of virtual-target, which the same length
376    # as 'target-types' attribute and with corresponding types.
377    #
378    # When 'name' is empty, all source targets must have the same value of
379    # the 'name' attribute, which will be used instead of the 'name' argument.
380    #
381    # The value of 'name' attribute for each generated target will be equal to
382    # the 'name' parameter if there's no name pattern for this type. Otherwise,
383    # the '%' symbol in the name pattern will be replaced with the 'name' parameter
384    # to obtain the 'name' attribute.
385    #
386    # For example, if targets types are T1 and T2(with name pattern "%_x"), suffixes
387    # for T1 and T2 are .t1 and t2, and source if foo.z, then created files would
388    # be "foo.t1" and "foo_x.t2". The 'name' attribute actually determined the
389    # basename of a file.
390    #
391    # Note that this pattern mechanism has nothing to do with implicit patterns
392    # in make. It's a way to produce target which name is different for name of
393    # source.
394    rule generated-targets ( sources + : property-set : project name ? )
395    {
396        if ! $(name)
397        {
398            # Determine the name of the produced target from the
399            # names of the sources. The simple case if when a name
400            # of source has single dot. Then, we take the part before
401            # dot. Several dots can be caused by:
402            # - Using source file like a.host.cpp
403            # - A type which suffix has a dot. Say, we can
404            #   type 'host_cpp' with extension 'host.cpp'.
405            # In the first case, we want to take the part till the last
406            # dot. In the second case -- no sure, but for now take
407            # the part till the last dot too.
408            name = [ utility.basename [ $(sources[1]).name ] ] ;
409                       
410            for local s in $(sources[2])
411            {
412                local n2 = [ utility.basename [ $(s).name ] ] ;
413                if $(n2) != $(name)
414                {
415                    error "$(self.id): source targets have different names: cannot determine target name" ;
416                }
417            }
418
419            # Names of sources might include directory. We should strip it.
420            name = $(name:D=) ;
421        }
422       
423        # Assign an action for each target
424        local action = [ action-class ] ;
425        local a = [ class.new $(action) $(sources) : $(self.id) :
426                    $(property-set) ] ;
427               
428        # Create generated target for each target type.
429        local targets ;
430        local pre = $(self.name-prefix) ;
431        local post = $(self.name-postfix) ;
432        for local t in $(self.target-types)                 
433        {     
434            local generated-name = $(pre[1])$(name)$(post[1]) ;
435            pre = $(pre[2-]) ;
436            post = $(post[2-]) ;
437           
438            targets += [ class.new file-target $(generated-name)
439              : $(t) : $(project) : $(a) ] ;
440        }                 
441       
442        return [ sequence.transform virtual-target.register : $(targets) ] ;
443    }   
444   
445    # Attempts to convert 'source' to the types that this generator can
446    # handle. The intention is to produce the set of targets can should be
447    # used when generator is run.
448    rule convert-to-consumable-types ( project name ? :
449        property-set : sources + 
450        : only-one ? # convert 'source' to only one of source types
451                     # if there's more that one possibility, report an
452                     # error
453        : consumed-var # name of variable which recieves all targets which
454                       # can be consumed.
455          bypassed-var # name variable which recieves all targets which
456                       # cannot be consumed 
457    )
458    {       
459        # We're likely to be passed 'consumed' and 'bypassed'
460        # var names. Use "_" to avoid name conflicts.
461        local _consumed ;
462        local _bypassed ;
463        local missing-types ;
464
465        if $(sources[2])
466        {
467            # Don't know how to handle several sources yet. Just try
468            # to pass the request to other generator
469            missing-types = $(self.source-types) ;
470        }
471        else
472        {           
473            consume-directly $(sources) : _consumed : missing-types ;
474        }
475       
476        # No need to search for transformation if
477        # some source type has consumed source and
478        # no more source types are needed.
479        if $(only-one) && $(_consumed)
480        {
481            missing-types = ;
482        }
483           
484        #TODO: we should check that only one source type
485        #if create of 'only-one' is true.
486        # TODO: consider if consuned/bypassed separation should
487        # be done by 'construct-types'.
488                   
489        if $(missing-types)
490        {           
491            local transformed = [ generators.construct-types $(project) $(name)
492              : $(missing-types) : $(property-set) : $(sources) ] ;
493                               
494            # Add targets of right type to 'consumed'. Add others to
495            # 'bypassed'. The 'generators.construct' rule has done
496            # its best to convert everything to the required type.
497            # There's no need to rerun it on targets of different types.
498               
499            # NOTE: ignoring usage requirements
500            for local t in $(transformed[2-])
501            {
502                if [ $(t).type ] in $(missing-types)
503                {
504                    _consumed += $(t) ;
505                }
506                else
507                {
508                    _bypassed += $(t) ;
509                }
510            }               
511        }   
512       
513        _consumed = [ sequence.unique $(_consumed) ] ;       
514        _bypassed = [ sequence.unique $(_bypassed) ] ;
515       
516        # remove elements of '_bypassed' that are in '_consumed'
517       
518        # Suppose the target type of current generator, X is produced from
519        # X_1 and X_2, which are produced from Y by one generator.
520        # When creating X_1 from Y, X_2 will be added to 'bypassed'
521        # Likewise, when creating X_2 from Y, X_1 will be added to 'bypassed'
522        # But they are also in 'consumed'. We have to remove them from
523        # bypassed, so that generators up the call stack don't try to convert
524        # them.
525           
526        # In this particular case, X_1 instance in 'consumed' and X_1 instance
527        # in 'bypassed' will be the same: because they have the same source and
528        # action name, and 'virtual-target.register' won't allow two different
529        # instances. Therefore, it's OK to use 'set.difference'.
530       
531        _bypassed = [ set.difference $(_bypassed) : $(_consumed) ] ;
532       
533               
534        $(consumed-var) += $(_consumed) ;
535        $(bypassed-var) += $(_bypassed) ;
536    }
537   
538    # Converts several files to consumable types.
539    rule convert-multiple-sources-to-consumable-types
540      ( project : property-set : sources * : consumed-var bypassed-var )
541    {
542        # We process each source one-by-one, trying to convert it to
543        # a usable type.
544        local failed ;
545        while $(sources) && ! $(failed)
546        {
547            local _c ;
548            local _b ;
549            # TODO: need to check for failure on each source.
550            convert-to-consumable-types $(project) : $(property-set)
551              : $(sources[1]) : true : _c _b ;
552            if ! $(_c)
553            {
554                generators.dout [ indent ] " failed to convert " $(sources[1]) ;
555                # failed = true ;
556            }
557            $(consumed-var) += $(_c) ;           
558            $(bypassed-var) += $(_b) ;
559            sources = $(sources[2-]) ;
560        }           
561        if $(failed)
562        {
563            $(consumed-var) = ;
564            $(bypassed-var) = ;
565        }       
566    }
567       
568    rule consume-directly ( source : consumed-var : missing-types-var )
569    {
570        local real-source-type = [ $(source).type ] ;
571       
572        # If there are no source types, we can consume anything
573        local source-types = $(self.source-types) ;
574        source-types ?= $(real-source-type) ;
575       
576        for local st in $(source-types)
577        {
578            # The 'source' if of right type already)
579            if $(real-source-type) = $(st) ||
580              [ type.is-derived $(real-source-type) $(st) ]
581            {
582                $(consumed-var) += $(source) ;
583            }
584            else
585            {
586               $(missing-types-var) += $(st) ;
587            }
588        }       
589    }
590   
591   
592    # Returns the class to be used to actions. Default implementation
593    # returns "action".
594    rule action-class ( )
595    {
596        return "action" ;
597    }       
598}
599
600import errors : error ;
601
602.generators = ;
603
604# Registers new generator instance 'g'.
605rule register ( g )
606{
607    .generators += $(g) ;
608                   
609    for local t in [ $(g).target-types ]
610    {           
611        .generators.$(t) += $(g) ;
612    }   
613   
614    # Update the set of generators for toolset
615   
616    # TODO: should we check that generator with this id
617    # is not already registered. For example, the fop.jam
618    # module intentionally declared two generators with the
619    # same id, so such check will break it.
620    local id = [ $(g).id ] ;
621       
622    # Some generators have multiple periods in their name, so the
623    # normal $(id:S=) won't generate the right toolset name.
624    # e.g. if id = gcc.compile.c++, then
625    # .generators-for-toolset.$(id:S=) will append to
626    # .generators-for-toolset.gcc.compile, which is a separate
627    # value from .generators-for-toolset.gcc. Correcting this
628    # makes generator inheritance work properly.
629    # See also inherit-generators in module toolset
630    local base = $(id) ;
631    while $(base:S)
632    {
633        base = $(base:B) ;
634    }
635    .generators-for-toolset.$(base) += $(g) ;
636}
637   
638# Creates new instance of the 'generator' class and registers it.
639# Retursn the creates instance.
640# Rationale: the instance is returned so that it's possible to first register
641# a generator and then call 'run' method on that generator, bypassing all
642# generator selection.
643rule register-standard ( id : source-types * : target-types + : requirements * )
644
645{
646    local g = [ new generator $(id) : $(source-types) : $(target-types)
647      : $(requirements) ] ;
648    register $(g) ;   
649    return $(g) ;
650}
651
652# Creates new instance of the 'composing-generator' class and
653# registers it.
654rule register-composing ( id : source-types + : target-types + : requirements * )
655{
656    local g = [ new generator $(id) true : $(source-types)
657                : $(target-types) : $(requirements) ] ;
658    register $(g) ;
659    return $(g) ;
660}
661
662# Returns all generators which belong to 'toolset', i.e. which
663# ids are $(toolset).<something>
664rule generators-for-toolset ( toolset )
665{
666    return $(.generators-for-toolset.$(toolset)) ;
667}
668
669rule override ( overrider-id : overridee-id )
670{
671    .override.$(overrider-id) += $(overridee-id) ;   
672}
673
674
675
676   
677# Set if results of the current generators search are going to be cached
678# This means no futher attempts to cache generators search should be
679# made.
680.caching = ;
681
682# Returns a list of source type which can possibly be converted
683# to 'target-type' by some chain of generator invocation.
684#
685# More formally, takes all generators for 'target-type' and
686# returns union of source types for those generators and result
687# of calling itself recusrively on source types.
688local rule viable-source-types-real ( target-type )
689{
690    local generators ;
691
692    local t = [ type.all-bases $(target-type) ] ;
693   
694    local result ;
695    # 't' is the list of types which are not yet processed   
696    while $(t)
697    {
698        # Find all generators for current type.
699        # Unlike 'find-viable-generators' we don't care about property-set.
700        local generators = $(.generators.$(t[1])) ;
701        t = $(t[2-]) ;
702       
703
704       
705        while $(generators)
706        {
707            local g = $(generators[1]) ;
708            generators = $(generators[2-]) ;
709           
710            if ! [ $(g).source-types ]
711            {
712                # Empty source types -- everything can be accepted
713                result = * ;
714                # This will terminate this loop.
715                generators = ;
716                # This will terminate outer loop.
717                t = ;
718            }
719             
720            for local source-type in [ $(g).source-types ]
721            {
722                if ! $(source-type) in $(result)
723                {
724                    # If generator accepts 'source-type' it
725                    # will happily accept any type derived from it
726                    local all = [ type.all-derived $(source-type) ] ;
727                    for local n in $(all)
728                    {
729                        if ! $(n) in $(result)
730                        {   
731                            t += $(n) ;
732                            result += $(n) ;
733                        }   
734                    }                   
735                }               
736            }           
737        }       
738    }         
739       
740    result = [ sequence.unique $(result) ] ;
741   
742    return $(result) ;               
743}
744
745# Helper rule, caches the result of 'viable-source-types-real'.
746rule viable-source-types ( target-type )
747{
748    local key = .vst.$(target-type) ;
749    if ! $($(key))
750    {
751        local v = [ viable-source-types-real $(target-type) ] ;
752        if ! $(v)
753        {
754            v = none ;
755        }
756        $(key) = $(v) ;
757    }
758   
759    if $($(key)) != none
760    {
761        return $($(key)) ;
762    }       
763}
764
765# Returns the list of source types, which, when passed to 'run'
766# method of 'generator', has some change of being eventually used
767# (probably after conversion by other generators)
768rule viable-source-types-for-generator-real ( generator )
769{       
770    local source-types = [ $(generator).source-types ] ;
771    if ! $(source-types)
772    {
773        # If generator does not specify any source types,
774        # it might be special generator like builtin.lib-generator
775        # which just relays to other generators. Return '*' to
776        # indicate that any source type is possibly OK, since we don't
777        # know for sure.
778        return * ;
779    }
780    else
781    {           
782        local result ;
783        for local s in $(source-types)
784        {
785            result += [ type.all-derived $(s) ]
786              [ generators.viable-source-types $(s) ] ;
787        }
788        result = [ sequence.unique $(result) ] ;       
789        if * in $(result)
790        {
791            result = * ;
792        }       
793        return $(result) ;
794    }       
795}   
796
797# Helper rule, caches the result of 'viable-source-types-for-genrator'.
798local rule viable-source-types-for-generator ( generator )
799{
800    local key = .vstg.$(generator) ;
801    if ! $($(key))
802    {
803        local v = [ viable-source-types-for-generator-real $(generator) ] ;
804        if ! $(v)
805        {
806            v = none ;
807        }
808        $(key) = $(v) ;       
809    }
810   
811    if $($(key)) != none
812    {
813        return $($(key)) ;
814    }       
815}
816
817
818
819# Returns usage requirements + list of created targets
820local rule try-one-generator-really ( project name ? : generator :
821    target-type : property-set : sources * )
822{
823    local targets =
824      [ $(generator).run $(project) $(name)
825                       : $(property-set)
826                       : $(sources)
827      ] ;
828           
829    local usage-requirements ;
830    if $(targets) && [ class.is-a $(targets[1]) : property-set ]
831    {
832        usage-requirements = $(targets[1]) ;
833        targets = $(targets[2-]) ;
834    }
835    else
836    {
837        usage-requirements = [ property-set.empty ] ;
838    }
839   
840    generators.dout [ indent ] "  generator" [ $(generator).id ] " spawned " ;
841    generators.dout [ indent ] " " $(targets) ;
842    if $(targets)
843    {
844        return $(usage-requirements) $(targets) ;
845    }   
846}
847
848# Checks if generator invocation can be pruned, because it's guaranteed
849# to fail. If so, quickly returns empty list. Otherwise, calls
850# try-one-generator-really.
851local rule try-one-generator ( project name ? : generator :
852    target-type : property-set : sources * )
853{   
854    local source-types ;
855    for local s in $(sources)
856    {
857        source-types += [ $(s).type ] ;
858    }
859    local viable-source-types =
860      [ viable-source-types-for-generator $(generator) ] ;
861   
862    if  $(source-types) && $(viable-source-types) != * &&
863      ! [ set.intersection $(source-types) : $(viable-source-types) ]
864    {
865        local id = [ $(generator).id ] ;
866        generators.dout [ indent ] "generator '$(id)' pruned" ;
867        #generators.dout [ indent ] "source-types" '$(source-types)' ;
868        #generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ;
869    }         
870    else {
871        return [ try-one-generator-really $(project) $(name)
872          : $(generator)
873            : $(target-type) : $(property-set) : $(sources) ] ;
874    }       
875}
876
877
878
879rule construct-types ( project name ? : target-types + :
880    property-set : sources + )
881{
882    local result ;
883    local matched-types ;
884    local usage-requirements = [ property-set.empty ] ;
885    for local t in $(target-types)
886    {
887        local r = [ construct $(project) $(name) : $(t) : $(property-set) :
888          $(sources) ] ;
889        if $(r)
890        {
891            usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
892            result += $(r[2-]) ;
893            matched-types += $(t) ;
894        }
895    }
896    # TODO: have to introduce parameter controlling if
897    # several types can be matches and add appropriate
898    # checks
899
900    # TODO: need to review the documentation for
901    # 'construct' to see if it should return $(source) even
902    # if nothing can be done with it. Currents docs seem to
903    # imply that, contrary to the behaviour.
904    if $(result)
905    {
906        return $(usage-requirements) $(result) ;
907    }
908    else
909    {
910        return $(usage-requirements) $(sources) ;
911    }
912}
913
914# Ensures all 'targets' have types. If this is not so, exists with
915# error.
916local rule ensure-type ( targets * )
917{
918    for local t in $(targets)
919    {
920        if ! [ $(t).type ]
921        {
922            errors.error "target" [ $(t).str ] "has no type" ;
923        }       
924    }   
925}
926   
927# Returns generators which can be used to construct target of specified type
928# with specified properties. Uses the following algorithm:
929# - iterates over requested target-type and all it's bases (in the order returned bt
930#   type.all-bases.
931# - for each type find all generators that generate that type and which requirements
932#   are satisfied by properties.
933# - if the set of generators is not empty, returns that set.
934#
935# Note: this algorithm explicitly ignores generators for base classes if there's
936# at least one generator for requested target-type.
937local rule find-viable-generators-aux ( target-type : property-set )
938{
939    # Select generators that can create the required target type.
940    local viable-generators = ;
941    local generator-rank = ;
942
943    import type ;
944    local t = [ type.all-bases $(target-type) ] ;
945   
946    generators.dout  [ indent ] find-viable-generators target-type= $(target-type)
947      property-set= [ $(property-set).as-path ]
948          ;
949   
950    # Get the lit of generators for the requested type.
951    # If no generator is registered, try base type, and so on.
952    local generators ;
953    while $(t[1])
954    {
955        generators.dout  [ indent ] "trying type" $(t[1]) ;   
956        if $(.generators.$(t[1]))
957        {
958            generators.dout [ indent ] "there are generators for this type" ;
959            generators = $(.generators.$(t[1])) ;
960           
961            if $(t[1]) != $(target-type)
962            {
963                # We're here, when no generators for target-type are found,
964                # but there are some generators for a base type.
965                # We'll try to use them, but they will produce targets of
966                # base type, not of 'target-type'. So, we clone the generators
967                # and modify the list of target types.
968                local generators2 ;
969                for local g in $(generators)
970                {
971                    # generators.register adds generator to the list of generators
972                    # for toolsets, which is a bit strange, but should work.
973                    # That list is only used when inheriting toolset, which
974                    # should have being done before generators are run.
975                    generators2 += [
976                      $(g).clone-and-change-target-type $(t[1]) : $(target-type) ] ;
977                    generators.register $(generators2[-1]) ;
978                }               
979                generators = $(generators2) ;
980            }                       
981            t = ;
982        }       
983        t = $(t[2-]) ;           
984    }
985   
986   
987    for local g in $(generators)
988    {
989        generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ;
990           
991        local m = [ $(g).match-rank $(property-set) ] ;
992        if $(m)
993        {
994            generators.dout [ indent ] "  is viable" ;
995            viable-generators += $(g) ;
996        }                                   
997    }
998                           
999    return $(viable-generators) ;
1000}
1001
1002local rule find-viable-generators ( target-type : property-set )
1003{
1004    local key = $(target-type).$(property-set) ;
1005    local l = $(.fv.$(key)) ;
1006    if ! $(l)
1007    {
1008        l = [ find-viable-generators-aux $(target-type) : $(property-set) ] ;
1009        if ! $(l)
1010        {
1011            l = none ;
1012        }       
1013        .fv.$(key) = $(l) ;
1014    }
1015   
1016    if $(l) = none
1017    {
1018        l = ;
1019    }
1020                   
1021    local viable-generators ;
1022    for local g in $(l)
1023    {
1024        # Avoid trying the same generator twice on different levels.       
1025        if ! $(g) in $(.active-generators)
1026        {
1027            viable-generators += $(g) ;
1028        }       
1029    }
1030   
1031    # Generators which override 'all'.
1032    local all-overrides ;
1033    # Generators which are overriden
1034    local overriden-ids ;   
1035    for local g in $(viable-generators)
1036    {
1037        local id = [ $(g).id ] ;
1038        local this-overrides = $(.override.$(id)) ;
1039        overriden-ids += $(this-overrides) ;
1040        if all in $(this-overrides)
1041        {
1042            all-overrides += $(g) ;
1043        }       
1044    }         
1045    if $(all-overrides)
1046    {
1047        viable-generators = $(all-overrides) ;
1048    }
1049    local result ;
1050    for local g in $(viable-generators)
1051    {
1052        if ! [ $(g).id ] in $(overriden-ids)
1053        {
1054            result += $(g) ;
1055        }       
1056    }
1057       
1058    return $(result) ;
1059}   
1060       
1061.construct-stack = ;
1062
1063# Attempts to construct target by finding viable generators, running them
1064# and selecting the dependency graph
1065local rule construct-really (
1066   project name ? : target-type : property-set : sources * )
1067{
1068    viable-generators = [ find-viable-generators $(target-type) : $(property-set) ] ;
1069                       
1070    generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ]
1071      " viable generators" ;
1072   
1073    local result ;
1074    for local g in $(viable-generators)
1075    {
1076        # This variable will be restored on exit from this scope.
1077        local .active-generators = $(g) $(.active-generators) ;
1078       
1079        local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type) :
1080          $(property-set) : $(sources) ] ;
1081       
1082        if $(r)
1083        {
1084            if $(result)
1085            {
1086                error "Ambiguity found when searching for best transformation" ;
1087            }
1088            else
1089            {
1090                result = $(r) ;
1091            }           
1092        }
1093    }
1094   
1095    return $(result) ;
1096}       
1097   
1098       
1099# Attempts to create target of 'target-type' with 'properties'
1100# from 'sources'. The 'sources' are treated as a collection of
1101# *possible* ingridients -- i.e. it is not required to consume
1102# them all. If 'multiple' is true, the rule is allowed to return
1103# several targets of 'target-type'.         
1104#
1105#
1106# Returns a list of target. When this invocation is first instance of
1107# 'construct' in stack, returns only targets of requested 'target-type',
1108# otherwise, returns also unused sources and additionally generated
1109# targets.   
1110#
1111# Does not return target which are not of 'allowed-type' or of type derived from
1112# it. If 'allowed-type' is not specified, it's defaulted to 'target-type'.
1113# See lib-target-class for use case of this.
1114rule construct ( project name ? : target-type : property-set * : sources *
1115   : allowed-type * )
1116{
1117    if (.construct-stack)
1118    {
1119        ensure-type $(sources) ;
1120    }
1121       
1122    .construct-stack += 1 ;
1123
1124    increase-indent ;
1125
1126    if $(.debug)
1127    {       
1128        generators.dout [ indent ] "*** construct" $(target-type) ;
1129   
1130        for local s in $(sources)
1131        {
1132            generators.dout [ indent ] "    from" $(s) ;
1133        }
1134        generators.dout [ indent ] "    properties:" [ $(property-set).raw ] ;       
1135    }
1136   
1137               
1138    local result = [ construct-really $(project) $(name) 
1139      : $(target-type) : $(property-set) : $(sources) ] ;
1140                   
1141    decrease-indent ;
1142       
1143    .construct-stack = $(.construct-stack[2-]) ;
1144               
1145   
1146    return $(result) ;       
1147}
1148
Note: See TracBrowser for help on using the repository browser.