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