]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/build/targets.py
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / tools / build / src / build / targets.py
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.txt or copy at
9 # https://www.bfgroup.xyz/b2/LICENSE.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
73 import re
74 import os.path
75 import sys
76
77 from b2.manager import get_manager
78
79 from b2.util.utility import *
80 import property, project, virtual_target, property_set, feature, generators, toolset
81 from virtual_target import Subvariant
82 from b2.exceptions import *
83 from b2.util.sequence import unique
84 from b2.util import path, bjam_signature, safe_isinstance, is_iterable_typed
85 from b2.build import errors
86 from b2.build.errors import user_error_checkpoint
87
88 import b2.build.build_request as build_request
89
90 import b2.util.set
91 _re_separate_target_from_properties = re.compile (r'^([^<]*)(/(<.*))?$')
92
93 class 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
263 class 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
289 class 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
358 class 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
667 class 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
805 class 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
841 def 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
866 def 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
890 class 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
1380 class 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 https://www.bfgroup.xyz/b2/manual/release/index.html#bbv2.overview.configuration"
1412
1413 print "To debug this problem, try the --debug-generators option."
1414 sys.exit(1)
1415
1416 return r
1417
1418 def 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
1467 def 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
1487 def 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
1505 def 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