1# Copyright 2003 Dave Abrahams 2# Copyright 2005, 2006 Rene Rivera 3# Copyright 2002, 2003, 2004, 2005, 2006 Vladimir Prus 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or copy at 6# http://www.boost.org/LICENSE_1_0.txt) 7 8# Implements virtual targets, which correspond to actual files created during a 9# build, but are not yet targets in Jam sense. They are needed, for example, 10# when searching for possible transformation sequences, when it is not yet known 11# whether a particular target should be created at all. 12# 13# +--------------------------+ 14# | virtual-target | 15# +==========================+ 16# | actualize | 17# +--------------------------+ 18# | actualize-action() = 0 | 19# | actualize-location() = 0 | 20# +----------------+---------+ 21# | 22# ^ 23# / \ 24# +-+-+ 25# | 26# +---------------------+ +-------+--------------+ 27# | action | | abstract-file-target | 28# +=====================| * +======================+ 29# | action-name | +--+ action | 30# | properties | | +----------------------+ 31# +---------------------+--+ | actualize-action() | 32# | actualize() |0..1 +-----------+----------+ 33# | path() | | 34# | adjust-properties() | sources | 35# | actualize-sources() | targets | 36# +------+--------------+ ^ 37# | / \ 38# ^ +-+-+ 39# / \ | 40# +-+-+ +-------------+-------------+ 41# | | | 42# | +------+---------------+ +--------+-------------+ 43# | | file-target | | searched-lib-target | 44# | +======================+ +======================+ 45# | | actualize-location() | | actualize-location() | 46# | +----------------------+ +----------------------+ 47# | 48# +-+------------------------------+ 49# | | 50# +----+----------------+ +---------+-----------+ 51# | compile-action | | link-action | 52# +=====================+ +=====================+ 53# | adjust-properties() | | adjust-properties() | 54# +---------------------+ | actualize-sources() | 55# +---------------------+ 56# 57# The 'compile-action' and 'link-action' classes are not defined here but in 58# builtin.jam modules. They are shown in the diagram to give the big picture. 59 60import "class" : new ; 61import path ; 62import sequence ; 63import set ; 64import type ; 65import utility ; 66 67 68# Models a potential target. It can be converted into a Jam target and used in 69# building, if needed. However, it can be also dropped, which allows us to 70# search for different transformations and select only one. 71# 72class virtual-target 73{ 74 import scanner ; 75 import sequence ; 76 import utility ; 77 import virtual-target ; 78 79 rule __init__ ( 80 name # Target/project name. 81 : project # Project to which this target belongs. 82 ) 83 { 84 self.name = $(name) ; 85 self.project = $(project) ; 86 self.dependencies = ; 87 } 88 89 # Name of this target. 90 # 91 rule name ( ) 92 { 93 return $(self.name) ; 94 } 95 96 # Project of this target. 97 # 98 rule project ( ) 99 { 100 return $(self.project) ; 101 } 102 103 # Adds additional 'virtual-target' instances this one depends on. 104 # 105 rule depends ( d + ) 106 { 107 self.dependencies = [ sequence.merge $(self.dependencies) : 108 [ sequence.insertion-sort $(d) ] ] ; 109 } 110 111 rule dependencies ( ) 112 { 113 return $(self.dependencies) ; 114 } 115 116 rule always ( ) 117 { 118 .always = 1 ; 119 } 120 121 # Generates all the actual targets and sets up build actions for this 122 # target. 123 # 124 # If 'scanner' is specified, creates an additional target with the same 125 # location as the actual target, which will depend on the actual target and 126 # be associated with a 'scanner'. That additional target is returned. See 127 # the docs (#dependency_scanning) for rationale. Target must correspond to a 128 # file if 'scanner' is specified. 129 # 130 # If scanner is not specified then the actual target is returned. 131 # 132 rule actualize ( scanner ? ) 133 { 134 local actual-name = [ actualize-no-scanner ] ; 135 136 if $(.always) 137 { 138 ALWAYS $(actual-name) ; 139 } 140 141 if ! $(scanner) 142 { 143 return $(actual-name) ; 144 } 145 else 146 { 147 # Add the scanner instance to the grist for name. 148 local g = [ sequence.join [ utility.ungrist $(actual-name:G) ] 149 $(scanner) : - ] ; 150 local name = $(actual-name:G=$(g)) ; 151 152 if ! $(self.made.$(scanner)) 153 { 154 self.made.$(scanner) = true ; 155 actualize-location $(name) ; 156 scanner.install $(scanner) : $(name) ; 157 } 158 return $(name) ; 159 } 160 } 161 162# private: (overridables) 163 164 # Sets up build actions for 'target'. Should call appropriate rules and set 165 # target variables. 166 # 167 rule actualize-action ( target ) 168 { 169 import errors : error : errors.error ; 170 errors.error "method should be defined in derived classes" ; 171 } 172 173 # Sets up variables on 'target' which specify its location. 174 # 175 rule actualize-location ( target ) 176 { 177 import errors : error : errors.error ; 178 errors.error "method should be defined in derived classes" ; 179 } 180 181 # If the target is a generated one, returns the path where it will be 182 # generated. Otherwise, returns an empty list. 183 # 184 rule path ( ) 185 { 186 import errors : error : errors.error ; 187 errors.error "method should be defined in derived classes" ; 188 } 189 190 # Returns the actual target name to be used in case when no scanner is 191 # involved. 192 # 193 rule actual-name ( ) 194 { 195 import errors : error : errors.error ; 196 errors.error "method should be defined in derived classes" ; 197 } 198 199# implementation 200 rule actualize-no-scanner ( ) 201 { 202 # In fact, we just need to merge virtual-target with 203 # abstract-file-target as the latter is the only class derived from the 204 # former. But that has been left for later. 205 206 import errors : error : errors.error ; 207 errors.error "method should be defined in derived classes" ; 208 } 209} 210 211 212# Target corresponding to a file. The exact mapping for file is not yet 213# specified in this class. (TODO: Actually, the class name could be better...) 214# 215# May be a source file (when no action is specified) or a derived file 216# (otherwise). 217# 218# The target's grist is a concatenation of its project's location, action 219# properties (for derived targets) and, optionally, value identifying the main 220# target. 221# 222class abstract-file-target : virtual-target 223{ 224 import project ; 225 import regex ; 226 import sequence ; 227 import path ; 228 import type ; 229 import property-set ; 230 import indirect ; 231 232 rule __init__ ( 233 name # Target's name. 234 exact ? # If non-empty, the name is exactly the name created file 235 # should have. Otherwise, the '__init__' method will add a 236 # suffix obtained from 'type' by calling 237 # 'type.generated-target-suffix'. 238 : type ? # Target's type. 239 : project 240 : action ? 241 ) 242 { 243 virtual-target.__init__ $(name) : $(project) ; 244 245 self.type = $(type) ; 246 self.action = $(action) ; 247 if $(action) 248 { 249 $(action).add-targets $(__name__) ; 250 251 if $(self.type) && ! $(exact) 252 { 253 _adjust-name $(name) ; 254 } 255 } 256 } 257 258 rule type ( ) 259 { 260 return $(self.type) ; 261 } 262 263 # Sets the path. When generating target name, it will override any path 264 # computation from properties. 265 # 266 rule set-path ( path ) 267 { 268 self.path = [ path.native $(path) ] ; 269 } 270 271 # Returns the currently set action. 272 # 273 rule action ( ) 274 { 275 return $(self.action) ; 276 } 277 278 # Sets/gets the 'root' flag. Target is root if it directly corresponds to 279 # some variant of a main target. 280 # 281 rule root ( set ? ) 282 { 283 if $(set) 284 { 285 self.root = true ; 286 } 287 return $(self.root) ; 288 } 289 290 # Gets or sets the subvariant which created this target. Subvariant is set 291 # when target is brought into existance and is never changed after that. In 292 # particular, if a target is shared by multiple subvariants, only the first 293 # one is stored. 294 # 295 rule creating-subvariant ( s ? # If specified, specifies the value to set, 296 # which should be a 'subvariant' class 297 # instance. 298 ) 299 { 300 if $(s) && ! $(self.creating-subvariant) 301 { 302 self.creating-subvariant = $(s) ; 303 } 304 return $(self.creating-subvariant) ; 305 } 306 307 rule actualize-action ( target ) 308 { 309 if $(self.action) 310 { 311 $(self.action).actualize ; 312 } 313 } 314 315 # Return a human-readable representation of this target. If this target has 316 # an action, that is: 317 # 318 # { <action-name>-<self.name>.<self.type> <action-sources>... } 319 # 320 # otherwise, it is: 321 # 322 # { <self.name>.<self.type> } 323 # 324 rule str ( ) 325 { 326 local action = [ action ] ; 327 local name-dot-type = [ sequence.join $(self.name) "." $(self.type) ] ; 328 329 if $(action) 330 { 331 local sources = [ $(action).sources ] ; 332 local action-name = [ $(action).action-name ] ; 333 334 local ss ; 335 for local s in $(sources) 336 { 337 ss += [ $(s).str ] ; 338 } 339 340 return "{" $(action-name)-$(name-dot-type) $(ss) "}" ; 341 } 342 else 343 { 344 return "{" $(name-dot-type) "}" ; 345 } 346 } 347 348 rule less ( a ) 349 { 350 if [ str ] < [ $(a).str ] 351 { 352 return true ; 353 } 354 } 355 356 rule equal ( a ) 357 { 358 if [ str ] = [ $(a).str ] 359 { 360 return true ; 361 } 362 } 363 364# private: 365 rule actual-name ( ) 366 { 367 if ! $(self.actual-name) 368 { 369 local grist = [ grist ] ; 370 local basename = [ path.native $(self.name) ] ; 371 self.actual-name = <$(grist)>$(basename) ; 372 } 373 return $(self.actual-name) ; 374 } 375 376 # Helper to 'actual-name', above. Computes a unique prefix used to 377 # distinguish this target from other targets with the same name creating 378 # different files. 379 # 380 rule grist ( ) 381 { 382 # Depending on target, there may be different approaches to generating 383 # unique prefixes. We generate prefixes in the form: 384 # <one letter approach code> <the actual prefix> 385 local path = [ path ] ; 386 if $(path) 387 { 388 # The target will be generated to a known path. Just use the path 389 # for identification, since path is as unique as it can get. 390 return p$(path) ; 391 } 392 else 393 { 394 # File is either source, which will be searched for, or is not a 395 # file at all. Use the location of project for distinguishing. 396 local project-location = [ $(self.project).get location ] ; 397 local location-grist = [ sequence.join [ regex.split 398 $(project-location) "/" ] : "!" ] ; 399 400 if $(self.action) 401 { 402 local ps = [ $(self.action).properties ] ; 403 local property-grist = [ $(ps).as-path ] ; 404 # 'property-grist' can be empty when 'ps' is an empty property 405 # set. 406 if $(property-grist) 407 { 408 location-grist = $(location-grist)/$(property-grist) ; 409 } 410 } 411 412 return l$(location-grist) ; 413 } 414 } 415 416 # Given the target name specified in constructor, returns the name which 417 # should be really used, by looking at the <tag> properties. Tag properties 418 # need to be specified as <tag>@rule-name. This makes Boost Build call the 419 # specified rule with the target name, type and properties to get the new 420 # name. If no <tag> property is specified or the rule specified by <tag> 421 # returns nothing, returns the result of calling 422 # virtual-target.add-prefix-and-suffix. 423 # 424 rule _adjust-name ( specified-name ) 425 { 426 local ps ; 427 if $(self.action) 428 { 429 ps = [ $(self.action).properties ] ; 430 } 431 else 432 { 433 ps = [ property-set.empty ] ; 434 } 435 436 # Add this target object for use in getting additional information 437 # when tagging. 438 ps = [ property-set.create [ $(ps).raw ] <target>$(__name__) ] ; 439 440 local tag = [ $(ps).get <tag> ] ; 441 442 if $(tag) 443 { 444 local rule-name = [ MATCH ^@(.*) : $(tag) ] ; 445 if $(rule-name) 446 { 447 if $(tag[2]) 448 { 449 import errors : error : errors.error ; 450 errors.error <tag>@rulename is present but is not the only 451 <tag> feature. ; 452 } 453 454 self.name = [ indirect.call $(rule-name) $(specified-name) 455 : $(self.type) : $(ps) ] ; 456 } 457 else 458 { 459 import errors : error : errors.error ; 460 errors.error <tag> property value must be '@rule-name'. ; 461 } 462 } 463 464 # If there is no tag or the tag rule returned nothing. 465 if ! $(tag) || ! $(self.name) 466 { 467 self.name = [ virtual-target.add-prefix-and-suffix $(specified-name) 468 : $(self.type) : $(ps) ] ; 469 } 470 } 471 472 rule actualize-no-scanner ( ) 473 { 474 local name = [ actual-name ] ; 475 476 # Do anything only on the first invocation. 477 if ! $(self.made-no-scanner) 478 { 479 self.made-no-scanner = true ; 480 481 if $(self.action) 482 { 483 # For non-derived target, we do not care if there are several 484 # virtual targets that refer to the same name. One case when 485 # this is unavoidable is when the file name is main.cpp and two 486 # targets have types CPP (for compiling) and MOCCABLE_CPP (for 487 # conversion to H via Qt tools). 488 virtual-target.register-actual-name $(name) : $(__name__) ; 489 } 490 491 for local i in $(self.dependencies) 492 { 493 DEPENDS $(name) : [ $(i).actualize ] ; 494 } 495 496 actualize-location $(name) ; 497 actualize-action $(name) ; 498 } 499 return $(name) ; 500 } 501} 502 503 504# Appends the suffix appropriate to 'type/property-set' combination to the 505# specified name and returns the result. 506# 507rule add-prefix-and-suffix ( specified-name : type ? : property-set ) 508{ 509 local suffix = [ type.generated-target-suffix $(type) : $(property-set) ] ; 510 511 # Handle suffixes for which no leading dot is desired. Those are specified 512 # by enclosing them in <...>. Needed by python so it can create "_d.so" 513 # extensions, for example. 514 if $(suffix:G) 515 { 516 suffix = [ utility.ungrist $(suffix) ] ; 517 } 518 else 519 { 520 suffix = .$(suffix) ; 521 } 522 523 local prefix = [ type.generated-target-prefix $(type) : $(property-set) ] ; 524 525 if [ MATCH ^($(prefix)) : $(specified-name) ] 526 { 527 prefix = ; 528 } 529 return $(prefix:E="")$(specified-name)$(suffix:E="") ; 530} 531 532 533# File targets with explicitly known location. 534# 535# The file path is determined as 536# * Value passed to the 'set-path' method, if any. 537# * For derived files, project's build dir, joined with components that 538# describe action properties. If free properties are not equal to the 539# project's reference properties an element with the name of the main 540# target is added. 541# * For source files, project's source dir. 542# 543# The file suffix is determined as: 544# * The value passed to the 'suffix' method, if any. 545# * The suffix corresponding to the target's type. 546# 547class file-target : abstract-file-target 548{ 549 import "class" : new ; 550 import common ; 551 552 rule __init__ ( 553 name exact ? 554 : type ? # Optional type for this target. 555 : project 556 : action ? 557 : path ? 558 ) 559 { 560 abstract-file-target.__init__ $(name) $(exact) : $(type) : $(project) : 561 $(action) ; 562 563 self.path = $(path) ; 564 } 565 566 rule clone-with-different-type ( new-type ) 567 { 568 return [ new file-target $(self.name) exact : $(new-type) : 569 $(self.project) : $(self.action) : $(self.path) ] ; 570 } 571 572 rule actualize-location ( target ) 573 { 574 # Scanner targets are always bound to already existing files in already 575 # existing folder. They need to be marked as depending on their base 576 # target (i.e. the target being scanned) but, unlike regular 577 # dependencies set up by the DEPENDS rule, they must not depend on any 578 # targets already marked as included by the base target. Otherwise such 579 # an included file being newer than the file being scanned would cause 580 # the scanner target to be updated, further causing any target depending 581 # on that scanner target to be rebuilt. This is the exact relationship 582 # as set up by Boost Jam's SEARCH binding method (needed to support 583 # searching for generated targets) so we want to bind scanner targets 584 # using this method instead of explicitly specifying their location 585 # using LOCATE. 586 # 587 # FIXME: We recognize scanner targets by their given name being 588 # different from this target's actual name. This is a hack and should be 589 # cleaned up by reorganizing who knows about scanners in the 590 # virtual-target/abstract-file-target/file-target/notfile-target/ 591 # searched-lib-target/... class hierarchy. 592 local is-scanner-target ; 593 if $(target) != [ actual-name ] 594 { 595 is-scanner-target = true ; 596 } 597 598 if $(self.action) && ! $(is-scanner-target) 599 { 600 # This is a derived file. 601 local path = [ path ] ; 602 LOCATE on $(target) = $(path) ; 603 604 # Make sure the path exists. 605 DEPENDS $(target) : $(path) ; 606 common.MkDir $(path) ; 607 608 # It is possible that the target name includes a directory too, for 609 # example when installing headers. Create that directory. 610 if $(target:D) 611 { 612 local d = $(target:D) ; 613 d = $(d:R=$(path)) ; 614 DEPENDS $(target) : $(d) ; 615 common.MkDir $(d) ; 616 } 617 618 # For a real file target, we create a fake target depending on the 619 # real target. This allows us to run 620 # 621 # b2 hello.o 622 # 623 # without trying to guess the name of the real target. Note that the 624 # target has no directory name and uses a special <e> grist. 625 # 626 # First, that means that "b2 hello.o" will build all known hello.o 627 # targets. Second, the <e> grist makes sure this target will not be 628 # confused with other targets, for example, if we have subdir 'test' 629 # with target 'test' in it that includes a 'test.o' file, then the 630 # target for directory will be just 'test' the target for test.o 631 # will be <ptest/bin/gcc/debug>test.o and the target we create below 632 # will be <e>test.o 633 DEPENDS $(target:G=e) : $(target) ; 634 # Allow b2 <path-to-file>/<file> to work. This will not catch all 635 # possible ways to refer to the path (relative/absolute, extra ".", 636 # various "..", but should help in obvious cases. 637 DEPENDS $(target:G=e:R=$(path)) : $(target) ; 638 } 639 else 640 { 641 SEARCH on $(target) = [ path.native $(self.path) ] ; 642 } 643 } 644 645 # Returns the directory for this target. 646 # 647 rule path ( ) 648 { 649 if ! $(self.path) 650 { 651 if $(self.action) 652 { 653 local p = [ $(self.action).properties ] ; 654 local path,relative-to-build-dir = [ $(p).target-path ] ; 655 local path = $(path,relative-to-build-dir[1]) ; 656 local relative-to-build-dir = $(path,relative-to-build-dir[2]) ; 657 658 if $(relative-to-build-dir) 659 { 660 path = [ path.join [ $(self.project).build-dir ] $(path) ] ; 661 } 662 663 self.path = [ path.native $(path) ] ; 664 } 665 } 666 return $(self.path) ; 667 } 668} 669 670 671class notfile-target : abstract-file-target 672{ 673 rule __init__ ( name : project : action ? ) 674 { 675 abstract-file-target.__init__ $(name) : : $(project) : $(action) ; 676 } 677 678 # Returns nothing to indicate that the target's path is not known. 679 # 680 rule path ( ) 681 { 682 return ; 683 } 684 685 rule actualize-location ( target ) 686 { 687 NOTFILE $(target) ; 688 ALWAYS $(target) ; 689 # TEMPORARY $(target) ; 690 NOUPDATE $(target) ; 691 } 692} 693 694 695# Class representing an action. Both 'targets' and 'sources' should list 696# instances of 'virtual-target'. Action name should name a rule with this 697# prototype: 698# rule action-name ( targets + : sources * : properties * ) 699# Targets and sources are passed as actual Jam targets. The rule may not 700# establish additional dependency relationships. 701# 702class action 703{ 704 import "class" ; 705 import indirect ; 706 import path ; 707 import property-set ; 708 import set : difference ; 709 import toolset ; 710 import type ; 711 712 rule __init__ ( sources * : action-name + : property-set ? ) 713 { 714 self.sources = $(sources) ; 715 716 self.action-name = [ indirect.make-qualified $(action-name) ] ; 717 718 if ! $(property-set) 719 { 720 property-set = [ property-set.empty ] ; 721 } 722 723 if ! [ class.is-instance $(property-set) ] 724 { 725 import errors : error : errors.error ; 726 errors.error "Property set instance required" ; 727 } 728 729 self.properties = $(property-set) ; 730 } 731 732 rule add-targets ( targets * ) 733 { 734 self.targets += $(targets) ; 735 } 736 737 rule replace-targets ( old-targets * : new-targets * ) 738 { 739 self.targets = [ set.difference $(self.targets) : $(old-targets) ] ; 740 self.targets += $(new-targets) ; 741 } 742 743 rule targets ( ) 744 { 745 return $(self.targets) ; 746 } 747 748 rule sources ( ) 749 { 750 return $(self.sources) ; 751 } 752 753 rule action-name ( ) 754 { 755 return $(self.action-name) ; 756 } 757 758 rule properties ( ) 759 { 760 return $(self.properties) ; 761 } 762 763 # Generates actual build instructions. 764 # 765 rule actualize ( ) 766 { 767 if ! $(self.actualized) 768 { 769 self.actualized = true ; 770 771 local ps = [ properties ] ; 772 local properties = [ adjust-properties $(ps) ] ; 773 774 local actual-targets ; 775 for local i in [ targets ] 776 { 777 actual-targets += [ $(i).actualize ] ; 778 } 779 780 actualize-sources [ sources ] : $(properties) ; 781 782 DEPENDS $(actual-targets) : $(self.actual-sources) 783 $(self.dependency-only-sources) ; 784 785 # Action name can include additional rule arguments, which should 786 # not be passed to 'set-target-variables'. 787 toolset.set-target-variables 788 [ indirect.get-rule $(self.action-name[1]) ] $(actual-targets) 789 : $(properties) ; 790 791 # Reflect ourselves in a variable for the target. This allows 792 # looking up additional info for the action given the raw target. 793 # For example to debug or output action information from action 794 # rules. 795 .action on $(actual-targets) = $(__name__) ; 796 797 indirect.call $(self.action-name) $(actual-targets) 798 : $(self.actual-sources) : [ $(properties).raw ] ; 799 800 # Since we set up the creating action here, we set up the action for 801 # cleaning up as well. 802 common.Clean clean-all : $(actual-targets) ; 803 } 804 } 805 806 # Helper for 'actualize-sources'. For each passed source, actualizes it with 807 # the appropriate scanner. Returns the actualized virtual targets. 808 # 809 rule actualize-source-type ( sources * : property-set ) 810 { 811 local result = ; 812 for local i in $(sources) 813 { 814 local scanner ; 815 if [ $(i).type ] 816 { 817 scanner = [ type.get-scanner [ $(i).type ] : $(property-set) ] ; 818 } 819 result += [ $(i).actualize $(scanner) ] ; 820 } 821 return $(result) ; 822 } 823 824 # Creates actual Jam targets for sources. Initializes the following member 825 # variables: 826 # 'self.actual-sources' -- sources passed to the updating action. 827 # 'self.dependency-only-sources' -- sources marked as dependencies, but 828 # are not used otherwise. 829 # 830 # New values will be *appended* to the variables. They may be non-empty if 831 # caller wants it. 832 # 833 rule actualize-sources ( sources * : property-set ) 834 { 835 local dependencies = [ $(self.properties).get <dependency> ] ; 836 837 self.dependency-only-sources += 838 [ actualize-source-type $(dependencies) : $(property-set) ] ; 839 self.actual-sources += 840 [ actualize-source-type $(sources) : $(property-set) ] ; 841 842 # This is used to help b2 find dependencies in generated headers and 843 # other main targets, e.g. in: 844 # 845 # make a.h : ....... ; 846 # exe hello : hello.cpp : <implicit-dependency>a.h ; 847 # 848 # For b2 to find the dependency the generated target must be 849 # actualized (i.e. have its Jam target constructed). In the above case, 850 # if we are building just hello ("b2 hello"), 'a.h' will not be 851 # actualized unless we do it here. 852 local implicit = [ $(self.properties).get <implicit-dependency> ] ; 853 for local i in $(implicit) 854 { 855 $(i:G=).actualize ; 856 } 857 } 858 859 # Determines real properties when trying to build with 'properties'. This is 860 # the last chance to fix properties, for example to adjust includes to get 861 # generated headers correctly. Default implementation simply returns its 862 # argument. 863 # 864 rule adjust-properties ( property-set ) 865 { 866 return $(property-set) ; 867 } 868} 869 870 871# Action class which does nothing --- it produces the targets with specific 872# properties out of nowhere. It is needed to distinguish virtual targets with 873# different properties that are known to exist and have no actions which create 874# them. 875# 876class null-action : action 877{ 878 rule __init__ ( property-set ? ) 879 { 880 action.__init__ : .no-action : $(property-set) ; 881 } 882 883 rule actualize ( ) 884 { 885 if ! $(self.actualized) 886 { 887 self.actualized = true ; 888 for local i in [ targets ] 889 { 890 $(i).actualize ; 891 } 892 } 893 } 894} 895 896 897# Class which acts exactly like 'action', except that its sources are not 898# scanned for dependencies. 899# 900class non-scanning-action : action 901{ 902 rule __init__ ( sources * : action-name + : property-set ? ) 903 { 904 action.__init__ $(sources) : $(action-name) : $(property-set) ; 905 } 906 907 rule actualize-source-type ( sources * : property-set ) 908 { 909 local result ; 910 for local i in $(sources) 911 { 912 result += [ $(i).actualize ] ; 913 } 914 return $(result) ; 915 } 916} 917 918 919# Creates a virtual target with an appropriate name and type from 'file'. If a 920# target with that name in that project already exists, returns that already 921# created target. 922# 923# FIXME: a more correct way would be to compute the path to the file, based on 924# name and source location for the project, and use that path to determine if 925# the target has already been created. This logic should be shared with how we 926# usually find targets identified by a specific target id. It should also be 927# updated to work correctly when the file is specified using both relative and 928# absolute paths. 929# 930# TODO: passing a project with all virtual targets is starting to be annoying. 931# 932rule from-file ( file : file-loc : project ) 933{ 934 import type ; # Had to do this here to break a circular dependency. 935 936 # Check whether we already created a target corresponding to this file. 937 local path = [ path.root [ path.root $(file) $(file-loc) ] [ path.pwd ] ] ; 938 939 if $(.files.$(path)) 940 { 941 return $(.files.$(path)) ; 942 } 943 else 944 { 945 local name = [ path.make $(file) ] ; 946 local type = [ type.type $(file) ] ; 947 local result ; 948 949 result = [ new file-target $(file) : $(type) : $(project) : : 950 $(file-loc) ] ; 951 952 .files.$(path) = $(result) ; 953 return $(result) ; 954 } 955} 956 957 958# Registers a new virtual target. Checks if there is already a registered target 959# with the same name, type, project and subvariant properties as well as the 960# same sources and equal action. If such target is found it is returned and a 961# new 'target' is not registered. Otherwise, 'target' is registered and 962# returned. 963# 964rule register ( target ) 965{ 966 local signature = [ sequence.join [ $(target).path ] [ $(target).name ] : - 967 ] ; 968 969 local result ; 970 for local t in $(.cache.$(signature)) 971 { 972 local a1 = [ $(t).action ] ; 973 local a2 = [ $(target).action ] ; 974 975 if ! $(result) 976 { 977 if ! $(a1) && ! $(a2) 978 { 979 result = $(t) ; 980 } 981 else if $(a1) && $(a2) && 982 ( [ $(a1).action-name ] = [ $(a2).action-name ] ) && 983 ( [ $(a1).sources ] = [ $(a2).sources ] ) 984 { 985 local ps1 = [ $(a1).properties ] ; 986 local ps2 = [ $(a2).properties ] ; 987 local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference 988 [ $(ps1).dependency ] : [ $(ps1).incidental ] ] ; 989 local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference 990 [ $(ps2).dependency ] : [ $(ps2).incidental ] ] ; 991 if $(p1) = $(p2) 992 { 993 result = $(t) ; 994 } 995 } 996 } 997 } 998 999 if ! $(result) 1000 { 1001 .cache.$(signature) += $(target) ; 1002 result = $(target) ; 1003 } 1004 1005 .recent-targets += $(result) ; 1006 .all-targets += $(result) ; 1007 1008 return $(result) ; 1009} 1010 1011 1012# Each target returned by 'register' is added to the .recent-targets list, 1013# returned by this function. This allows us to find all virtual targets created 1014# when building a specific main target, even those constructed only as 1015# intermediate targets. 1016# 1017rule recent-targets ( ) 1018{ 1019 return $(.recent-targets) ; 1020} 1021 1022 1023rule clear-recent-targets ( ) 1024{ 1025 .recent-targets = ; 1026} 1027 1028 1029# Returns all virtual targets ever created. 1030# 1031rule all-targets ( ) 1032{ 1033 return $(.all-targets) ; 1034} 1035 1036 1037# Returns all targets from 'targets' with types equal to 'type' or derived from 1038# it. 1039# 1040rule select-by-type ( type : targets * ) 1041{ 1042 local result ; 1043 for local t in $(targets) 1044 { 1045 if [ type.is-subtype [ $(t).type ] $(type) ] 1046 { 1047 result += $(t) ; 1048 } 1049 } 1050 return $(result) ; 1051} 1052 1053 1054rule register-actual-name ( actual-name : virtual-target ) 1055{ 1056 if $(.actual.$(actual-name)) 1057 { 1058 local cs1 = [ $(.actual.$(actual-name)).creating-subvariant ] ; 1059 local cmt1-name ; 1060 if $(cs1)-is-defined 1061 { 1062 local cmt1 = [ $(cs1).main-target ] ; 1063 cmt1-name = [ $(cmt1).full-name ] ; 1064 } 1065 local cs2 = [ $(virtual-target).creating-subvariant ] ; 1066 local cmt2-name ; 1067 if $(cs2)-is-defined 1068 { 1069 local cmt2 = [ $(cs2).main-target ] ; 1070 cmt2-name = [ $(cmt2).full-name ] ; 1071 } 1072 local extra-error-information ; 1073 if ! $(cs1)-is-defined || ! $(cs2)-is-defined 1074 { 1075 extra-error-information = Encountered a virtual-target without a 1076 creating subvariant. It could be the virtual target has not been 1077 registered via the virtual-target.register rule. ; 1078 } 1079 1080 local action1 = [ $(.actual.$(actual-name)).action ] ; 1081 local action2 = [ $(virtual-target).action ] ; 1082 local properties-added ; 1083 local properties-removed ; 1084 if $(action1) && $(action2) 1085 { 1086 local p1 = [ $(action1).properties ] ; 1087 p1 = [ $(p1).raw ] ; 1088 local p2 = [ $(action2).properties ] ; 1089 p2 = [ $(p2).raw ] ; 1090 properties-removed = [ set.difference $(p1) : $(p2) ] ; 1091 properties-removed ?= "none" ; 1092 properties-added = [ set.difference $(p2) : $(p1) ] ; 1093 properties-added ?= "none" ; 1094 } 1095 import errors : user-error : errors.user-error ; 1096 errors.user-error "Name clash for '$(actual-name)'" 1097 : "" 1098 : "Tried to build the target twice, with property sets having " 1099 : "these incompatible properties:" 1100 : "" 1101 : " - " $(properties-removed) 1102 : " - " $(properties-added) 1103 : "" 1104 : "Please make sure to have consistent requirements for these " 1105 : "properties everywhere in your project, especially for install" 1106 : "targets." 1107 ; 1108 } 1109 else 1110 { 1111 .actual.$(actual-name) = $(virtual-target) ; 1112 } 1113} 1114 1115 1116# Traverses the dependency graph of 'target' and return all targets that will be 1117# created before this one is created. If the root of some dependency graph is 1118# found during traversal, it is either included or not, depending on the 1119# 'include-roots' value. In either case traversal stops at root targets, i.e. 1120# root target sources are not traversed. 1121# 1122rule traverse ( target : include-roots ? : include-sources ? ) 1123{ 1124 local result ; 1125 if [ $(target).action ] 1126 { 1127 local action = [ $(target).action ] ; 1128 # This includes the 'target' as well. 1129 result += [ $(action).targets ] ; 1130 1131 for local t in [ $(action).sources ] 1132 { 1133 if ! [ $(t).root ] 1134 { 1135 result += [ traverse $(t) : $(include-roots) : 1136 $(include-sources) ] ; 1137 } 1138 else if $(include-roots) 1139 { 1140 result += $(t) ; 1141 } 1142 } 1143 } 1144 else if $(include-sources) 1145 { 1146 result = $(target) ; 1147 } 1148 return $(result) ; 1149} 1150 1151 1152# Takes an 'action' instance and creates a new instance of it and all targets 1153# produced by the action. The rule-name and properties are set to 1154# 'new-rule-name' and 'new-properties', if those are specified. Returns the 1155# cloned action. 1156# 1157rule clone-action ( action : new-project : new-action-name ? : new-properties ? 1158 ) 1159{ 1160 if ! $(new-action-name) 1161 { 1162 new-action-name = [ $(action).action-name ] ; 1163 } 1164 if ! $(new-properties) 1165 { 1166 new-properties = [ $(action).properties ] ; 1167 } 1168 1169 local action-class = [ modules.peek $(action) : __class__ ] ; 1170 local cloned-action = [ class.new $(action-class) 1171 [ $(action).sources ] : $(new-action-name) : $(new-properties) ] ; 1172 1173 local cloned-targets ; 1174 for local target in [ $(action).targets ] 1175 { 1176 local n = [ $(target).name ] ; 1177 # Do not modify produced target names. 1178 local cloned-target = [ class.new file-target $(n) exact : 1179 [ $(target).type ] : $(new-project) : $(cloned-action) ] ; 1180 local d = [ $(target).dependencies ] ; 1181 if $(d) 1182 { 1183 $(cloned-target).depends $(d) ; 1184 } 1185 $(cloned-target).root [ $(target).root ] ; 1186 $(cloned-target).creating-subvariant [ $(target).creating-subvariant ] ; 1187 1188 cloned-targets += $(cloned-target) ; 1189 } 1190 1191 return $(cloned-action) ; 1192} 1193 1194 1195class subvariant 1196{ 1197 import sequence ; 1198 import type ; 1199 1200 rule __init__ ( main-target # The instance of main-target class. 1201 : property-set # Properties requested for this target. 1202 : sources * 1203 : build-properties # Actually used properties. 1204 : sources-usage-requirements # Properties propagated from sources. 1205 : created-targets * ) # Top-level created targets. 1206 { 1207 self.main-target = $(main-target) ; 1208 self.properties = $(property-set) ; 1209 self.sources = $(sources) ; 1210 self.build-properties = $(build-properties) ; 1211 self.sources-usage-requirements = $(sources-usage-requirements) ; 1212 self.created-targets = $(created-targets) ; 1213 1214 # Pre-compose a list of other dependency graphs this one depends on. 1215 local deps = [ $(build-properties).get <implicit-dependency> ] ; 1216 for local d in $(deps) 1217 { 1218 self.other-dg += [ $(d:G=).creating-subvariant ] ; 1219 } 1220 1221 self.other-dg = [ sequence.unique $(self.other-dg) ] ; 1222 } 1223 1224 rule main-target ( ) 1225 { 1226 return $(self.main-target) ; 1227 } 1228 1229 rule created-targets ( ) 1230 { 1231 return $(self.created-targets) ; 1232 } 1233 1234 rule requested-properties ( ) 1235 { 1236 return $(self.properties) ; 1237 } 1238 1239 rule build-properties ( ) 1240 { 1241 return $(self.build-properties) ; 1242 } 1243 1244 rule sources-usage-requirements ( ) 1245 { 1246 return $(self.sources-usage-requirements) ; 1247 } 1248 1249 rule set-usage-requirements ( usage-requirements ) 1250 { 1251 self.usage-requirements = $(usage-requirements) ; 1252 } 1253 1254 rule usage-requirements ( ) 1255 { 1256 return $(self.usage-requirements) ; 1257 } 1258 1259 # Returns all targets referenced by this subvariant, either directly or 1260 # indirectly, and either as sources, or as dependency properties. Targets 1261 # referred to using the dependency property are returned as properties, not 1262 # targets. 1263 # 1264 rule all-referenced-targets ( theset ) 1265 { 1266 # Find directly referenced targets. 1267 local deps = [ $(self.build-properties).dependency ] ; 1268 local all-targets = $(self.sources) $(deps) ; 1269 1270 # Find other subvariants. 1271 local r ; 1272 for local t in $(all-targets) 1273 { 1274 if ! [ $(theset).contains $(t) ] 1275 { 1276 $(theset).add $(t) ; 1277 r += [ $(t:G=).creating-subvariant ] ; 1278 } 1279 } 1280 r = [ sequence.unique $(r) ] ; 1281 for local s in $(r) 1282 { 1283 if $(s) != $(__name__) 1284 { 1285 $(s).all-referenced-targets $(theset) ; 1286 } 1287 } 1288 } 1289 1290 # Returns the properties specifying implicit include paths to generated 1291 # headers. This traverses all targets in this subvariant and subvariants 1292 # referred by <implicit-dependency> properties. For all targets of type 1293 # 'target-type' (or for all targets, if 'target-type' is not specified), the 1294 # result will contain <$(feature)>path-to-that-target. 1295 # 1296 rule implicit-includes ( feature : target-type ? ) 1297 { 1298 local key = ii$(feature)-$(target-type:E="") ; 1299 if ! $($(key))-is-not-empty 1300 { 1301 local target-paths = [ all-target-directories $(target-type) ] ; 1302 target-paths = [ sequence.unique $(target-paths) ] ; 1303 local result = $(target-paths:G=$(feature)) ; 1304 if ! $(result) 1305 { 1306 result = "" ; 1307 } 1308 $(key) = $(result) ; 1309 } 1310 if $($(key)) = "" 1311 { 1312 return ; 1313 } 1314 else 1315 { 1316 return $($(key)) ; 1317 } 1318 } 1319 1320 rule all-target-directories ( target-type ? ) 1321 { 1322 if ! $(self.target-directories) 1323 { 1324 compute-target-directories $(target-type) ; 1325 } 1326 return $(self.target-directories) ; 1327 } 1328 1329 rule compute-target-directories ( target-type ? ) 1330 { 1331 local result ; 1332 for local t in $(self.created-targets) 1333 { 1334 # Skip targets of the wrong type. 1335 local type = [ $(t).type ] ; 1336 if ! $(target-type) || 1337 ( $(type) && [ type.is-derived $(type) $(target-type) ] ) 1338 { 1339 result = [ sequence.merge $(result) : [ $(t).path ] ] ; 1340 } 1341 } 1342 for local d in $(self.other-dg) 1343 { 1344 result += [ $(d).all-target-directories $(target-type) ] ; 1345 } 1346 self.target-directories = $(result) ; 1347 } 1348} 1349