Ticket #33: virtual-target.jam

File virtual-target.jam, 36.8 KB (added by mse102950, 20 years ago)

Proposed implementation of feature

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