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