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