1# Status: ported.
2# Base revision: 64488
3
4# Copyright Vladimir Prus 2002-2007.
5# Copyright Rene Rivera 2006.
6#
7# Distributed under the Boost Software License, Version 1.0.
8#    (See accompanying file LICENSE_1_0.txt or copy at
9#          http://www.boost.org/LICENSE_1_0.txt)
10
11#   Supports 'abstract' targets, which are targets explicitly defined in Jamfile.
12#
13#   Abstract targets are represented by classes derived from 'AbstractTarget' class.
14#   The first abstract target is 'project_target', which is created for each
15#   Jamfile, and can be obtained by the 'target' rule in the Jamfile's module.
16#   (see project.jam).
17#
18#   Project targets keep a list of 'MainTarget' instances.
19#   A main target is what the user explicitly defines in a Jamfile. It is
20#   possible to have several definitions for a main target, for example to have
21#   different lists of sources for different platforms. So, main targets
22#   keep a list of alternatives.
23#
24#   Each alternative is an instance of 'AbstractTarget'. When a main target
25#   subvariant is defined by some rule, that rule will decide what class to
26#   use, create an instance of that class and add it to the list of alternatives
27#   for the main target.
28#
29#   Rules supplied by the build system will use only targets derived
30#   from 'BasicTarget' class, which will provide some default behaviour.
31#   There will be two classes derived from it, 'make-target', created by the
32#   'make' rule, and 'TypedTarget', created by rules such as 'exe' and 'dll'.
33
34#
35#                         +------------------------+
36#                         |AbstractTarget          |
37#                         +========================+
38#                         |name                    |
39#                         |project                 |
40#                         |                        |
41#                         |generate(properties) = 0|
42#                         +-----------+------------+
43#                                     |
44#                                     ^
45#                                    / \
46#                                   +-+-+
47#                                     |
48#                                     |
49#            +------------------------+------+------------------------------+
50#            |                               |                              |
51#            |                               |                              |
52# +----------+-----------+            +------+------+                +------+-------+
53# | project_target       |            | MainTarget  |                | BasicTarget  |
54# +======================+ 1        * +=============+  alternatives  +==============+
55# | generate(properties) |o-----------+ generate    |<>------------->| generate     |
56# | main-target          |            +-------------+                | construct = 0|
57# +----------------------+                                           +--------------+
58#                                                                           |
59#                                                                           ^
60#                                                                          / \
61#                                                                         +-+-+
62#                                                                           |
63#                                                                           |
64#                 ...--+----------------+------------------+----------------+---+
65#                      |                |                  |                    |
66#                      |                |                  |                    |
67#               ... ---+-----+   +------+-------+   +------+------+    +--------+-----+
68#                            |   | TypedTarget  |   | make-target |    | stage-target |
69#                            .   +==============+   +=============+    +==============+
70#                            .   | construct    |   | construct   |    | construct    |
71#                                +--------------+   +-------------+    +--------------+
72
73import re
74import os.path
75import sys
76
77from b2.manager import get_manager
78
79from b2.util.utility import *
80import property, project, virtual_target, property_set, feature, generators, toolset
81from virtual_target import Subvariant
82from b2.exceptions import *
83from b2.util.sequence import unique
84from b2.util import path, bjam_signature
85from b2.build.errors import user_error_checkpoint
86
87import b2.build.build_request as build_request
88
89import b2.util.set
90_re_separate_target_from_properties = re.compile (r'^([^<]*)(/(<.*))?$')
91
92class TargetRegistry:
93
94    def __init__ (self):
95        # All targets that are currently being built.
96        # Only the key is id (target), the value is the actual object.
97        self.targets_being_built_ = {}
98
99        # Current indent for debugging messages
100        self.indent_ = ""
101
102        self.debug_building_ = "--debug-building" in bjam.variable("ARGV")
103
104        self.targets_ = []
105
106    def main_target_alternative (self, target):
107        """ Registers the specified target as a main target alternatives.
108            Returns 'target'.
109        """
110        target.project ().add_alternative (target)
111        return target
112
113    def main_target_sources (self, sources, main_target_name, no_renaming=0):
114        """Return the list of sources to use, if main target rule is invoked
115        with 'sources'. If there are any objects in 'sources', they are treated
116        as main target instances, and the name of such targets are adjusted to
117        be '<name_of_this_target>__<name_of_source_target>'. Such renaming
118        is disabled is non-empty value is passed for 'no-renaming' parameter."""
119        result = []
120
121        for t in sources:
122
123            t = b2.util.jam_to_value_maybe(t)
124
125            if isinstance (t, AbstractTarget):
126                name = t.name ()
127
128                if not no_renaming:
129                    name = main_target_name + '__' + name
130                    t.rename (name)
131
132                # Inline targets are not built by default.
133                p = t.project()
134                p.mark_targets_as_explicit([name])
135                result.append(name)
136
137            else:
138                result.append (t)
139
140        return result
141
142
143    def main_target_requirements(self, specification, project):
144        """Returns the requirement to use when declaring a main target,
145         which are obtained by
146         - translating all specified property paths, and
147         - refining project requirements with the one specified for the target
148
149         'specification' are the properties xplicitly specified for a
150          main target
151         'project' is the project where the main taret is to be declared."""
152
153        specification.extend(toolset.requirements())
154
155        requirements = property_set.refine_from_user_input(
156            project.get("requirements"), specification,
157            project.project_module(), project.get("location"))
158
159        return requirements
160
161    def main_target_usage_requirements (self, specification, project):
162        """ Returns the use requirement to use when declaraing a main target,
163            which are obtained by
164            - translating all specified property paths, and
165            - adding project's usage requirements
166            specification:  Use-properties explicitly specified for a main target
167            project:        Project where the main target is to be declared
168        """
169        project_usage_requirements = project.get ('usage-requirements')
170
171        # We don't use 'refine-from-user-input' because I'm not sure if:
172        # - removing of parent's usage requirements makes sense
173        # - refining of usage requirements is not needed, since usage requirements
174        #   are always free.
175        usage_requirements = property_set.create_from_user_input(
176            specification, project.project_module(), project.get("location"))
177
178        return project_usage_requirements.add (usage_requirements)
179
180    def main_target_default_build (self, specification, project):
181        """ Return the default build value to use when declaring a main target,
182            which is obtained by using specified value if not empty and parent's
183            default build attribute otherwise.
184            specification:  Default build explicitly specified for a main target
185            project:        Project where the main target is to be declared
186        """
187        if specification:
188            return property_set.create_with_validation(specification)
189        else:
190            return project.get ('default-build')
191
192    def start_building (self, main_target_instance):
193        """ Helper rules to detect cycles in main target references.
194        """
195        if self.targets_being_built_.has_key(id(main_target_instance)):
196            names = []
197            for t in self.targets_being_built_.values() + [main_target_instance]:
198                names.append (t.full_name())
199
200            get_manager().errors()("Recursion in main target references\n")
201
202        self.targets_being_built_[id(main_target_instance)] = main_target_instance
203
204    def end_building (self, main_target_instance):
205        assert (self.targets_being_built_.has_key (id (main_target_instance)))
206        del self.targets_being_built_ [id (main_target_instance)]
207
208    def create_typed_target (self, type, project, name, sources, requirements, default_build, usage_requirements):
209        """ Creates a TypedTarget with the specified properties.
210            The 'name', 'sources', 'requirements', 'default_build' and
211            'usage_requirements' are assumed to be in the form specified
212            by the user in Jamfile corresponding to 'project'.
213        """
214        return self.main_target_alternative (TypedTarget (name, project, type,
215            self.main_target_sources (sources, name),
216            self.main_target_requirements (requirements, project),
217            self.main_target_default_build (default_build, project),
218            self.main_target_usage_requirements (usage_requirements, project)))
219
220    def increase_indent(self):
221        self.indent_ += "    "
222
223    def decrease_indent(self):
224        self.indent_ = self.indent_[0:-4]
225
226    def logging(self):
227        return self.debug_building_
228
229    def log(self, message):
230        if self.debug_building_:
231            print self.indent_ + message
232
233    def push_target(self, target):
234        self.targets_.append(target)
235
236    def pop_target(self):
237        self.targets_ = self.targets_[:-1]
238
239    def current(self):
240        return self.targets_[0]
241
242
243class GenerateResult:
244
245    def __init__ (self, ur=None, targets=None):
246        if not targets:
247            targets = []
248
249        self.__usage_requirements = ur
250        self.__targets = targets
251        assert all(isinstance(t, virtual_target.VirtualTarget) for t in targets)
252
253        if not self.__usage_requirements:
254            self.__usage_requirements = property_set.empty ()
255
256    def usage_requirements (self):
257        return self.__usage_requirements
258
259    def targets (self):
260        return self.__targets
261
262    def extend (self, other):
263        assert (isinstance (other, GenerateResult))
264
265        self.__usage_requirements = self.__usage_requirements.add (other.usage_requirements ())
266        self.__targets.extend (other.targets ())
267
268class AbstractTarget:
269    """ Base class for all abstract targets.
270    """
271    def __init__ (self, name, project, manager = None):
272        """ manager:     the Manager object
273            name:        name of the target
274            project:     the project target to which this one belongs
275            manager:the manager object. If none, uses project.manager ()
276        """
277        assert (isinstance (project, ProjectTarget))
278        # Note: it might seem that we don't need either name or project at all.
279        # However, there are places where we really need it. One example is error
280        # messages which should name problematic targets. Another is setting correct
281        # paths for sources and generated files.
282
283        # Why allow manager to be specified? Because otherwise project target could not derive
284        # from this class.
285        if manager:
286            self.manager_ = manager
287        else:
288            self.manager_ = project.manager ()
289
290        self.name_ = name
291        self.project_ = project
292
293    def manager (self):
294        return self.manager_
295
296    def name (self):
297        """ Returns the name of this target.
298        """
299        return self.name_
300
301    def project (self):
302        """ Returns the project for this target.
303        """
304        return self.project_
305
306    def location (self):
307        """ Return the location where the target was declared.
308        """
309        return self.location_
310
311    def full_name (self):
312        """ Returns a user-readable name for this target.
313        """
314        location = self.project ().get ('location')
315        return location + '/' + self.name_
316
317    def generate (self, property_set):
318        """ Takes a property set.  Generates virtual targets for this abstract
319            target, using the specified properties, unless a different value of some
320            feature is required by the target.
321            On success, returns a GenerateResult instance with:
322                - a property_set with the usage requirements to be
323                  applied to dependents
324                - a list of produced virtual targets, which may be
325                   empty.
326            If 'property_set' is empty, performs default build of this
327            target, in a way specific to derived class.
328        """
329        raise BaseException ("method should be defined in derived classes")
330
331    def rename (self, new_name):
332        self.name_ = new_name
333
334class ProjectTarget (AbstractTarget):
335    """ Project target class (derived from 'AbstractTarget')
336
337        This class these responsibilities:
338        - maintaining a list of main target in this project and
339          building it
340
341        Main targets are constructed in two stages:
342        - When Jamfile is read, a number of calls to 'add_alternative' is made.
343          At that time, alternatives can also be renamed to account for inline
344          targets.
345        - The first time 'main-target' or 'has-main-target' rule is called,
346          all alternatives are enumerated an main targets are created.
347    """
348    def __init__ (self, manager, name, project_module, parent_project, requirements, default_build):
349        AbstractTarget.__init__ (self, name, self, manager)
350
351        self.project_module_ = project_module
352        self.location_ = manager.projects().attribute (project_module, 'location')
353        self.requirements_ = requirements
354        self.default_build_ = default_build
355
356        self.build_dir_ = None
357
358        # A cache of IDs
359        self.ids_cache_ = {}
360
361        # True is main targets have already been built.
362        self.built_main_targets_ = False
363
364        # A list of the registered alternatives for this project.
365        self.alternatives_ = []
366
367        # A map from main target name to the target corresponding
368        # to it.
369        self.main_target_ = {}
370
371        # Targets marked as explicit.
372        self.explicit_targets_ = set()
373
374        # Targets marked as always
375        self.always_targets_ = set()
376
377        # The constants defined for this project.
378        self.constants_ = {}
379
380        # Whether targets for all main target are already created.
381        self.built_main_targets_ = 0
382
383        if parent_project:
384            self.inherit (parent_project)
385
386
387    # TODO: This is needed only by the 'make' rule. Need to find the
388    # way to make 'make' work without this method.
389    def project_module (self):
390        return self.project_module_
391
392    def get (self, attribute):
393        return self.manager().projects().attribute(
394            self.project_module_, attribute)
395
396    def build_dir (self):
397        if not self.build_dir_:
398            self.build_dir_ = self.get ('build-dir')
399            if not self.build_dir_:
400                self.build_dir_ = os.path.join(self.project_.get ('location'), 'bin')
401
402        return self.build_dir_
403
404    def generate (self, ps):
405        """ Generates all possible targets contained in this project.
406        """
407        self.manager_.targets().log(
408            "Building project '%s' with '%s'" % (self.name (), str(ps)))
409        self.manager_.targets().increase_indent ()
410
411        result = GenerateResult ()
412
413        for t in self.targets_to_build ():
414            g = t.generate (ps)
415            result.extend (g)
416
417        self.manager_.targets().decrease_indent ()
418        return result
419
420    def targets_to_build (self):
421        """ Computes and returns a list of AbstractTarget instances which
422            must be built when this project is built.
423        """
424        result = []
425
426        if not self.built_main_targets_:
427            self.build_main_targets ()
428
429        # Collect all main targets here, except for "explicit" ones.
430        for n, t  in self.main_target_.iteritems ():
431            if not t.name () in self.explicit_targets_:
432                result.append (t)
433
434        # Collect all projects referenced via "projects-to-build" attribute.
435        self_location = self.get ('location')
436        for pn in self.get ('projects-to-build'):
437            result.append (self.find(pn + "/"))
438
439        return result
440
441    def mark_targets_as_explicit (self, target_names):
442        """Add 'target' to the list of targets in this project
443        that should be build only by explicit request."""
444
445        # Record the name of the target, not instance, since this
446        # rule is called before main target instaces are created.
447        self.explicit_targets_.update(target_names)
448
449    def mark_targets_as_always(self, target_names):
450        self.always_targets_.update(target_names)
451
452    def add_alternative (self, target_instance):
453        """ Add new target alternative.
454        """
455        if self.built_main_targets_:
456            raise IllegalOperation ("add-alternative called when main targets are already created for project '%s'" % self.full_name ())
457
458        self.alternatives_.append (target_instance)
459
460    def main_target (self, name):
461        if not self.built_main_targets_:
462            self.build_main_targets()
463
464        return self.main_target_[name]
465
466    def has_main_target (self, name):
467        """Tells if a main target with the specified name exists."""
468        if not self.built_main_targets_:
469            self.build_main_targets()
470
471        return self.main_target_.has_key(name)
472
473    def create_main_target (self, name):
474        """ Returns a 'MainTarget' class instance corresponding to the 'name'.
475        """
476        if not self.built_main_targets_:
477            self.build_main_targets ()
478
479        return self.main_targets_.get (name, None)
480
481
482    def find_really(self, id):
483        """ Find and return the target with the specified id, treated
484            relative to self.
485        """
486        result = None
487        current_location = self.get ('location')
488
489        __re_split_project_target = re.compile (r'(.*)//(.*)')
490        split = __re_split_project_target.match (id)
491
492        project_part = None
493        target_part = None
494
495        if split:
496            project_part = split.group (1)
497            target_part = split.group (2)
498
499        project_registry = self.project_.manager ().projects ()
500
501        extra_error_message = ''
502        if project_part:
503            # There's explicit project part in id. Looks up the
504            # project and pass the request to it.
505            pm = project_registry.find (project_part, current_location)
506
507            if pm:
508                project_target = project_registry.target (pm)
509                result = project_target.find (target_part, no_error=1)
510
511            else:
512                extra_error_message = "error: could not find project '$(project_part)'"
513
514        else:
515            # Interpret target-name as name of main target
516            # Need to do this before checking for file. Consider this:
517            #
518            #  exe test : test.cpp ;
519            #  install s : test : <location>. ;
520            #
521            # After first build we'll have target 'test' in Jamfile and file
522            # 'test' on the disk. We need target to override the file.
523
524            result = None
525            if self.has_main_target(id):
526                result = self.main_target(id)
527
528            if not result:
529                result = FileReference (self.manager_, id, self.project_)
530                if not result.exists ():
531                    # File actually does not exist.
532                    # Reset 'target' so that an error is issued.
533                    result = None
534
535
536            if not result:
537                # Interpret id as project-id
538                project_module = project_registry.find (id, current_location)
539                if project_module:
540                    result = project_registry.target (project_module)
541
542        return result
543
544    def find (self, id, no_error = False):
545        v = self.ids_cache_.get (id, None)
546
547        if not v:
548            v = self.find_really (id)
549            self.ids_cache_ [id] = v
550
551        if v or no_error:
552            return v
553
554        raise BaseException ("Unable to find file or target named '%s'\nreferred from project at '%s'" % (id, self.get ('location')))
555
556
557    def build_main_targets (self):
558        self.built_main_targets_ = True
559
560        for a in self.alternatives_:
561            name = a.name ()
562            if not self.main_target_.has_key (name):
563                t = MainTarget (name, self.project_)
564                self.main_target_ [name] = t
565
566            if name in self.always_targets_:
567                a.always()
568
569            self.main_target_ [name].add_alternative (a)
570
571    def add_constant(self, name, value, path=0):
572        """Adds a new constant for this project.
573
574        The constant will be available for use in Jamfile
575        module for this project. If 'path' is true,
576        the constant will be interpreted relatively
577        to the location of project.
578        """
579
580        if path:
581            l = self.location_
582            if not l:
583                # Project corresponding to config files do not have
584                # 'location' attribute, but do have source location.
585                # It might be more reasonable to make every project have
586                # a location and use some other approach to prevent buildable
587                # targets in config files, but that's for later.
588                l = get('source-location')
589
590            value = os.path.join(l, value)
591            # Now make the value absolute path. Constants should be in
592            # platform-native form.
593            value = os.path.normpath(os.path.join(os.getcwd(), value))
594
595        self.constants_[name] = value
596        bjam.call("set-variable", self.project_module(), name, value)
597
598    def inherit(self, parent_project):
599        for c in parent_project.constants_:
600            # No need to pass the type. Path constants were converted to
601            # absolute paths already by parent.
602            self.add_constant(c, parent_project.constants_[c])
603
604        # Import rules from parent
605        this_module = self.project_module()
606        parent_module = parent_project.project_module()
607
608        rules = bjam.call("RULENAMES", parent_module)
609        if not rules:
610            rules = []
611        user_rules = [x for x in rules
612                      if x not in self.manager().projects().project_rules().all_names()]
613        if user_rules:
614            bjam.call("import-rules-from-parent", parent_module, this_module, user_rules)
615
616class MainTarget (AbstractTarget):
617    """ A named top-level target in Jamfile.
618    """
619    def __init__ (self, name, project):
620        AbstractTarget.__init__ (self, name, project)
621        self.alternatives_ = []
622        self.default_build_ = property_set.empty ()
623
624    def add_alternative (self, target):
625        """ Add a new alternative for this target.
626        """
627        d = target.default_build ()
628
629        if self.alternatives_ and self.default_build_ != d:
630            get_manager().errors()("default build must be identical in all alternatives\n"
631              "main target is '%s'\n"
632              "with '%s'\n"
633              "differing from previous default build: '%s'" % (self.full_name (), d.raw (), self.default_build_.raw ()))
634
635        else:
636            self.default_build_ = d
637
638        self.alternatives_.append (target)
639
640    def __select_alternatives (self, property_set, debug):
641        """ Returns the best viable alternative for this property_set
642            See the documentation for selection rules.
643            # TODO: shouldn't this be 'alternative' (singular)?
644        """
645        # When selecting alternatives we have to consider defaults,
646        # for example:
647        #    lib l : l.cpp : <variant>debug ;
648        #    lib l : l_opt.cpp : <variant>release ;
649        # won't work unless we add default value <variant>debug.
650        property_set = property_set.add_defaults ()
651
652        # The algorithm: we keep the current best viable alternative.
653        # When we've got new best viable alternative, we compare it
654        # with the current one.
655        best = None
656        best_properties = None
657
658        if len (self.alternatives_) == 0:
659            return None
660
661        if len (self.alternatives_) == 1:
662            return self.alternatives_ [0]
663
664        if debug:
665            print "Property set for selection:", property_set
666
667        for v in self.alternatives_:
668            properties = v.match (property_set, debug)
669
670            if properties is not None:
671                if not best:
672                    best = v
673                    best_properties = properties
674
675                else:
676                    if b2.util.set.equal (properties, best_properties):
677                        return None
678
679                    elif b2.util.set.contains (properties, best_properties):
680                        # Do nothing, this alternative is worse
681                        pass
682
683                    elif b2.util.set.contains (best_properties, properties):
684                        best = v
685                        best_properties = properties
686
687                    else:
688                        return None
689
690        return best
691
692    def apply_default_build (self, property_set):
693        return apply_default_build(property_set, self.default_build_)
694
695    def generate (self, ps):
696        """ Select an alternative for this main target, by finding all alternatives
697            which requirements are satisfied by 'properties' and picking the one with
698            longest requirements set.
699            Returns the result of calling 'generate' on that alternative.
700        """
701        self.manager_.targets ().start_building (self)
702
703        # We want composite properties in build request act as if
704        # all the properties it expands too are explicitly specified.
705        ps = ps.expand ()
706
707        all_property_sets = self.apply_default_build (ps)
708
709        result = GenerateResult ()
710
711        for p in all_property_sets:
712            result.extend (self.__generate_really (p))
713
714        self.manager_.targets ().end_building (self)
715
716        return result
717
718    def __generate_really (self, prop_set):
719        """ Generates the main target with the given property set
720            and returns a list which first element is property_set object
721            containing usage_requirements of generated target and with
722            generated virtual target in other elements. It's possible
723            that no targets are generated.
724        """
725        best_alternative = self.__select_alternatives (prop_set, debug=0)
726
727        if not best_alternative:
728            # FIXME: revive.
729            # self.__select_alternatives(prop_set, debug=1)
730            self.manager_.errors()(
731                "No best alternative for '%s'.\n"
732                  % (self.full_name(),))
733
734        result = best_alternative.generate (prop_set)
735
736        # Now return virtual targets for the only alternative
737        return result
738
739    def rename(self, new_name):
740        AbstractTarget.rename(self, new_name)
741        for a in self.alternatives_:
742            a.rename(new_name)
743
744class FileReference (AbstractTarget):
745    """ Abstract target which refers to a source file.
746        This is artificial creature; it's usefull so that sources to
747        a target can be represented as list of abstract target instances.
748    """
749    def __init__ (self, manager, file, project):
750        AbstractTarget.__init__ (self, file, project)
751        self.file_location_ = None
752
753    def generate (self, properties):
754         return GenerateResult (None, [
755             self.manager_.virtual_targets ().from_file (
756             self.name_, self.location(), self.project_) ])
757
758    def exists (self):
759        """ Returns true if the referred file really exists.
760        """
761        if self.location ():
762            return True
763        else:
764            return False
765
766    def location (self):
767        # Returns the location of target. Needed by 'testing.jam'
768        if not self.file_location_:
769            source_location = self.project_.get('source-location')
770
771            for src_dir in source_location:
772                location = os.path.join(src_dir, self.name())
773                if os.path.isfile(location):
774                    self.file_location_ = src_dir
775                    self.file_path = location
776                    break
777
778        return self.file_location_
779
780def resolve_reference(target_reference, project):
781    """ Given a target_reference, made in context of 'project',
782    returns the AbstractTarget instance that is referred to, as well
783    as properties explicitly specified for this reference.
784    """
785    # Separate target name from properties override
786    split = _re_separate_target_from_properties.match (target_reference)
787    if not split:
788        raise BaseException ("Invalid reference: '%s'" % target_reference)
789
790    id = split.group (1)
791
792    sproperties = []
793
794    if split.group (3):
795        sproperties = property.create_from_strings(feature.split(split.group(3)))
796        sproperties = feature.expand_composites(sproperties)
797
798    # Find the target
799    target = project.find (id)
800
801    return (target, property_set.create(sproperties))
802
803def generate_from_reference(target_reference, project, property_set):
804    """ Attempts to generate the target given by target reference, which
805    can refer both to a main target or to a file.
806    Returns a list consisting of
807    - usage requirements
808    - generated virtual targets, if any
809    target_reference:  Target reference
810    project:           Project where the reference is made
811    property_set:      Properties of the main target that makes the reference
812    """
813    target, sproperties = resolve_reference(target_reference, project)
814
815    # Take properties which should be propagated and refine them
816    # with source-specific requirements.
817    propagated = property_set.propagated()
818    rproperties = propagated.refine(sproperties)
819
820    return target.generate(rproperties)
821
822
823
824class BasicTarget (AbstractTarget):
825    """ Implements the most standard way of constructing main target
826        alternative from sources. Allows sources to be either file or
827        other main target and handles generation of those dependency
828        targets.
829    """
830    def __init__ (self, name, project, sources, requirements = None, default_build = None, usage_requirements = None):
831        AbstractTarget.__init__ (self, name, project)
832
833        for s in sources:
834            if get_grist (s):
835                raise InvalidSource ("property '%s' found in the 'sources' parameter for '%s'" % (s, name))
836
837        self.sources_ = sources
838
839        if not requirements: requirements = property_set.empty ()
840        self.requirements_ = requirements
841
842        if not default_build: default_build = property_set.empty ()
843        self.default_build_ = default_build
844
845        if not usage_requirements: usage_requirements = property_set.empty ()
846        self.usage_requirements_ = usage_requirements
847
848        # A cache for resolved references
849        self.source_targets_ = None
850
851        # A cache for generated targets
852        self.generated_ = {}
853
854        # A cache for build requests
855        self.request_cache = {}
856
857        # Result of 'capture_user_context' has everything. For example, if this
858        # target is declare as result of loading Jamfile which was loaded when
859        # building target B which was requested from A, then we'll have A, B and
860        # Jamroot location in context. We only care about Jamroot location, most
861        # of the times.
862        self.user_context_ = self.manager_.errors().capture_user_context()[-1:]
863
864        self.always_ = False
865
866    def always(self):
867        self.always_ = True
868
869    def sources (self):
870        """ Returns the list of AbstractTargets which are used as sources.
871            The extra properties specified for sources are not represented.
872            The only used of this rule at the moment is the '--dump-tests'
873            feature of the test system.
874        """
875        if self.source_targets_ == None:
876            self.source_targets_ = []
877            for s in self.sources_:
878                self.source_targets_.append(resolve_reference(s, self.project_)[0])
879
880        return self.source_targets_
881
882    def requirements (self):
883        return self.requirements_
884
885    def default_build (self):
886        return self.default_build_
887
888    def common_properties (self, build_request, requirements):
889        """ Given build request and requirements, return properties
890            common to dependency build request and target build
891            properties.
892        """
893        # For optimization, we add free unconditional requirements directly,
894        # without using complex algorithsm.
895        # This gives the complex algorithm better chance of caching results.
896        # The exact effect of this "optimization" is no longer clear
897        free_unconditional = []
898        other = []
899        for p in requirements.all():
900            if p.feature().free() and not p.condition() and p.feature().name() != 'conditional':
901                free_unconditional.append(p)
902            else:
903                other.append(p)
904        other = property_set.create(other)
905
906        key = (build_request, other)
907        if not self.request_cache.has_key(key):
908            self.request_cache[key] = self.__common_properties2 (build_request, other)
909
910        return self.request_cache[key].add_raw(free_unconditional)
911
912    # Given 'context' -- a set of already present properties, and 'requirements',
913    # decide which extra properties should be applied to 'context'.
914    # For conditional requirements, this means evaluating condition. For
915    # indirect conditional requirements, this means calling a rule. Ordinary
916    # requirements are always applied.
917    #
918    # Handles situation where evaluating one conditional requirements affects
919    # condition of another conditional requirements, for example:
920    #
921    #     <toolset>gcc:<variant>release <variant>release:<define>RELEASE
922    #
923    # If 'what' is 'refined' returns context refined with new requirements.
924    # If 'what' is 'added' returns just the requirements that must be applied.
925    def evaluate_requirements(self, requirements, context, what):
926        # Apply non-conditional requirements.
927        # It's possible that that further conditional requirement change
928        # a value set by non-conditional requirements. For example:
929        #
930        #    exe a : a.cpp : <threading>single <toolset>foo:<threading>multi ;
931        #
932        # I'm not sure if this should be an error, or not, especially given that
933        #
934        #    <threading>single
935        #
936        # might come from project's requirements.
937        unconditional = feature.expand(requirements.non_conditional())
938
939        context = context.refine(property_set.create(unconditional))
940
941        # We've collected properties that surely must be present in common
942        # properties. We now try to figure out what other properties
943        # should be added in order to satisfy rules (4)-(6) from the docs.
944
945        conditionals = property_set.create(requirements.conditional())
946
947        # It's supposed that #conditionals iterations
948        # should be enough for properties to propagate along conditions in any
949        # direction.
950        max_iterations = len(conditionals.all()) +\
951                         len(requirements.get("<conditional>")) + 1
952
953        added_requirements = []
954        current = context
955
956        # It's assumed that ordinary conditional requirements can't add
957        # <indirect-conditional> properties, and that rules referred
958        # by <indirect-conditional> properties can't add new
959        # <indirect-conditional> properties. So the list of indirect conditionals
960        # does not change.
961        indirect = requirements.get("<conditional>")
962
963        ok = 0
964        for i in range(0, max_iterations):
965
966            e = conditionals.evaluate_conditionals(current).all()[:]
967
968            # Evaluate indirect conditionals.
969            for i in indirect:
970                i = b2.util.jam_to_value_maybe(i)
971                if callable(i):
972                    # This is Python callable, yeah.
973                    e.extend(i(current))
974                else:
975                    # Name of bjam function. Because bjam is unable to handle
976                    # list of Property, pass list of strings.
977                    br = b2.util.call_jam_function(i[1:], [str(p) for p in current.all()])
978                    if br:
979                        e.extend(property.create_from_strings(br))
980
981            if e == added_requirements:
982                # If we got the same result, we've found final properties.
983                ok = 1
984                break
985            else:
986                # Oops, results of evaluation of conditionals has changed.
987                # Also 'current' contains leftover from previous evaluation.
988                # Recompute 'current' using initial properties and conditional
989                # requirements.
990                added_requirements = e
991                current = context.refine(property_set.create(feature.expand(e)))
992
993        if not ok:
994            self.manager().errors()("Can't evaluate conditional properties "
995                                    + str(conditionals))
996
997
998        if what == "added":
999            return property_set.create(unconditional + added_requirements)
1000        elif what == "refined":
1001            return current
1002        else:
1003            self.manager().errors("Invalid value of the 'what' parameter")
1004
1005    def __common_properties2(self, build_request, requirements):
1006        # This guarantees that default properties are present
1007        # in result, unless they are overrided by some requirement.
1008        # TODO: There is possibility that we've added <foo>bar, which is composite
1009        # and expands to <foo2>bar2, but default value of <foo2> is not bar2,
1010        # in which case it's not clear what to do.
1011        #
1012        build_request = build_request.add_defaults()
1013        # Featured added by 'add-default' can be composite and expand
1014        # to features without default values -- so they are not added yet.
1015        # It could be clearer/faster to expand only newly added properties
1016        # but that's not critical.
1017        build_request = build_request.expand()
1018
1019        return self.evaluate_requirements(requirements, build_request,
1020                                          "refined")
1021
1022    def match (self, property_set, debug):
1023        """ Returns the alternative condition for this alternative, if
1024            the condition is satisfied by 'property_set'.
1025        """
1026        # The condition is composed of all base non-conditional properties.
1027        # It's not clear if we should expand 'self.requirements_' or not.
1028        # For one thing, it would be nice to be able to put
1029        #    <toolset>msvc-6.0
1030        # in requirements.
1031        # On the other hand, if we have <variant>release in condition it
1032        # does not make sense to require <optimization>full to be in
1033        # build request just to select this variant.
1034        bcondition = self.requirements_.base ()
1035        ccondition = self.requirements_.conditional ()
1036        condition = b2.util.set.difference (bcondition, ccondition)
1037
1038        if debug:
1039            print "    next alternative: required properties:", [str(p) for p in condition]
1040
1041        if b2.util.set.contains (condition, property_set.all()):
1042
1043            if debug:
1044                print "        matched"
1045
1046            return condition
1047
1048        else:
1049            return None
1050
1051
1052    def generate_dependency_targets (self, target_ids, property_set):
1053        targets = []
1054        usage_requirements = []
1055        for id in target_ids:
1056
1057            result = generate_from_reference(id, self.project_, property_set)
1058            targets += result.targets()
1059            usage_requirements += result.usage_requirements().all()
1060
1061        return (targets, usage_requirements)
1062
1063    def generate_dependency_properties(self, properties, ps):
1064        """ Takes a target reference, which might be either target id
1065            or a dependency property, and generates that target using
1066            'property_set' as build request.
1067
1068            Returns a tuple (result, usage_requirements).
1069        """
1070        result_properties = []
1071        usage_requirements = []
1072        for p in properties:
1073
1074            result = generate_from_reference(p.value(), self.project_, ps)
1075
1076            for t in result.targets():
1077                result_properties.append(property.Property(p.feature(), t))
1078
1079            usage_requirements += result.usage_requirements().all()
1080
1081        return (result_properties, usage_requirements)
1082
1083
1084
1085
1086    @user_error_checkpoint
1087    def generate (self, ps):
1088        """ Determines final build properties, generates sources,
1089        and calls 'construct'. This method should not be
1090        overridden.
1091        """
1092        self.manager_.errors().push_user_context(
1093            "Generating target " + self.full_name(), self.user_context_)
1094
1095        if self.manager().targets().logging():
1096            self.manager().targets().log(
1097                "Building target '%s'" % self.name_)
1098            self.manager().targets().increase_indent ()
1099            self.manager().targets().log(
1100                "Build request: '%s'" % str (ps.raw ()))
1101            cf = self.manager().command_line_free_features()
1102            self.manager().targets().log(
1103                "Command line free features: '%s'" % str (cf.raw ()))
1104            self.manager().targets().log(
1105                "Target requirements: %s'" % str (self.requirements().raw ()))
1106
1107        self.manager().targets().push_target(self)
1108
1109        if not self.generated_.has_key(ps):
1110
1111            # Apply free features form the command line.  If user
1112            # said
1113            #   define=FOO
1114            # he most likely want this define to be set for all compiles.
1115            ps = ps.refine(self.manager().command_line_free_features())
1116            rproperties = self.common_properties (ps, self.requirements_)
1117
1118            self.manager().targets().log(
1119                "Common properties are '%s'" % str (rproperties))
1120
1121            if rproperties.get("<build>") != ["no"]:
1122
1123                result = GenerateResult ()
1124
1125                properties = rproperties.non_dependency ()
1126
1127                (p, u) = self.generate_dependency_properties (rproperties.dependency (), rproperties)
1128                properties += p
1129                assert all(isinstance(p, property.Property) for p in properties)
1130                usage_requirements = u
1131
1132                (source_targets, u) = self.generate_dependency_targets (self.sources_, rproperties)
1133                usage_requirements += u
1134
1135                self.manager_.targets().log(
1136                    "Usage requirements for '%s' are '%s'" % (self.name_, usage_requirements))
1137
1138                # FIXME:
1139
1140                rproperties = property_set.create(properties + usage_requirements)
1141                usage_requirements = property_set.create (usage_requirements)
1142
1143                self.manager_.targets().log(
1144                    "Build properties: '%s'" % str(rproperties))
1145
1146                source_targets += rproperties.get('<source>')
1147
1148                # We might get duplicate sources, for example if
1149                # we link to two library which have the same <library> in
1150                # usage requirements.
1151                # Use stable sort, since for some targets the order is
1152                # important. E.g. RUN_PY target need python source to come
1153                # first.
1154                source_targets = unique(source_targets, stable=True)
1155
1156                # FIXME: figure why this call messes up source_targets in-place
1157                result = self.construct (self.name_, source_targets[:], rproperties)
1158
1159                if result:
1160                    assert len(result) == 2
1161                    gur = result [0]
1162                    result = result [1]
1163
1164                    if self.always_:
1165                        for t in result:
1166                            t.always()
1167
1168                    s = self.create_subvariant (
1169                        result,
1170                        self.manager().virtual_targets().recent_targets(), ps,
1171                        source_targets, rproperties, usage_requirements)
1172                    self.manager().virtual_targets().clear_recent_targets()
1173
1174                    ur = self.compute_usage_requirements (s)
1175                    ur = ur.add (gur)
1176                    s.set_usage_requirements (ur)
1177
1178                    self.manager_.targets().log (
1179                        "Usage requirements from '%s' are '%s'" %
1180                        (self.name(), str(rproperties)))
1181
1182                    self.generated_[ps] = GenerateResult (ur, result)
1183                else:
1184                    self.generated_[ps] = GenerateResult (property_set.empty(), [])
1185            else:
1186                # If we just see <build>no, we cannot produce any reasonable
1187                # diagnostics. The code that adds this property is expected
1188                # to explain why a target is not built, for example using
1189                # the configure.log-component-configuration function.
1190
1191                # If this target fails to build, add <build>no to properties
1192                # to cause any parent target to fail to build.  Except that it
1193                # - does not work now, since we check for <build>no only in
1194                #   common properties, but not in properties that came from
1195                #   dependencies
1196                # - it's not clear if that's a good idea anyway.  The alias
1197                #   target, for example, should not fail to build if a dependency
1198                #   fails.
1199                self.generated_[ps] = GenerateResult(
1200                    property_set.create(["<build>no"]), [])
1201        else:
1202            self.manager().targets().log ("Already built")
1203
1204        self.manager().targets().pop_target()
1205        self.manager().targets().decrease_indent()
1206
1207        return self.generated_[ps]
1208
1209    def compute_usage_requirements (self, subvariant):
1210        """ Given the set of generated targets, and refined build
1211            properties, determines and sets appripriate usage requirements
1212            on those targets.
1213        """
1214        rproperties = subvariant.build_properties ()
1215        xusage_requirements =self.evaluate_requirements(
1216            self.usage_requirements_, rproperties, "added")
1217
1218        # We generate all dependency properties and add them,
1219        # as well as their usage requirements, to result.
1220        (r1, r2) = self.generate_dependency_properties(xusage_requirements.dependency (), rproperties)
1221        extra = r1 + r2
1222
1223        result = property_set.create (xusage_requirements.non_dependency () + extra)
1224
1225        # Propagate usage requirements we've got from sources, except
1226        # for the <pch-header> and <pch-file> features.
1227        #
1228        # That feature specifies which pch file to use, and should apply
1229        # only to direct dependents. Consider:
1230        #
1231        #   pch pch1 : ...
1232        #   lib lib1 : ..... pch1 ;
1233        #   pch pch2 :
1234        #   lib lib2 : pch2 lib1 ;
1235        #
1236        # Here, lib2 should not get <pch-header> property from pch1.
1237        #
1238        # Essentially, when those two features are in usage requirements,
1239        # they are propagated only to direct dependents. We might need
1240        # a more general mechanism, but for now, only those two
1241        # features are special.
1242        removed_pch = filter(lambda prop: prop.feature().name() not in ['<pch-header>', '<pch-file>'], subvariant.sources_usage_requirements().all())
1243        result = result.add(property_set.PropertySet(removed_pch))
1244
1245        return result
1246
1247    def create_subvariant (self, root_targets, all_targets,
1248                           build_request, sources,
1249                           rproperties, usage_requirements):
1250        """Creates a new subvariant-dg instances for 'targets'
1251         - 'root-targets' the virtual targets will be returned to dependents
1252         - 'all-targets' all virtual
1253              targets created while building this main target
1254         - 'build-request' is property-set instance with
1255         requested build properties"""
1256
1257        for e in root_targets:
1258            e.root (True)
1259
1260        s = Subvariant (self, build_request, sources,
1261                        rproperties, usage_requirements, all_targets)
1262
1263        for v in all_targets:
1264            if not v.creating_subvariant():
1265                v.creating_subvariant(s)
1266
1267        return s
1268
1269    def construct (self, name, source_targets, properties):
1270        """ Constructs the virtual targets for this abstract targets and
1271            the dependecy graph. Returns a tuple consisting of the properties and the list of virtual targets.
1272            Should be overrided in derived classes.
1273        """
1274        raise BaseException ("method should be defined in derived classes")
1275
1276
1277class TypedTarget (BasicTarget):
1278    import generators
1279
1280    def __init__ (self, name, project, type, sources, requirements, default_build, usage_requirements):
1281        BasicTarget.__init__ (self, name, project, sources, requirements, default_build, usage_requirements)
1282        self.type_ = type
1283
1284    def __jam_repr__(self):
1285        return b2.util.value_to_jam(self)
1286
1287    def type (self):
1288        return self.type_
1289
1290    def construct (self, name, source_targets, prop_set):
1291
1292        r = generators.construct (self.project_, os.path.splitext(name)[0],
1293                                  self.type_,
1294                                  prop_set.add_raw(['<main-target-type>' + self.type_]),
1295                                  source_targets, True)
1296
1297        if not r:
1298            print "warning: Unable to construct '%s'" % self.full_name ()
1299
1300            # Are there any top-level generators for this type/property set.
1301            if not generators.find_viable_generators (self.type_, prop_set):
1302                print "error: no generators were found for type '" + self.type_ + "'"
1303                print "error: and the requested properties"
1304                print "error: make sure you've configured the needed tools"
1305                print "See http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html"
1306
1307                print "To debug this problem, try the --debug-generators option."
1308                sys.exit(1)
1309
1310        return r
1311
1312def apply_default_build(property_set, default_build):
1313    # 1. First, see what properties from default_build
1314    # are already present in property_set.
1315
1316    specified_features = set(p.feature() for p in property_set.all())
1317
1318    defaults_to_apply = []
1319    for d in default_build.all():
1320        if not d.feature() in specified_features:
1321            defaults_to_apply.append(d)
1322
1323    # 2. If there's any defaults to be applied, form the new
1324    # build request. Pass it throw 'expand-no-defaults', since
1325    # default_build might contain "release debug", which will
1326    # result in two property_sets.
1327    result = []
1328    if defaults_to_apply:
1329
1330        # We have to compress subproperties here to prevent
1331        # property lists like:
1332        #
1333        #    <toolset>msvc <toolset-msvc:version>7.1 <threading>multi
1334        #
1335        # from being expanded into:
1336        #
1337        #    <toolset-msvc:version>7.1/<threading>multi
1338        #    <toolset>msvc/<toolset-msvc:version>7.1/<threading>multi
1339        #
1340        # due to cross-product property combination.  That may
1341        # be an indication that
1342        # build_request.expand-no-defaults is the wrong rule
1343        # to use here.
1344        compressed = feature.compress_subproperties(property_set.all())
1345
1346        result = build_request.expand_no_defaults(
1347            b2.build.property_set.create(feature.expand([p])) for p in (compressed + defaults_to_apply))
1348
1349    else:
1350        result.append (property_set)
1351
1352    return result
1353
1354
1355def create_typed_metatarget(name, type, sources, requirements, default_build, usage_requirements):
1356
1357    from b2.manager import get_manager
1358    t = get_manager().targets()
1359
1360    project = get_manager().projects().current()
1361
1362    return t.main_target_alternative(
1363        TypedTarget(name, project, type,
1364                    t.main_target_sources(sources, name),
1365                    t.main_target_requirements(requirements, project),
1366                    t.main_target_default_build(default_build, project),
1367                    t.main_target_usage_requirements(usage_requirements, project)))
1368
1369
1370def create_metatarget(klass, name, sources, requirements=[], default_build=[], usage_requirements=[]):
1371    from b2.manager import get_manager
1372    t = get_manager().targets()
1373
1374    project = get_manager().projects().current()
1375
1376    return t.main_target_alternative(
1377        klass(name, project,
1378              t.main_target_sources(sources, name),
1379              t.main_target_requirements(requirements, project),
1380              t.main_target_default_build(default_build, project),
1381              t.main_target_usage_requirements(usage_requirements, project)))
1382
1383def metatarget_function_for_class(class_):
1384
1385    @bjam_signature((["name"], ["sources", "*"], ["requirements", "*"],
1386                     ["default_build", "*"], ["usage_requirements", "*"]))
1387    def create_metatarget(name, sources, requirements = [], default_build = None, usage_requirements = []):
1388
1389        from b2.manager import get_manager
1390        t = get_manager().targets()
1391
1392        project = get_manager().projects().current()
1393
1394        return t.main_target_alternative(
1395            class_(name, project,
1396                   t.main_target_sources(sources, name),
1397                   t.main_target_requirements(requirements, project),
1398                   t.main_target_default_build(default_build, project),
1399                   t.main_target_usage_requirements(usage_requirements, project)))
1400
1401    return create_metatarget
1402