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