]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/build/generators.jam
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / tools / build / src / build / generators.jam
CommitLineData
7c673cae
FG
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
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 ;
11fdf7f2 170 import property-set ;
7c673cae
FG
171 import sequence ;
172 import set ;
11fdf7f2 173 import toolset ;
7c673cae
FG
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)".
11fdf7f2 220 local m = [ MATCH "([^\\(]*)(\\((.*)%(.*)\\))?" : $(e) ] ;
7c673cae
FG
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) ;
11fdf7f2
TL
241
242 local relevant-for-generator =
243 [ sequence.transform utility.ungrist : $(requirements:G) ] ;
244 self.relevant-features = [ property-set.create <relevant>$(relevant-for-generator) ] ;
7c673cae
FG
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 {
11fdf7f2 336 local m = [ MATCH "([^\\(]*)(\\(.*\\))?" : $(t) ] ;
7c673cae
FG
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 ;
11fdf7f2 415 if $(consumed[2])
7c673cae 416 {
11fdf7f2
TL
417 result = [ construct-result $(consumed[2-]) : $(project) $(name) :
418 [ $(property-set).add $(consumed[1]) ] ] ;
7c673cae
FG
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 ;
11fdf7f2
TL
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 }
7c673cae
FG
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 ;
11fdf7f2
TL
456
457 local relevant = [ toolset.relevant $(self.rule-name) ] ;
458 relevant = [ $(relevant).add $(self.relevant-features) ] ;
459 property-set = [ $(property-set).add $(relevant) ] ;
460
7c673cae
FG
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 }
11fdf7f2
TL
476 if $(result)
477 {
478 return $(relevant) $(result) ;
479 }
7c673cae
FG
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 ;
11fdf7f2 597 local usage-requirements ;
7c673cae
FG
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 {
11fdf7f2 610 usage-requirements = [ property-set.empty ] ;
7c673cae
FG
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
11fdf7f2 636 usage-requirements = $(transformed[1]) ;
7c673cae
FG
637 for local t in $(transformed[2-])
638 {
639 if [ $(t).type ] in $(missing-types)
640 {
641 _consumed += $(t) ;
642 }
643 }
644 }
645
11fdf7f2 646 return $(usage-requirements) [ sequence.unique $(_consumed) ] ;
7c673cae
FG
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 acceptible
11fdf7f2 661 return [ property-set.empty ] $(sources) ;
7c673cae
FG
662 }
663 else
664 {
11fdf7f2 665 local usage-requirements = [ property-set.empty ] ;
7c673cae
FG
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 }
11fdf7f2
TL
685 else
686 {
687 usage-requirements = [ $(usage-requirements).add $(transformed[1]) ] ;
688 }
7c673cae
FG
689 }
690 else
691 {
692 result += $(source) ;
693 }
694 }
11fdf7f2 695 return $(usage-requirements) [ sequence.unique $(result) : stable ] ;
7c673cae
FG
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:" $(x) ;
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 overriden.
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{
b32b8144 1375 local saved-active ;
7c673cae
FG
1376 if $(top-level)
1377 {
1378 saved-active = $(.active-generators) ;
1379 .active-generators = ;
1380 }
1381
11fdf7f2
TL
1382 # FIXME This is probably not intended be be run unconditionally,
1383 # but changing it causes no_type to fail.
1384 if "(.construct-stack)"
7c673cae
FG
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