]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2003 Dave Abrahams |
2 | # Copyright 2005, 2006 Rene Rivera | |
3 | # Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus | |
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 | # Implements virtual targets, which correspond to actual files created during a | |
9 | # build, but are not yet targets in Jam sense. They are needed, for example, | |
10 | # when searching for possible transformation sequences, when it is not yet known | |
11 | # whether a particular target should be created at all. | |
12 | # | |
13 | # +--------------------------+ | |
14 | # | virtual-target | | |
15 | # +==========================+ | |
16 | # | actualize | | |
17 | # +--------------------------+ | |
18 | # | actualize-action() = 0 | | |
19 | # | actualize-location() = 0 | | |
20 | # +----------------+---------+ | |
21 | # | | |
22 | # ^ | |
23 | # / \ | |
24 | # +-+-+ | |
25 | # | | |
26 | # +---------------------+ +-------+--------------+ | |
27 | # | action | | abstract-file-target | | |
28 | # +=====================| * +======================+ | |
29 | # | action-name | +--+ action | | |
30 | # | properties | | +----------------------+ | |
31 | # +---------------------+--+ | actualize-action() | | |
32 | # | actualize() |0..1 +-----------+----------+ | |
33 | # | path() | | | |
34 | # | adjust-properties() | sources | | |
35 | # | actualize-sources() | targets | | |
36 | # +------+--------------+ ^ | |
37 | # | / \ | |
38 | # ^ +-+-+ | |
39 | # / \ | | |
40 | # +-+-+ +-------------+-------------+ | |
41 | # | | | | |
42 | # | +------+---------------+ +--------+-------------+ | |
43 | # | | file-target | | searched-lib-target | | |
44 | # | +======================+ +======================+ | |
45 | # | | actualize-location() | | actualize-location() | | |
46 | # | +----------------------+ +----------------------+ | |
47 | # | | |
48 | # +-+------------------------------+ | |
49 | # | | | |
50 | # +----+----------------+ +---------+-----------+ | |
51 | # | compile-action | | link-action | | |
52 | # +=====================+ +=====================+ | |
53 | # | adjust-properties() | | adjust-properties() | | |
54 | # +---------------------+ | actualize-sources() | | |
55 | # +---------------------+ | |
56 | # | |
57 | # The 'compile-action' and 'link-action' classes are not defined here but in | |
58 | # builtin.jam modules. They are shown in the diagram to give the big picture. | |
59 | ||
60 | import "class" : new ; | |
61 | import path ; | |
62 | import sequence ; | |
63 | import set ; | |
64 | import type ; | |
65 | import utility ; | |
66 | ||
67 | ||
68 | # Models a potential target. It can be converted into a Jam target and used in | |
69 | # building, if needed. However, it can be also dropped, which allows us to | |
70 | # search for different transformations and select only one. | |
71 | # | |
72 | class virtual-target | |
73 | { | |
74 | import scanner ; | |
75 | import sequence ; | |
76 | import utility ; | |
77 | import virtual-target ; | |
78 | ||
79 | rule __init__ ( | |
80 | name # Target/project name. | |
81 | : project # Project to which this target belongs. | |
82 | ) | |
83 | { | |
84 | self.name = $(name) ; | |
85 | self.project = $(project) ; | |
86 | self.dependencies = ; | |
87 | } | |
88 | ||
89 | # Name of this target. | |
90 | # | |
91 | rule name ( ) | |
92 | { | |
93 | return $(self.name) ; | |
94 | } | |
95 | ||
96 | # Project of this target. | |
97 | # | |
98 | rule project ( ) | |
99 | { | |
100 | return $(self.project) ; | |
101 | } | |
102 | ||
103 | # Adds additional 'virtual-target' instances this one depends on. | |
104 | # | |
105 | rule depends ( d + ) | |
106 | { | |
107 | self.dependencies = [ sequence.merge $(self.dependencies) : | |
108 | [ sequence.insertion-sort $(d) ] ] ; | |
109 | } | |
110 | ||
111 | rule dependencies ( ) | |
112 | { | |
113 | return $(self.dependencies) ; | |
114 | } | |
115 | ||
116 | rule always ( ) | |
117 | { | |
118 | .always = 1 ; | |
119 | } | |
120 | ||
121 | # Generates all the actual targets and sets up build actions for this | |
122 | # target. | |
123 | # | |
124 | # If 'scanner' is specified, creates an additional target with the same | |
125 | # location as the actual target, which will depend on the actual target and | |
126 | # be associated with a 'scanner'. That additional target is returned. See | |
127 | # the docs (#dependency_scanning) for rationale. Target must correspond to a | |
128 | # file if 'scanner' is specified. | |
129 | # | |
130 | # If scanner is not specified then the actual target is returned. | |
131 | # | |
132 | rule actualize ( scanner ? ) | |
133 | { | |
134 | local actual-name = [ actualize-no-scanner ] ; | |
135 | ||
136 | if $(.always) | |
137 | { | |
138 | ALWAYS $(actual-name) ; | |
139 | } | |
140 | ||
141 | if ! $(scanner) | |
142 | { | |
143 | return $(actual-name) ; | |
144 | } | |
145 | else | |
146 | { | |
147 | # Add the scanner instance to the grist for name. | |
148 | local g = [ sequence.join [ utility.ungrist $(actual-name:G) ] | |
149 | $(scanner) : - ] ; | |
150 | local name = $(actual-name:G=$(g)) ; | |
151 | ||
152 | if ! $(self.made.$(scanner)) | |
153 | { | |
154 | self.made.$(scanner) = true ; | |
155 | actualize-location $(name) ; | |
156 | scanner.install $(scanner) : $(name) ; | |
157 | } | |
158 | return $(name) ; | |
159 | } | |
160 | } | |
161 | ||
162 | # private: (overridables) | |
163 | ||
164 | # Sets up build actions for 'target'. Should call appropriate rules and set | |
165 | # target variables. | |
166 | # | |
167 | rule actualize-action ( target ) | |
168 | { | |
169 | import errors : error : errors.error ; | |
170 | errors.error "method should be defined in derived classes" ; | |
171 | } | |
172 | ||
173 | # Sets up variables on 'target' which specify its location. | |
174 | # | |
175 | rule actualize-location ( target ) | |
176 | { | |
177 | import errors : error : errors.error ; | |
178 | errors.error "method should be defined in derived classes" ; | |
179 | } | |
180 | ||
181 | # If the target is a generated one, returns the path where it will be | |
182 | # generated. Otherwise, returns an empty list. | |
183 | # | |
184 | rule path ( ) | |
185 | { | |
186 | import errors : error : errors.error ; | |
187 | errors.error "method should be defined in derived classes" ; | |
188 | } | |
189 | ||
190 | # Returns the actual target name to be used in case when no scanner is | |
191 | # involved. | |
192 | # | |
193 | rule actual-name ( ) | |
194 | { | |
195 | import errors : error : errors.error ; | |
196 | errors.error "method should be defined in derived classes" ; | |
197 | } | |
198 | ||
199 | # implementation | |
200 | rule actualize-no-scanner ( ) | |
201 | { | |
202 | # In fact, we just need to merge virtual-target with | |
203 | # abstract-file-target as the latter is the only class derived from the | |
204 | # former. But that has been left for later. | |
205 | ||
206 | import errors : error : errors.error ; | |
207 | errors.error "method should be defined in derived classes" ; | |
208 | } | |
209 | } | |
210 | ||
211 | ||
212 | # Target corresponding to a file. The exact mapping for file is not yet | |
213 | # specified in this class. (TODO: Actually, the class name could be better...) | |
214 | # | |
215 | # May be a source file (when no action is specified) or a derived file | |
216 | # (otherwise). | |
217 | # | |
218 | # The target's grist is a concatenation of its project's location, action | |
219 | # properties (for derived targets) and, optionally, value identifying the main | |
220 | # target. | |
221 | # | |
222 | class abstract-file-target : virtual-target | |
223 | { | |
224 | import project ; | |
225 | import regex ; | |
226 | import sequence ; | |
227 | import path ; | |
228 | import type ; | |
229 | import property-set ; | |
230 | import indirect ; | |
231 | ||
232 | rule __init__ ( | |
233 | name # Target's name. | |
234 | exact ? # If non-empty, the name is exactly the name created file | |
235 | # should have. Otherwise, the '__init__' method will add a | |
236 | # suffix obtained from 'type' by calling | |
237 | # 'type.generated-target-suffix'. | |
238 | : type ? # Target's type. | |
239 | : project | |
240 | : action ? | |
241 | ) | |
242 | { | |
243 | virtual-target.__init__ $(name) : $(project) ; | |
244 | ||
245 | self.type = $(type) ; | |
246 | self.action = $(action) ; | |
247 | if $(action) | |
248 | { | |
249 | $(action).add-targets $(__name__) ; | |
250 | ||
251 | if $(self.type) && ! $(exact) | |
252 | { | |
253 | _adjust-name $(name) ; | |
254 | } | |
255 | } | |
256 | } | |
257 | ||
258 | rule type ( ) | |
259 | { | |
260 | return $(self.type) ; | |
261 | } | |
262 | ||
263 | # Sets the path. When generating target name, it will override any path | |
264 | # computation from properties. | |
265 | # | |
266 | rule set-path ( path ) | |
267 | { | |
268 | self.path = [ path.native $(path) ] ; | |
269 | } | |
270 | ||
271 | # Returns the currently set action. | |
272 | # | |
273 | rule action ( ) | |
274 | { | |
275 | return $(self.action) ; | |
276 | } | |
277 | ||
278 | # Sets/gets the 'root' flag. Target is root if it directly corresponds to | |
279 | # some variant of a main target. | |
280 | # | |
281 | rule root ( set ? ) | |
282 | { | |
283 | if $(set) | |
284 | { | |
285 | self.root = true ; | |
286 | } | |
287 | return $(self.root) ; | |
288 | } | |
289 | ||
290 | # Gets or sets the subvariant which created this target. Subvariant is set | |
291 | # when target is brought into existance and is never changed after that. In | |
292 | # particular, if a target is shared by multiple subvariants, only the first | |
293 | # one is stored. | |
294 | # | |
295 | rule creating-subvariant ( s ? # If specified, specifies the value to set, | |
296 | # which should be a 'subvariant' class | |
297 | # instance. | |
298 | ) | |
299 | { | |
300 | if $(s) && ! $(self.creating-subvariant) | |
301 | { | |
302 | self.creating-subvariant = $(s) ; | |
303 | } | |
304 | return $(self.creating-subvariant) ; | |
305 | } | |
306 | ||
307 | rule actualize-action ( target ) | |
308 | { | |
309 | if $(self.action) | |
310 | { | |
311 | $(self.action).actualize ; | |
312 | } | |
313 | } | |
314 | ||
315 | # Return a human-readable representation of this target. If this target has | |
316 | # an action, that is: | |
317 | # | |
318 | # { <action-name>-<self.name>.<self.type> <action-sources>... } | |
319 | # | |
320 | # otherwise, it is: | |
321 | # | |
322 | # { <self.name>.<self.type> } | |
323 | # | |
324 | rule str ( ) | |
325 | { | |
326 | local action = [ action ] ; | |
327 | local name-dot-type = [ sequence.join $(self.name) "." $(self.type) ] ; | |
328 | ||
329 | if $(action) | |
330 | { | |
331 | local sources = [ $(action).sources ] ; | |
332 | local action-name = [ $(action).action-name ] ; | |
333 | ||
334 | local ss ; | |
335 | for local s in $(sources) | |
336 | { | |
337 | ss += [ $(s).str ] ; | |
338 | } | |
339 | ||
340 | return "{" $(action-name)-$(name-dot-type) $(ss) "}" ; | |
341 | } | |
342 | else | |
343 | { | |
344 | return "{" $(name-dot-type) "}" ; | |
345 | } | |
346 | } | |
347 | ||
348 | rule less ( a ) | |
349 | { | |
350 | if [ str ] < [ $(a).str ] | |
351 | { | |
352 | return true ; | |
353 | } | |
354 | } | |
355 | ||
356 | rule equal ( a ) | |
357 | { | |
358 | if [ str ] = [ $(a).str ] | |
359 | { | |
360 | return true ; | |
361 | } | |
362 | } | |
363 | ||
364 | # private: | |
365 | rule actual-name ( ) | |
366 | { | |
367 | if ! $(self.actual-name) | |
368 | { | |
369 | local grist = [ grist ] ; | |
370 | local basename = [ path.native $(self.name) ] ; | |
371 | self.actual-name = <$(grist)>$(basename) ; | |
372 | } | |
373 | return $(self.actual-name) ; | |
374 | } | |
375 | ||
376 | # Helper to 'actual-name', above. Computes a unique prefix used to | |
377 | # distinguish this target from other targets with the same name creating | |
378 | # different files. | |
379 | # | |
380 | rule grist ( ) | |
381 | { | |
382 | # Depending on target, there may be different approaches to generating | |
383 | # unique prefixes. We generate prefixes in the form: | |
384 | # <one letter approach code> <the actual prefix> | |
385 | local path = [ path ] ; | |
386 | if $(path) | |
387 | { | |
388 | # The target will be generated to a known path. Just use the path | |
389 | # for identification, since path is as unique as it can get. | |
390 | return p$(path) ; | |
391 | } | |
392 | else | |
393 | { | |
394 | # File is either source, which will be searched for, or is not a | |
395 | # file at all. Use the location of project for distinguishing. | |
396 | local project-location = [ $(self.project).get location ] ; | |
397 | local location-grist = [ sequence.join [ regex.split | |
398 | $(project-location) "/" ] : "!" ] ; | |
399 | ||
400 | if $(self.action) | |
401 | { | |
402 | local ps = [ $(self.action).properties ] ; | |
403 | local property-grist = [ $(ps).as-path ] ; | |
404 | # 'property-grist' can be empty when 'ps' is an empty property | |
405 | # set. | |
406 | if $(property-grist) | |
407 | { | |
408 | location-grist = $(location-grist)/$(property-grist) ; | |
409 | } | |
410 | } | |
411 | ||
412 | return l$(location-grist) ; | |
413 | } | |
414 | } | |
415 | ||
416 | # Given the target name specified in constructor, returns the name which | |
417 | # should be really used, by looking at the <tag> properties. Tag properties | |
418 | # need to be specified as <tag>@rule-name. This makes Boost Build call the | |
419 | # specified rule with the target name, type and properties to get the new | |
420 | # name. If no <tag> property is specified or the rule specified by <tag> | |
421 | # returns nothing, returns the result of calling | |
422 | # virtual-target.add-prefix-and-suffix. | |
423 | # | |
424 | rule _adjust-name ( specified-name ) | |
425 | { | |
426 | local ps ; | |
427 | if $(self.action) | |
428 | { | |
429 | ps = [ $(self.action).properties ] ; | |
430 | } | |
431 | else | |
432 | { | |
433 | ps = [ property-set.empty ] ; | |
434 | } | |
435 | ||
436 | # Add this target object for use in getting additional information | |
437 | # when tagging. | |
438 | ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ; | |
439 | ||
440 | local tag = [ $(ps).get <tag> ] ; | |
441 | ||
442 | if $(tag) | |
443 | { | |
444 | local rule-name = [ MATCH ^@(.*) : $(tag) ] ; | |
445 | if $(rule-name) | |
446 | { | |
447 | if $(tag[2]) | |
448 | { | |
449 | import errors : error : errors.error ; | |
450 | errors.error <tag>@rulename is present but is not the only | |
451 | <tag> feature. ; | |
452 | } | |
453 | ||
454 | self.name = [ indirect.call $(rule-name) $(specified-name) | |
455 | : $(self.type) : $(ps) ] ; | |
456 | } | |
457 | else | |
458 | { | |
459 | import errors : error : errors.error ; | |
460 | errors.error <tag> property value must be '@rule-name'. ; | |
461 | } | |
462 | } | |
463 | ||
464 | # If there is no tag or the tag rule returned nothing. | |
465 | if ! $(tag) || ! $(self.name) | |
466 | { | |
467 | self.name = [ virtual-target.add-prefix-and-suffix $(specified-name) | |
468 | : $(self.type) : $(ps) ] ; | |
469 | } | |
470 | } | |
471 | ||
472 | rule actualize-no-scanner ( ) | |
473 | { | |
474 | local name = [ actual-name ] ; | |
475 | ||
476 | # Do anything only on the first invocation. | |
477 | if ! $(self.made-no-scanner) | |
478 | { | |
479 | self.made-no-scanner = true ; | |
480 | ||
481 | if $(self.action) | |
482 | { | |
483 | # For non-derived target, we do not care if there are several | |
484 | # virtual targets that refer to the same name. One case when | |
485 | # this is unavoidable is when the file name is main.cpp and two | |
486 | # targets have types CPP (for compiling) and MOCCABLE_CPP (for | |
487 | # conversion to H via Qt tools). | |
488 | virtual-target.register-actual-name $(name) : $(__name__) ; | |
489 | } | |
490 | ||
491 | for local i in $(self.dependencies) | |
492 | { | |
493 | DEPENDS $(name) : [ $(i).actualize ] ; | |
494 | } | |
495 | ||
496 | actualize-location $(name) ; | |
497 | actualize-action $(name) ; | |
498 | } | |
499 | return $(name) ; | |
500 | } | |
501 | } | |
502 | ||
503 | ||
504 | # Appends the suffix appropriate to 'type/property-set' combination to the | |
505 | # specified name and returns the result. | |
506 | # | |
507 | rule add-prefix-and-suffix ( specified-name : type ? : property-set ) | |
508 | { | |
509 | local suffix = [ type.generated-target-suffix $(type) : $(property-set) ] ; | |
510 | ||
511 | # Handle suffixes for which no leading dot is desired. Those are specified | |
512 | # by enclosing them in <...>. Needed by python so it can create "_d.so" | |
513 | # extensions, for example. | |
514 | if $(suffix:G) | |
515 | { | |
516 | suffix = [ utility.ungrist $(suffix) ] ; | |
517 | } | |
518 | else | |
519 | { | |
520 | suffix = .$(suffix) ; | |
521 | } | |
522 | ||
523 | local prefix = [ type.generated-target-prefix $(type) : $(property-set) ] ; | |
524 | ||
525 | if [ MATCH ^($(prefix)) : $(specified-name) ] | |
526 | { | |
527 | prefix = ; | |
528 | } | |
529 | return $(prefix:E="")$(specified-name)$(suffix:E="") ; | |
530 | } | |
531 | ||
532 | ||
533 | # File targets with explicitly known location. | |
534 | # | |
535 | # The file path is determined as | |
536 | # * Value passed to the 'set-path' method, if any. | |
537 | # * For derived files, project's build dir, joined with components that | |
538 | # describe action properties. If free properties are not equal to the | |
539 | # project's reference properties an element with the name of the main | |
540 | # target is added. | |
541 | # * For source files, project's source dir. | |
542 | # | |
543 | # The file suffix is determined as: | |
544 | # * The value passed to the 'suffix' method, if any. | |
545 | # * The suffix corresponding to the target's type. | |
546 | # | |
547 | class file-target : abstract-file-target | |
548 | { | |
549 | import "class" : new ; | |
550 | import common ; | |
551 | ||
552 | rule __init__ ( | |
553 | name exact ? | |
554 | : type ? # Optional type for this target. | |
555 | : project | |
556 | : action ? | |
557 | : path ? | |
558 | ) | |
559 | { | |
560 | abstract-file-target.__init__ $(name) $(exact) : $(type) : $(project) : | |
561 | $(action) ; | |
562 | ||
563 | self.path = $(path) ; | |
564 | } | |
565 | ||
566 | rule clone-with-different-type ( new-type ) | |
567 | { | |
568 | return [ new file-target $(self.name) exact : $(new-type) : | |
569 | $(self.project) : $(self.action) : $(self.path) ] ; | |
570 | } | |
571 | ||
572 | rule actualize-location ( target ) | |
573 | { | |
574 | # Scanner targets are always bound to already existing files in already | |
575 | # existing folder. They need to be marked as depending on their base | |
576 | # target (i.e. the target being scanned) but, unlike regular | |
577 | # dependencies set up by the DEPENDS rule, they must not depend on any | |
578 | # targets already marked as included by the base target. Otherwise such | |
579 | # an included file being newer than the file being scanned would cause | |
580 | # the scanner target to be updated, further causing any target depending | |
581 | # on that scanner target to be rebuilt. This is the exact relationship | |
582 | # as set up by Boost Jam's SEARCH binding method (needed to support | |
583 | # searching for generated targets) so we want to bind scanner targets | |
584 | # using this method instead of explicitly specifying their location | |
585 | # using LOCATE. | |
586 | # | |
587 | # FIXME: We recognize scanner targets by their given name being | |
588 | # different from this target's actual name. This is a hack and should be | |
589 | # cleaned up by reorganizing who knows about scanners in the | |
590 | # virtual-target/abstract-file-target/file-target/notfile-target/ | |
591 | # searched-lib-target/... class hierarchy. | |
592 | local is-scanner-target ; | |
593 | if $(target) != [ actual-name ] | |
594 | { | |
595 | is-scanner-target = true ; | |
596 | } | |
597 | ||
598 | if $(self.action) && ! $(is-scanner-target) | |
599 | { | |
600 | # This is a derived file. | |
601 | local path = [ path ] ; | |
602 | LOCATE on $(target) = $(path) ; | |
603 | ||
604 | # Make sure the path exists. | |
605 | DEPENDS $(target) : $(path) ; | |
606 | common.MkDir $(path) ; | |
607 | ||
608 | # It is possible that the target name includes a directory too, for | |
609 | # example when installing headers. Create that directory. | |
610 | if $(target:D) | |
611 | { | |
612 | local d = $(target:D) ; | |
613 | d = $(d:R=$(path)) ; | |
614 | DEPENDS $(target) : $(d) ; | |
615 | common.MkDir $(d) ; | |
616 | } | |
617 | ||
618 | # For a real file target, we create a fake target depending on the | |
619 | # real target. This allows us to run | |
620 | # | |
621 | # b2 hello.o | |
622 | # | |
623 | # without trying to guess the name of the real target. Note that the | |
624 | # target has no directory name and uses a special <e> grist. | |
625 | # | |
626 | # First, that means that "b2 hello.o" will build all known hello.o | |
627 | # targets. Second, the <e> grist makes sure this target will not be | |
628 | # confused with other targets, for example, if we have subdir 'test' | |
629 | # with target 'test' in it that includes a 'test.o' file, then the | |
630 | # target for directory will be just 'test' the target for test.o | |
631 | # will be <ptest/bin/gcc/debug>test.o and the target we create below | |
632 | # will be <e>test.o | |
633 | DEPENDS $(target:G=e) : $(target) ; | |
634 | # Allow b2 <path-to-file>/<file> to work. This will not catch all | |
635 | # possible ways to refer to the path (relative/absolute, extra ".", | |
636 | # various "..", but should help in obvious cases. | |
637 | DEPENDS $(target:G=e:R=$(path)) : $(target) ; | |
638 | } | |
639 | else | |
640 | { | |
641 | SEARCH on $(target) = [ path.native $(self.path) ] ; | |
642 | } | |
643 | } | |
644 | ||
645 | # Returns the directory for this target. | |
646 | # | |
647 | rule path ( ) | |
648 | { | |
649 | if ! $(self.path) | |
650 | { | |
651 | if $(self.action) | |
652 | { | |
653 | local p = [ $(self.action).properties ] ; | |
654 | local path,relative-to-build-dir = [ $(p).target-path ] ; | |
655 | local path = $(path,relative-to-build-dir[1]) ; | |
656 | local relative-to-build-dir = $(path,relative-to-build-dir[2]) ; | |
657 | ||
658 | if $(relative-to-build-dir) | |
659 | { | |
660 | path = [ path.join [ $(self.project).build-dir ] $(path) ] ; | |
661 | } | |
662 | ||
663 | self.path = [ path.native $(path) ] ; | |
664 | } | |
665 | } | |
666 | return $(self.path) ; | |
667 | } | |
668 | } | |
669 | ||
670 | ||
671 | class notfile-target : abstract-file-target | |
672 | { | |
673 | rule __init__ ( name : project : action ? ) | |
674 | { | |
675 | abstract-file-target.__init__ $(name) : : $(project) : $(action) ; | |
676 | } | |
677 | ||
678 | # Returns nothing to indicate that the target's path is not known. | |
679 | # | |
680 | rule path ( ) | |
681 | { | |
682 | return ; | |
683 | } | |
684 | ||
685 | rule actualize-location ( target ) | |
686 | { | |
687 | NOTFILE $(target) ; | |
688 | ALWAYS $(target) ; | |
689 | # TEMPORARY $(target) ; | |
690 | NOUPDATE $(target) ; | |
691 | } | |
692 | } | |
693 | ||
694 | ||
695 | # Class representing an action. Both 'targets' and 'sources' should list | |
696 | # instances of 'virtual-target'. Action name should name a rule with this | |
697 | # prototype: | |
698 | # rule action-name ( targets + : sources * : properties * ) | |
699 | # Targets and sources are passed as actual Jam targets. The rule may not | |
700 | # establish additional dependency relationships. | |
701 | # | |
702 | class action | |
703 | { | |
704 | import "class" ; | |
705 | import indirect ; | |
706 | import path ; | |
707 | import property-set ; | |
708 | import set : difference ; | |
709 | import toolset ; | |
710 | import type ; | |
711 | ||
712 | rule __init__ ( sources * : action-name + : property-set ? ) | |
713 | { | |
714 | self.sources = $(sources) ; | |
715 | ||
716 | self.action-name = [ indirect.make-qualified $(action-name) ] ; | |
717 | ||
718 | if ! $(property-set) | |
719 | { | |
720 | property-set = [ property-set.empty ] ; | |
721 | } | |
722 | ||
723 | if ! [ class.is-instance $(property-set) ] | |
724 | { | |
725 | import errors : error : errors.error ; | |
726 | errors.error "Property set instance required" ; | |
727 | } | |
728 | ||
729 | self.properties = $(property-set) ; | |
730 | } | |
731 | ||
732 | rule add-targets ( targets * ) | |
733 | { | |
734 | self.targets += $(targets) ; | |
735 | } | |
736 | ||
737 | rule replace-targets ( old-targets * : new-targets * ) | |
738 | { | |
739 | self.targets = [ set.difference $(self.targets) : $(old-targets) ] ; | |
740 | self.targets += $(new-targets) ; | |
741 | } | |
742 | ||
743 | rule targets ( ) | |
744 | { | |
745 | return $(self.targets) ; | |
746 | } | |
747 | ||
748 | rule sources ( ) | |
749 | { | |
750 | return $(self.sources) ; | |
751 | } | |
752 | ||
753 | rule action-name ( ) | |
754 | { | |
755 | return $(self.action-name) ; | |
756 | } | |
757 | ||
758 | rule properties ( ) | |
759 | { | |
760 | return $(self.properties) ; | |
761 | } | |
762 | ||
763 | # Generates actual build instructions. | |
764 | # | |
765 | rule actualize ( ) | |
766 | { | |
767 | if ! $(self.actualized) | |
768 | { | |
769 | self.actualized = true ; | |
770 | ||
771 | local ps = [ properties ] ; | |
772 | local properties = [ adjust-properties $(ps) ] ; | |
773 | ||
774 | local actual-targets ; | |
775 | for local i in [ targets ] | |
776 | { | |
777 | actual-targets += [ $(i).actualize ] ; | |
778 | } | |
779 | ||
780 | actualize-sources [ sources ] : $(properties) ; | |
781 | ||
782 | DEPENDS $(actual-targets) : $(self.actual-sources) | |
783 | $(self.dependency-only-sources) ; | |
784 | ||
785 | # Action name can include additional rule arguments, which should | |
786 | # not be passed to 'set-target-variables'. | |
787 | toolset.set-target-variables | |
788 | [ indirect.get-rule $(self.action-name[1]) ] $(actual-targets) | |
789 | : $(properties) ; | |
790 | ||
791 | # Reflect ourselves in a variable for the target. This allows | |
792 | # looking up additional info for the action given the raw target. | |
793 | # For example to debug or output action information from action | |
794 | # rules. | |
795 | .action on $(actual-targets) = $(__name__) ; | |
796 | ||
b32b8144 FG |
797 | #indirect.call $(self.action-name) $(actual-targets) |
798 | # : $(self.actual-sources) : [ $(properties).raw ] ; | |
799 | execute $(self.action-name) $(actual-targets) | |
7c673cae FG |
800 | : $(self.actual-sources) : [ $(properties).raw ] ; |
801 | ||
802 | # Since we set up the creating action here, we set up the action for | |
803 | # cleaning up as well. | |
804 | common.Clean clean-all : $(actual-targets) ; | |
805 | } | |
806 | } | |
807 | ||
808 | # Helper for 'actualize-sources'. For each passed source, actualizes it with | |
809 | # the appropriate scanner. Returns the actualized virtual targets. | |
810 | # | |
811 | rule actualize-source-type ( sources * : property-set ) | |
812 | { | |
813 | local result = ; | |
814 | for local i in $(sources) | |
815 | { | |
816 | local scanner ; | |
817 | if [ $(i).type ] | |
818 | { | |
819 | scanner = [ type.get-scanner [ $(i).type ] : $(property-set) ] ; | |
820 | } | |
821 | result += [ $(i).actualize $(scanner) ] ; | |
822 | } | |
823 | return $(result) ; | |
824 | } | |
825 | ||
826 | # Creates actual Jam targets for sources. Initializes the following member | |
827 | # variables: | |
828 | # 'self.actual-sources' -- sources passed to the updating action. | |
829 | # 'self.dependency-only-sources' -- sources marked as dependencies, but | |
830 | # are not used otherwise. | |
831 | # | |
832 | # New values will be *appended* to the variables. They may be non-empty if | |
833 | # caller wants it. | |
834 | # | |
835 | rule actualize-sources ( sources * : property-set ) | |
836 | { | |
837 | local dependencies = [ $(self.properties).get <dependency> ] ; | |
838 | ||
839 | self.dependency-only-sources += | |
840 | [ actualize-source-type $(dependencies) : $(property-set) ] ; | |
841 | self.actual-sources += | |
842 | [ actualize-source-type $(sources) : $(property-set) ] ; | |
843 | ||
844 | # This is used to help b2 find dependencies in generated headers and | |
845 | # other main targets, e.g. in: | |
846 | # | |
847 | # make a.h : ....... ; | |
848 | # exe hello : hello.cpp : <implicit-dependency>a.h ; | |
849 | # | |
850 | # For b2 to find the dependency the generated target must be | |
851 | # actualized (i.e. have its Jam target constructed). In the above case, | |
852 | # if we are building just hello ("b2 hello"), 'a.h' will not be | |
853 | # actualized unless we do it here. | |
854 | local implicit = [ $(self.properties).get <implicit-dependency> ] ; | |
855 | for local i in $(implicit) | |
856 | { | |
857 | $(i:G=).actualize ; | |
858 | } | |
859 | } | |
860 | ||
861 | # Determines real properties when trying to build with 'properties'. This is | |
862 | # the last chance to fix properties, for example to adjust includes to get | |
863 | # generated headers correctly. Default implementation simply returns its | |
864 | # argument. | |
865 | # | |
866 | rule adjust-properties ( property-set ) | |
867 | { | |
868 | return $(property-set) ; | |
869 | } | |
b32b8144 FG |
870 | |
871 | # Execute the action rule on the given targets, sources, and properties. | |
872 | # Since this does the final call to the engine action rule this takes | |
873 | # engine level targets and raw properties. One could override this, for | |
874 | # example, to set additional variables on hte target that might be | |
875 | # difficult to determine just using toolset flags. | |
876 | # Note, you must call this base rule when overriding as otherwise the | |
877 | # actions will not execute and the engine will not run commands. | |
878 | # | |
879 | rule execute ( action-name targets + : sources * : properties * ) | |
880 | { | |
881 | indirect.call $(action-name) $(targets) : $(sources) : $(properties) ; | |
882 | } | |
7c673cae FG |
883 | } |
884 | ||
885 | ||
886 | # Action class which does nothing --- it produces the targets with specific | |
887 | # properties out of nowhere. It is needed to distinguish virtual targets with | |
888 | # different properties that are known to exist and have no actions which create | |
889 | # them. | |
890 | # | |
891 | class null-action : action | |
892 | { | |
893 | rule __init__ ( property-set ? ) | |
894 | { | |
895 | action.__init__ : .no-action : $(property-set) ; | |
896 | } | |
897 | ||
898 | rule actualize ( ) | |
899 | { | |
900 | if ! $(self.actualized) | |
901 | { | |
902 | self.actualized = true ; | |
903 | for local i in [ targets ] | |
904 | { | |
905 | $(i).actualize ; | |
906 | } | |
907 | } | |
908 | } | |
909 | } | |
910 | ||
911 | ||
912 | # Class which acts exactly like 'action', except that its sources are not | |
913 | # scanned for dependencies. | |
914 | # | |
915 | class non-scanning-action : action | |
916 | { | |
917 | rule __init__ ( sources * : action-name + : property-set ? ) | |
918 | { | |
919 | action.__init__ $(sources) : $(action-name) : $(property-set) ; | |
920 | } | |
921 | ||
922 | rule actualize-source-type ( sources * : property-set ) | |
923 | { | |
924 | local result ; | |
925 | for local i in $(sources) | |
926 | { | |
927 | result += [ $(i).actualize ] ; | |
928 | } | |
929 | return $(result) ; | |
930 | } | |
931 | } | |
932 | ||
933 | ||
934 | # Creates a virtual target with an appropriate name and type from 'file'. If a | |
935 | # target with that name in that project already exists, returns that already | |
936 | # created target. | |
937 | # | |
938 | # FIXME: a more correct way would be to compute the path to the file, based on | |
939 | # name and source location for the project, and use that path to determine if | |
940 | # the target has already been created. This logic should be shared with how we | |
941 | # usually find targets identified by a specific target id. It should also be | |
942 | # updated to work correctly when the file is specified using both relative and | |
943 | # absolute paths. | |
944 | # | |
945 | # TODO: passing a project with all virtual targets is starting to be annoying. | |
946 | # | |
947 | rule from-file ( file : file-loc : project ) | |
948 | { | |
949 | import type ; # Had to do this here to break a circular dependency. | |
950 | ||
951 | # Check whether we already created a target corresponding to this file. | |
952 | local path = [ path.root [ path.root $(file) $(file-loc) ] [ path.pwd ] ] ; | |
953 | ||
954 | if $(.files.$(path)) | |
955 | { | |
956 | return $(.files.$(path)) ; | |
957 | } | |
958 | else | |
959 | { | |
960 | local name = [ path.make $(file) ] ; | |
961 | local type = [ type.type $(file) ] ; | |
962 | local result ; | |
963 | ||
964 | result = [ new file-target $(file) : $(type) : $(project) : : | |
965 | $(file-loc) ] ; | |
966 | ||
967 | .files.$(path) = $(result) ; | |
968 | return $(result) ; | |
969 | } | |
970 | } | |
971 | ||
972 | ||
973 | # Registers a new virtual target. Checks if there is already a registered target | |
974 | # with the same name, type, project and subvariant properties as well as the | |
975 | # same sources and equal action. If such target is found it is returned and a | |
976 | # new 'target' is not registered. Otherwise, 'target' is registered and | |
977 | # returned. | |
978 | # | |
979 | rule register ( target ) | |
980 | { | |
981 | local signature = [ sequence.join [ $(target).path ] [ $(target).name ] : - | |
982 | ] ; | |
983 | ||
984 | local result ; | |
985 | for local t in $(.cache.$(signature)) | |
986 | { | |
987 | local a1 = [ $(t).action ] ; | |
988 | local a2 = [ $(target).action ] ; | |
989 | ||
990 | if ! $(result) | |
991 | { | |
992 | if ! $(a1) && ! $(a2) | |
993 | { | |
994 | result = $(t) ; | |
995 | } | |
996 | else if $(a1) && $(a2) && | |
997 | ( [ $(a1).action-name ] = [ $(a2).action-name ] ) && | |
998 | ( [ $(a1).sources ] = [ $(a2).sources ] ) | |
999 | { | |
1000 | local ps1 = [ $(a1).properties ] ; | |
1001 | local ps2 = [ $(a2).properties ] ; | |
1002 | local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference | |
1003 | [ $(ps1).dependency ] : [ $(ps1).incidental ] ] ; | |
1004 | local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference | |
1005 | [ $(ps2).dependency ] : [ $(ps2).incidental ] ] ; | |
1006 | if $(p1) = $(p2) | |
1007 | { | |
1008 | result = $(t) ; | |
1009 | } | |
1010 | } | |
1011 | } | |
1012 | } | |
1013 | ||
1014 | if ! $(result) | |
1015 | { | |
1016 | .cache.$(signature) += $(target) ; | |
1017 | result = $(target) ; | |
1018 | } | |
1019 | ||
1020 | .recent-targets += $(result) ; | |
1021 | .all-targets += $(result) ; | |
1022 | ||
1023 | return $(result) ; | |
1024 | } | |
1025 | ||
1026 | ||
1027 | # Each target returned by 'register' is added to the .recent-targets list, | |
1028 | # returned by this function. This allows us to find all virtual targets created | |
1029 | # when building a specific main target, even those constructed only as | |
1030 | # intermediate targets. | |
1031 | # | |
1032 | rule recent-targets ( ) | |
1033 | { | |
1034 | return $(.recent-targets) ; | |
1035 | } | |
1036 | ||
1037 | ||
1038 | rule clear-recent-targets ( ) | |
1039 | { | |
1040 | .recent-targets = ; | |
1041 | } | |
1042 | ||
1043 | ||
1044 | # Returns all virtual targets ever created. | |
1045 | # | |
1046 | rule all-targets ( ) | |
1047 | { | |
1048 | return $(.all-targets) ; | |
1049 | } | |
1050 | ||
1051 | ||
1052 | # Returns all targets from 'targets' with types equal to 'type' or derived from | |
1053 | # it. | |
1054 | # | |
1055 | rule select-by-type ( type : targets * ) | |
1056 | { | |
1057 | local result ; | |
1058 | for local t in $(targets) | |
1059 | { | |
1060 | if [ type.is-subtype [ $(t).type ] $(type) ] | |
1061 | { | |
1062 | result += $(t) ; | |
1063 | } | |
1064 | } | |
1065 | return $(result) ; | |
1066 | } | |
1067 | ||
1068 | ||
1069 | rule register-actual-name ( actual-name : virtual-target ) | |
1070 | { | |
1071 | if $(.actual.$(actual-name)) | |
1072 | { | |
1073 | local cs1 = [ $(.actual.$(actual-name)).creating-subvariant ] ; | |
1074 | local cmt1-name ; | |
1075 | if $(cs1)-is-defined | |
1076 | { | |
1077 | local cmt1 = [ $(cs1).main-target ] ; | |
1078 | cmt1-name = [ $(cmt1).full-name ] ; | |
1079 | } | |
1080 | local cs2 = [ $(virtual-target).creating-subvariant ] ; | |
1081 | local cmt2-name ; | |
1082 | if $(cs2)-is-defined | |
1083 | { | |
1084 | local cmt2 = [ $(cs2).main-target ] ; | |
1085 | cmt2-name = [ $(cmt2).full-name ] ; | |
1086 | } | |
1087 | local extra-error-information ; | |
1088 | if ! $(cs1)-is-defined || ! $(cs2)-is-defined | |
1089 | { | |
1090 | extra-error-information = Encountered a virtual-target without a | |
1091 | creating subvariant. It could be the virtual target has not been | |
1092 | registered via the virtual-target.register rule. ; | |
1093 | } | |
1094 | ||
1095 | local action1 = [ $(.actual.$(actual-name)).action ] ; | |
1096 | local action2 = [ $(virtual-target).action ] ; | |
1097 | local properties-added ; | |
1098 | local properties-removed ; | |
1099 | if $(action1) && $(action2) | |
1100 | { | |
1101 | local p1 = [ $(action1).properties ] ; | |
1102 | p1 = [ $(p1).raw ] ; | |
1103 | local p2 = [ $(action2).properties ] ; | |
1104 | p2 = [ $(p2).raw ] ; | |
1105 | properties-removed = [ set.difference $(p1) : $(p2) ] ; | |
1106 | properties-removed ?= "none" ; | |
1107 | properties-added = [ set.difference $(p2) : $(p1) ] ; | |
1108 | properties-added ?= "none" ; | |
1109 | } | |
1110 | import errors : user-error : errors.user-error ; | |
1111 | errors.user-error "Name clash for '$(actual-name)'" | |
1112 | : "" | |
1113 | : "Tried to build the target twice, with property sets having " | |
1114 | : "these incompatible properties:" | |
1115 | : "" | |
1116 | : " - " $(properties-removed) | |
1117 | : " - " $(properties-added) | |
1118 | : "" | |
1119 | : "Please make sure to have consistent requirements for these " | |
1120 | : "properties everywhere in your project, especially for install" | |
1121 | : "targets." | |
1122 | ; | |
1123 | } | |
1124 | else | |
1125 | { | |
1126 | .actual.$(actual-name) = $(virtual-target) ; | |
1127 | } | |
1128 | } | |
1129 | ||
1130 | ||
1131 | # Traverses the dependency graph of 'target' and return all targets that will be | |
1132 | # created before this one is created. If the root of some dependency graph is | |
1133 | # found during traversal, it is either included or not, depending on the | |
1134 | # 'include-roots' value. In either case traversal stops at root targets, i.e. | |
1135 | # root target sources are not traversed. | |
1136 | # | |
1137 | rule traverse ( target : include-roots ? : include-sources ? ) | |
1138 | { | |
1139 | local result ; | |
1140 | if [ $(target).action ] | |
1141 | { | |
1142 | local action = [ $(target).action ] ; | |
1143 | # This includes the 'target' as well. | |
1144 | result += [ $(action).targets ] ; | |
1145 | ||
1146 | for local t in [ $(action).sources ] | |
1147 | { | |
1148 | if ! [ $(t).root ] | |
1149 | { | |
1150 | result += [ traverse $(t) : $(include-roots) : | |
1151 | $(include-sources) ] ; | |
1152 | } | |
1153 | else if $(include-roots) | |
1154 | { | |
1155 | result += $(t) ; | |
1156 | } | |
1157 | } | |
1158 | } | |
1159 | else if $(include-sources) | |
1160 | { | |
1161 | result = $(target) ; | |
1162 | } | |
1163 | return $(result) ; | |
1164 | } | |
1165 | ||
1166 | ||
1167 | # Takes an 'action' instance and creates a new instance of it and all targets | |
1168 | # produced by the action. The rule-name and properties are set to | |
1169 | # 'new-rule-name' and 'new-properties', if those are specified. Returns the | |
1170 | # cloned action. | |
1171 | # | |
1172 | rule clone-action ( action : new-project : new-action-name ? : new-properties ? | |
1173 | ) | |
1174 | { | |
1175 | if ! $(new-action-name) | |
1176 | { | |
1177 | new-action-name = [ $(action).action-name ] ; | |
1178 | } | |
1179 | if ! $(new-properties) | |
1180 | { | |
1181 | new-properties = [ $(action).properties ] ; | |
1182 | } | |
1183 | ||
1184 | local action-class = [ modules.peek $(action) : __class__ ] ; | |
1185 | local cloned-action = [ class.new $(action-class) | |
1186 | [ $(action).sources ] : $(new-action-name) : $(new-properties) ] ; | |
1187 | ||
1188 | local cloned-targets ; | |
1189 | for local target in [ $(action).targets ] | |
1190 | { | |
1191 | local n = [ $(target).name ] ; | |
1192 | # Do not modify produced target names. | |
1193 | local cloned-target = [ class.new file-target $(n) exact : | |
1194 | [ $(target).type ] : $(new-project) : $(cloned-action) ] ; | |
1195 | local d = [ $(target).dependencies ] ; | |
1196 | if $(d) | |
1197 | { | |
1198 | $(cloned-target).depends $(d) ; | |
1199 | } | |
1200 | $(cloned-target).root [ $(target).root ] ; | |
1201 | $(cloned-target).creating-subvariant [ $(target).creating-subvariant ] ; | |
1202 | ||
1203 | cloned-targets += $(cloned-target) ; | |
1204 | } | |
1205 | ||
1206 | return $(cloned-action) ; | |
1207 | } | |
1208 | ||
1209 | ||
1210 | class subvariant | |
1211 | { | |
1212 | import sequence ; | |
1213 | import type ; | |
1214 | ||
1215 | rule __init__ ( main-target # The instance of main-target class. | |
1216 | : property-set # Properties requested for this target. | |
1217 | : sources * | |
1218 | : build-properties # Actually used properties. | |
1219 | : sources-usage-requirements # Properties propagated from sources. | |
1220 | : created-targets * ) # Top-level created targets. | |
1221 | { | |
1222 | self.main-target = $(main-target) ; | |
1223 | self.properties = $(property-set) ; | |
1224 | self.sources = $(sources) ; | |
1225 | self.build-properties = $(build-properties) ; | |
1226 | self.sources-usage-requirements = $(sources-usage-requirements) ; | |
1227 | self.created-targets = $(created-targets) ; | |
1228 | ||
1229 | # Pre-compose a list of other dependency graphs this one depends on. | |
1230 | local deps = [ $(build-properties).get <implicit-dependency> ] ; | |
1231 | for local d in $(deps) | |
1232 | { | |
1233 | self.other-dg += [ $(d:G=).creating-subvariant ] ; | |
1234 | } | |
1235 | ||
1236 | self.other-dg = [ sequence.unique $(self.other-dg) ] ; | |
1237 | } | |
1238 | ||
1239 | rule main-target ( ) | |
1240 | { | |
1241 | return $(self.main-target) ; | |
1242 | } | |
1243 | ||
1244 | rule created-targets ( ) | |
1245 | { | |
1246 | return $(self.created-targets) ; | |
1247 | } | |
1248 | ||
1249 | rule requested-properties ( ) | |
1250 | { | |
1251 | return $(self.properties) ; | |
1252 | } | |
1253 | ||
1254 | rule build-properties ( ) | |
1255 | { | |
1256 | return $(self.build-properties) ; | |
1257 | } | |
1258 | ||
1259 | rule sources-usage-requirements ( ) | |
1260 | { | |
1261 | return $(self.sources-usage-requirements) ; | |
1262 | } | |
1263 | ||
1264 | rule set-usage-requirements ( usage-requirements ) | |
1265 | { | |
1266 | self.usage-requirements = $(usage-requirements) ; | |
1267 | } | |
1268 | ||
1269 | rule usage-requirements ( ) | |
1270 | { | |
1271 | return $(self.usage-requirements) ; | |
1272 | } | |
1273 | ||
1274 | # Returns all targets referenced by this subvariant, either directly or | |
1275 | # indirectly, and either as sources, or as dependency properties. Targets | |
1276 | # referred to using the dependency property are returned as properties, not | |
1277 | # targets. | |
1278 | # | |
1279 | rule all-referenced-targets ( theset ) | |
1280 | { | |
1281 | # Find directly referenced targets. | |
1282 | local deps = [ $(self.build-properties).dependency ] ; | |
1283 | local all-targets = $(self.sources) $(deps) ; | |
1284 | ||
1285 | # Find other subvariants. | |
1286 | local r ; | |
1287 | for local t in $(all-targets) | |
1288 | { | |
1289 | if ! [ $(theset).contains $(t) ] | |
1290 | { | |
1291 | $(theset).add $(t) ; | |
1292 | r += [ $(t:G=).creating-subvariant ] ; | |
1293 | } | |
1294 | } | |
1295 | r = [ sequence.unique $(r) ] ; | |
1296 | for local s in $(r) | |
1297 | { | |
1298 | if $(s) != $(__name__) | |
1299 | { | |
1300 | $(s).all-referenced-targets $(theset) ; | |
1301 | } | |
1302 | } | |
1303 | } | |
1304 | ||
1305 | # Returns the properties specifying implicit include paths to generated | |
1306 | # headers. This traverses all targets in this subvariant and subvariants | |
1307 | # referred by <implicit-dependency> properties. For all targets of type | |
1308 | # 'target-type' (or for all targets, if 'target-type' is not specified), the | |
1309 | # result will contain <$(feature)>path-to-that-target. | |
1310 | # | |
1311 | rule implicit-includes ( feature : target-type ? ) | |
1312 | { | |
1313 | local key = ii$(feature)-$(target-type:E="") ; | |
1314 | if ! $($(key))-is-not-empty | |
1315 | { | |
1316 | local target-paths = [ all-target-directories $(target-type) ] ; | |
1317 | target-paths = [ sequence.unique $(target-paths) ] ; | |
1318 | local result = $(target-paths:G=$(feature)) ; | |
1319 | if ! $(result) | |
1320 | { | |
1321 | result = "" ; | |
1322 | } | |
1323 | $(key) = $(result) ; | |
1324 | } | |
1325 | if $($(key)) = "" | |
1326 | { | |
1327 | return ; | |
1328 | } | |
1329 | else | |
1330 | { | |
1331 | return $($(key)) ; | |
1332 | } | |
1333 | } | |
1334 | ||
1335 | rule all-target-directories ( target-type ? ) | |
1336 | { | |
1337 | if ! $(self.target-directories) | |
1338 | { | |
1339 | compute-target-directories $(target-type) ; | |
1340 | } | |
1341 | return $(self.target-directories) ; | |
1342 | } | |
1343 | ||
1344 | rule compute-target-directories ( target-type ? ) | |
1345 | { | |
1346 | local result ; | |
1347 | for local t in $(self.created-targets) | |
1348 | { | |
1349 | # Skip targets of the wrong type. | |
1350 | local type = [ $(t).type ] ; | |
1351 | if ! $(target-type) || | |
1352 | ( $(type) && [ type.is-derived $(type) $(target-type) ] ) | |
1353 | { | |
1354 | result = [ sequence.merge $(result) : [ $(t).path ] ] ; | |
1355 | } | |
1356 | } | |
1357 | for local d in $(self.other-dg) | |
1358 | { | |
1359 | result += [ $(d).all-target-directories $(target-type) ] ; | |
1360 | } | |
1361 | self.target-directories = $(result) ; | |
1362 | } | |
1363 | } |