]>
Commit | Line | Data |
---|---|---|
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.txt or copy at | |
6 | # https://www.bfgroup.xyz/b2/LICENSE.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 | ||
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 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 | if [ class.is-a $(result[1]) : property-set ] | |
479 | { | |
480 | return [ $(result[1]).add $(relevant) ] $(result[2-]) ; | |
481 | } | |
482 | else { | |
483 | return $(relevant) $(result) ; | |
484 | } | |
485 | } | |
486 | } | |
487 | ||
488 | # Determine target name from fullname (maybe including path components) | |
489 | # Place optional prefix and postfix around basename | |
490 | # | |
491 | rule determine-target-name ( fullname : prefix ? : postfix ? ) | |
492 | { | |
493 | # See if we need to add directory to the target name. | |
494 | local dir = $(fullname:D) ; | |
495 | local name = $(fullname:B) ; | |
496 | ||
497 | name = $(prefix:E=)$(name) ; | |
498 | name = $(name)$(postfix:E=) ; | |
499 | ||
500 | if $(dir) | |
501 | # Never append '..' to target path. | |
502 | && ! [ MATCH .*(\\.\\.).* : $(dir) ] | |
503 | && ! [ path.is-rooted $(dir) ] | |
504 | { | |
505 | # Relative path is always relative to the source directory. Retain | |
506 | # it, so that users can have files with the same name in two | |
507 | # different subdirectories. | |
508 | name = $(dir)/$(name) ; | |
509 | } | |
510 | return $(name) ; | |
511 | } | |
512 | ||
513 | # Determine the name of the produced target from the names of the sources. | |
514 | # | |
515 | rule determine-output-name ( sources + ) | |
516 | { | |
517 | # The simple case if when a name of source has single dot. Then, we take | |
518 | # the part before dot. Several dots can be caused by: | |
519 | # - using source file like a.host.cpp, or | |
520 | # - a type whose suffix has a dot. Say, we can type 'host_cpp' with | |
521 | # extension 'host.cpp'. | |
522 | # In the first case, we want to take the part up to the last dot. In the | |
523 | # second case -- not sure, but for now take the part up to the last dot | |
524 | # too. | |
525 | name = [ utility.basename [ $(sources[1]).name ] ] ; | |
526 | for local s in $(sources[2-]) | |
527 | { | |
528 | if [ utility.basename [ $(s).name ] ] != $(name) | |
529 | { | |
530 | import errors : error : errors.error ; | |
531 | errors.error "$(self.id): source targets have different names: cannot determine target name" ; | |
532 | } | |
533 | } | |
534 | return [ determine-target-name [ $(sources[1]).name ] ] ; | |
535 | } | |
536 | ||
537 | # Constructs targets that are created after consuming 'sources'. The result | |
538 | # will be the list of virtual-target, which has the same length as the | |
539 | # 'target-types' attribute and with corresponding types. | |
540 | # | |
541 | # When 'name' is empty, all source targets must have the same 'name' | |
542 | # attribute value, which will be used instead of the 'name' argument. | |
543 | # | |
544 | # The 'name' attribute value for each generated target will be equal to the | |
545 | # 'name' parameter if there is no name pattern for this type. Otherwise, the | |
546 | # '%' symbol in the name pattern will be replaced with the 'name' parameter | |
547 | # to obtain the 'name' attribute. | |
548 | # | |
549 | # For example, if targets types are T1 and T2 (with name pattern "%_x"), | |
550 | # suffixes for T1 and T2 are .t1 and .t2, and source is foo.z, then created | |
551 | # files would be "foo.t1" and "foo_x.t2". The 'name' attribute actually | |
552 | # determines the basename of a file. | |
553 | # | |
554 | # Note that this pattern mechanism has nothing to do with implicit patterns | |
555 | # in make. It is a way to produce a target whose name is different than the | |
556 | # name of its source. | |
557 | # | |
558 | rule generated-targets ( sources + : property-set : project name ? ) | |
559 | { | |
560 | if ! $(name) | |
561 | { | |
562 | name = [ determine-output-name $(sources) ] ; | |
563 | } | |
564 | ||
565 | # Assign an action for each target. | |
566 | local action = [ action-class ] ; | |
567 | local a = [ class.new $(action) $(sources) : $(self.rule-name) : | |
568 | $(property-set) ] ; | |
569 | ||
570 | # Create generated target for each target type. | |
571 | local targets ; | |
572 | local pre = $(self.name-prefix) ; | |
573 | local post = $(self.name-postfix) ; | |
574 | for local t in $(self.target-types) | |
575 | { | |
576 | local generated-name = $(pre[1])$(name:BS)$(post[1]) ; | |
577 | generated-name = $(generated-name:R=$(name:D)) ; | |
578 | pre = $(pre[2-]) ; | |
579 | post = $(post[2-]) ; | |
580 | ||
581 | targets += [ class.new file-target $(generated-name) : $(t) : | |
582 | $(project) : $(a) ] ; | |
583 | } | |
584 | ||
585 | return [ sequence.transform virtual-target.register : $(targets) ] ; | |
586 | } | |
587 | ||
588 | # Attempts to convert 'sources' to targets of types that this generator can | |
589 | # handle. The intention is to produce the set of targets that can be used | |
590 | # when the generator is run. | |
591 | # | |
592 | rule convert-to-consumable-types | |
593 | ( | |
594 | project name ? | |
595 | : property-set | |
596 | : sources + | |
597 | : only-one ? # Convert 'source' to only one of the source types. If | |
598 | # there is more that one possibility, report an error. | |
599 | ) | |
600 | { | |
601 | local _consumed ; | |
602 | local missing-types ; | |
603 | local usage-requirements ; | |
604 | ||
605 | if $(sources[2]) | |
606 | { | |
607 | # Do not know how to handle several sources yet. Just try to pass | |
608 | # the request to other generator. | |
609 | missing-types = $(self.source-types) ; | |
610 | } | |
611 | else | |
612 | { | |
613 | local temp = [ consume-directly $(sources) ] ; | |
614 | if $(temp[1]) | |
615 | { | |
616 | usage-requirements = [ property-set.empty ] ; | |
617 | _consumed = $(temp[1]) ; | |
618 | } | |
619 | missing-types = $(temp[2-]) ; | |
620 | } | |
621 | ||
622 | # No need to search for transformation if some source type has consumed | |
623 | # source and no more source types are needed. | |
624 | if $(only-one) && $(_consumed) | |
625 | { | |
626 | missing-types = ; | |
627 | } | |
628 | ||
629 | # TODO: we should check that only one source type is created if | |
630 | # 'only-one' is true. | |
631 | ||
632 | if $(missing-types) | |
633 | { | |
634 | local transformed = [ generators.construct-types $(project) $(name) | |
635 | : $(missing-types) : $(property-set) : $(sources) ] ; | |
636 | ||
637 | # Add targets of right type to 'consumed'. Add others to 'bypassed'. | |
638 | # The 'generators.construct' rule has done its best to convert | |
639 | # everything to the required type. There is no need to rerun it on | |
640 | # targets of different types. | |
641 | ||
642 | usage-requirements = $(transformed[1]) ; | |
643 | for local t in $(transformed[2-]) | |
644 | { | |
645 | if [ $(t).type ] in $(missing-types) | |
646 | { | |
647 | _consumed += $(t) ; | |
648 | } | |
649 | } | |
650 | } | |
651 | ||
652 | return $(usage-requirements) [ sequence.unique $(_consumed) ] ; | |
653 | } | |
654 | ||
655 | # Converts several files to consumable types. Called for composing | |
656 | # generators only. | |
657 | # | |
658 | rule convert-multiple-sources-to-consumable-types ( project : property-set : | |
659 | sources * ) | |
660 | { | |
661 | local result ; | |
662 | # We process each source one-by-one, trying to convert it to a usable | |
663 | # type. | |
664 | if ! $(self.source-types) | |
665 | { | |
666 | # Anything is acceptable | |
667 | return [ property-set.empty ] $(sources) ; | |
668 | } | |
669 | else | |
670 | { | |
671 | local usage-requirements = [ property-set.empty ] ; | |
672 | local acceptible-types = [ sequence.unique | |
673 | [ sequence.transform type.all-derived : $(self.source-types) ] ] ; | |
674 | for local source in $(sources) | |
675 | { | |
676 | if ! [ $(source).type ] in $(acceptible-types) | |
677 | { | |
678 | local transformed = [ generators.construct-types $(project) | |
679 | : $(self.source-types) : $(property-set) : $(source) ] ; | |
680 | for local t in $(transformed[2-]) | |
681 | { | |
682 | if [ $(t).type ] in $(self.source-types) | |
683 | { | |
684 | result += $(t) ; | |
685 | } | |
686 | } | |
687 | if ! $(transformed) | |
688 | { | |
689 | generators.dout [ indent ] " failed to convert " $(source) ; | |
690 | } | |
691 | else | |
692 | { | |
693 | usage-requirements = [ $(usage-requirements).add $(transformed[1]) ] ; | |
694 | } | |
695 | } | |
696 | else | |
697 | { | |
698 | result += $(source) ; | |
699 | } | |
700 | } | |
701 | return $(usage-requirements) [ sequence.unique $(result) : stable ] ; | |
702 | } | |
703 | } | |
704 | ||
705 | rule consume-directly ( source ) | |
706 | { | |
707 | local real-source-type = [ $(source).type ] ; | |
708 | ||
709 | # If there are no source types, we can consume anything. | |
710 | local source-types = $(self.source-types) ; | |
711 | source-types ?= $(real-source-type) ; | |
712 | ||
713 | local result = "" ; | |
714 | local missing-types ; | |
715 | ||
716 | for local st in $(source-types) | |
717 | { | |
718 | # The 'source' if of the right type already. | |
719 | if $(real-source-type) = $(st) || [ type.is-derived | |
720 | $(real-source-type) $(st) ] | |
721 | { | |
722 | result = $(source) ; | |
723 | } | |
724 | else | |
725 | { | |
726 | missing-types += $(st) ; | |
727 | } | |
728 | } | |
729 | return $(result) $(missing-types) ; | |
730 | } | |
731 | ||
732 | # Returns the class to be used to actions. Default implementation returns | |
733 | # "action". | |
734 | # | |
735 | rule action-class ( ) | |
736 | { | |
737 | return "action" ; | |
738 | } | |
739 | } | |
740 | ||
741 | ||
742 | # Registers a new generator instance 'g'. | |
743 | # | |
744 | rule register ( g ) | |
745 | { | |
746 | .all-generators += $(g) ; | |
747 | ||
748 | # A generator can produce several targets of the same type. We want unique | |
749 | # occurrence of that generator in .generators.$(t) in that case, otherwise, | |
750 | # it will be tried twice and we will get a false ambiguity. | |
751 | for local t in [ sequence.unique [ $(g).target-types ] ] | |
752 | { | |
753 | .generators.$(t) += $(g) ; | |
754 | } | |
755 | ||
756 | # Update the set of generators for toolset. | |
757 | ||
758 | # TODO: should we check that generator with this id is not already | |
759 | # registered. For example, the fop.jam module intentionally declared two | |
760 | # generators with the same id, so such check will break it. | |
761 | local id = [ $(g).id ] ; | |
762 | ||
763 | # Some generators have multiple periods in their name, so a simple $(id:S=) | |
764 | # will not generate the right toolset name. E.g. if id = gcc.compile.c++, | |
765 | # then .generators-for-toolset.$(id:S=) will append to | |
766 | # .generators-for-toolset.gcc.compile, which is a separate value from | |
767 | # .generators-for-toolset.gcc. Correcting this makes generator inheritance | |
768 | # work properly. See also inherit-generators in the toolset module. | |
769 | local base = $(id) ; | |
770 | while $(base:S) | |
771 | { | |
772 | base = $(base:B) ; | |
773 | } | |
774 | .generators-for-toolset.$(base) += $(g) ; | |
775 | ||
776 | ||
777 | # After adding a new generator that can construct new target types, we need | |
778 | # to clear the related cached viable source target type information for | |
779 | # constructing a specific target type or using a specific generator. Cached | |
780 | # viable source target type lists affected by this are those containing any | |
781 | # of the target types constructed by the new generator or any of their base | |
782 | # target types. | |
783 | # | |
784 | # A more advanced alternative to clearing that cached viable source target | |
785 | # type information would be to expand it with additional source types or | |
786 | # even better - mark it as needing to be expanded on next use. | |
787 | # | |
788 | # Also see the http://thread.gmane.org/gmane.comp.lib.boost.build/19077 | |
789 | # mailing list thread for an even more advanced idea of how we could convert | |
790 | # Boost Build's Jamfile processing, target selection and generator selection | |
791 | # into separate steps which would prevent these caches from ever being | |
792 | # invalidated. | |
793 | # | |
794 | # For now we just clear all the cached viable source target type information | |
795 | # that does not simply state 'all types' and may implement a more detailed | |
796 | # algorithm later on if it becomes needed. | |
797 | ||
798 | invalidate-extendable-viable-source-target-type-cache ; | |
799 | } | |
800 | ||
801 | ||
802 | # Creates a new non-composing 'generator' class instance and registers it. | |
803 | # Returns the created instance. Rationale: the instance is returned so that it | |
804 | # is possible to first register a generator and then call its 'run' method, | |
805 | # bypassing the whole generator selection process. | |
806 | # | |
807 | rule register-standard ( id : source-types * : target-types + : requirements * ) | |
808 | { | |
809 | local g = [ new generator $(id) : $(source-types) : $(target-types) : | |
810 | $(requirements) ] ; | |
811 | register $(g) ; | |
812 | return $(g) ; | |
813 | } | |
814 | ||
815 | ||
816 | # Creates a new composing 'generator' class instance and registers it. | |
817 | # | |
818 | rule register-composing ( id : source-types * : target-types + : requirements * | |
819 | ) | |
820 | { | |
821 | local g = [ new generator $(id) true : $(source-types) : $(target-types) : | |
822 | $(requirements) ] ; | |
823 | register $(g) ; | |
824 | return $(g) ; | |
825 | } | |
826 | ||
827 | ||
828 | # Returns all generators belonging to the given 'toolset', i.e. whose ids are | |
829 | # '$(toolset).<something>'. | |
830 | # | |
831 | rule generators-for-toolset ( toolset ) | |
832 | { | |
833 | return $(.generators-for-toolset.$(toolset)) ; | |
834 | } | |
835 | ||
836 | ||
837 | # Make generator 'overrider-id' be preferred to 'overridee-id'. If, when | |
838 | # searching for generators that could produce a target of a certain type, both | |
839 | # those generators are among viable generators, the overridden generator is | |
840 | # immediately discarded. | |
841 | # | |
842 | # The overridden generators are discarded immediately after computing the list | |
843 | # of viable generators but before running any of them. | |
844 | # | |
845 | rule override ( overrider-id : overridee-id ) | |
846 | { | |
847 | .override.$(overrider-id) += $(overridee-id) ; | |
848 | } | |
849 | ||
850 | ||
851 | # Returns a list of source type which can possibly be converted to 'target-type' | |
852 | # by some chain of generator invocation. | |
853 | # | |
854 | # More formally, takes all generators for 'target-type' and returns a union of | |
855 | # source types for those generators and result of calling itself recursively on | |
856 | # source types. | |
857 | # | |
858 | # Returns '*' in case any type should be considered a viable source type for the | |
859 | # given type. | |
860 | # | |
861 | local rule viable-source-types-real ( target-type ) | |
862 | { | |
863 | local result ; | |
864 | ||
865 | # 't0' is the initial list of target types we need to process to get a list | |
866 | # of their viable source target types. New target types will not be added to | |
867 | # this list. | |
868 | local t0 = [ type.all-bases $(target-type) ] ; | |
869 | ||
870 | # 't' is the list of target types which have not yet been processed to get a | |
871 | # list of their viable source target types. This list will get expanded as | |
872 | # we locate more target types to process. | |
873 | local t = $(t0) ; | |
874 | ||
875 | while $(t) | |
876 | { | |
877 | # Find all generators for the current type. Unlike | |
878 | # 'find-viable-generators' we do not care about the property-set. | |
879 | local generators = $(.generators.$(t[1])) ; | |
880 | t = $(t[2-]) ; | |
881 | ||
882 | while $(generators) | |
883 | { | |
884 | local g = $(generators[1]) ; | |
885 | generators = $(generators[2-]) ; | |
886 | ||
887 | if ! [ $(g).source-types ] | |
888 | { | |
889 | # Empty source types -- everything can be accepted. | |
890 | result = * ; | |
891 | # This will terminate this loop. | |
892 | generators = ; | |
893 | # This will terminate the outer loop. | |
894 | t = ; | |
895 | } | |
896 | ||
897 | for local source-type in [ $(g).source-types ] | |
898 | { | |
899 | if ! $(source-type) in $(result) | |
900 | { | |
901 | # If a generator accepts a 'source-type' it will also | |
902 | # happily accept any type derived from it. | |
903 | for local n in [ type.all-derived $(source-type) ] | |
904 | { | |
905 | if ! $(n) in $(result) | |
906 | { | |
907 | # Here there is no point in adding target types to | |
908 | # the list of types to process in case they are or | |
909 | # have already been on that list. We optimize this | |
910 | # check by realizing that we only need to avoid the | |
911 | # original target type's base types. Other target | |
912 | # types that are or have been on the list of target | |
913 | # types to process have been added to the 'result' | |
914 | # list as well and have thus already been eliminated | |
915 | # by the previous if. | |
916 | if ! $(n) in $(t0) | |
917 | { | |
918 | t += $(n) ; | |
919 | } | |
920 | result += $(n) ; | |
921 | } | |
922 | } | |
923 | } | |
924 | } | |
925 | } | |
926 | } | |
927 | ||
928 | return $(result) ; | |
929 | } | |
930 | ||
931 | ||
932 | # Helper rule, caches the result of 'viable-source-types-real'. | |
933 | # | |
934 | rule viable-source-types ( target-type ) | |
935 | { | |
936 | local key = .vst.$(target-type) ; | |
937 | if ! $($(key)) | |
938 | { | |
939 | .vst-cached-types += $(target-type) ; | |
940 | local v = [ viable-source-types-real $(target-type) ] ; | |
941 | if ! $(v) | |
942 | { | |
943 | v = none ; | |
944 | } | |
945 | $(key) = $(v) ; | |
946 | } | |
947 | ||
948 | if $($(key)) != none | |
949 | { | |
950 | return $($(key)) ; | |
951 | } | |
952 | } | |
953 | ||
954 | ||
955 | # Returns the list of source types, which, when passed to 'run' method of | |
956 | # 'generator', has some change of being eventually used (probably after | |
957 | # conversion by other generators). | |
958 | # | |
959 | # Returns '*' in case any type should be considered a viable source type for the | |
960 | # given generator. | |
961 | # | |
962 | rule viable-source-types-for-generator-real ( generator ) | |
963 | { | |
964 | local source-types = [ $(generator).source-types ] ; | |
965 | if ! $(source-types) | |
966 | { | |
967 | # If generator does not specify any source types, it might be a special | |
968 | # generator like builtin.lib-generator which just relays to other | |
969 | # generators. Return '*' to indicate that any source type is possibly | |
970 | # OK, since we do not know for sure. | |
971 | return * ; | |
972 | } | |
973 | else | |
974 | { | |
975 | local result ; | |
976 | while $(source-types) | |
977 | { | |
978 | local s = $(source-types[1]) ; | |
979 | source-types = $(source-types[2-]) ; | |
980 | local viable-sources = [ generators.viable-source-types $(s) ] ; | |
981 | if $(viable-sources) = * | |
982 | { | |
983 | result = * ; | |
984 | source-types = ; # Terminate the loop. | |
985 | } | |
986 | else | |
987 | { | |
988 | result += [ type.all-derived $(s) ] $(viable-sources) ; | |
989 | } | |
990 | } | |
991 | return [ sequence.unique $(result) ] ; | |
992 | } | |
993 | } | |
994 | ||
995 | ||
996 | # Helper rule, caches the result of 'viable-source-types-for-generator'. | |
997 | # | |
998 | local rule viable-source-types-for-generator ( generator ) | |
999 | { | |
1000 | local key = .vstg.$(generator) ; | |
1001 | if ! $($(key)) | |
1002 | { | |
1003 | .vstg-cached-generators += $(generator) ; | |
1004 | local v = [ viable-source-types-for-generator-real $(generator) ] ; | |
1005 | if ! $(v) | |
1006 | { | |
1007 | v = none ; | |
1008 | } | |
1009 | $(key) = $(v) ; | |
1010 | } | |
1011 | ||
1012 | if $($(key)) != none | |
1013 | { | |
1014 | return $($(key)) ; | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | ||
1019 | # Returns usage requirements + list of created targets. | |
1020 | # | |
1021 | local rule try-one-generator-really ( project name ? : generator : target-type | |
1022 | : property-set : sources * ) | |
1023 | { | |
1024 | local targets = | |
1025 | [ $(generator).run $(project) $(name) : $(property-set) : $(sources) ] ; | |
1026 | ||
1027 | local usage-requirements ; | |
1028 | local success ; | |
1029 | ||
1030 | generators.dout [ indent ] returned $(targets) ; | |
1031 | ||
1032 | if $(targets) | |
1033 | { | |
1034 | success = true ; | |
1035 | ||
1036 | if [ class.is-a $(targets[1]) : property-set ] | |
1037 | { | |
1038 | usage-requirements = $(targets[1]) ; | |
1039 | targets = $(targets[2-]) ; | |
1040 | } | |
1041 | else | |
1042 | { | |
1043 | usage-requirements = [ property-set.empty ] ; | |
1044 | } | |
1045 | } | |
1046 | ||
1047 | generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ; | |
1048 | generators.dout [ indent ] " " $(targets) ; | |
1049 | if $(usage-requirements) | |
1050 | { | |
1051 | generators.dout [ indent ] " with usage requirements:" $(usage-requirements) ; | |
1052 | } | |
1053 | ||
1054 | if $(success) | |
1055 | { | |
1056 | return $(usage-requirements) $(targets) ; | |
1057 | } | |
1058 | } | |
1059 | ||
1060 | ||
1061 | # Checks if generator invocation can be pruned, because it is guaranteed to | |
1062 | # fail. If so, quickly returns an empty list. Otherwise, calls | |
1063 | # try-one-generator-really. | |
1064 | # | |
1065 | local rule try-one-generator ( project name ? : generator : target-type | |
1066 | : property-set : sources * ) | |
1067 | { | |
1068 | local source-types ; | |
1069 | for local s in $(sources) | |
1070 | { | |
1071 | source-types += [ $(s).type ] ; | |
1072 | } | |
1073 | local viable-source-types = [ viable-source-types-for-generator $(generator) | |
1074 | ] ; | |
1075 | ||
1076 | if $(source-types) && $(viable-source-types) != * && | |
1077 | ! [ set.intersection $(source-types) : $(viable-source-types) ] | |
1078 | { | |
1079 | local id = [ $(generator).id ] ; | |
1080 | generators.dout [ indent ] " ** generator '$(id)' pruned" ; | |
1081 | #generators.dout [ indent ] "source-types" '$(source-types)' ; | |
1082 | #generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ; | |
1083 | } | |
1084 | else | |
1085 | { | |
1086 | return [ try-one-generator-really $(project) $(name) : $(generator) : | |
1087 | $(target-type) : $(property-set) : $(sources) ] ; | |
1088 | } | |
1089 | } | |
1090 | ||
1091 | ||
1092 | rule construct-types ( project name ? : target-types + : property-set | |
1093 | : sources + ) | |
1094 | { | |
1095 | local result ; | |
1096 | local usage-requirements = [ property-set.empty ] ; | |
1097 | for local t in $(target-types) | |
1098 | { | |
1099 | local r = [ construct $(project) $(name) : $(t) : $(property-set) : | |
1100 | $(sources) ] ; | |
1101 | if $(r) | |
1102 | { | |
1103 | usage-requirements = [ $(usage-requirements).add $(r[1]) ] ; | |
1104 | result += $(r[2-]) ; | |
1105 | } | |
1106 | } | |
1107 | # TODO: have to introduce parameter controlling if several types can be | |
1108 | # matched and add appropriate checks. | |
1109 | ||
1110 | # TODO: need to review the documentation for 'construct' to see if it should | |
1111 | # return $(source) even if nothing can be done with it. Currents docs seem | |
1112 | # to imply that, contrary to the behaviour. | |
1113 | if $(result) | |
1114 | { | |
1115 | return $(usage-requirements) $(result) ; | |
1116 | } | |
1117 | else | |
1118 | { | |
1119 | return $(usage-requirements) $(sources) ; | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | ||
1124 | # Ensures all 'targets' have their type. If this is not so, exists with error. | |
1125 | # | |
1126 | local rule ensure-type ( targets * ) | |
1127 | { | |
1128 | for local t in $(targets) | |
1129 | { | |
1130 | if ! [ $(t).type ] | |
1131 | { | |
1132 | import errors ; | |
1133 | errors.error "target" [ $(t).str ] "has no type" ; | |
1134 | } | |
1135 | } | |
1136 | } | |
1137 | ||
1138 | ||
1139 | # Returns generators which can be used to construct target of specified type | |
1140 | # with specified properties. Uses the following algorithm: | |
1141 | # - iterates over requested target-type and all its bases (in the order returned | |
1142 | # by type.all-bases). | |
1143 | # - for each type find all generators that generate that type and whose | |
1144 | # requirements are satisfied by properties. | |
1145 | # - if the set of generators is not empty, returns that set. | |
1146 | # | |
1147 | # Note: this algorithm explicitly ignores generators for base classes if there | |
1148 | # is at least one generator for the requested target-type. | |
1149 | # | |
1150 | local rule find-viable-generators-aux ( target-type : property-set ) | |
1151 | { | |
1152 | # Select generators that can create the required target type. | |
1153 | local viable-generators = ; | |
1154 | ||
1155 | import type ; | |
1156 | local t = $(target-type) ; | |
1157 | ||
1158 | if $(.debug) | |
1159 | { | |
1160 | generators.dout [ indent ] find-viable-generators target-type= $(target-type) | |
1161 | property-set= [ $(property-set).as-path ] ; | |
1162 | generators.dout [ indent ] "trying type" $(target-type) ; | |
1163 | } | |
1164 | ||
1165 | local generators = $(.generators.$(target-type)) ; | |
1166 | if $(generators) | |
1167 | { | |
1168 | if $(.debug) | |
1169 | { | |
1170 | generators.dout [ indent ] "there are generators for this type" ; | |
1171 | } | |
1172 | } | |
1173 | else | |
1174 | { | |
1175 | local t = [ type.base $(target-type) ] ; | |
1176 | ||
1177 | # Get the list of generators for the requested type. If no generator is | |
1178 | # registered, try base type, and so on. | |
1179 | while $(t) | |
1180 | { | |
1181 | if $(.debug) | |
1182 | { | |
1183 | generators.dout [ indent ] "trying type" $(t) ; | |
1184 | } | |
1185 | if $(.generators.$(t)) | |
1186 | { | |
1187 | generators.dout [ indent ] "there are generators for this type" ; | |
1188 | generators = $(.generators.$(t)) ; | |
1189 | ||
1190 | # We are here because there were no generators found for | |
1191 | # target-type but there are some generators for its base type. | |
1192 | # We will try to use them, but they will produce targets of | |
1193 | # base type, not of 'target-type'. So, we clone the generators | |
1194 | # and modify the list of target types. | |
1195 | local generators2 ; | |
1196 | for local g in $(generators) | |
1197 | { | |
1198 | # generators.register adds a generator to the list of | |
1199 | # generators for toolsets, which is a bit strange, but | |
1200 | # should work. That list is only used when inheriting a | |
1201 | # toolset, which should have been done before running | |
1202 | # generators. | |
1203 | generators2 += [ $(g).clone-and-change-target-type $(t) : | |
1204 | $(target-type) ] ; | |
1205 | generators.register $(generators2[-1]) ; | |
1206 | } | |
1207 | generators = $(generators2) ; | |
1208 | t = ; | |
1209 | } | |
1210 | else | |
1211 | { | |
1212 | t = [ type.base $(t) ] ; | |
1213 | } | |
1214 | } | |
1215 | } | |
1216 | ||
1217 | for local g in $(generators) | |
1218 | { | |
1219 | if $(.debug) | |
1220 | { | |
1221 | generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ; | |
1222 | } | |
1223 | ||
1224 | if [ $(g).match-rank $(property-set) ] | |
1225 | { | |
1226 | if $(.debug) | |
1227 | { | |
1228 | generators.dout [ indent ] " is viable" ; | |
1229 | } | |
1230 | viable-generators += $(g) ; | |
1231 | } | |
1232 | } | |
1233 | ||
1234 | return $(viable-generators) ; | |
1235 | } | |
1236 | ||
1237 | ||
1238 | rule find-viable-generators ( target-type : property-set ) | |
1239 | { | |
1240 | local key = $(target-type).$(property-set) ; | |
1241 | local l = $(.fv.$(key)) ; | |
1242 | if ! $(l) | |
1243 | { | |
1244 | l = [ find-viable-generators-aux $(target-type) : $(property-set) ] ; | |
1245 | if ! $(l) | |
1246 | { | |
1247 | l = none ; | |
1248 | } | |
1249 | .fv.$(key) = $(l) ; | |
1250 | } | |
1251 | ||
1252 | if $(l) = none | |
1253 | { | |
1254 | l = ; | |
1255 | } | |
1256 | ||
1257 | local viable-generators ; | |
1258 | for local g in $(l) | |
1259 | { | |
1260 | # Avoid trying the same generator twice on different levels. | |
1261 | if ! $(g) in $(.active-generators) | |
1262 | { | |
1263 | viable-generators += $(g) ; | |
1264 | } | |
1265 | else | |
1266 | { | |
1267 | generators.dout [ indent ] " generator " [ $(g).id ] "is active, discaring" ; | |
1268 | } | |
1269 | } | |
1270 | ||
1271 | # Generators which override 'all'. | |
1272 | local all-overrides ; | |
1273 | # Generators which are overridden. | |
1274 | local overriden-ids ; | |
1275 | for local g in $(viable-generators) | |
1276 | { | |
1277 | local id = [ $(g).id ] ; | |
1278 | local this-overrides = $(.override.$(id)) ; | |
1279 | overriden-ids += $(this-overrides) ; | |
1280 | if all in $(this-overrides) | |
1281 | { | |
1282 | all-overrides += $(g) ; | |
1283 | } | |
1284 | } | |
1285 | if $(all-overrides) | |
1286 | { | |
1287 | viable-generators = $(all-overrides) ; | |
1288 | } | |
1289 | local result ; | |
1290 | for local g in $(viable-generators) | |
1291 | { | |
1292 | if ! [ $(g).id ] in $(overriden-ids) | |
1293 | { | |
1294 | result += $(g) ; | |
1295 | } | |
1296 | } | |
1297 | ||
1298 | return $(result) ; | |
1299 | } | |
1300 | ||
1301 | ||
1302 | .construct-stack = ; | |
1303 | ||
1304 | ||
1305 | # Attempts to construct a target by finding viable generators, running them and | |
1306 | # selecting the dependency graph. | |
1307 | # | |
1308 | local rule construct-really ( project name ? : target-type : property-set : | |
1309 | sources * ) | |
1310 | { | |
1311 | viable-generators = [ find-viable-generators $(target-type) : | |
1312 | $(property-set) ] ; | |
1313 | ||
1314 | generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ] | |
1315 | " viable generators" ; | |
1316 | ||
1317 | local result ; | |
1318 | local generators-that-succeeded ; | |
1319 | for local g in $(viable-generators) | |
1320 | { | |
1321 | # This variable will be restored on exit from this scope. | |
1322 | local .active-generators = $(g) $(.active-generators) ; | |
1323 | ||
1324 | local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type) | |
1325 | : $(property-set) : $(sources) ] ; | |
1326 | ||
1327 | if $(r) | |
1328 | { | |
1329 | generators-that-succeeded += $(g) ; | |
1330 | if $(result) | |
1331 | { | |
1332 | ECHO "Error: ambiguity found when searching for best transformation" ; | |
1333 | ECHO "Trying to produce type '$(target-type)' from: " ; | |
1334 | for local s in $(sources) | |
1335 | { | |
1336 | ECHO " - " [ $(s).str ] ; | |
1337 | } | |
1338 | ECHO "Generators that succeeded:" ; | |
1339 | for local g in $(generators-that-succeeded) | |
1340 | { | |
1341 | ECHO " - " [ $(g).id ] ; | |
1342 | } | |
1343 | ECHO "First generator produced: " ; | |
1344 | for local t in $(result[2-]) | |
1345 | { | |
1346 | ECHO " - " [ $(t).str ] ; | |
1347 | } | |
1348 | ECHO "Second generator produced: " ; | |
1349 | for local t in $(r[2-]) | |
1350 | { | |
1351 | ECHO " - " [ $(t).str ] ; | |
1352 | } | |
1353 | EXIT ; | |
1354 | } | |
1355 | else | |
1356 | { | |
1357 | result = $(r) ; | |
1358 | } | |
1359 | } | |
1360 | } | |
1361 | ||
1362 | return $(result) ; | |
1363 | } | |
1364 | ||
1365 | ||
1366 | # Attempts to create a target of 'target-type' with 'properties' from 'sources'. | |
1367 | # The 'sources' are treated as a collection of *possible* ingridients, i.e. | |
1368 | # there is no obligation to consume them all. | |
1369 | # | |
1370 | # Returns a list of targets. When this invocation is first instance of | |
1371 | # 'construct' in stack, returns only targets of requested 'target-type', | |
1372 | # otherwise, returns also unused sources and additionally generated targets. | |
1373 | # | |
1374 | # If 'top-level' is set, does not suppress generators that are already | |
1375 | # used in the stack. This may be useful in cases where a generator | |
1376 | # has to build a metatargets -- for example a target corresponding to | |
1377 | # built tool. | |
1378 | # | |
1379 | rule construct ( project name ? : target-type : property-set * : sources * : top-level ? ) | |
1380 | { | |
1381 | local saved-active ; | |
1382 | if $(top-level) | |
1383 | { | |
1384 | saved-active = $(.active-generators) ; | |
1385 | .active-generators = ; | |
1386 | } | |
1387 | ||
1388 | # FIXME This is probably not intended be be run unconditionally, | |
1389 | # but changing it causes no_type to fail. | |
1390 | if "(.construct-stack)" | |
1391 | { | |
1392 | ensure-type $(sources) ; | |
1393 | } | |
1394 | ||
1395 | .construct-stack += 1 ; | |
1396 | ||
1397 | increase-indent ; | |
1398 | ||
1399 | if $(.debug) | |
1400 | { | |
1401 | generators.dout [ indent ] "*** construct" $(target-type) ; | |
1402 | ||
1403 | for local s in $(sources) | |
1404 | { | |
1405 | generators.dout [ indent ] " from" $(s) ; | |
1406 | } | |
1407 | generators.dout [ indent ] " properties:" [ $(property-set).raw ] ; | |
1408 | } | |
1409 | ||
1410 | local result = [ construct-really $(project) $(name) : $(target-type) : | |
1411 | $(property-set) : $(sources) ] ; | |
1412 | ||
1413 | decrease-indent ; | |
1414 | ||
1415 | .construct-stack = $(.construct-stack[2-]) ; | |
1416 | ||
1417 | if $(top-level) | |
1418 | { | |
1419 | .active-generators = $(saved-active) ; | |
1420 | } | |
1421 | ||
1422 | return $(result) ; | |
1423 | } | |
1424 | ||
1425 | # Given 'result', obtained from some generator or generators.construct, adds | |
1426 | # 'raw-properties' as usage requirements to it. If result already contains usage | |
1427 | # requirements -- that is the first element of result of an instance of the | |
1428 | # property-set class, the existing usage requirements and 'raw-properties' are | |
1429 | # combined. | |
1430 | # | |
1431 | rule add-usage-requirements ( result * : raw-properties * ) | |
1432 | { | |
1433 | if $(result) | |
1434 | { | |
1435 | if [ class.is-a $(result[1]) : property-set ] | |
1436 | { | |
1437 | return [ $(result[1]).add-raw $(raw-properties) ] $(result[2-]) ; | |
1438 | } | |
1439 | else | |
1440 | { | |
1441 | return [ property-set.create $(raw-properties) ] $(result) ; | |
1442 | } | |
1443 | } | |
1444 | } | |
1445 | ||
1446 | rule dump ( ) | |
1447 | { | |
1448 | for local g in $(.all-generators) | |
1449 | { | |
1450 | ECHO [ $(g).id ] ":" [ $(g).source-types ] -> [ $(g).target-types ] ; | |
1451 | } | |
1452 | } | |
1453 |