2 # Base revision: 64488.
4 # Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and
5 # distribute this software is granted provided this copyright notice appears in
6 # all copies. This software is provided "as is" without express or implied
7 # warranty, and with no claim as to its suitability for any purpose.
9 # Implements virtual targets, which correspond to actual files created during
10 # build, but are not yet targets in Jam sense. They are needed, for example,
11 # when searching for possible transormation sequences, when it's not known
12 # if particular target should be created at all.
15 # +--------------------------+
17 # +==========================+
19 # +--------------------------+
20 # | actualize_action() = 0 |
21 # | actualize_location() = 0 |
22 # +----------------+---------+
28 # +---------------------+ +-------+--------------+
29 # | Action | | AbstractFileTarget |
30 # +=====================| * +======================+
31 # | action_name | +--+ action |
32 # | properties | | +----------------------+
33 # +---------------------+--+ | actualize_action() |
34 # | actualize() |0..1 +-----------+----------+
36 # | adjust_properties() | sources |
37 # | actualize_sources() | targets |
38 # +------+--------------+ ^
42 # +-+-+ +-------------+-------------+
44 # | +------+---------------+ +--------+-------------+
45 # | | FileTarget | | SearchedLibTarget |
46 # | +======================+ +======================+
47 # | | actualize-location() | | actualize-location() |
48 # | +----------------------+ +----------------------+
50 # +-+------------------------------+
52 # +----+----------------+ +---------+-----------+
53 # | CompileAction | | LinkAction |
54 # +=====================+ +=====================+
55 # | adjust_properties() | | adjust_properties() |
56 # +---------------------+ | actualize_sources() |
57 # +---------------------+
59 # The 'CompileAction' and 'LinkAction' classes are defined not here,
60 # but in builtin.jam modules. They are shown in the diagram to give
70 from b2
.util
import path
, utility
, set, is_iterable_typed
71 from b2
.util
.utility
import add_grist
, get_grist
, ungrist
, replace_grist
, get_value
72 from b2
.util
.sequence
import unique
73 from b2
.tools
import common
74 from b2
.exceptions
import *
76 import b2
.build
.property_set
as property_set
78 import b2
.build
.property as property
80 from b2
.manager
import get_manager
81 from b2
.util
import bjam_signature
83 __re_starts_with_at
= re
.compile ('^@(.*)')
85 class VirtualTargetRegistry
:
86 def __init__ (self
, manager
):
87 self
.manager_
= manager
89 # A cache for FileTargets
92 # A cache for targets.
95 # A map of actual names to virtual targets.
96 # Used to make sure we don't associate same
97 # actual target to two virtual targets.
100 self
.recent_targets_
= []
102 # All targets ever registered
103 self
.all_targets_
= []
107 def register (self
, target
):
108 """ Registers a new virtual target. Checks if there's already registered target, with the same
109 name, type, project and subvariant properties, and also with the same sources
110 and equal action. If such target is found it is returned and 'target' is not registered.
111 Otherwise, 'target' is registered and returned.
113 assert isinstance(target
, VirtualTarget
)
115 signature
= target
.path() + "-" + target
.name()
117 signature
= "-" + target
.name()
120 if signature
not in self
.cache_
:
121 self
.cache_
[signature
] = []
123 for t
in self
.cache_
[signature
]:
125 a2
= target
.action ()
127 # TODO: why are we checking for not result?
129 if not a1
and not a2
:
132 if a1
and a2
and a1
.action_name () == a2
.action_name () and a1
.sources () == a2
.sources ():
133 ps1
= a1
.properties ()
134 ps2
= a2
.properties ()
135 p1
= ps1
.base () + ps1
.free () +\
136 b2
.util
.set.difference(ps1
.dependency(), ps1
.incidental())
137 p2
= ps2
.base () + ps2
.free () +\
138 b2
.util
.set.difference(ps2
.dependency(), ps2
.incidental())
143 self
.cache_
[signature
].append (target
)
146 # TODO: Don't append if we found pre-existing target?
147 self
.recent_targets_
.append(result
)
148 self
.all_targets_
.append(result
)
152 def from_file (self
, file, file_location
, project
):
153 """ Creates a virtual target with appropriate name and type from 'file'.
154 If a target with that name in that project was already created, returns that already
156 TODO: more correct way would be to compute path to the file, based on name and source location
157 for the project, and use that path to determine if the target was already created.
158 TODO: passing project with all virtual targets starts to be annoying.
161 from .targets
import ProjectTarget
162 assert isinstance(file, basestring
)
163 assert isinstance(file_location
, basestring
)
164 assert isinstance(project
, ProjectTarget
)
165 # Check if we've created a target corresponding to this file.
166 path
= os
.path
.join(os
.getcwd(), file_location
, file)
167 path
= os
.path
.normpath(path
)
169 if path
in self
.files_
:
170 return self
.files_
[path
]
172 file_type
= b2
.build
.type.type (file)
174 result
= FileTarget (file, file_type
, project
,
176 self
.files_
[path
] = result
180 def recent_targets(self
):
181 """Each target returned by 'register' is added to a list of
182 'recent-target', returned by this function. So, this allows
183 us to find all targets created when building a given main
184 target, even if the target."""
186 return self
.recent_targets_
188 def clear_recent_targets(self
):
189 self
.recent_targets_
= []
191 def all_targets(self
):
192 # Returns all virtual targets ever created
193 return self
.all_targets_
195 # Returns all targets from 'targets' with types
196 # equal to 'type' or derived from it.
197 def select_by_type(self
, type, targets
):
198 return [t
for t
in targets
if b2
.build
.type.is_sybtype(t
.type(), type)]
200 def register_actual_name (self
, actual_name
, virtual_target
):
201 assert isinstance(actual_name
, basestring
)
202 assert isinstance(virtual_target
, VirtualTarget
)
203 if actual_name
in self
.actual_
:
204 cs1
= self
.actual_
[actual_name
].creating_subvariant ()
205 cs2
= virtual_target
.creating_subvariant ()
206 cmt1
= cs1
.main_target ()
207 cmt2
= cs2
.main_target ()
209 action1
= self
.actual_
[actual_name
].action ()
210 action2
= virtual_target
.action ()
212 properties_added
= []
213 properties_removed
= []
214 if action1
and action2
:
215 p1
= action1
.properties ()
217 p2
= action2
.properties ()
220 properties_removed
= set.difference (p1
, p2
)
221 if not properties_removed
:
222 properties_removed
= ["none"]
224 properties_added
= set.difference (p2
, p1
)
225 if not properties_added
:
226 properties_added
= ["none"]
228 # FIXME: Revive printing of real location.
229 get_manager().errors()(
230 "Duplicate name of actual target: '%s'\n"
231 "previous virtual target '%s'\n"
232 "created from '%s'\n"
233 "another virtual target '%s'\n"
234 "created from '%s'\n"
235 "added properties:\n%s\n"
236 "removed properties:\n%s\n"
238 self
.actual_
[actual_name
], cmt1
.project().location(),
240 cmt2
.project().location(),
241 '\n'.join('\t' + p
for p
in properties_added
),
242 '\n'.join('\t' + p
for p
in properties_removed
)))
245 self
.actual_
[actual_name
] = virtual_target
248 def add_suffix (self
, specified_name
, file_type
, prop_set
):
249 """ Appends the suffix appropriate to 'type/property_set' combination
250 to the specified name and returns the result.
252 assert isinstance(specified_name
, basestring
)
253 assert isinstance(file_type
, basestring
)
254 assert isinstance(prop_set
, property_set
.PropertySet
)
255 suffix
= b2
.build
.type.generated_target_suffix (file_type
, prop_set
)
258 return specified_name
+ '.' + suffix
261 return specified_name
264 """ Potential target. It can be converted into jam target and used in
265 building, if needed. However, it can be also dropped, which allows
266 to search for different transformation and select only one.
267 name: name of this target.
268 project: project to which this target belongs.
270 def __init__ (self
, name
, project
):
272 from .targets
import ProjectTarget
273 assert isinstance(name
, basestring
)
274 assert isinstance(project
, ProjectTarget
)
276 self
.project_
= project
277 self
.dependencies_
= []
280 # Caches if dapendencies for scanners have already been set.
284 return self
.project_
.manager()
286 def virtual_targets(self
):
287 return self
.manager().virtual_targets()
290 """ Name of this target.
295 """ Project of this target.
299 def depends (self
, d
):
300 """ Adds additional instances of 'VirtualTarget' that this
303 self
.dependencies_
= unique (self
.dependencies_
+ d
).sort ()
305 def dependencies (self
):
306 return self
.dependencies_
311 def actualize (self
, scanner
= None):
312 """ Generates all the actual targets and sets up build actions for
315 If 'scanner' is specified, creates an additional target
316 with the same location as actual target, which will depend on the
317 actual target and be associated with 'scanner'. That additional
318 target is returned. See the docs (#dependency_scanning) for rationale.
319 Target must correspond to a file if 'scanner' is specified.
321 If scanner is not specified, then actual target is returned.
324 from .scanner
import Scanner
325 assert scanner
is None or isinstance(scanner
, Scanner
)
326 actual_name
= self
.actualize_no_scanner ()
329 bjam
.call("ALWAYS", actual_name
)
335 # Add the scanner instance to the grist for name.
336 g
= '-'.join ([ungrist(get_grist(actual_name
)), str(id(scanner
))])
338 name
= replace_grist (actual_name
, '<' + g
+ '>')
340 if name
not in self
.made_
:
341 self
.made_
[name
] = True
343 self
.project_
.manager ().engine ().add_dependency (name
, actual_name
)
345 self
.actualize_location (name
)
347 self
.project_
.manager ().scanners ().install (scanner
, name
, str (self
))
351 # private: (overridables)
353 def actualize_action (self
, target
):
354 """ Sets up build actions for 'target'. Should call appropriate rules
355 and set target variables.
357 raise BaseException ("method should be defined in derived classes")
359 def actualize_location (self
, target
):
360 """ Sets up variables on 'target' which specify its location.
362 raise BaseException ("method should be defined in derived classes")
365 """ If the target is generated one, returns the path where it will be
366 generated. Otherwise, returns empty list.
368 raise BaseException ("method should be defined in derived classes")
370 def actual_name (self
):
371 """ Return that actual target name that should be used
372 (for the case where no scanner is involved)
374 raise BaseException ("method should be defined in derived classes")
377 class AbstractFileTarget (VirtualTarget
):
378 """ Target which correspond to a file. The exact mapping for file
379 is not yet specified in this class. (TODO: Actually, the class name
382 May be a source file (when no action is specified), or
383 derived file (otherwise).
385 The target's grist is concatenation of project's location,
386 properties of action (for derived files), and, optionally,
387 value identifying the main target.
389 exact: If non-empty, the name is exactly the name
390 created file should have. Otherwise, the '__init__'
391 method will add suffix obtained from 'type' by
392 calling 'type.generated-target-suffix'.
394 type: optional type of this target.
396 def __init__ (self
, name
, type, project
, action
= None, exact
=False):
397 assert isinstance(type, basestring
) or type is None
398 assert action
is None or isinstance(action
, Action
)
399 assert isinstance(exact
, (int, bool))
400 VirtualTarget
.__init
__ (self
, name
, project
)
404 self
.action_
= action
408 action
.add_targets ([self
])
410 if self
.type and not exact
:
411 self
.__adjust
_name
(name
)
414 self
.actual_name_
= None
416 self
.intermediate_
= False
417 self
.creating_subvariant_
= None
419 # True if this is a root target.
425 def set_path (self
, path
):
426 """ Sets the path. When generating target name, it will override any path
427 computation from properties.
429 assert isinstance(path
, basestring
)
430 self
.path_
= os
.path
.normpath(path
)
433 """ Returns the action.
437 def root (self
, set = None):
438 """ Sets/gets the 'root' flag. Target is root is it directly correspods to some
439 variant of a main target.
441 assert isinstance(set, (int, bool, type(None)))
446 def creating_subvariant (self
, s
= None):
447 """ Gets or sets the subvariant which created this target. Subvariant
448 is set when target is brought into existence, and is never changed
449 after that. In particual, if target is shared by subvariant, only
451 s: If specified, specified the value to set,
452 which should be instance of 'subvariant' class.
454 assert s
is None or isinstance(s
, Subvariant
)
455 if s
and not self
.creating_subvariant ():
456 if self
.creating_subvariant ():
457 raise BaseException ("Attempt to change 'dg'")
460 self
.creating_subvariant_
= s
462 return self
.creating_subvariant_
464 def actualize_action (self
, target
):
465 assert isinstance(target
, basestring
)
467 self
.action_
.actualize ()
469 # Return a human-readable representation of this target
471 # If this target has an action, that's:
473 # { <action-name>-<self.name>.<self.type> <action-sources>... }
477 # { <self.name>.<self.type> }
482 name_dot_type
= self
.name_
+ "." + self
.type_
485 action_name
= a
.action_name()
486 ss
= [ s
.str() for s
in a
.sources()]
488 return "{ %s-%s %s}" % (action_name
, name_dot_type
, str(ss
))
490 return "{ " + name_dot_type
+ " }"
494 def actual_name (self
):
495 if not self
.actual_name_
:
496 self
.actual_name_
= '<' + self
.grist() + '>' + os
.path
.normpath(self
.name_
)
498 return self
.actual_name_
501 """Helper to 'actual_name', above. Compute unique prefix used to distinguish
502 this target from other targets with the same name which create different
505 # Depending on target, there may be different approaches to generating
506 # unique prefixes. We'll generate prefixes in the form
507 # <one letter approach code> <the actual prefix>
511 # The target will be generated to a known path. Just use the path
512 # for identification, since path is as unique as it can get.
516 # File is either source, which will be searched for, or is not a file at
517 # all. Use the location of project for distinguishing.
518 project_location
= self
.project_
.get ('location')
519 path_components
= b2
.util
.path
.split(project_location
)
520 location_grist
= '!'.join (path_components
)
523 ps
= self
.action_
.properties ()
524 property_grist
= ps
.as_path ()
525 # 'property_grist' can be empty when 'ps' is an empty
528 location_grist
= location_grist
+ '/' + property_grist
530 return 'l' + location_grist
532 def __adjust_name(self
, specified_name
):
533 """Given the target name specified in constructor, returns the
534 name which should be really used, by looking at the <tag> properties.
535 The tag properties come in two flavour:
538 In the first case, value is just added to name
539 In the second case, the specified rule is called with specified name,
540 target type and properties and should return the new name.
541 If not <tag> property is specified, or the rule specified by
542 <tag> returns nothing, returns the result of calling
543 virtual-target.add-suffix"""
544 assert isinstance(specified_name
, basestring
)
546 ps
= self
.action_
.properties()
548 ps
= property_set
.empty()
550 # FIXME: I'm not sure how this is used, need to check with
551 # Rene to figure out how to implement
552 #~ We add ourselves to the properties so that any tag rule can get
553 #~ more direct information about the target than just that available
554 #~ through the properties. This is useful in implementing
555 #~ name changes based on the sources of the target. For example to
556 #~ make unique names of object files based on the source file.
558 #ps = property_set.create(ps.raw() + ["<target>%s" % "XXXX"])
559 #ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ;
561 tag
= ps
.get("<tag>")
566 get_manager().errors()(
567 """<tag>@rulename is present but is not the only <tag> feature""")
571 self
.name_
= tag(specified_name
, self
.type_
, ps
)
573 if not tag
[0] == '@':
574 self
.manager_
.errors()("""The value of the <tag> feature must be '@rule-nane'""")
576 exported_ps
= b2
.util
.value_to_jam(ps
, methods
=True)
577 self
.name_
= b2
.util
.call_jam_function(
578 tag
[1:], specified_name
, self
.type_
, exported_ps
)
580 self
.name_
= self
.name_
[0]
582 # If there's no tag or the tag rule returned nothing.
583 if not tag
or not self
.name_
:
584 self
.name_
= add_prefix_and_suffix(specified_name
, self
.type_
, ps
)
586 def actualize_no_scanner(self
):
587 name
= self
.actual_name()
589 # Do anything only on the first invocation
591 self
.made_
[name
] = True
594 # For non-derived target, we don't care if there
595 # are several virtual targets that refer to the same name.
596 # One case when this is unavoidable is when file name is
597 # main.cpp and two targets have types CPP (for compiling)
598 # and MOCCABLE_CPP (for conversion to H via Qt tools).
599 self
.virtual_targets().register_actual_name(name
, self
)
601 for i
in self
.dependencies_
:
602 self
.manager_
.engine().add_dependency(name
, i
.actualize())
604 self
.actualize_location(name
)
605 self
.actualize_action(name
)
609 @bjam_signature((["specified_name"], ["type"], ["property_set"]))
610 def add_prefix_and_suffix(specified_name
, type, property_set
):
611 """Appends the suffix appropriate to 'type/property-set' combination
612 to the specified name and returns the result."""
614 property_set
= b2
.util
.jam_to_value_maybe(property_set
)
618 suffix
= b2
.build
.type.generated_target_suffix(type, property_set
)
620 # Handle suffixes for which no leading dot is desired. Those are
621 # specified by enclosing them in <...>. Needed by python so it
622 # can create "_d.so" extensions, for example.
623 if get_grist(suffix
):
624 suffix
= ungrist(suffix
)
626 suffix
= "." + suffix
630 prefix
= b2
.build
.type.generated_target_prefix(type, property_set
)
632 if specified_name
.startswith(prefix
):
639 return prefix
+ specified_name
+ suffix
642 class FileTarget (AbstractFileTarget
):
643 """ File target with explicitly known location.
645 The file path is determined as
646 - value passed to the 'set_path' method, if any
647 - for derived files, project's build dir, joined with components
648 that describe action's properties. If the free properties
649 are not equal to the project's reference properties
650 an element with name of main target is added.
651 - for source files, project's source dir
654 - the value passed to the 'suffix' method, if any, or
655 - the suffix which correspond to the target's type.
657 def __init__ (self
, name
, type, project
, action
= None, path
=None, exact
=False):
658 assert isinstance(type, basestring
) or type is None
659 assert action
is None or isinstance(action
, Action
)
660 assert isinstance(exact
, (int, bool))
661 AbstractFileTarget
.__init
__ (self
, name
, type, project
, action
, exact
)
667 return self
.name_
+ "." + self
.type_
671 def clone_with_different_type(self
, new_type
):
672 assert isinstance(new_type
, basestring
)
673 return FileTarget(self
.name_
, new_type
, self
.project_
,
674 self
.action_
, self
.path_
, exact
=True)
676 def actualize_location (self
, target
):
677 assert isinstance(target
, basestring
)
678 engine
= self
.project_
.manager_
.engine ()
681 # This is a derived file.
683 engine
.set_target_variable (target
, 'LOCATE', path
)
685 # Make sure the path exists.
686 engine
.add_dependency (target
, path
)
687 common
.mkdir(engine
, path
)
689 # It's possible that the target name includes a directory
690 # too, for example when installing headers. Create that
692 d
= os
.path
.dirname(get_value(target
))
694 d
= os
.path
.join(path
, d
)
695 engine
.add_dependency(target
, d
)
696 common
.mkdir(engine
, d
)
698 # For real file target, we create a fake target that
699 # depends on the real target. This allows to run
703 # without trying to guess the name of the real target.
704 # Note the that target has no directory name, and a special
707 # First, that means that "bjam hello.o" will build all
708 # known hello.o targets.
709 # Second, the <e> grist makes sure this target won't be confused
710 # with other targets, for example, if we have subdir 'test'
711 # with target 'test' in it that includes 'test.o' file,
712 # then the target for directory will be just 'test' the target
713 # for test.o will be <ptest/bin/gcc/debug>test.o and the target
714 # we create below will be <e>test.o
715 engine
.add_dependency("<e>%s" % get_value(target
), target
)
717 # Allow bjam <path-to-file>/<file> to work. This won't catch all
718 # possible ways to refer to the path (relative/absolute, extra ".",
719 # various "..", but should help in obvious cases.
720 engine
.add_dependency("<e>%s" % (os
.path
.join(path
, get_value(target
))), target
)
723 # This is a source file.
724 engine
.set_target_variable (target
, 'SEARCH', self
.project_
.get ('source-location'))
728 """ Returns the directory for this target.
732 p
= self
.action_
.properties ()
733 (target_path
, relative_to_build_dir
) = p
.target_path ()
735 if relative_to_build_dir
:
736 # Indicates that the path is relative to
738 target_path
= os
.path
.join (self
.project_
.build_dir (), target_path
)
740 # Store the computed path, so that it's not recomputed
742 self
.path_
= target_path
744 return os
.path
.normpath(self
.path_
)
747 class NotFileTarget(AbstractFileTarget
):
749 def __init__(self
, name
, project
, action
):
750 assert isinstance(action
, Action
)
751 AbstractFileTarget
.__init
__(self
, name
, None, project
, action
)
754 """Returns nothing, to indicate that target path is not known."""
757 def actualize_location(self
, target
):
758 assert isinstance(target
, basestring
)
759 bjam
.call("NOTFILE", target
)
760 bjam
.call("ALWAYS", target
)
761 bjam
.call("NOUPDATE", target
)
765 """ Class which represents an action.
766 Both 'targets' and 'sources' should list instances of 'VirtualTarget'.
767 Action name should name a rule with this prototype
768 rule action_name ( targets + : sources * : properties * )
769 Targets and sources are passed as actual jam targets. The rule may
770 not establish dependency relationship, but should do everything else.
772 def __init__ (self
, manager
, sources
, action_name
, prop_set
):
773 assert is_iterable_typed(sources
, VirtualTarget
)
774 assert isinstance(action_name
, basestring
) or action_name
is None
775 assert(isinstance(prop_set
, property_set
.PropertySet
))
776 self
.sources_
= sources
777 self
.action_name_
= action_name
779 prop_set
= property_set
.empty()
780 self
.properties_
= prop_set
781 if not all(isinstance(v
, VirtualTarget
) for v
in prop_set
.get('implicit-dependency')):
785 self
.manager_
= manager
786 self
.engine_
= self
.manager_
.engine ()
789 # Indicates whether this has been actualized or not.
790 self
.actualized_
= False
792 self
.dependency_only_sources_
= []
793 self
.actual_sources_
= []
796 def add_targets (self
, targets
):
797 assert is_iterable_typed(targets
, VirtualTarget
)
798 self
.targets_
+= targets
801 def replace_targets(self
, old_targets
, new_targets
):
802 assert is_iterable_typed(old_targets
, VirtualTarget
)
803 assert is_iterable_typed(new_targets
, VirtualTarget
)
804 self
.targets_
= [t
for t
in self
.targets_
if not t
in old_targets
] + new_targets
812 def action_name (self
):
813 return self
.action_name_
815 def properties (self
):
816 return self
.properties_
818 def actualize (self
):
819 """ Generates actual build instructions.
824 self
.actualized_
= True
826 ps
= self
.properties ()
827 properties
= self
.adjust_properties (ps
)
832 for i
in self
.targets ():
833 actual_targets
.append (i
.actualize ())
835 self
.actualize_sources (self
.sources (), properties
)
837 self
.engine_
.add_dependency (actual_targets
, self
.actual_sources_
+ self
.dependency_only_sources_
)
839 # FIXME: check the comment below. Was self.action_name_ [1]
840 # Action name can include additional rule arguments, which should not
841 # be passed to 'set-target-variables'.
842 # FIXME: breaking circular dependency
844 toolset
.set_target_variables (self
.manager_
, self
.action_name_
, actual_targets
, properties
)
846 engine
= self
.manager_
.engine ()
848 # FIXME: this is supposed to help --out-xml option, but we don't
849 # implement that now, and anyway, we should handle it in Python,
850 # not but putting variables on bjam-level targets.
851 bjam
.call("set-target-variable", actual_targets
, ".action", repr(self
))
853 self
.manager_
.engine ().set_update_action (self
.action_name_
, actual_targets
, self
.actual_sources_
,
856 # Since we set up creating action here, we also set up
857 # action for cleaning up
858 self
.manager_
.engine ().set_update_action ('common.Clean', 'clean-all',
861 return actual_targets
863 def actualize_source_type (self
, sources
, prop_set
):
864 """ Helper for 'actualize_sources'.
865 For each passed source, actualizes it with the appropriate scanner.
866 Returns the actualized virtual targets.
868 assert is_iterable_typed(sources
, VirtualTarget
)
869 assert isinstance(prop_set
, property_set
.PropertySet
)
874 # FIXME: what's this?
875 # if isinstance (i, str):
876 # i = self.manager_.get_object (i)
879 scanner
= b2
.build
.type.get_scanner (i
.type (), prop_set
)
881 r
= i
.actualize (scanner
)
886 def actualize_sources (self
, sources
, prop_set
):
887 """ Creates actual jam targets for sources. Initializes two member
889 'self.actual_sources_' -- sources which are passed to updating action
890 'self.dependency_only_sources_' -- sources which are made dependencies, but
891 are not used otherwise.
893 New values will be *appended* to the variables. They may be non-empty,
896 assert is_iterable_typed(sources
, VirtualTarget
)
897 assert isinstance(prop_set
, property_set
.PropertySet
)
898 dependencies
= self
.properties_
.get ('<dependency>')
900 self
.dependency_only_sources_
+= self
.actualize_source_type (dependencies
, prop_set
)
901 self
.actual_sources_
+= self
.actualize_source_type (sources
, prop_set
)
903 # This is used to help bjam find dependencies in generated headers
904 # in other main targets.
907 # make a.h : ....... ;
908 # exe hello : hello.cpp : <implicit-dependency>a.h ;
910 # However, for bjam to find the dependency the generated target must
911 # be actualized (i.e. have the jam target). In the above case,
912 # if we're building just hello ("bjam hello"), 'a.h' won't be
913 # actualized unless we do it here.
914 implicit
= self
.properties_
.get("<implicit-dependency>")
919 def adjust_properties (self
, prop_set
):
920 """ Determines real properties when trying building with 'properties'.
921 This is last chance to fix properties, for example to adjust includes
922 to get generated headers correctly. Default implementation returns
925 assert isinstance(prop_set
, property_set
.PropertySet
)
929 class NullAction (Action
):
930 """ Action class which does nothing --- it produces the targets with
931 specific properties out of nowhere. It's needed to distinguish virtual
932 targets with different properties that are known to exist, and have no
933 actions which create them.
935 def __init__ (self
, manager
, prop_set
):
936 assert isinstance(prop_set
, property_set
.PropertySet
)
937 Action
.__init
__ (self
, manager
, [], None, prop_set
)
939 def actualize (self
):
940 if not self
.actualized_
:
941 self
.actualized_
= True
943 for i
in self
.targets ():
946 class NonScanningAction(Action
):
947 """Class which acts exactly like 'action', except that the sources
948 are not scanned for dependencies."""
950 def __init__(self
, sources
, action_name
, property_set
):
951 #FIXME: should the manager parameter of Action.__init__
952 #be removed? -- Steven Watanabe
953 Action
.__init
__(self
, b2
.manager
.get_manager(), sources
, action_name
, property_set
)
955 def actualize_source_type(self
, sources
, ps
=None):
956 assert is_iterable_typed(sources
, VirtualTarget
)
957 assert isinstance(ps
, property_set
.PropertySet
) or ps
is None
960 result
.append(s
.actualize())
963 def traverse (target
, include_roots
= False, include_sources
= False):
964 """ Traverses the dependency graph of 'target' and return all targets that will
965 be created before this one is created. If root of some dependency graph is
966 found during traversal, it's either included or not, dependencing of the
967 value of 'include_roots'. In either case, sources of root are not traversed.
969 assert isinstance(target
, VirtualTarget
)
970 assert isinstance(include_roots
, (int, bool))
971 assert isinstance(include_sources
, (int, bool))
975 action
= target
.action ()
977 # This includes 'target' as well
978 result
+= action
.targets ()
980 for t
in action
.sources ():
983 # TODO: see comment in Manager.register_object ()
984 #if not isinstance (t, VirtualTarget):
985 # t = target.project_.manager_.get_object (t)
988 result
+= traverse (t
, include_roots
, include_sources
)
993 elif include_sources
:
994 result
.append (target
)
998 def clone_action (action
, new_project
, new_action_name
, new_properties
):
999 """Takes an 'action' instances and creates new instance of it
1000 and all produced target. The rule-name and properties are set
1001 to 'new-rule-name' and 'new-properties', if those are specified.
1002 Returns the cloned action."""
1004 from .targets
import ProjectTarget
1005 assert isinstance(action
, Action
)
1006 assert isinstance(new_project
, ProjectTarget
)
1007 assert isinstance(new_action_name
, basestring
)
1008 assert isinstance(new_properties
, property_set
.PropertySet
)
1009 if not new_action_name
:
1010 new_action_name
= action
.action_name()
1012 if not new_properties
:
1013 new_properties
= action
.properties()
1015 cloned_action
= action
.__class
__(action
.manager_
, action
.sources(), new_action_name
,
1019 for target
in action
.targets():
1022 # Don't modify the name of the produced targets. Strip the directory f
1023 cloned_target
= FileTarget(n
, target
.type(), new_project
,
1024 cloned_action
, exact
=True)
1026 d
= target
.dependencies()
1028 cloned_target
.depends(d
)
1029 cloned_target
.root(target
.root())
1030 cloned_target
.creating_subvariant(target
.creating_subvariant())
1032 cloned_targets
.append(cloned_target
)
1034 return cloned_action
1038 def __init__ (self
, main_target
, prop_set
, sources
, build_properties
, sources_usage_requirements
, created_targets
):
1040 main_target: The instance of MainTarget class
1041 prop_set: Properties requested for this target
1043 build_properties: Actually used properties
1044 sources_usage_requirements: Properties propagated from sources
1045 created_targets: Top-level created targets
1048 from .targets
import AbstractTarget
1049 assert isinstance(main_target
, AbstractTarget
)
1050 assert isinstance(prop_set
, property_set
.PropertySet
)
1051 assert is_iterable_typed(sources
, VirtualTarget
)
1052 assert isinstance(build_properties
, property_set
.PropertySet
)
1053 assert isinstance(sources_usage_requirements
, property_set
.PropertySet
)
1054 assert is_iterable_typed(created_targets
, VirtualTarget
)
1055 self
.main_target_
= main_target
1056 self
.properties_
= prop_set
1057 self
.sources_
= sources
1058 self
.build_properties_
= build_properties
1059 self
.sources_usage_requirements_
= sources_usage_requirements
1060 self
.created_targets_
= created_targets
1062 self
.usage_requirements_
= None
1064 # Pre-compose the list of other dependency graphs, on which this one
1066 deps
= build_properties
.get('<implicit-dependency>')
1070 self
.other_dg_
.append(d
.creating_subvariant ())
1072 self
.other_dg_
= unique (self
.other_dg_
)
1074 self
.implicit_includes_cache_
= {}
1075 self
.target_directories_
= None
1077 def main_target (self
):
1078 return self
.main_target_
1080 def created_targets (self
):
1081 return self
.created_targets_
1083 def requested_properties (self
):
1084 return self
.properties_
1086 def build_properties (self
):
1087 return self
.build_properties_
1089 def sources_usage_requirements (self
):
1090 return self
.sources_usage_requirements_
1092 def set_usage_requirements (self
, usage_requirements
):
1093 assert isinstance(usage_requirements
, property_set
.PropertySet
)
1094 self
.usage_requirements_
= usage_requirements
1096 def usage_requirements (self
):
1097 return self
.usage_requirements_
1099 def all_referenced_targets(self
, result
):
1100 """Returns all targets referenced by this subvariant,
1101 either directly or indirectly, and either as sources,
1102 or as dependency properties. Targets referred with
1103 dependency property are returned a properties, not targets."""
1105 from .property import Property
1106 assert is_iterable_typed(result
, (VirtualTarget
, Property
))
1107 # Find directly referenced targets.
1108 deps
= self
.build_properties().dependency()
1109 all_targets
= self
.sources_
+ deps
1111 # Find other subvariants.
1113 for e
in all_targets
:
1116 if isinstance(e
, property.Property
):
1121 # FIXME: how can this be?
1122 cs
= t
.creating_subvariant()
1128 s
.all_referenced_targets(result
)
1131 def implicit_includes (self
, feature
, target_type
):
1132 """ Returns the properties which specify implicit include paths to
1133 generated headers. This traverses all targets in this subvariant,
1134 and subvariants referred by <implcit-dependecy>properties.
1135 For all targets which are of type 'target-type' (or for all targets,
1136 if 'target_type' is not specified), the result will contain
1137 <$(feature)>path-to-that-target.
1139 assert isinstance(feature
, basestring
)
1140 assert isinstance(target_type
, basestring
)
1144 key
= feature
+ "-" + target_type
1147 result
= self
.implicit_includes_cache_
.get(key
)
1149 target_paths
= self
.all_target_directories(target_type
)
1150 target_paths
= unique(target_paths
)
1151 result
= ["<%s>%s" % (feature
, p
) for p
in target_paths
]
1152 self
.implicit_includes_cache_
[key
] = result
1156 def all_target_directories(self
, target_type
= None):
1157 assert isinstance(target_type
, (basestring
, type(None)))
1158 # TODO: does not appear to use target_type in deciding
1159 # if we've computed this already.
1160 if not self
.target_directories_
:
1161 self
.target_directories_
= self
.compute_target_directories(target_type
)
1162 return self
.target_directories_
1164 def compute_target_directories(self
, target_type
=None):
1165 assert isinstance(target_type
, (basestring
, type(None)))
1167 for t
in self
.created_targets():
1168 if not target_type
or b2
.build
.type.is_derived(t
.type(), target_type
):
1169 result
.append(t
.path())
1171 for d
in self
.other_dg_
:
1172 result
.extend(d
.all_target_directories(target_type
))
1174 result
= unique(result
)