1# Status: ported. 2# Base revision: 64488. 3# 4# Copyright (C) Vladimir Prus 2002. Permission to copy, use, modify, sell and 5# distribute this software is granted provided this copyright notice appears in 6# all copies. This software is provided "as is" without express or implied 7# warranty, and with no claim as to its suitability for any purpose. 8 9# Implements virtual targets, which correspond to actual files created during 10# build, but are not yet targets in Jam sense. They are needed, for example, 11# when searching for possible transormation sequences, when it's not known 12# if particular target should be created at all. 13# 14# 15# +--------------------------+ 16# | VirtualTarget | 17# +==========================+ 18# | actualize | 19# +--------------------------+ 20# | actualize_action() = 0 | 21# | actualize_location() = 0 | 22# +----------------+---------+ 23# | 24# ^ 25# / \ 26# +-+-+ 27# | 28# +---------------------+ +-------+--------------+ 29# | Action | | AbstractFileTarget | 30# +=====================| * +======================+ 31# | action_name | +--+ action | 32# | properties | | +----------------------+ 33# +---------------------+--+ | actualize_action() | 34# | actualize() |0..1 +-----------+----------+ 35# | path() | | 36# | adjust_properties() | sources | 37# | actualize_sources() | targets | 38# +------+--------------+ ^ 39# | / \ 40# ^ +-+-+ 41# / \ | 42# +-+-+ +-------------+-------------+ 43# | | | 44# | +------+---------------+ +--------+-------------+ 45# | | FileTarget | | SearchedLibTarget | 46# | +======================+ +======================+ 47# | | actualize-location() | | actualize-location() | 48# | +----------------------+ +----------------------+ 49# | 50# +-+------------------------------+ 51# | | 52# +----+----------------+ +---------+-----------+ 53# | CompileAction | | LinkAction | 54# +=====================+ +=====================+ 55# | adjust_properties() | | adjust_properties() | 56# +---------------------+ | actualize_sources() | 57# +---------------------+ 58# 59# The 'CompileAction' and 'LinkAction' classes are defined not here, 60# but in builtin.jam modules. They are shown in the diagram to give 61# the big picture. 62 63import bjam 64 65import re 66import os.path 67import string 68import types 69 70from b2.util import path, utility, set 71from b2.util.utility import add_grist, get_grist, ungrist, replace_grist, get_value 72from b2.util.sequence import unique 73from b2.tools import common 74from b2.exceptions import * 75import b2.build.type 76import b2.build.property_set as property_set 77 78import b2.build.property as property 79 80from b2.manager import get_manager 81from b2.util import bjam_signature 82 83__re_starts_with_at = re.compile ('^@(.*)') 84 85class VirtualTargetRegistry: 86 def __init__ (self, manager): 87 self.manager_ = manager 88 89 # A cache for FileTargets 90 self.files_ = {} 91 92 # A cache for targets. 93 self.cache_ = {} 94 95 # A map of actual names to virtual targets. 96 # Used to make sure we don't associate same 97 # actual target to two virtual targets. 98 self.actual_ = {} 99 100 self.recent_targets_ = [] 101 102 # All targets ever registed 103 self.all_targets_ = [] 104 105 self.next_id_ = 0 106 107 def register (self, target): 108 """ Registers a new virtual target. Checks if there's already registered target, with the same 109 name, type, project and subvariant properties, and also with the same sources 110 and equal action. If such target is found it is retured and 'target' is not registered. 111 Otherwise, 'target' is registered and returned. 112 """ 113 if target.path(): 114 signature = target.path() + "-" + target.name() 115 else: 116 signature = "-" + target.name() 117 118 result = None 119 if not self.cache_.has_key (signature): 120 self.cache_ [signature] = [] 121 122 for t in self.cache_ [signature]: 123 a1 = t.action () 124 a2 = target.action () 125 126 # TODO: why are we checking for not result? 127 if not result: 128 if not a1 and not a2: 129 result = t 130 else: 131 if a1 and a2 and a1.action_name () == a2.action_name () and a1.sources () == a2.sources (): 132 ps1 = a1.properties () 133 ps2 = a2.properties () 134 p1 = ps1.base () + ps1.free () +\ 135 b2.util.set.difference(ps1.dependency(), ps1.incidental()) 136 p2 = ps2.base () + ps2.free () +\ 137 b2.util.set.difference(ps2.dependency(), ps2.incidental()) 138 if p1 == p2: 139 result = t 140 141 if not result: 142 self.cache_ [signature].append (target) 143 result = target 144 145 # TODO: Don't append if we found pre-existing target? 146 self.recent_targets_.append(result) 147 self.all_targets_.append(result) 148 149 return result 150 151 def from_file (self, file, file_location, project): 152 """ Creates a virtual target with appropriate name and type from 'file'. 153 If a target with that name in that project was already created, returns that already 154 created target. 155 TODO: more correct way would be to compute path to the file, based on name and source location 156 for the project, and use that path to determine if the target was already created. 157 TODO: passing project with all virtual targets starts to be annoying. 158 """ 159 # Check if we've created a target corresponding to this file. 160 path = os.path.join(os.getcwd(), file_location, file) 161 path = os.path.normpath(path) 162 163 if self.files_.has_key (path): 164 return self.files_ [path] 165 166 file_type = b2.build.type.type (file) 167 168 result = FileTarget (file, file_type, project, 169 None, file_location) 170 self.files_ [path] = result 171 172 return result 173 174 def recent_targets(self): 175 """Each target returned by 'register' is added to a list of 176 'recent-target', returned by this function. So, this allows 177 us to find all targets created when building a given main 178 target, even if the target.""" 179 180 return self.recent_targets_ 181 182 def clear_recent_targets(self): 183 self.recent_targets_ = [] 184 185 def all_targets(self): 186 # Returns all virtual targets ever created 187 return self.all_targets_ 188 189 # Returns all targets from 'targets' with types 190 # equal to 'type' or derived from it. 191 def select_by_type(self, type, targets): 192 return [t for t in targets if b2.build.type.is_sybtype(t.type(), type)] 193 194 def register_actual_name (self, actual_name, virtual_target): 195 if self.actual_.has_key (actual_name): 196 cs1 = self.actual_ [actual_name].creating_subvariant () 197 cs2 = virtual_target.creating_subvariant () 198 cmt1 = cs1.main_target () 199 cmt2 = cs2.main_target () 200 201 action1 = self.actual_ [actual_name].action () 202 action2 = virtual_target.action () 203 204 properties_added = [] 205 properties_removed = [] 206 if action1 and action2: 207 p1 = action1.properties () 208 p1 = p1.raw () 209 p2 = action2.properties () 210 p2 = p2.raw () 211 212 properties_removed = set.difference (p1, p2) 213 if not properties_removed: properties_removed = "none" 214 215 properties_added = set.difference (p2, p1) 216 if not properties_added: properties_added = "none" 217 218 # FIXME: Revive printing of real location. 219 get_manager().errors()( 220 "Duplicate name of actual target: '%s'\n" 221 "previous virtual target '%s'\n" 222 "created from '%s'\n" 223 "another virtual target '%s'\n" 224 "created from '%s'\n" 225 "added properties: '%s'\n" 226 "removed properties: '%s'\n" 227 % (actual_name, 228 self.actual_ [actual_name], "loc", #cmt1.location (), 229 virtual_target, 230 "loc", #cmt2.location (), 231 properties_added, properties_removed)) 232 233 else: 234 self.actual_ [actual_name] = virtual_target 235 236 237 def add_suffix (self, specified_name, file_type, prop_set): 238 """ Appends the suffix appropriate to 'type/property_set' combination 239 to the specified name and returns the result. 240 """ 241 suffix = b2.build.type.generated_target_suffix (file_type, prop_set) 242 243 if suffix: 244 return specified_name + '.' + suffix 245 246 else: 247 return specified_name 248 249class VirtualTarget: 250 """ Potential target. It can be converted into jam target and used in 251 building, if needed. However, it can be also dropped, which allows 252 to search for different transformation and select only one. 253 name: name of this target. 254 project: project to which this target belongs. 255 """ 256 def __init__ (self, name, project): 257 self.name_ = name 258 self.project_ = project 259 self.dependencies_ = [] 260 self.always_ = False 261 262 # Caches if dapendencies for scanners have already been set. 263 self.made_ = {} 264 265 def manager(self): 266 return self.project_.manager() 267 268 def virtual_targets(self): 269 return self.manager().virtual_targets() 270 271 def name (self): 272 """ Name of this target. 273 """ 274 return self.name_ 275 276 def project (self): 277 """ Project of this target. 278 """ 279 return self.project_ 280 281 def depends (self, d): 282 """ Adds additional instances of 'VirtualTarget' that this 283 one depends on. 284 """ 285 self.dependencies_ = unique (self.dependencies_ + d).sort () 286 287 def dependencies (self): 288 return self.dependencies_ 289 290 def always(self): 291 self.always_ = True 292 293 def actualize (self, scanner = None): 294 """ Generates all the actual targets and sets up build actions for 295 this target. 296 297 If 'scanner' is specified, creates an additional target 298 with the same location as actual target, which will depend on the 299 actual target and be associated with 'scanner'. That additional 300 target is returned. See the docs (#dependency_scanning) for rationale. 301 Target must correspond to a file if 'scanner' is specified. 302 303 If scanner is not specified, then actual target is returned. 304 """ 305 actual_name = self.actualize_no_scanner () 306 307 if self.always_: 308 bjam.call("ALWAYS", actual_name) 309 310 if not scanner: 311 return actual_name 312 313 else: 314 # Add the scanner instance to the grist for name. 315 g = '-'.join ([ungrist(get_grist(actual_name)), str(id(scanner))]) 316 317 name = replace_grist (actual_name, '<' + g + '>') 318 319 if not self.made_.has_key (name): 320 self.made_ [name] = True 321 322 self.project_.manager ().engine ().add_dependency (name, actual_name) 323 324 self.actualize_location (name) 325 326 self.project_.manager ().scanners ().install (scanner, name, str (self)) 327 328 return name 329 330# private: (overridables) 331 332 def actualize_action (self, target): 333 """ Sets up build actions for 'target'. Should call appropriate rules 334 and set target variables. 335 """ 336 raise BaseException ("method should be defined in derived classes") 337 338 def actualize_location (self, target): 339 """ Sets up variables on 'target' which specify its location. 340 """ 341 raise BaseException ("method should be defined in derived classes") 342 343 def path (self): 344 """ If the target is generated one, returns the path where it will be 345 generated. Otherwise, returns empty list. 346 """ 347 raise BaseException ("method should be defined in derived classes") 348 349 def actual_name (self): 350 """ Return that actual target name that should be used 351 (for the case where no scanner is involved) 352 """ 353 raise BaseException ("method should be defined in derived classes") 354 355 356class AbstractFileTarget (VirtualTarget): 357 """ Target which correspond to a file. The exact mapping for file 358 is not yet specified in this class. (TODO: Actually, the class name 359 could be better...) 360 361 May be a source file (when no action is specified), or 362 derived file (otherwise). 363 364 The target's grist is concatenation of project's location, 365 properties of action (for derived files), and, optionally, 366 value identifying the main target. 367 368 exact: If non-empty, the name is exactly the name 369 created file should have. Otherwise, the '__init__' 370 method will add suffix obtained from 'type' by 371 calling 'type.generated-target-suffix'. 372 373 type: optional type of this target. 374 """ 375 def __init__ (self, name, type, project, action = None, exact=False): 376 VirtualTarget.__init__ (self, name, project) 377 378 self.type_ = type 379 380 self.action_ = action 381 self.exact_ = exact 382 383 if action: 384 action.add_targets ([self]) 385 386 if self.type and not exact: 387 self.__adjust_name (name) 388 389 390 self.actual_name_ = None 391 self.path_ = None 392 self.intermediate_ = False 393 self.creating_subvariant_ = None 394 395 # True if this is a root target. 396 self.root_ = False 397 398 def type (self): 399 return self.type_ 400 401 def set_path (self, path): 402 """ Sets the path. When generating target name, it will override any path 403 computation from properties. 404 """ 405 self.path_ = os.path.normpath(path) 406 407 def action (self): 408 """ Returns the action. 409 """ 410 return self.action_ 411 412 def root (self, set = None): 413 """ Sets/gets the 'root' flag. Target is root is it directly correspods to some 414 variant of a main target. 415 """ 416 if set: 417 self.root_ = True 418 return self.root_ 419 420 def creating_subvariant (self, s = None): 421 """ Gets or sets the subvariant which created this target. Subvariant 422 is set when target is brought into existance, and is never changed 423 after that. In particual, if target is shared by subvariant, only 424 the first is stored. 425 s: If specified, specified the value to set, 426 which should be instance of 'subvariant' class. 427 """ 428 if s and not self.creating_subvariant (): 429 if self.creating_subvariant (): 430 raise BaseException ("Attempt to change 'dg'") 431 432 else: 433 self.creating_subvariant_ = s 434 435 return self.creating_subvariant_ 436 437 def actualize_action (self, target): 438 if self.action_: 439 self.action_.actualize () 440 441 # Return a human-readable representation of this target 442 # 443 # If this target has an action, that's: 444 # 445 # { <action-name>-<self.name>.<self.type> <action-sources>... } 446 # 447 # otherwise, it's: 448 # 449 # { <self.name>.<self.type> } 450 # 451 def str(self): 452 a = self.action() 453 454 name_dot_type = self.name_ + "." + self.type_ 455 456 if a: 457 action_name = a.action_name() 458 ss = [ s.str() for s in a.sources()] 459 460 return "{ %s-%s %s}" % (action_name, name_dot_type, str(ss)) 461 else: 462 return "{ " + name_dot_type + " }" 463 464# private: 465 466 def actual_name (self): 467 if not self.actual_name_: 468 self.actual_name_ = '<' + self.grist() + '>' + os.path.normpath(self.name_) 469 470 return self.actual_name_ 471 472 def grist (self): 473 """Helper to 'actual_name', above. Compute unique prefix used to distinguish 474 this target from other targets with the same name which create different 475 file. 476 """ 477 # Depending on target, there may be different approaches to generating 478 # unique prefixes. We'll generate prefixes in the form 479 # <one letter approach code> <the actual prefix> 480 path = self.path () 481 482 if path: 483 # The target will be generated to a known path. Just use the path 484 # for identification, since path is as unique as it can get. 485 return 'p' + path 486 487 else: 488 # File is either source, which will be searched for, or is not a file at 489 # all. Use the location of project for distinguishing. 490 project_location = self.project_.get ('location') 491 path_components = b2.util.path.split(project_location) 492 location_grist = '!'.join (path_components) 493 494 if self.action_: 495 ps = self.action_.properties () 496 property_grist = ps.as_path () 497 # 'property_grist' can be empty when 'ps' is an empty 498 # property set. 499 if property_grist: 500 location_grist = location_grist + '/' + property_grist 501 502 return 'l' + location_grist 503 504 def __adjust_name(self, specified_name): 505 """Given the target name specified in constructor, returns the 506 name which should be really used, by looking at the <tag> properties. 507 The tag properties come in two flavour: 508 - <tag>value, 509 - <tag>@rule-name 510 In the first case, value is just added to name 511 In the second case, the specified rule is called with specified name, 512 target type and properties and should return the new name. 513 If not <tag> property is specified, or the rule specified by 514 <tag> returns nothing, returns the result of calling 515 virtual-target.add-suffix""" 516 517 if self.action_: 518 ps = self.action_.properties() 519 else: 520 ps = property_set.empty() 521 522 # FIXME: I'm not sure how this is used, need to check with 523 # Rene to figure out how to implement 524 #~ We add ourselves to the properties so that any tag rule can get 525 #~ more direct information about the target than just that available 526 #~ through the properties. This is useful in implementing 527 #~ name changes based on the sources of the target. For example to 528 #~ make unique names of object files based on the source file. 529 #~ --grafik 530 #ps = property_set.create(ps.raw() + ["<target>%s" % "XXXX"]) 531 #ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ; 532 533 tag = ps.get("<tag>") 534 535 if tag: 536 537 if len(tag) > 1: 538 get_manager().errors()( 539 """<tag>@rulename is present but is not the only <tag> feature""") 540 541 tag = tag[0] 542 if callable(tag): 543 self.name_ = tag(specified_name, self.type_, ps) 544 else: 545 if not tag[0] == '@': 546 self.manager_.errors()("""The value of the <tag> feature must be '@rule-nane'""") 547 548 exported_ps = b2.util.value_to_jam(ps, methods=True) 549 self.name_ = b2.util.call_jam_function( 550 tag[1:], specified_name, self.type_, exported_ps) 551 if self.name_: 552 self.name_ = self.name_[0] 553 554 # If there's no tag or the tag rule returned nothing. 555 if not tag or not self.name_: 556 self.name_ = add_prefix_and_suffix(specified_name, self.type_, ps) 557 558 def actualize_no_scanner(self): 559 name = self.actual_name() 560 561 # Do anything only on the first invocation 562 if not self.made_: 563 self.made_[name] = True 564 565 if self.action_: 566 # For non-derived target, we don't care if there 567 # are several virtual targets that refer to the same name. 568 # One case when this is unavoidable is when file name is 569 # main.cpp and two targets have types CPP (for compiling) 570 # and MOCCABLE_CPP (for convertion to H via Qt tools). 571 self.virtual_targets().register_actual_name(name, self) 572 573 for i in self.dependencies_: 574 self.manager_.engine().add_dependency(name, i.actualize()) 575 576 self.actualize_location(name) 577 self.actualize_action(name) 578 579 return name 580 581@bjam_signature((["specified_name"], ["type"], ["property_set"])) 582def add_prefix_and_suffix(specified_name, type, property_set): 583 """Appends the suffix appropriate to 'type/property-set' combination 584 to the specified name and returns the result.""" 585 586 property_set = b2.util.jam_to_value_maybe(property_set) 587 588 suffix = "" 589 if type: 590 suffix = b2.build.type.generated_target_suffix(type, property_set) 591 592 # Handle suffixes for which no leading dot is desired. Those are 593 # specified by enclosing them in <...>. Needed by python so it 594 # can create "_d.so" extensions, for example. 595 if get_grist(suffix): 596 suffix = ungrist(suffix) 597 elif suffix: 598 suffix = "." + suffix 599 600 prefix = "" 601 if type: 602 prefix = b2.build.type.generated_target_prefix(type, property_set) 603 604 if specified_name.startswith(prefix): 605 prefix = "" 606 607 if not prefix: 608 prefix = "" 609 if not suffix: 610 suffix = "" 611 return prefix + specified_name + suffix 612 613 614class FileTarget (AbstractFileTarget): 615 """ File target with explicitly known location. 616 617 The file path is determined as 618 - value passed to the 'set_path' method, if any 619 - for derived files, project's build dir, joined with components 620 that describe action's properties. If the free properties 621 are not equal to the project's reference properties 622 an element with name of main target is added. 623 - for source files, project's source dir 624 625 The file suffix is 626 - the value passed to the 'suffix' method, if any, or 627 - the suffix which correspond to the target's type. 628 """ 629 def __init__ (self, name, type, project, action = None, path=None, exact=False): 630 AbstractFileTarget.__init__ (self, name, type, project, action, exact) 631 632 self.path_ = path 633 634 def __str__(self): 635 if self.type_: 636 return self.name_ + "." + self.type_ 637 else: 638 return self.name_ 639 640 def clone_with_different_type(self, new_type): 641 return FileTarget(self.name_, new_type, self.project_, 642 self.action_, self.path_, exact=True) 643 644 def actualize_location (self, target): 645 engine = self.project_.manager_.engine () 646 647 if self.action_: 648 # This is a derived file. 649 path = self.path () 650 engine.set_target_variable (target, 'LOCATE', path) 651 652 # Make sure the path exists. 653 engine.add_dependency (target, path) 654 common.mkdir(engine, path) 655 656 # It's possible that the target name includes a directory 657 # too, for example when installing headers. Create that 658 # directory. 659 d = os.path.dirname(get_value(target)) 660 if d: 661 d = os.path.join(path, d) 662 engine.add_dependency(target, d) 663 common.mkdir(engine, d) 664 665 # For real file target, we create a fake target that 666 # depends on the real target. This allows to run 667 # 668 # bjam hello.o 669 # 670 # without trying to guess the name of the real target. 671 # Note the that target has no directory name, and a special 672 # grist <e>. 673 # 674 # First, that means that "bjam hello.o" will build all 675 # known hello.o targets. 676 # Second, the <e> grist makes sure this target won't be confused 677 # with other targets, for example, if we have subdir 'test' 678 # with target 'test' in it that includes 'test.o' file, 679 # then the target for directory will be just 'test' the target 680 # for test.o will be <ptest/bin/gcc/debug>test.o and the target 681 # we create below will be <e>test.o 682 engine.add_dependency("<e>%s" % get_value(target), target) 683 684 # Allow bjam <path-to-file>/<file> to work. This won't catch all 685 # possible ways to refer to the path (relative/absolute, extra ".", 686 # various "..", but should help in obvious cases. 687 engine.add_dependency("<e>%s" % (os.path.join(path, get_value(target))), target) 688 689 else: 690 # This is a source file. 691 engine.set_target_variable (target, 'SEARCH', self.project_.get ('source-location')) 692 693 694 def path (self): 695 """ Returns the directory for this target. 696 """ 697 if not self.path_: 698 if self.action_: 699 p = self.action_.properties () 700 (target_path, relative_to_build_dir) = p.target_path () 701 702 if relative_to_build_dir: 703 # Indicates that the path is relative to 704 # build dir. 705 target_path = os.path.join (self.project_.build_dir (), target_path) 706 707 # Store the computed path, so that it's not recomputed 708 # any more 709 self.path_ = target_path 710 711 return os.path.normpath(self.path_) 712 713 714class NotFileTarget(AbstractFileTarget): 715 716 def __init__(self, name, project, action): 717 AbstractFileTarget.__init__(self, name, None, project, action) 718 719 def path(self): 720 """Returns nothing, to indicate that target path is not known.""" 721 return None 722 723 def actualize_location(self, target): 724 bjam.call("NOTFILE", target) 725 bjam.call("ALWAYS", target) 726 bjam.call("NOUPDATE", target) 727 728 729class Action: 730 """ Class which represents an action. 731 Both 'targets' and 'sources' should list instances of 'VirtualTarget'. 732 Action name should name a rule with this prototype 733 rule action_name ( targets + : sources * : properties * ) 734 Targets and sources are passed as actual jam targets. The rule may 735 not establish dependency relationship, but should do everything else. 736 """ 737 def __init__ (self, manager, sources, action_name, prop_set): 738 assert(isinstance(prop_set, property_set.PropertySet)) 739 assert type(sources) == types.ListType 740 self.sources_ = sources 741 self.action_name_ = action_name 742 if not prop_set: 743 prop_set = property_set.empty() 744 self.properties_ = prop_set 745 if not all(isinstance(v, VirtualTarget) for v in prop_set.get('implicit-dependency')): 746 import pdb 747 pdb.set_trace() 748 749 self.manager_ = manager 750 self.engine_ = self.manager_.engine () 751 self.targets_ = [] 752 753 # Indicates whether this has been actualized or not. 754 self.actualized_ = False 755 756 self.dependency_only_sources_ = [] 757 self.actual_sources_ = [] 758 759 760 def add_targets (self, targets): 761 self.targets_ += targets 762 763 764 def replace_targets (old_targets, new_targets): 765 self.targets_ = [t for t in targets if not t in old_targets] + new_targets 766 767 def targets (self): 768 return self.targets_ 769 770 def sources (self): 771 return self.sources_ 772 773 def action_name (self): 774 return self.action_name_ 775 776 def properties (self): 777 return self.properties_ 778 779 def actualize (self): 780 """ Generates actual build instructions. 781 """ 782 if self.actualized_: 783 return 784 785 self.actualized_ = True 786 787 ps = self.properties () 788 properties = self.adjust_properties (ps) 789 790 791 actual_targets = [] 792 793 for i in self.targets (): 794 actual_targets.append (i.actualize ()) 795 796 self.actualize_sources (self.sources (), properties) 797 798 self.engine_.add_dependency (actual_targets, self.actual_sources_ + self.dependency_only_sources_) 799 800 # FIXME: check the comment below. Was self.action_name_ [1] 801 # Action name can include additional rule arguments, which should not 802 # be passed to 'set-target-variables'. 803 # FIXME: breaking circular dependency 804 import toolset 805 toolset.set_target_variables (self.manager_, self.action_name_, actual_targets, properties) 806 807 engine = self.manager_.engine () 808 809 # FIXME: this is supposed to help --out-xml option, but we don't 810 # implement that now, and anyway, we should handle it in Python, 811 # not but putting variables on bjam-level targets. 812 bjam.call("set-target-variable", actual_targets, ".action", repr(self)) 813 814 self.manager_.engine ().set_update_action (self.action_name_, actual_targets, self.actual_sources_, 815 properties) 816 817 # Since we set up creating action here, we also set up 818 # action for cleaning up 819 self.manager_.engine ().set_update_action ('common.Clean', 'clean-all', 820 actual_targets) 821 822 return actual_targets 823 824 def actualize_source_type (self, sources, prop_set): 825 """ Helper for 'actualize_sources'. 826 For each passed source, actualizes it with the appropriate scanner. 827 Returns the actualized virtual targets. 828 """ 829 result = [] 830 for i in sources: 831 scanner = None 832 833# FIXME: what's this? 834# if isinstance (i, str): 835# i = self.manager_.get_object (i) 836 837 if i.type (): 838 scanner = b2.build.type.get_scanner (i.type (), prop_set) 839 840 r = i.actualize (scanner) 841 result.append (r) 842 843 return result 844 845 def actualize_sources (self, sources, prop_set): 846 """ Creates actual jam targets for sources. Initializes two member 847 variables: 848 'self.actual_sources_' -- sources which are passed to updating action 849 'self.dependency_only_sources_' -- sources which are made dependencies, but 850 are not used otherwise. 851 852 New values will be *appended* to the variables. They may be non-empty, 853 if caller wants it. 854 """ 855 dependencies = self.properties_.get ('<dependency>') 856 857 self.dependency_only_sources_ += self.actualize_source_type (dependencies, prop_set) 858 self.actual_sources_ += self.actualize_source_type (sources, prop_set) 859 860 # This is used to help bjam find dependencies in generated headers 861 # in other main targets. 862 # Say: 863 # 864 # make a.h : ....... ; 865 # exe hello : hello.cpp : <implicit-dependency>a.h ; 866 # 867 # However, for bjam to find the dependency the generated target must 868 # be actualized (i.e. have the jam target). In the above case, 869 # if we're building just hello ("bjam hello"), 'a.h' won't be 870 # actualized unless we do it here. 871 implicit = self.properties_.get("<implicit-dependency>") 872 873 for i in implicit: 874 i.actualize() 875 876 def adjust_properties (self, prop_set): 877 """ Determines real properties when trying building with 'properties'. 878 This is last chance to fix properties, for example to adjust includes 879 to get generated headers correctly. Default implementation returns 880 its argument. 881 """ 882 return prop_set 883 884 885class NullAction (Action): 886 """ Action class which does nothing --- it produces the targets with 887 specific properties out of nowhere. It's needed to distinguish virtual 888 targets with different properties that are known to exist, and have no 889 actions which create them. 890 """ 891 def __init__ (self, manager, prop_set): 892 Action.__init__ (self, manager, [], None, prop_set) 893 894 def actualize (self): 895 if not self.actualized_: 896 self.actualized_ = True 897 898 for i in self.targets (): 899 i.actualize () 900 901class NonScanningAction(Action): 902 """Class which acts exactly like 'action', except that the sources 903 are not scanned for dependencies.""" 904 905 def __init__(self, sources, action_name, property_set): 906 #FIXME: should the manager parameter of Action.__init__ 907 #be removed? -- Steven Watanabe 908 Action.__init__(self, b2.manager.get_manager(), sources, action_name, property_set) 909 910 def actualize_source_type(self, sources, property_set): 911 912 result = [] 913 for s in sources: 914 result.append(s.actualize()) 915 return result 916 917def traverse (target, include_roots = False, include_sources = False): 918 """ Traverses the dependency graph of 'target' and return all targets that will 919 be created before this one is created. If root of some dependency graph is 920 found during traversal, it's either included or not, dependencing of the 921 value of 'include_roots'. In either case, sources of root are not traversed. 922 """ 923 result = [] 924 925 if target.action (): 926 action = target.action () 927 928 # This includes 'target' as well 929 result += action.targets () 930 931 for t in action.sources (): 932 933 # FIXME: 934 # TODO: see comment in Manager.register_object () 935 #if not isinstance (t, VirtualTarget): 936 # t = target.project_.manager_.get_object (t) 937 938 if not t.root (): 939 result += traverse (t, include_roots, include_sources) 940 941 elif include_roots: 942 result.append (t) 943 944 elif include_sources: 945 result.append (target) 946 947 return result 948 949def clone_action (action, new_project, new_action_name, new_properties): 950 """Takes an 'action' instances and creates new instance of it 951 and all produced target. The rule-name and properties are set 952 to 'new-rule-name' and 'new-properties', if those are specified. 953 Returns the cloned action.""" 954 955 if not new_action_name: 956 new_action_name = action.action_name() 957 958 if not new_properties: 959 new_properties = action.properties() 960 961 cloned_action = action.__class__(action.manager_, action.sources(), new_action_name, 962 new_properties) 963 964 cloned_targets = [] 965 for target in action.targets(): 966 967 n = target.name() 968 # Don't modify the name of the produced targets. Strip the directory f 969 cloned_target = FileTarget(n, target.type(), new_project, 970 cloned_action, exact=True) 971 972 d = target.dependencies() 973 if d: 974 cloned_target.depends(d) 975 cloned_target.root(target.root()) 976 cloned_target.creating_subvariant(target.creating_subvariant()) 977 978 cloned_targets.append(cloned_target) 979 980 return cloned_action 981 982class Subvariant: 983 984 def __init__ (self, main_target, prop_set, sources, build_properties, sources_usage_requirements, created_targets): 985 """ 986 main_target: The instance of MainTarget class 987 prop_set: Properties requested for this target 988 sources: 989 build_properties: Actually used properties 990 sources_usage_requirements: Properties propagated from sources 991 created_targets: Top-level created targets 992 """ 993 self.main_target_ = main_target 994 self.properties_ = prop_set 995 self.sources_ = sources 996 self.build_properties_ = build_properties 997 self.sources_usage_requirements_ = sources_usage_requirements 998 self.created_targets_ = created_targets 999 1000 self.usage_requirements_ = None 1001 1002 # Pre-compose the list of other dependency graphs, on which this one 1003 # depends 1004 deps = build_properties.get('<implicit-dependency>') 1005 1006 self.other_dg_ = [] 1007 for d in deps: 1008 self.other_dg_.append(d.creating_subvariant ()) 1009 1010 self.other_dg_ = unique (self.other_dg_) 1011 1012 self.implicit_includes_cache_ = {} 1013 self.target_directories_ = None 1014 1015 def main_target (self): 1016 return self.main_target_ 1017 1018 def created_targets (self): 1019 return self.created_targets_ 1020 1021 def requested_properties (self): 1022 return self.properties_ 1023 1024 def build_properties (self): 1025 return self.build_properties_ 1026 1027 def sources_usage_requirements (self): 1028 return self.sources_usage_requirements_ 1029 1030 def set_usage_requirements (self, usage_requirements): 1031 self.usage_requirements_ = usage_requirements 1032 1033 def usage_requirements (self): 1034 return self.usage_requirements_ 1035 1036 def all_referenced_targets(self, result): 1037 """Returns all targets referenced by this subvariant, 1038 either directly or indirectly, and either as sources, 1039 or as dependency properties. Targets referred with 1040 dependency property are returned a properties, not targets.""" 1041 1042 # Find directly referenced targets. 1043 deps = self.build_properties().dependency() 1044 all_targets = self.sources_ + deps 1045 1046 # Find other subvariants. 1047 r = [] 1048 for e in all_targets: 1049 if not e in result: 1050 result.add(e) 1051 if isinstance(e, property.Property): 1052 t = e.value() 1053 else: 1054 t = e 1055 1056 # FIXME: how can this be? 1057 cs = t.creating_subvariant() 1058 if cs: 1059 r.append(cs) 1060 r = unique(r) 1061 for s in r: 1062 if s != self: 1063 s.all_referenced_targets(result) 1064 1065 1066 def implicit_includes (self, feature, target_type): 1067 """ Returns the properties which specify implicit include paths to 1068 generated headers. This traverses all targets in this subvariant, 1069 and subvariants referred by <implcit-dependecy>properties. 1070 For all targets which are of type 'target-type' (or for all targets, 1071 if 'target_type' is not specified), the result will contain 1072 <$(feature)>path-to-that-target. 1073 """ 1074 1075 if not target_type: 1076 key = feature 1077 else: 1078 key = feature + "-" + target_type 1079 1080 1081 result = self.implicit_includes_cache_.get(key) 1082 if not result: 1083 target_paths = self.all_target_directories(target_type) 1084 target_paths = unique(target_paths) 1085 result = ["<%s>%s" % (feature, p) for p in target_paths] 1086 self.implicit_includes_cache_[key] = result 1087 1088 return result 1089 1090 def all_target_directories(self, target_type = None): 1091 # TODO: does not appear to use target_type in deciding 1092 # if we've computed this already. 1093 if not self.target_directories_: 1094 self.target_directories_ = self.compute_target_directories(target_type) 1095 return self.target_directories_ 1096 1097 def compute_target_directories(self, target_type=None): 1098 result = [] 1099 for t in self.created_targets(): 1100 if not target_type or b2.build.type.is_derived(t.type(), target_type): 1101 result.append(t.path()) 1102 1103 for d in self.other_dg_: 1104 result.extend(d.all_target_directories(target_type)) 1105 1106 result = unique(result) 1107 return result 1108