• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2002. Vladimir Prus
2# Copyright 2006. Rene Rivera
3#
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or copy at
6# http://www.boost.org/LICENSE_1_0.txt)
7
8# Manages 'generators' --- objects which can do transformation between different
9# target types and contain algorithm for finding transformation from sources to
10# targets.
11#
12# The main entry point to this module is generators.construct rule. It is given
13# a list of source targets, desired target type and a set of properties. It
14# starts by selecting 'viable generators', which have any chances of producing
15# the desired target type with the required properties. Generators are ranked
16# and a set of the most specific ones is selected.
17#
18# The most specific generators have their 'run' methods called, with the
19# properties and list of sources. Each one selects a target which can be
20# directly consumed, and tries to convert the remaining ones to the types it can
21# consume. This is done by recursively calling 'construct' with all consumable
22# types.
23#
24# If the generator has collected all the targets it needs, it creates targets
25# corresponding to result, and returns it. When all generators have been run,
26# results of one of them are selected and returned as a result.
27#
28# It is quite possible for 'construct' to return more targets that it was asked
29# for. For example, if it were asked to generate a target of type EXE, but the
30# only found generator produces both EXE and TDS (file with debug) information.
31# The extra target will be returned.
32#
33# Likewise, when generator tries to convert sources to consumable types, it can
34# get more targets that it was asked for. The question is what to do with extra
35# targets. B2 attempts to convert them to requested types, and attempts
36# that as early as possible. Specifically, this is done after invoking each
37# generator. TODO: An example is needed to document the rationale for trying
38# extra target conversion at that point.
39#
40# In order for the system to be able to use a specific generator instance 'when
41# needed', the instance needs to be registered with the system using
42# generators.register() or one of its related rules. Unregistered generators may
43# only be run explicitly and will not be considered by B2 when when
44# converting between given target types.
45
46import "class" : new ;
47import property-set ;
48import sequence ;
49import set ;
50import type ;
51import utility ;
52import virtual-target ;
53
54
55if "--debug-generators" in [ modules.peek : ARGV ]
56{
57    .debug = true ;
58}
59
60
61# Updated cached viable source target type information as needed after a new
62# target type gets defined. This is needed because if a target type is a viable
63# source target type for some generator then all of the target type's derived
64# target types should automatically be considered as viable source target types
65# for the same generator as well. Does nothing if a non-derived target type is
66# passed to it.
67#
68rule update-cached-information-with-a-new-type ( type )
69{
70    local base-type = [ type.base $(type) ] ;
71    if $(base-type)
72    {
73        for local g in $(.vstg-cached-generators)
74        {
75            if $(base-type) in $(.vstg.$(g))
76            {
77                .vstg.$(g) += $(type) ;
78            }
79        }
80
81        for local t in $(.vst-cached-types)
82        {
83            if $(base-type) in $(.vst.$(t))
84            {
85                .vst.$(t) += $(type) ;
86            }
87        }
88    }
89}
90
91
92# Clears cached viable source target type information except for target types
93# and generators with all source types listed as viable. Should be called when
94# something invalidates those cached values by possibly causing some new source
95# types to become viable.
96#
97local rule invalidate-extendable-viable-source-target-type-cache ( )
98{
99    local generators-with-cached-source-types = $(.vstg-cached-generators) ;
100    .vstg-cached-generators = ;
101    for local g in $(generators-with-cached-source-types)
102    {
103        if $(.vstg.$(g)) = *
104        {
105            .vstg-cached-generators += $(g) ;
106        }
107        else
108        {
109            .vstg.$(g) = ;
110        }
111    }
112
113    local types-with-cached-source-types = $(.vst-cached-types) ;
114    .vst-cached-types = ;
115    for local t in $(types-with-cached-source-types)
116    {
117        if $(.vst.$(t)) = *
118        {
119            .vst-cached-types += $(t) ;
120        }
121        else
122        {
123            .vst.$(t) = ;
124        }
125    }
126}
127
128
129# Outputs a debug message if generators debugging is on. Each element of
130# 'message' is checked to see if it is a class instance. If so, instead of the
131# value, the result of 'str' call is output.
132#
133local rule generators.dout ( message * )
134{
135    if $(.debug)
136    {
137        ECHO [ sequence.transform utility.str : $(message) ] ;
138    }
139}
140
141
142local rule indent ( )
143{
144    return $(.indent:J="") ;
145}
146
147
148local rule increase-indent ( )
149{
150    .indent += "    " ;
151}
152
153
154local rule decrease-indent ( )
155{
156    .indent = $(.indent[2-]) ;
157}
158
159
160# Models a generator.
161#
162class generator
163{
164    import "class" : new ;
165    import feature ;
166    import generators : indent increase-indent decrease-indent generators.dout ;
167    import utility ;
168    import path ;
169    import property ;
170    import property-set ;
171    import sequence ;
172    import set ;
173    import toolset ;
174    import type ;
175    import virtual-target ;
176
177    EXPORT class@generator : indent increase-indent decrease-indent
178        generators.dout ;
179
180    rule __init__ (
181        id                          # Identifies the generator - should be name
182                                    # of the rule which sets up the build
183                                    # actions.
184
185        composing ?                 # Whether generator processes each source
186                                    # target in turn, converting it to required
187                                    # types. Ordinary generators pass all
188                                    # sources together to the recursive
189                                    # generators.construct-types call.
190
191        : source-types *            # Types that this generator can handle. If
192                                    # empty, the generator can consume anything.
193
194        : target-types-and-names +  # Types the generator will create and,
195                                    # optionally, names for created targets.
196                                    # Each element should have the form
197                                    # type["(" name-pattern ")"], for example,
198                                    # obj(%_x). Generated target name will be
199                                    # found by replacing % with the name of
200                                    # source, provided an explicit name was not
201                                    # specified.
202
203        : requirements *
204    )
205    {
206        self.id = $(id) ;
207        self.rule-name = $(id) ;
208        self.composing = $(composing) ;
209        self.source-types = $(source-types) ;
210        self.target-types-and-names = $(target-types-and-names) ;
211        self.requirements = $(requirements) ;
212
213        for local e in $(target-types-and-names)
214        {
215            # Create three parallel lists: one with the list of target types,
216            # and two other with prefixes and postfixes to be added to target
217            # name. We use parallel lists for prefix and postfix (as opposed to
218            # mapping), because given target type might occur several times, for
219            # example "H H(%_symbols)".
220            local m = [ MATCH "([^\\(]*)(\\((.*)%(.*)\\))?" : $(e) ] ;
221            self.target-types += $(m[1]) ;
222            self.name-prefix += $(m[3]:E="") ;
223            self.name-postfix += $(m[4]:E="") ;
224        }
225
226        for local r in [ requirements ]
227        {
228            if $(r:G=)
229            {
230                self.property-requirements += $(r) ;
231            }
232            else
233            {
234                self.feature-requirements += $(r) ;
235            }
236        }
237
238        # Note that 'transform' here, is the same as 'for_each'.
239        sequence.transform type.validate : $(self.source-types) ;
240        sequence.transform type.validate : $(self.target-types) ;
241
242        local relevant-for-generator =
243            [ sequence.transform utility.ungrist : $(requirements:G) ] ;
244        self.relevant-features = [ property-set.create <relevant>$(relevant-for-generator) ] ;
245    }
246
247    ################# End of constructor #################
248
249    rule id ( )
250    {
251        return $(self.id) ;
252    }
253
254    # Returns the list of target type the generator accepts.
255    #
256    rule source-types ( )
257    {
258        return $(self.source-types) ;
259    }
260
261    # Returns the list of target types that this generator produces. It is
262    # assumed to be always the same -- i.e. it can not change depending on some
263    # provided list of sources.
264    #
265    rule target-types ( )
266    {
267        return $(self.target-types) ;
268    }
269
270    # Returns the required properties for this generator. Properties in returned
271    # set must be present in build properties if this generator is to be used.
272    # If result has grist-only element, that build properties must include some
273    # value of that feature.
274    #
275    # XXX: remove this method?
276    #
277    rule requirements ( )
278    {
279        return $(self.requirements) ;
280    }
281
282    rule set-rule-name ( rule-name )
283    {
284        self.rule-name = $(rule-name) ;
285    }
286
287    rule rule-name ( )
288    {
289        return $(self.rule-name) ;
290    }
291
292    # Returns a true value if the generator can be run with the specified
293    # properties.
294    #
295    rule match-rank ( property-set-to-match )
296    {
297        # See if generator requirements are satisfied by 'properties'. Treat a
298        # feature name in requirements (i.e. grist-only element), as matching
299        # any value of the feature.
300
301        if [ $(property-set-to-match).contains-raw $(self.property-requirements) ] &&
302            [ $(property-set-to-match).contains-features $(self.feature-requirements) ]
303        {
304            return true ;
305        }
306        else
307        {
308            return ;
309        }
310    }
311
312    # Returns another generator which differs from $(self) in
313    #   - id
314    #   - value to <toolset> feature in properties
315    #
316    rule clone ( new-id : new-toolset-properties + )
317    {
318        local g = [ new $(__class__) $(new-id) $(self.composing) :
319            $(self.source-types) : $(self.target-types-and-names) :
320            # Note: this does not remove any subfeatures of <toolset> which
321            # might cause problems.
322            [ property.change $(self.requirements) : <toolset> ]
323            $(new-toolset-properties) ] ;
324        return $(g) ;
325    }
326
327    # Creates another generator that is the same as $(self), except that if
328    # 'base' is in target types of $(self), 'type' will in target types of the
329    # new generator.
330    #
331    rule clone-and-change-target-type ( base : type )
332    {
333        local target-types ;
334        for local t in $(self.target-types-and-names)
335        {
336            local m = [ MATCH "([^\\(]*)(\\(.*\\))?" : $(t) ] ;
337            if $(m) = $(base)
338            {
339                target-types += $(type)$(m[2]:E="") ;
340            }
341            else
342            {
343                target-types += $(t) ;
344            }
345        }
346
347        local g = [ new $(__class__) $(self.id) $(self.composing) :
348            $(self.source-types) : $(target-types) : $(self.requirements) ] ;
349        if $(self.rule-name)
350        {
351            $(g).set-rule-name $(self.rule-name) ;
352        }
353        return $(g) ;
354    }
355
356    # Tries to invoke this generator on the given sources. Returns a list of
357    # generated targets (instances of 'virtual-target') and optionally a set of
358    # properties to be added to the usage-requirements for all the generated
359    # targets. Returning nothing from run indicates that the generator was
360    # unable to create the target.
361    #
362    rule run
363    (
364        project         # Project for which the targets are generated.
365        name ?          # Used when determining the 'name' attribute for all
366                        # generated targets. See the 'generated-targets' method.
367        : property-set  # Desired properties for generated targets.
368        : sources +     # Source targets.
369    )
370    {
371        generators.dout [ indent ] "  ** generator" $(self.id) ;
372        generators.dout [ indent ] "  composing:" $(self.composing) ;
373
374        if ! $(self.composing) && $(sources[2]) && $(self.source-types[2])
375        {
376            import errors : error : errors.error ;
377            errors.error "Unsupported source/source-type combination" ;
378        }
379
380        # We do not run composing generators if no name is specified. The reason
381        # is that composing generator combines several targets, which can have
382        # different names, and it cannot decide which name to give for produced
383        # target. Therefore, the name must be passed.
384        #
385        # This in effect, means that composing generators are runnable only at
386        # the top-level of a transformation graph, or if their name is passed
387        # explicitly. Thus, we dissallow composing generators in the middle. For
388        # example, the transformation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE
389        # will not be allowed as the OBJ -> STATIC_LIB generator is composing.
390        if ! $(self.composing) || $(name)
391        {
392            run-really $(project) $(name) : $(property-set) : $(sources) ;
393        }
394    }
395
396    rule run-really ( project name ? : property-set : sources + )
397    {
398        # Targets that this generator will consume directly.
399        local consumed = ;
400        # Targets that can not be consumed and will be returned as-is.
401        local bypassed = ;
402
403        if $(self.composing)
404        {
405            consumed = [ convert-multiple-sources-to-consumable-types $(project)
406                : $(property-set) : $(sources) ] ;
407        }
408        else
409        {
410            consumed = [ convert-to-consumable-types $(project) $(name)
411                : $(property-set) : $(sources) ] ;
412        }
413
414        local result ;
415        if $(consumed[2])
416        {
417            result = [ construct-result $(consumed[2-]) : $(project) $(name) :
418                [ $(property-set).add $(consumed[1]) ] ] ;
419        }
420
421        if $(result)
422        {
423            generators.dout [ indent ] "  SUCCESS: " $(result) ;
424        }
425        else
426        {
427            generators.dout [ indent ] "  FAILURE" ;
428        }
429        generators.dout ;
430        if $(result)
431        {
432            # Make sure that we propagate usage-requirements up the stack.
433            return [ $(result[1]).add $(consumed[1]) ] $(result[2-]) ;
434        }
435    }
436
437    # Constructs the dependency graph to be returned by this generator.
438    #
439    rule construct-result
440    (
441        consumed +        # Already prepared list of consumable targets.
442                          # Composing generators may receive multiple sources
443                          # all of which will have types matching those in
444                          # $(self.source-types). Non-composing generators with
445                          # multiple $(self.source-types) will receive exactly
446                          # len $(self.source-types) sources with types matching
447                          # those in $(self.source-types). And non-composing
448                          # generators with only a single source type may
449                          # receive multiple sources with all of them of the
450                          # type listed in $(self.source-types).
451        : project name ?
452        : property-set    # Properties to be used for all actions created here.
453    )
454    {
455        local result ;
456
457        local relevant = [ toolset.relevant $(self.rule-name) ] ;
458        relevant = [ $(relevant).add $(self.relevant-features) ] ;
459        property-set = [ $(property-set).add $(relevant) ] ;
460
461        # If this is a 1->1 transformation, apply it to all consumed targets in
462        # order.
463        if ! $(self.source-types[2]) && ! $(self.composing)
464        {
465            for local r in $(consumed)
466            {
467                result += [ generated-targets $(r) : $(property-set) :
468                    $(project) $(name) ] ;
469            }
470        }
471        else if $(consumed)
472        {
473            result += [ generated-targets $(consumed) : $(property-set) :
474                $(project) $(name) ] ;
475        }
476        if $(result)
477        {
478            return $(relevant) $(result) ;
479        }
480    }
481
482    # Determine target name from fullname (maybe including path components)
483    # Place optional prefix and postfix around basename
484    #
485    rule determine-target-name ( fullname  : prefix ? : postfix ? )
486    {
487        # See if we need to add directory to the target name.
488        local dir  = $(fullname:D) ;
489        local name = $(fullname:B) ;
490
491        name = $(prefix:E=)$(name) ;
492        name = $(name)$(postfix:E=) ;
493
494        if $(dir)
495            # Never append '..' to target path.
496            && ! [ MATCH .*(\\.\\.).* : $(dir) ]
497            && ! [ path.is-rooted $(dir) ]
498        {
499            # Relative path is always relative to the source directory. Retain
500            # it, so that users can have files with the same name in two
501            # different subdirectories.
502            name = $(dir)/$(name) ;
503        }
504        return $(name) ;
505    }
506
507    # Determine the name of the produced target from the names of the sources.
508    #
509    rule determine-output-name ( sources + )
510    {
511        # The simple case if when a name of source has single dot. Then, we take
512        # the part before dot. Several dots can be caused by:
513        #   - using source file like a.host.cpp, or
514        #   - a type whose suffix has a dot. Say, we can type 'host_cpp' with
515        #     extension 'host.cpp'.
516        # In the first case, we want to take the part up to the last dot. In the
517        # second case -- not sure, but for now take the part up to the last dot
518        # too.
519        name = [ utility.basename [ $(sources[1]).name ] ] ;
520        for local s in $(sources[2-])
521        {
522            if [ utility.basename [ $(s).name ] ] != $(name)
523            {
524                import errors : error : errors.error ;
525                errors.error "$(self.id): source targets have different names: cannot determine target name" ;
526            }
527        }
528        return [ determine-target-name [ $(sources[1]).name ] ] ;
529    }
530
531    # Constructs targets that are created after consuming 'sources'. The result
532    # will be the list of virtual-target, which has the same length as the
533    # 'target-types' attribute and with corresponding types.
534    #
535    # When 'name' is empty, all source targets must have the same 'name'
536    # attribute value, which will be used instead of the 'name' argument.
537    #
538    # The 'name' attribute value for each generated target will be equal to the
539    # 'name' parameter if there is no name pattern for this type. Otherwise, the
540    # '%' symbol in the name pattern will be replaced with the 'name' parameter
541    # to obtain the 'name' attribute.
542    #
543    # For example, if targets types are T1 and T2 (with name pattern "%_x"),
544    # suffixes for T1 and T2 are .t1 and .t2, and source is foo.z, then created
545    # files would be "foo.t1" and "foo_x.t2". The 'name' attribute actually
546    # determines the basename of a file.
547    #
548    # Note that this pattern mechanism has nothing to do with implicit patterns
549    # in make. It is a way to produce a target whose name is different than the
550    # name of its source.
551    #
552    rule generated-targets ( sources + : property-set : project name ? )
553    {
554        if ! $(name)
555        {
556            name = [ determine-output-name $(sources) ] ;
557        }
558
559        # Assign an action for each target.
560        local action = [ action-class ] ;
561        local a = [ class.new $(action) $(sources) : $(self.rule-name) :
562                    $(property-set) ] ;
563
564        # Create generated target for each target type.
565        local targets ;
566        local pre = $(self.name-prefix) ;
567        local post = $(self.name-postfix) ;
568        for local t in $(self.target-types)
569        {
570            local generated-name = $(pre[1])$(name:BS)$(post[1]) ;
571            generated-name = $(generated-name:R=$(name:D)) ;
572            pre = $(pre[2-]) ;
573            post = $(post[2-]) ;
574
575            targets += [ class.new file-target $(generated-name) : $(t) :
576                $(project) : $(a) ] ;
577        }
578
579        return [ sequence.transform virtual-target.register : $(targets) ] ;
580    }
581
582    # Attempts to convert 'sources' to targets of types that this generator can
583    # handle. The intention is to produce the set of targets that can be used
584    # when the generator is run.
585    #
586    rule convert-to-consumable-types
587    (
588        project name ?
589        : property-set
590        : sources +
591        : only-one ?    # Convert 'source' to only one of the source types. If
592                        # there is more that one possibility, report an error.
593    )
594    {
595        local _consumed ;
596        local missing-types ;
597        local usage-requirements ;
598
599        if $(sources[2])
600        {
601            # Do not know how to handle several sources yet. Just try to pass
602            # the request to other generator.
603            missing-types = $(self.source-types) ;
604        }
605        else
606        {
607            local temp = [ consume-directly $(sources) ] ;
608            if $(temp[1])
609            {
610                usage-requirements = [ property-set.empty ] ;
611                _consumed = $(temp[1]) ;
612            }
613            missing-types = $(temp[2-]) ;
614        }
615
616        # No need to search for transformation if some source type has consumed
617        # source and no more source types are needed.
618        if $(only-one) && $(_consumed)
619        {
620            missing-types = ;
621        }
622
623        # TODO: we should check that only one source type is created if
624        # 'only-one' is true.
625
626        if $(missing-types)
627        {
628            local transformed = [ generators.construct-types $(project) $(name)
629                : $(missing-types) : $(property-set) : $(sources) ] ;
630
631            # Add targets of right type to 'consumed'. Add others to 'bypassed'.
632            # The 'generators.construct' rule has done its best to convert
633            # everything to the required type. There is no need to rerun it on
634            # targets of different types.
635
636            usage-requirements = $(transformed[1]) ;
637            for local t in $(transformed[2-])
638            {
639                if [ $(t).type ] in $(missing-types)
640                {
641                    _consumed += $(t) ;
642                }
643            }
644        }
645
646        return $(usage-requirements) [ sequence.unique $(_consumed) ] ;
647    }
648
649    # Converts several files to consumable types. Called for composing
650    # generators only.
651    #
652    rule convert-multiple-sources-to-consumable-types ( project : property-set :
653        sources * )
654    {
655        local result ;
656        # We process each source one-by-one, trying to convert it to a usable
657        # type.
658        if ! $(self.source-types)
659        {
660            # Anything is acceptable
661            return [ property-set.empty ] $(sources) ;
662        }
663        else
664        {
665            local usage-requirements = [ property-set.empty ] ;
666            local acceptible-types = [ sequence.unique
667                [ sequence.transform type.all-derived : $(self.source-types) ] ] ;
668            for local source in $(sources)
669            {
670                if ! [ $(source).type ] in $(acceptible-types)
671                {
672                    local transformed = [ generators.construct-types $(project)
673                        : $(self.source-types) : $(property-set) : $(source) ] ;
674                    for local t in $(transformed[2-])
675                    {
676                        if [ $(t).type ] in $(self.source-types)
677                        {
678                            result += $(t) ;
679                        }
680                    }
681                    if ! $(transformed)
682                    {
683                        generators.dout [ indent ] " failed to convert " $(source) ;
684                    }
685                    else
686                    {
687                        usage-requirements = [ $(usage-requirements).add $(transformed[1]) ] ;
688                    }
689                }
690                else
691                {
692                    result += $(source) ;
693                }
694            }
695            return $(usage-requirements) [ sequence.unique $(result) : stable ] ;
696        }
697    }
698
699    rule consume-directly ( source )
700    {
701        local real-source-type = [ $(source).type ] ;
702
703        # If there are no source types, we can consume anything.
704        local source-types = $(self.source-types) ;
705        source-types ?= $(real-source-type) ;
706
707        local result = "" ;
708        local missing-types ;
709
710        for local st in $(source-types)
711        {
712            # The 'source' if of the right type already.
713            if $(real-source-type) = $(st) || [ type.is-derived
714                $(real-source-type) $(st) ]
715            {
716                result = $(source) ;
717            }
718            else
719            {
720                missing-types += $(st) ;
721            }
722        }
723        return $(result) $(missing-types) ;
724    }
725
726    # Returns the class to be used to actions. Default implementation returns
727    # "action".
728    #
729    rule action-class ( )
730    {
731        return "action" ;
732    }
733}
734
735
736# Registers a new generator instance 'g'.
737#
738rule register ( g )
739{
740    .all-generators += $(g) ;
741
742    # A generator can produce several targets of the same type. We want unique
743    # occurrence of that generator in .generators.$(t) in that case, otherwise,
744    # it will be tried twice and we will get a false ambiguity.
745    for local t in [ sequence.unique [ $(g).target-types ] ]
746    {
747        .generators.$(t) += $(g) ;
748    }
749
750    # Update the set of generators for toolset.
751
752    # TODO: should we check that generator with this id is not already
753    # registered. For example, the fop.jam module intentionally declared two
754    # generators with the same id, so such check will break it.
755    local id = [ $(g).id ] ;
756
757    # Some generators have multiple periods in their name, so a simple $(id:S=)
758    # will not generate the right toolset name. E.g. if id = gcc.compile.c++,
759    # then .generators-for-toolset.$(id:S=) will append to
760    # .generators-for-toolset.gcc.compile, which is a separate value from
761    # .generators-for-toolset.gcc. Correcting this makes generator inheritance
762    # work properly. See also inherit-generators in the toolset module.
763    local base = $(id) ;
764    while $(base:S)
765    {
766        base = $(base:B) ;
767    }
768    .generators-for-toolset.$(base) += $(g) ;
769
770
771    # After adding a new generator that can construct new target types, we need
772    # to clear the related cached viable source target type information for
773    # constructing a specific target type or using a specific generator. Cached
774    # viable source target type lists affected by this are those containing any
775    # of the target types constructed by the new generator or any of their base
776    # target types.
777    #
778    # A more advanced alternative to clearing that cached viable source target
779    # type information would be to expand it with additional source types or
780    # even better - mark it as needing to be expanded on next use.
781    #
782    # Also see the http://thread.gmane.org/gmane.comp.lib.boost.build/19077
783    # mailing list thread for an even more advanced idea of how we could convert
784    # Boost Build's Jamfile processing, target selection and generator selection
785    # into separate steps which would prevent these caches from ever being
786    # invalidated.
787    #
788    # For now we just clear all the cached viable source target type information
789    # that does not simply state 'all types' and may implement a more detailed
790    # algorithm later on if it becomes needed.
791
792    invalidate-extendable-viable-source-target-type-cache ;
793}
794
795
796# Creates a new non-composing 'generator' class instance and registers it.
797# Returns the created instance. Rationale: the instance is returned so that it
798# is possible to first register a generator and then call its 'run' method,
799# bypassing the whole generator selection process.
800#
801rule register-standard ( id : source-types * : target-types + : requirements * )
802{
803    local g = [ new generator $(id) : $(source-types) : $(target-types) :
804        $(requirements) ] ;
805    register $(g) ;
806    return $(g) ;
807}
808
809
810# Creates a new composing 'generator' class instance and registers it.
811#
812rule register-composing ( id : source-types * : target-types + : requirements *
813    )
814{
815    local g = [ new generator $(id) true : $(source-types) : $(target-types) :
816        $(requirements) ] ;
817    register $(g) ;
818    return $(g) ;
819}
820
821
822# Returns all generators belonging to the given 'toolset', i.e. whose ids are
823# '$(toolset).<something>'.
824#
825rule generators-for-toolset ( toolset )
826{
827    return $(.generators-for-toolset.$(toolset)) ;
828}
829
830
831# Make generator 'overrider-id' be preferred to 'overridee-id'. If, when
832# searching for generators that could produce a target of a certain type, both
833# those generators are among viable generators, the overridden generator is
834# immediately discarded.
835#
836# The overridden generators are discarded immediately after computing the list
837# of viable generators but before running any of them.
838#
839rule override ( overrider-id : overridee-id )
840{
841    .override.$(overrider-id) += $(overridee-id) ;
842}
843
844
845# Returns a list of source type which can possibly be converted to 'target-type'
846# by some chain of generator invocation.
847#
848# More formally, takes all generators for 'target-type' and returns a union of
849# source types for those generators and result of calling itself recursively on
850# source types.
851#
852# Returns '*' in case any type should be considered a viable source type for the
853# given type.
854#
855local rule viable-source-types-real ( target-type )
856{
857    local result ;
858
859    # 't0' is the initial list of target types we need to process to get a list
860    # of their viable source target types. New target types will not be added to
861    # this list.
862    local t0 = [ type.all-bases $(target-type) ] ;
863
864    # 't' is the list of target types which have not yet been processed to get a
865    # list of their viable source target types. This list will get expanded as
866    # we locate more target types to process.
867    local t = $(t0) ;
868
869    while $(t)
870    {
871        # Find all generators for the current type. Unlike
872        # 'find-viable-generators' we do not care about the property-set.
873        local generators = $(.generators.$(t[1])) ;
874        t = $(t[2-]) ;
875
876        while $(generators)
877        {
878            local g = $(generators[1]) ;
879            generators = $(generators[2-]) ;
880
881            if ! [ $(g).source-types ]
882            {
883                # Empty source types -- everything can be accepted.
884                result = * ;
885                # This will terminate this loop.
886                generators = ;
887                # This will terminate the outer loop.
888                t = ;
889            }
890
891            for local source-type in [ $(g).source-types ]
892            {
893                if ! $(source-type) in $(result)
894                {
895                    # If a generator accepts a 'source-type' it will also
896                    # happily accept any type derived from it.
897                    for local n in [ type.all-derived $(source-type) ]
898                    {
899                        if ! $(n) in $(result)
900                        {
901                            # Here there is no point in adding target types to
902                            # the list of types to process in case they are or
903                            # have already been on that list. We optimize this
904                            # check by realizing that we only need to avoid the
905                            # original target type's base types. Other target
906                            # types that are or have been on the list of target
907                            # types to process have been added to the 'result'
908                            # list as well and have thus already been eliminated
909                            # by the previous if.
910                            if ! $(n) in $(t0)
911                            {
912                                t += $(n) ;
913                            }
914                            result += $(n) ;
915                        }
916                    }
917                }
918            }
919        }
920    }
921
922    return $(result) ;
923}
924
925
926# Helper rule, caches the result of 'viable-source-types-real'.
927#
928rule viable-source-types ( target-type )
929{
930    local key = .vst.$(target-type) ;
931    if ! $($(key))
932    {
933        .vst-cached-types += $(target-type) ;
934        local v = [ viable-source-types-real $(target-type) ] ;
935        if ! $(v)
936        {
937            v = none ;
938        }
939        $(key) = $(v) ;
940    }
941
942    if $($(key)) != none
943    {
944        return $($(key)) ;
945    }
946}
947
948
949# Returns the list of source types, which, when passed to 'run' method of
950# 'generator', has some change of being eventually used (probably after
951# conversion by other generators).
952#
953# Returns '*' in case any type should be considered a viable source type for the
954# given generator.
955#
956rule viable-source-types-for-generator-real ( generator )
957{
958    local source-types = [ $(generator).source-types ] ;
959    if ! $(source-types)
960    {
961        # If generator does not specify any source types, it might be a special
962        # generator like builtin.lib-generator which just relays to other
963        # generators. Return '*' to indicate that any source type is possibly
964        # OK, since we do not know for sure.
965        return * ;
966    }
967    else
968    {
969        local result ;
970        while $(source-types)
971        {
972            local s = $(source-types[1]) ;
973            source-types = $(source-types[2-]) ;
974            local viable-sources = [ generators.viable-source-types $(s) ] ;
975            if $(viable-sources) = *
976            {
977                result = * ;
978                source-types = ;  # Terminate the loop.
979            }
980            else
981            {
982                result += [ type.all-derived $(s) ] $(viable-sources) ;
983            }
984        }
985        return [ sequence.unique $(result) ] ;
986    }
987}
988
989
990# Helper rule, caches the result of 'viable-source-types-for-generator'.
991#
992local rule viable-source-types-for-generator ( generator )
993{
994    local key = .vstg.$(generator) ;
995    if ! $($(key))
996    {
997        .vstg-cached-generators += $(generator) ;
998        local v = [ viable-source-types-for-generator-real $(generator) ] ;
999        if ! $(v)
1000        {
1001            v = none ;
1002        }
1003        $(key) = $(v) ;
1004    }
1005
1006    if $($(key)) != none
1007    {
1008        return $($(key)) ;
1009    }
1010}
1011
1012
1013# Returns usage requirements + list of created targets.
1014#
1015local rule try-one-generator-really ( project name ? : generator : target-type
1016    : property-set : sources * )
1017{
1018    local targets =
1019        [ $(generator).run $(project) $(name) : $(property-set) : $(sources) ] ;
1020
1021    local usage-requirements ;
1022    local success ;
1023
1024    generators.dout [ indent ] returned $(targets) ;
1025
1026    if $(targets)
1027    {
1028        success = true ;
1029
1030        if  [ class.is-a $(targets[1]) : property-set ]
1031        {
1032            usage-requirements = $(targets[1]) ;
1033            targets = $(targets[2-]) ;
1034        }
1035        else
1036        {
1037            usage-requirements = [ property-set.empty ] ;
1038        }
1039    }
1040
1041    generators.dout [ indent ] "  generator" [ $(generator).id ] " spawned " ;
1042    generators.dout [ indent ] " " $(targets) ;
1043    if $(usage-requirements)
1044    {
1045        generators.dout [ indent ] "  with usage requirements:" $(usage-requirements) ;
1046    }
1047
1048    if $(success)
1049    {
1050        return $(usage-requirements) $(targets) ;
1051    }
1052}
1053
1054
1055# Checks if generator invocation can be pruned, because it is guaranteed to
1056# fail. If so, quickly returns an empty list. Otherwise, calls
1057# try-one-generator-really.
1058#
1059local rule try-one-generator ( project name ? : generator : target-type
1060    : property-set : sources * )
1061{
1062    local source-types ;
1063    for local s in $(sources)
1064    {
1065        source-types += [ $(s).type ] ;
1066    }
1067    local viable-source-types = [ viable-source-types-for-generator $(generator)
1068        ] ;
1069
1070    if  $(source-types) && $(viable-source-types) != * &&
1071        ! [ set.intersection $(source-types) : $(viable-source-types) ]
1072    {
1073        local id = [ $(generator).id ] ;
1074        generators.dout [ indent ] "  ** generator '$(id)' pruned" ;
1075        #generators.dout [ indent ] "source-types" '$(source-types)' ;
1076        #generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ;
1077    }
1078    else
1079    {
1080        return [ try-one-generator-really $(project) $(name) : $(generator) :
1081            $(target-type) : $(property-set) : $(sources) ] ;
1082    }
1083}
1084
1085
1086rule construct-types ( project name ? : target-types + : property-set
1087    : sources + )
1088{
1089    local result ;
1090    local usage-requirements = [ property-set.empty ] ;
1091    for local t in $(target-types)
1092    {
1093        local r = [ construct $(project) $(name) : $(t) : $(property-set) :
1094            $(sources) ] ;
1095        if $(r)
1096        {
1097            usage-requirements = [ $(usage-requirements).add $(r[1]) ] ;
1098            result += $(r[2-]) ;
1099        }
1100    }
1101    # TODO: have to introduce parameter controlling if several types can be
1102    # matched and add appropriate checks.
1103
1104    # TODO: need to review the documentation for 'construct' to see if it should
1105    # return $(source) even if nothing can be done with it. Currents docs seem
1106    # to imply that, contrary to the behaviour.
1107    if $(result)
1108    {
1109        return $(usage-requirements) $(result) ;
1110    }
1111    else
1112    {
1113        return $(usage-requirements) $(sources) ;
1114    }
1115}
1116
1117
1118# Ensures all 'targets' have their type. If this is not so, exists with error.
1119#
1120local rule ensure-type ( targets * )
1121{
1122    for local t in $(targets)
1123    {
1124        if ! [ $(t).type ]
1125        {
1126            import errors ;
1127            errors.error "target" [ $(t).str ] "has no type" ;
1128        }
1129    }
1130}
1131
1132
1133# Returns generators which can be used to construct target of specified type
1134# with specified properties. Uses the following algorithm:
1135# - iterates over requested target-type and all its bases (in the order returned
1136#   by type.all-bases).
1137# - for each type find all generators that generate that type and whose
1138#   requirements are satisfied by properties.
1139# - if the set of generators is not empty, returns that set.
1140#
1141# Note: this algorithm explicitly ignores generators for base classes if there
1142# is at least one generator for the requested target-type.
1143#
1144local rule find-viable-generators-aux ( target-type : property-set )
1145{
1146    # Select generators that can create the required target type.
1147    local viable-generators = ;
1148
1149    import type ;
1150    local t = $(target-type) ;
1151
1152    if $(.debug)
1153    {
1154        generators.dout [ indent ] find-viable-generators target-type= $(target-type)
1155            property-set= [ $(property-set).as-path ] ;
1156        generators.dout [ indent ] "trying type" $(target-type) ;
1157    }
1158
1159    local generators = $(.generators.$(target-type)) ;
1160    if $(generators)
1161    {
1162        if $(.debug)
1163        {
1164            generators.dout [ indent ] "there are generators for this type" ;
1165        }
1166    }
1167    else
1168    {
1169        local t = [ type.base $(target-type) ] ;
1170
1171        # Get the list of generators for the requested type. If no generator is
1172        # registered, try base type, and so on.
1173        while $(t)
1174        {
1175            if $(.debug)
1176            {
1177                generators.dout [ indent ] "trying type" $(t) ;
1178            }
1179            if $(.generators.$(t))
1180            {
1181                generators.dout [ indent ] "there are generators for this type" ;
1182                generators = $(.generators.$(t)) ;
1183
1184                # We are here because there were no generators found for
1185                # target-type but there are some generators for its base type.
1186                # We will try to use them, but they will produce targets of
1187                # base type, not of 'target-type'. So, we clone the generators
1188                # and modify the list of target types.
1189                local generators2 ;
1190                for local g in $(generators)
1191                {
1192                    # generators.register adds a generator to the list of
1193                    # generators for toolsets, which is a bit strange, but
1194                    # should work. That list is only used when inheriting a
1195                    # toolset, which should have been done before running
1196                    # generators.
1197                    generators2 += [ $(g).clone-and-change-target-type $(t) :
1198                        $(target-type) ] ;
1199                    generators.register $(generators2[-1]) ;
1200                }
1201                generators = $(generators2) ;
1202                t = ;
1203            }
1204            else
1205            {
1206                t = [ type.base $(t) ] ;
1207            }
1208        }
1209    }
1210
1211    for local g in $(generators)
1212    {
1213        if $(.debug)
1214        {
1215            generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ;
1216        }
1217
1218        if [ $(g).match-rank $(property-set) ]
1219        {
1220            if $(.debug)
1221            {
1222                generators.dout [ indent ] "  is viable" ;
1223            }
1224            viable-generators += $(g) ;
1225        }
1226    }
1227
1228    return $(viable-generators) ;
1229}
1230
1231
1232rule find-viable-generators ( target-type : property-set )
1233{
1234    local key = $(target-type).$(property-set) ;
1235    local l = $(.fv.$(key)) ;
1236    if ! $(l)
1237    {
1238        l = [ find-viable-generators-aux $(target-type) : $(property-set) ] ;
1239        if ! $(l)
1240        {
1241            l = none ;
1242        }
1243        .fv.$(key) = $(l) ;
1244    }
1245
1246    if $(l) = none
1247    {
1248        l = ;
1249    }
1250
1251    local viable-generators ;
1252    for local g in $(l)
1253    {
1254        # Avoid trying the same generator twice on different levels.
1255        if ! $(g) in $(.active-generators)
1256        {
1257            viable-generators += $(g) ;
1258        }
1259        else
1260        {
1261            generators.dout [ indent ] "   generator " [ $(g).id ] "is active, discaring" ;
1262        }
1263    }
1264
1265    # Generators which override 'all'.
1266    local all-overrides ;
1267    # Generators which are overridden.
1268    local overriden-ids ;
1269    for local g in $(viable-generators)
1270    {
1271        local id = [ $(g).id ] ;
1272        local this-overrides = $(.override.$(id)) ;
1273        overriden-ids += $(this-overrides) ;
1274        if all in $(this-overrides)
1275        {
1276            all-overrides += $(g) ;
1277        }
1278    }
1279    if $(all-overrides)
1280    {
1281        viable-generators = $(all-overrides) ;
1282    }
1283    local result ;
1284    for local g in $(viable-generators)
1285    {
1286        if ! [ $(g).id ] in $(overriden-ids)
1287        {
1288            result += $(g) ;
1289        }
1290    }
1291
1292    return $(result) ;
1293}
1294
1295
1296.construct-stack = ;
1297
1298
1299# Attempts to construct a target by finding viable generators, running them and
1300# selecting the dependency graph.
1301#
1302local rule construct-really ( project name ? : target-type : property-set :
1303    sources * )
1304{
1305    viable-generators = [ find-viable-generators $(target-type) :
1306        $(property-set) ] ;
1307
1308    generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ]
1309        " viable generators" ;
1310
1311    local result ;
1312    local generators-that-succeeded ;
1313    for local g in $(viable-generators)
1314    {
1315        # This variable will be restored on exit from this scope.
1316        local .active-generators = $(g) $(.active-generators) ;
1317
1318        local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type)
1319            : $(property-set) : $(sources) ] ;
1320
1321        if $(r)
1322        {
1323            generators-that-succeeded += $(g) ;
1324            if $(result)
1325            {
1326                ECHO "Error: ambiguity found when searching for best transformation" ;
1327                ECHO "Trying to produce type '$(target-type)' from: " ;
1328                for local s in $(sources)
1329                {
1330                    ECHO " - " [ $(s).str ] ;
1331                }
1332                ECHO "Generators that succeeded:" ;
1333                for local g in $(generators-that-succeeded)
1334                {
1335                    ECHO " - " [ $(g).id ] ;
1336                }
1337                ECHO "First generator produced: " ;
1338                for local t in $(result[2-])
1339                {
1340                    ECHO " - " [ $(t).str ] ;
1341                }
1342                ECHO "Second generator produced: " ;
1343                for local t in $(r[2-])
1344                {
1345                    ECHO " - " [ $(t).str ] ;
1346                }
1347                EXIT ;
1348            }
1349            else
1350            {
1351                result = $(r) ;
1352            }
1353        }
1354    }
1355
1356    return $(result) ;
1357}
1358
1359
1360# Attempts to create a target of 'target-type' with 'properties' from 'sources'.
1361# The 'sources' are treated as a collection of *possible* ingridients, i.e.
1362# there is no obligation to consume them all.
1363#
1364# Returns a list of targets. When this invocation is first instance of
1365# 'construct' in stack, returns only targets of requested 'target-type',
1366# otherwise, returns also unused sources and additionally generated targets.
1367#
1368# If 'top-level' is set, does not suppress generators that are already
1369# used in the stack. This may be useful in cases where a generator
1370# has to build a metatargets -- for example a target corresponding to
1371# built tool.
1372#
1373rule construct ( project name ? : target-type : property-set * : sources * : top-level ? )
1374{
1375    local saved-active ;
1376    if $(top-level)
1377    {
1378        saved-active = $(.active-generators) ;
1379        .active-generators = ;
1380    }
1381
1382    # FIXME This is probably not intended be be run unconditionally,
1383    # but changing it causes no_type to fail.
1384    if "(.construct-stack)"
1385    {
1386        ensure-type $(sources) ;
1387    }
1388
1389    .construct-stack += 1 ;
1390
1391    increase-indent ;
1392
1393    if $(.debug)
1394    {
1395        generators.dout [ indent ] "*** construct" $(target-type) ;
1396
1397        for local s in $(sources)
1398        {
1399            generators.dout [ indent ] "    from" $(s) ;
1400        }
1401        generators.dout [ indent ] "    properties:" [ $(property-set).raw ] ;
1402    }
1403
1404    local result = [ construct-really $(project) $(name) : $(target-type) :
1405        $(property-set) : $(sources) ] ;
1406
1407    decrease-indent ;
1408
1409    .construct-stack = $(.construct-stack[2-]) ;
1410
1411    if $(top-level)
1412    {
1413        .active-generators = $(saved-active) ;
1414    }
1415
1416    return $(result) ;
1417}
1418
1419# Given 'result', obtained from some generator or generators.construct, adds
1420# 'raw-properties' as usage requirements to it. If result already contains usage
1421# requirements -- that is the first element of result of an instance of the
1422# property-set class, the existing usage requirements and 'raw-properties' are
1423# combined.
1424#
1425rule add-usage-requirements ( result * : raw-properties * )
1426{
1427    if $(result)
1428    {
1429        if [ class.is-a $(result[1]) : property-set ]
1430        {
1431            return [ $(result[1]).add-raw $(raw-properties) ] $(result[2-]) ;
1432        }
1433        else
1434        {
1435            return [ property-set.create $(raw-properties) ] $(result) ;
1436        }
1437    }
1438}
1439
1440rule dump ( )
1441{
1442    for local g in $(.all-generators)
1443    {
1444        ECHO [ $(g).id ] ":" [ $(g).source-types ] -> [ $(g).target-types ] ;
1445    }
1446}
1447
1448