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
60import "class" : new ;
61import path ;
62import sequence ;
63import set ;
64import type ;
65import 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#
72class 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#
222class 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#
507rule 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#
547class 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
671class 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#
702class 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
797            indirect.call $(self.action-name) $(actual-targets)
798                : $(self.actual-sources) : [ $(properties).raw ] ;
799
800            # Since we set up the creating action here, we set up the action for
801            # cleaning up as well.
802            common.Clean clean-all : $(actual-targets) ;
803        }
804    }
805
806    # Helper for 'actualize-sources'. For each passed source, actualizes it with
807    # the appropriate scanner. Returns the actualized virtual targets.
808    #
809    rule actualize-source-type ( sources * : property-set )
810    {
811        local result = ;
812        for local i in $(sources)
813        {
814            local scanner ;
815            if [ $(i).type ]
816            {
817                scanner = [ type.get-scanner [ $(i).type ] : $(property-set) ] ;
818            }
819            result += [ $(i).actualize $(scanner) ] ;
820        }
821        return $(result) ;
822    }
823
824    # Creates actual Jam targets for sources. Initializes the following member
825    # variables:
826    #   'self.actual-sources'          -- sources passed to the updating action.
827    #   'self.dependency-only-sources' -- sources marked as dependencies, but
828    #                                     are not used otherwise.
829    #
830    # New values will be *appended* to the variables. They may be non-empty if
831    # caller wants it.
832    #
833    rule actualize-sources ( sources * : property-set )
834    {
835        local dependencies = [ $(self.properties).get <dependency> ] ;
836
837        self.dependency-only-sources +=
838            [ actualize-source-type $(dependencies) : $(property-set) ] ;
839        self.actual-sources +=
840            [ actualize-source-type $(sources) : $(property-set) ] ;
841
842        # This is used to help b2 find dependencies in generated headers and
843        # other main targets, e.g. in:
844        #
845        #   make a.h : ....... ;
846        #   exe hello : hello.cpp : <implicit-dependency>a.h ;
847        #
848        # For b2 to find the dependency the generated target must be
849        # actualized (i.e. have its Jam target constructed). In the above case,
850        # if we are building just hello ("b2 hello"), 'a.h' will not be
851        # actualized unless we do it here.
852        local implicit = [ $(self.properties).get <implicit-dependency> ] ;
853        for local i in $(implicit)
854        {
855            $(i:G=).actualize ;
856        }
857    }
858
859    # Determines real properties when trying to build with 'properties'. This is
860    # the last chance to fix properties, for example to adjust includes to get
861    # generated headers correctly. Default implementation simply returns its
862    # argument.
863    #
864    rule adjust-properties ( property-set )
865    {
866        return $(property-set) ;
867    }
868}
869
870
871# Action class which does nothing --- it produces the targets with specific
872# properties out of nowhere. It is needed to distinguish virtual targets with
873# different properties that are known to exist and have no actions which create
874# them.
875#
876class null-action : action
877{
878    rule __init__ ( property-set ? )
879    {
880        action.__init__ : .no-action : $(property-set) ;
881    }
882
883    rule actualize ( )
884    {
885        if ! $(self.actualized)
886        {
887            self.actualized = true ;
888            for local i in [ targets ]
889            {
890                $(i).actualize ;
891            }
892        }
893    }
894}
895
896
897# Class which acts exactly like 'action', except that its sources are not
898# scanned for dependencies.
899#
900class non-scanning-action : action
901{
902    rule __init__ ( sources * : action-name + : property-set ? )
903    {
904        action.__init__ $(sources) : $(action-name) : $(property-set) ;
905    }
906
907    rule actualize-source-type ( sources * : property-set )
908    {
909        local result ;
910        for local i in $(sources)
911        {
912            result += [ $(i).actualize ] ;
913        }
914        return $(result) ;
915    }
916}
917
918
919# Creates a virtual target with an appropriate name and type from 'file'. If a
920# target with that name in that project already exists, returns that already
921# created target.
922#
923# FIXME: a more correct way would be to compute the path to the file, based on
924# name and source location for the project, and use that path to determine if
925# the target has already been created. This logic should be shared with how we
926# usually find targets identified by a specific target id. It should also be
927# updated to work correctly when the file is specified using both relative and
928# absolute paths.
929#
930# TODO: passing a project with all virtual targets is starting to be annoying.
931#
932rule from-file ( file : file-loc : project )
933{
934    import type ;  # Had to do this here to break a circular dependency.
935
936    # Check whether we already created a target corresponding to this file.
937    local path = [ path.root [ path.root $(file) $(file-loc) ] [ path.pwd ] ] ;
938
939    if $(.files.$(path))
940    {
941        return $(.files.$(path)) ;
942    }
943    else
944    {
945        local name = [ path.make $(file) ] ;
946        local type = [ type.type $(file) ] ;
947        local result ;
948
949        result = [ new file-target $(file) : $(type) : $(project) : :
950            $(file-loc) ] ;
951
952        .files.$(path) = $(result) ;
953        return $(result) ;
954    }
955}
956
957
958# Registers a new virtual target. Checks if there is already a registered target
959# with the same name, type, project and subvariant properties as well as the
960# same sources and equal action. If such target is found it is returned and a
961# new 'target' is not registered. Otherwise, 'target' is registered and
962# returned.
963#
964rule register ( target )
965{
966    local signature = [ sequence.join [ $(target).path ] [ $(target).name ] : -
967        ] ;
968
969    local result ;
970    for local t in $(.cache.$(signature))
971    {
972        local a1 = [ $(t).action ] ;
973        local a2 = [ $(target).action ] ;
974
975        if ! $(result)
976        {
977            if ! $(a1) && ! $(a2)
978            {
979                result = $(t) ;
980            }
981            else if $(a1) && $(a2) &&
982                ( [ $(a1).action-name ] = [ $(a2).action-name ] ) &&
983                ( [ $(a1).sources     ] = [ $(a2).sources     ] )
984            {
985                local ps1 = [ $(a1).properties ] ;
986                local ps2 = [ $(a2).properties ] ;
987                local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference
988                    [ $(ps1).dependency ] : [ $(ps1).incidental ] ] ;
989                local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference
990                    [ $(ps2).dependency ] : [ $(ps2).incidental ] ] ;
991                if $(p1) = $(p2)
992                {
993                    result = $(t) ;
994                }
995            }
996        }
997    }
998
999    if ! $(result)
1000    {
1001        .cache.$(signature) += $(target) ;
1002        result = $(target) ;
1003    }
1004
1005    .recent-targets += $(result) ;
1006    .all-targets += $(result) ;
1007
1008    return $(result) ;
1009}
1010
1011
1012# Each target returned by 'register' is added to the .recent-targets list,
1013# returned by this function. This allows us to find all virtual targets created
1014# when building a specific main target, even those constructed only as
1015# intermediate targets.
1016#
1017rule recent-targets ( )
1018{
1019    return $(.recent-targets) ;
1020}
1021
1022
1023rule clear-recent-targets ( )
1024{
1025    .recent-targets = ;
1026}
1027
1028
1029# Returns all virtual targets ever created.
1030#
1031rule all-targets ( )
1032{
1033    return $(.all-targets) ;
1034}
1035
1036
1037# Returns all targets from 'targets' with types equal to 'type' or derived from
1038# it.
1039#
1040rule select-by-type ( type : targets * )
1041{
1042    local result ;
1043    for local t in $(targets)
1044    {
1045        if [ type.is-subtype [ $(t).type ] $(type) ]
1046        {
1047            result += $(t) ;
1048        }
1049    }
1050    return $(result) ;
1051}
1052
1053
1054rule register-actual-name ( actual-name : virtual-target )
1055{
1056    if $(.actual.$(actual-name))
1057    {
1058        local cs1 = [ $(.actual.$(actual-name)).creating-subvariant ] ;
1059        local cmt1-name ;
1060        if $(cs1)-is-defined
1061        {
1062            local cmt1 = [ $(cs1).main-target ] ;
1063            cmt1-name = [ $(cmt1).full-name ] ;
1064        }
1065        local cs2 = [ $(virtual-target).creating-subvariant ] ;
1066        local cmt2-name ;
1067        if $(cs2)-is-defined
1068        {
1069            local cmt2 = [ $(cs2).main-target ] ;
1070            cmt2-name = [ $(cmt2).full-name ] ;
1071        }
1072        local extra-error-information ;
1073        if ! $(cs1)-is-defined || ! $(cs2)-is-defined
1074        {
1075            extra-error-information = Encountered a virtual-target without a
1076                creating subvariant. It could be the virtual target has not been
1077                registered via the virtual-target.register rule. ;
1078        }
1079
1080        local action1 = [ $(.actual.$(actual-name)).action ] ;
1081        local action2 = [ $(virtual-target).action ] ;
1082        local properties-added ;
1083        local properties-removed ;
1084        if $(action1) && $(action2)
1085        {
1086            local p1 = [ $(action1).properties ] ;
1087            p1 = [ $(p1).raw ] ;
1088            local p2 = [ $(action2).properties ] ;
1089            p2 = [ $(p2).raw ] ;
1090            properties-removed = [ set.difference $(p1) : $(p2) ] ;
1091            properties-removed ?= "none" ;
1092            properties-added = [ set.difference $(p2) : $(p1) ] ;
1093            properties-added ?= "none" ;
1094        }
1095        import errors : user-error : errors.user-error ;
1096        errors.user-error "Name clash for '$(actual-name)'"
1097            : ""
1098            : "Tried to build the target twice, with property sets having "
1099            : "these incompatible properties:"
1100            : ""
1101            : "    - " $(properties-removed)
1102            : "    - " $(properties-added)
1103            : ""
1104            : "Please make sure to have consistent requirements for these "
1105            : "properties everywhere in your project, especially for install"
1106            : "targets."
1107            ;
1108    }
1109    else
1110    {
1111        .actual.$(actual-name) = $(virtual-target) ;
1112    }
1113}
1114
1115
1116# Traverses the dependency graph of 'target' and return all targets that will be
1117# created before this one is created. If the root of some dependency graph is
1118# found during traversal, it is either included or not, depending on the
1119# 'include-roots' value. In either case traversal stops at root targets, i.e.
1120# root target sources are not traversed.
1121#
1122rule traverse ( target : include-roots ? : include-sources ? )
1123{
1124    local result ;
1125    if [ $(target).action ]
1126    {
1127        local action = [ $(target).action ] ;
1128        # This includes the 'target' as well.
1129        result += [ $(action).targets ] ;
1130
1131        for local t in [ $(action).sources ]
1132        {
1133            if ! [ $(t).root ]
1134            {
1135                result += [ traverse $(t) : $(include-roots) :
1136                    $(include-sources) ] ;
1137            }
1138            else if $(include-roots)
1139            {
1140                result += $(t) ;
1141            }
1142        }
1143    }
1144    else if $(include-sources)
1145    {
1146        result = $(target) ;
1147    }
1148    return $(result) ;
1149}
1150
1151
1152# Takes an 'action' instance and creates a new instance of it and all targets
1153# produced by the action. The rule-name and properties are set to
1154# 'new-rule-name' and 'new-properties', if those are specified. Returns the
1155# cloned action.
1156#
1157rule clone-action ( action : new-project : new-action-name ? : new-properties ?
1158    )
1159{
1160    if ! $(new-action-name)
1161    {
1162        new-action-name = [ $(action).action-name ] ;
1163    }
1164    if ! $(new-properties)
1165    {
1166        new-properties = [ $(action).properties ] ;
1167    }
1168
1169    local action-class = [ modules.peek $(action) : __class__ ] ;
1170    local cloned-action = [ class.new $(action-class)
1171        [ $(action).sources ] : $(new-action-name) : $(new-properties) ] ;
1172
1173    local cloned-targets ;
1174    for local target in [ $(action).targets ]
1175    {
1176        local n = [ $(target).name ] ;
1177        # Do not modify produced target names.
1178        local cloned-target = [ class.new file-target $(n) exact :
1179            [ $(target).type ] : $(new-project) : $(cloned-action) ] ;
1180        local d = [ $(target).dependencies ] ;
1181        if $(d)
1182        {
1183            $(cloned-target).depends $(d) ;
1184        }
1185        $(cloned-target).root [ $(target).root ] ;
1186        $(cloned-target).creating-subvariant [ $(target).creating-subvariant ] ;
1187
1188        cloned-targets += $(cloned-target) ;
1189    }
1190
1191    return $(cloned-action) ;
1192}
1193
1194
1195class subvariant
1196{
1197    import sequence ;
1198    import type ;
1199
1200    rule __init__ ( main-target       # The instance of main-target class.
1201        : property-set                # Properties requested for this target.
1202        : sources *
1203        : build-properties            # Actually used properties.
1204        : sources-usage-requirements  # Properties propagated from sources.
1205        : created-targets * )         # Top-level created targets.
1206    {
1207        self.main-target = $(main-target) ;
1208        self.properties = $(property-set) ;
1209        self.sources = $(sources) ;
1210        self.build-properties = $(build-properties) ;
1211        self.sources-usage-requirements = $(sources-usage-requirements) ;
1212        self.created-targets = $(created-targets) ;
1213
1214        # Pre-compose a list of other dependency graphs this one depends on.
1215        local deps = [ $(build-properties).get <implicit-dependency> ] ;
1216        for local d in $(deps)
1217        {
1218            self.other-dg += [ $(d:G=).creating-subvariant ] ;
1219        }
1220
1221        self.other-dg = [ sequence.unique $(self.other-dg) ] ;
1222    }
1223
1224    rule main-target ( )
1225    {
1226        return $(self.main-target) ;
1227    }
1228
1229    rule created-targets ( )
1230    {
1231        return $(self.created-targets) ;
1232    }
1233
1234    rule requested-properties ( )
1235    {
1236        return $(self.properties) ;
1237    }
1238
1239    rule build-properties ( )
1240    {
1241        return $(self.build-properties) ;
1242    }
1243
1244    rule sources-usage-requirements ( )
1245    {
1246        return $(self.sources-usage-requirements) ;
1247    }
1248
1249    rule set-usage-requirements ( usage-requirements )
1250    {
1251        self.usage-requirements = $(usage-requirements) ;
1252    }
1253
1254    rule usage-requirements ( )
1255    {
1256        return $(self.usage-requirements) ;
1257    }
1258
1259    # Returns all targets referenced by this subvariant, either directly or
1260    # indirectly, and either as sources, or as dependency properties. Targets
1261    # referred to using the dependency property are returned as properties, not
1262    # targets.
1263    #
1264    rule all-referenced-targets ( theset )
1265    {
1266        # Find directly referenced targets.
1267        local deps = [ $(self.build-properties).dependency ] ;
1268        local all-targets = $(self.sources) $(deps) ;
1269
1270        # Find other subvariants.
1271        local r ;
1272        for local t in $(all-targets)
1273        {
1274            if ! [ $(theset).contains $(t) ]
1275            {
1276                $(theset).add $(t) ;
1277                r += [ $(t:G=).creating-subvariant ] ;
1278            }
1279        }
1280        r = [ sequence.unique $(r) ] ;
1281        for local s in $(r)
1282        {
1283            if $(s) != $(__name__)
1284            {
1285                $(s).all-referenced-targets $(theset) ;
1286            }
1287        }
1288    }
1289
1290    # Returns the properties specifying implicit include paths to generated
1291    # headers. This traverses all targets in this subvariant and subvariants
1292    # referred by <implicit-dependency> properties. For all targets of type
1293    # 'target-type' (or for all targets, if 'target-type' is not specified), the
1294    # result will contain <$(feature)>path-to-that-target.
1295    #
1296    rule implicit-includes ( feature : target-type ? )
1297    {
1298        local key = ii$(feature)-$(target-type:E="") ;
1299        if ! $($(key))-is-not-empty
1300        {
1301            local target-paths = [ all-target-directories $(target-type) ] ;
1302            target-paths = [ sequence.unique $(target-paths) ] ;
1303            local result = $(target-paths:G=$(feature)) ;
1304            if ! $(result)
1305            {
1306                result = "" ;
1307            }
1308            $(key) = $(result) ;
1309        }
1310        if $($(key)) = ""
1311        {
1312            return ;
1313        }
1314        else
1315        {
1316            return $($(key)) ;
1317        }
1318    }
1319
1320    rule all-target-directories ( target-type ? )
1321    {
1322        if ! $(self.target-directories)
1323        {
1324            compute-target-directories $(target-type) ;
1325        }
1326        return $(self.target-directories) ;
1327    }
1328
1329    rule compute-target-directories ( target-type ? )
1330    {
1331        local result ;
1332        for local t in $(self.created-targets)
1333        {
1334            # Skip targets of the wrong type.
1335            local type = [ $(t).type ] ;
1336            if ! $(target-type) ||
1337                ( $(type) && [ type.is-derived $(type) $(target-type) ] )
1338            {
1339                result = [ sequence.merge $(result) : [ $(t).path ] ] ;
1340            }
1341        }
1342        for local d in $(self.other-dg)
1343        {
1344            result += [ $(d).all-target-directories $(target-type) ] ;
1345        }
1346        self.target-directories = $(result) ;
1347    }
1348}
1349