1# Copyright 2002. Vladimir Prus 2# Copyright 2006. Rene Rivera 3# 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# Manages 'generators' --- objects which can do transformation between different 9# target types and contain algorithm for finding transformation from sources to 10# targets. 11# 12# The main entry point to this module is generators.construct rule. It is given 13# a list of source targets, desired target type and a set of properties. It 14# starts by selecting 'viable generators', which have any chances of producing 15# the desired target type with the required properties. Generators are ranked 16# and a set of the most specific ones is selected. 17# 18# The most specific generators have their 'run' methods called, with the 19# properties and list of sources. Each one selects a target which can be 20# directly consumed, and tries to convert the remaining ones to the types it can 21# consume. This is done by recursively calling 'construct' with all consumable 22# types. 23# 24# If the generator has collected all the targets it needs, it creates targets 25# corresponding to result, and returns it. When all generators have been run, 26# results of one of them are selected and returned as a result. 27# 28# It is quite possible for 'construct' to return more targets that it was asked 29# for. For example, if it were asked to generate a target of type EXE, but the 30# only found generator produces both EXE and TDS (file with debug) information. 31# The extra target will be returned. 32# 33# Likewise, when generator tries to convert sources to consumable types, it can 34# get more targets that it was asked for. The question is what to do with extra 35# targets. B2 attempts to convert them to requested types, and attempts 36# that as early as possible. Specifically, this is done after invoking each 37# generator. TODO: An example is needed to document the rationale for trying 38# extra target conversion at that point. 39# 40# In order for the system to be able to use a specific generator instance 'when 41# needed', the instance needs to be registered with the system using 42# generators.register() or one of its related rules. Unregistered generators may 43# only be run explicitly and will not be considered by B2 when when 44# converting between given target types. 45 46import "class" : new ; 47import property-set ; 48import sequence ; 49import set ; 50import type ; 51import utility ; 52import virtual-target ; 53 54 55if "--debug-generators" in [ modules.peek : ARGV ] 56{ 57 .debug = true ; 58} 59 60 61# Updated cached viable source target type information as needed after a new 62# target type gets defined. This is needed because if a target type is a viable 63# source target type for some generator then all of the target type's derived 64# target types should automatically be considered as viable source target types 65# for the same generator as well. Does nothing if a non-derived target type is 66# passed to it. 67# 68rule update-cached-information-with-a-new-type ( type ) 69{ 70 local base-type = [ type.base $(type) ] ; 71 if $(base-type) 72 { 73 for local g in $(.vstg-cached-generators) 74 { 75 if $(base-type) in $(.vstg.$(g)) 76 { 77 .vstg.$(g) += $(type) ; 78 } 79 } 80 81 for local t in $(.vst-cached-types) 82 { 83 if $(base-type) in $(.vst.$(t)) 84 { 85 .vst.$(t) += $(type) ; 86 } 87 } 88 } 89} 90 91 92# Clears cached viable source target type information except for target types 93# and generators with all source types listed as viable. Should be called when 94# something invalidates those cached values by possibly causing some new source 95# types to become viable. 96# 97local rule invalidate-extendable-viable-source-target-type-cache ( ) 98{ 99 local generators-with-cached-source-types = $(.vstg-cached-generators) ; 100 .vstg-cached-generators = ; 101 for local g in $(generators-with-cached-source-types) 102 { 103 if $(.vstg.$(g)) = * 104 { 105 .vstg-cached-generators += $(g) ; 106 } 107 else 108 { 109 .vstg.$(g) = ; 110 } 111 } 112 113 local types-with-cached-source-types = $(.vst-cached-types) ; 114 .vst-cached-types = ; 115 for local t in $(types-with-cached-source-types) 116 { 117 if $(.vst.$(t)) = * 118 { 119 .vst-cached-types += $(t) ; 120 } 121 else 122 { 123 .vst.$(t) = ; 124 } 125 } 126} 127 128 129# Outputs a debug message if generators debugging is on. Each element of 130# 'message' is checked to see if it is a class instance. If so, instead of the 131# value, the result of 'str' call is output. 132# 133local rule generators.dout ( message * ) 134{ 135 if $(.debug) 136 { 137 ECHO [ sequence.transform utility.str : $(message) ] ; 138 } 139} 140 141 142local rule indent ( ) 143{ 144 return $(.indent:J="") ; 145} 146 147 148local rule increase-indent ( ) 149{ 150 .indent += " " ; 151} 152 153 154local rule decrease-indent ( ) 155{ 156 .indent = $(.indent[2-]) ; 157} 158 159 160# Models a generator. 161# 162class generator 163{ 164 import "class" : new ; 165 import feature ; 166 import generators : indent increase-indent decrease-indent generators.dout ; 167 import utility ; 168 import path ; 169 import property ; 170 import property-set ; 171 import sequence ; 172 import set ; 173 import toolset ; 174 import type ; 175 import virtual-target ; 176 177 EXPORT class@generator : indent increase-indent decrease-indent 178 generators.dout ; 179 180 rule __init__ ( 181 id # Identifies the generator - should be name 182 # of the rule which sets up the build 183 # actions. 184 185 composing ? # Whether generator processes each source 186 # target in turn, converting it to required 187 # types. Ordinary generators pass all 188 # sources together to the recursive 189 # generators.construct-types call. 190 191 : source-types * # Types that this generator can handle. If 192 # empty, the generator can consume anything. 193 194 : target-types-and-names + # Types the generator will create and, 195 # optionally, names for created targets. 196 # Each element should have the form 197 # type["(" name-pattern ")"], for example, 198 # obj(%_x). Generated target name will be 199 # found by replacing % with the name of 200 # source, provided an explicit name was not 201 # specified. 202 203 : requirements * 204 ) 205 { 206 self.id = $(id) ; 207 self.rule-name = $(id) ; 208 self.composing = $(composing) ; 209 self.source-types = $(source-types) ; 210 self.target-types-and-names = $(target-types-and-names) ; 211 self.requirements = $(requirements) ; 212 213 for local e in $(target-types-and-names) 214 { 215 # Create three parallel lists: one with the list of target types, 216 # and two other with prefixes and postfixes to be added to target 217 # name. We use parallel lists for prefix and postfix (as opposed to 218 # mapping), because given target type might occur several times, for 219 # example "H H(%_symbols)". 220 local m = [ MATCH "([^\\(]*)(\\((.*)%(.*)\\))?" : $(e) ] ; 221 self.target-types += $(m[1]) ; 222 self.name-prefix += $(m[3]:E="") ; 223 self.name-postfix += $(m[4]:E="") ; 224 } 225 226 for local r in [ requirements ] 227 { 228 if $(r:G=) 229 { 230 self.property-requirements += $(r) ; 231 } 232 else 233 { 234 self.feature-requirements += $(r) ; 235 } 236 } 237 238 # Note that 'transform' here, is the same as 'for_each'. 239 sequence.transform type.validate : $(self.source-types) ; 240 sequence.transform type.validate : $(self.target-types) ; 241 242 local relevant-for-generator = 243 [ sequence.transform utility.ungrist : $(requirements:G) ] ; 244 self.relevant-features = [ property-set.create <relevant>$(relevant-for-generator) ] ; 245 } 246 247 ################# End of constructor ################# 248 249 rule id ( ) 250 { 251 return $(self.id) ; 252 } 253 254 # Returns the list of target type the generator accepts. 255 # 256 rule source-types ( ) 257 { 258 return $(self.source-types) ; 259 } 260 261 # Returns the list of target types that this generator produces. It is 262 # assumed to be always the same -- i.e. it can not change depending on some 263 # provided list of sources. 264 # 265 rule target-types ( ) 266 { 267 return $(self.target-types) ; 268 } 269 270 # Returns the required properties for this generator. Properties in returned 271 # set must be present in build properties if this generator is to be used. 272 # If result has grist-only element, that build properties must include some 273 # value of that feature. 274 # 275 # XXX: remove this method? 276 # 277 rule requirements ( ) 278 { 279 return $(self.requirements) ; 280 } 281 282 rule set-rule-name ( rule-name ) 283 { 284 self.rule-name = $(rule-name) ; 285 } 286 287 rule rule-name ( ) 288 { 289 return $(self.rule-name) ; 290 } 291 292 # Returns a true value if the generator can be run with the specified 293 # properties. 294 # 295 rule match-rank ( property-set-to-match ) 296 { 297 # See if generator requirements are satisfied by 'properties'. Treat a 298 # feature name in requirements (i.e. grist-only element), as matching 299 # any value of the feature. 300 301 if [ $(property-set-to-match).contains-raw $(self.property-requirements) ] && 302 [ $(property-set-to-match).contains-features $(self.feature-requirements) ] 303 { 304 return true ; 305 } 306 else 307 { 308 return ; 309 } 310 } 311 312 # Returns another generator which differs from $(self) in 313 # - id 314 # - value to <toolset> feature in properties 315 # 316 rule clone ( new-id : new-toolset-properties + ) 317 { 318 local g = [ new $(__class__) $(new-id) $(self.composing) : 319 $(self.source-types) : $(self.target-types-and-names) : 320 # Note: this does not remove any subfeatures of <toolset> which 321 # might cause problems. 322 [ property.change $(self.requirements) : <toolset> ] 323 $(new-toolset-properties) ] ; 324 return $(g) ; 325 } 326 327 # Creates another generator that is the same as $(self), except that if 328 # 'base' is in target types of $(self), 'type' will in target types of the 329 # new generator. 330 # 331 rule clone-and-change-target-type ( base : type ) 332 { 333 local target-types ; 334 for local t in $(self.target-types-and-names) 335 { 336 local m = [ MATCH "([^\\(]*)(\\(.*\\))?" : $(t) ] ; 337 if $(m) = $(base) 338 { 339 target-types += $(type)$(m[2]:E="") ; 340 } 341 else 342 { 343 target-types += $(t) ; 344 } 345 } 346 347 local g = [ new $(__class__) $(self.id) $(self.composing) : 348 $(self.source-types) : $(target-types) : $(self.requirements) ] ; 349 if $(self.rule-name) 350 { 351 $(g).set-rule-name $(self.rule-name) ; 352 } 353 return $(g) ; 354 } 355 356 # Tries to invoke this generator on the given sources. Returns a list of 357 # generated targets (instances of 'virtual-target') and optionally a set of 358 # properties to be added to the usage-requirements for all the generated 359 # targets. Returning nothing from run indicates that the generator was 360 # unable to create the target. 361 # 362 rule run 363 ( 364 project # Project for which the targets are generated. 365 name ? # Used when determining the 'name' attribute for all 366 # generated targets. See the 'generated-targets' method. 367 : property-set # Desired properties for generated targets. 368 : sources + # Source targets. 369 ) 370 { 371 generators.dout [ indent ] " ** generator" $(self.id) ; 372 generators.dout [ indent ] " composing:" $(self.composing) ; 373 374 if ! $(self.composing) && $(sources[2]) && $(self.source-types[2]) 375 { 376 import errors : error : errors.error ; 377 errors.error "Unsupported source/source-type combination" ; 378 } 379 380 # We do not run composing generators if no name is specified. The reason 381 # is that composing generator combines several targets, which can have 382 # different names, and it cannot decide which name to give for produced 383 # target. Therefore, the name must be passed. 384 # 385 # This in effect, means that composing generators are runnable only at 386 # the top-level of a transformation graph, or if their name is passed 387 # explicitly. Thus, we dissallow composing generators in the middle. For 388 # example, the transformation CPP -> OBJ -> STATIC_LIB -> RSP -> EXE 389 # will not be allowed as the OBJ -> STATIC_LIB generator is composing. 390 if ! $(self.composing) || $(name) 391 { 392 run-really $(project) $(name) : $(property-set) : $(sources) ; 393 } 394 } 395 396 rule run-really ( project name ? : property-set : sources + ) 397 { 398 # Targets that this generator will consume directly. 399 local consumed = ; 400 # Targets that can not be consumed and will be returned as-is. 401 local bypassed = ; 402 403 if $(self.composing) 404 { 405 consumed = [ convert-multiple-sources-to-consumable-types $(project) 406 : $(property-set) : $(sources) ] ; 407 } 408 else 409 { 410 consumed = [ convert-to-consumable-types $(project) $(name) 411 : $(property-set) : $(sources) ] ; 412 } 413 414 local result ; 415 if $(consumed[2]) 416 { 417 result = [ construct-result $(consumed[2-]) : $(project) $(name) : 418 [ $(property-set).add $(consumed[1]) ] ] ; 419 } 420 421 if $(result) 422 { 423 generators.dout [ indent ] " SUCCESS: " $(result) ; 424 } 425 else 426 { 427 generators.dout [ indent ] " FAILURE" ; 428 } 429 generators.dout ; 430 if $(result) 431 { 432 # Make sure that we propagate usage-requirements up the stack. 433 return [ $(result[1]).add $(consumed[1]) ] $(result[2-]) ; 434 } 435 } 436 437 # Constructs the dependency graph to be returned by this generator. 438 # 439 rule construct-result 440 ( 441 consumed + # Already prepared list of consumable targets. 442 # Composing generators may receive multiple sources 443 # all of which will have types matching those in 444 # $(self.source-types). Non-composing generators with 445 # multiple $(self.source-types) will receive exactly 446 # len $(self.source-types) sources with types matching 447 # those in $(self.source-types). And non-composing 448 # generators with only a single source type may 449 # receive multiple sources with all of them of the 450 # type listed in $(self.source-types). 451 : project name ? 452 : property-set # Properties to be used for all actions created here. 453 ) 454 { 455 local result ; 456 457 local relevant = [ toolset.relevant $(self.rule-name) ] ; 458 relevant = [ $(relevant).add $(self.relevant-features) ] ; 459 property-set = [ $(property-set).add $(relevant) ] ; 460 461 # If this is a 1->1 transformation, apply it to all consumed targets in 462 # order. 463 if ! $(self.source-types[2]) && ! $(self.composing) 464 { 465 for local r in $(consumed) 466 { 467 result += [ generated-targets $(r) : $(property-set) : 468 $(project) $(name) ] ; 469 } 470 } 471 else if $(consumed) 472 { 473 result += [ generated-targets $(consumed) : $(property-set) : 474 $(project) $(name) ] ; 475 } 476 if $(result) 477 { 478 return $(relevant) $(result) ; 479 } 480 } 481 482 # Determine target name from fullname (maybe including path components) 483 # Place optional prefix and postfix around basename 484 # 485 rule determine-target-name ( fullname : prefix ? : postfix ? ) 486 { 487 # See if we need to add directory to the target name. 488 local dir = $(fullname:D) ; 489 local name = $(fullname:B) ; 490 491 name = $(prefix:E=)$(name) ; 492 name = $(name)$(postfix:E=) ; 493 494 if $(dir) 495 # Never append '..' to target path. 496 && ! [ MATCH .*(\\.\\.).* : $(dir) ] 497 && ! [ path.is-rooted $(dir) ] 498 { 499 # Relative path is always relative to the source directory. Retain 500 # it, so that users can have files with the same name in two 501 # different subdirectories. 502 name = $(dir)/$(name) ; 503 } 504 return $(name) ; 505 } 506 507 # Determine the name of the produced target from the names of the sources. 508 # 509 rule determine-output-name ( sources + ) 510 { 511 # The simple case if when a name of source has single dot. Then, we take 512 # the part before dot. Several dots can be caused by: 513 # - using source file like a.host.cpp, or 514 # - a type whose suffix has a dot. Say, we can type 'host_cpp' with 515 # extension 'host.cpp'. 516 # In the first case, we want to take the part up to the last dot. In the 517 # second case -- not sure, but for now take the part up to the last dot 518 # too. 519 name = [ utility.basename [ $(sources[1]).name ] ] ; 520 for local s in $(sources[2-]) 521 { 522 if [ utility.basename [ $(s).name ] ] != $(name) 523 { 524 import errors : error : errors.error ; 525 errors.error "$(self.id): source targets have different names: cannot determine target name" ; 526 } 527 } 528 return [ determine-target-name [ $(sources[1]).name ] ] ; 529 } 530 531 # Constructs targets that are created after consuming 'sources'. The result 532 # will be the list of virtual-target, which has the same length as the 533 # 'target-types' attribute and with corresponding types. 534 # 535 # When 'name' is empty, all source targets must have the same 'name' 536 # attribute value, which will be used instead of the 'name' argument. 537 # 538 # The 'name' attribute value for each generated target will be equal to the 539 # 'name' parameter if there is no name pattern for this type. Otherwise, the 540 # '%' symbol in the name pattern will be replaced with the 'name' parameter 541 # to obtain the 'name' attribute. 542 # 543 # For example, if targets types are T1 and T2 (with name pattern "%_x"), 544 # suffixes for T1 and T2 are .t1 and .t2, and source is foo.z, then created 545 # files would be "foo.t1" and "foo_x.t2". The 'name' attribute actually 546 # determines the basename of a file. 547 # 548 # Note that this pattern mechanism has nothing to do with implicit patterns 549 # in make. It is a way to produce a target whose name is different than the 550 # name of its source. 551 # 552 rule generated-targets ( sources + : property-set : project name ? ) 553 { 554 if ! $(name) 555 { 556 name = [ determine-output-name $(sources) ] ; 557 } 558 559 # Assign an action for each target. 560 local action = [ action-class ] ; 561 local a = [ class.new $(action) $(sources) : $(self.rule-name) : 562 $(property-set) ] ; 563 564 # Create generated target for each target type. 565 local targets ; 566 local pre = $(self.name-prefix) ; 567 local post = $(self.name-postfix) ; 568 for local t in $(self.target-types) 569 { 570 local generated-name = $(pre[1])$(name:BS)$(post[1]) ; 571 generated-name = $(generated-name:R=$(name:D)) ; 572 pre = $(pre[2-]) ; 573 post = $(post[2-]) ; 574 575 targets += [ class.new file-target $(generated-name) : $(t) : 576 $(project) : $(a) ] ; 577 } 578 579 return [ sequence.transform virtual-target.register : $(targets) ] ; 580 } 581 582 # Attempts to convert 'sources' to targets of types that this generator can 583 # handle. The intention is to produce the set of targets that can be used 584 # when the generator is run. 585 # 586 rule convert-to-consumable-types 587 ( 588 project name ? 589 : property-set 590 : sources + 591 : only-one ? # Convert 'source' to only one of the source types. If 592 # there is more that one possibility, report an error. 593 ) 594 { 595 local _consumed ; 596 local missing-types ; 597 local usage-requirements ; 598 599 if $(sources[2]) 600 { 601 # Do not know how to handle several sources yet. Just try to pass 602 # the request to other generator. 603 missing-types = $(self.source-types) ; 604 } 605 else 606 { 607 local temp = [ consume-directly $(sources) ] ; 608 if $(temp[1]) 609 { 610 usage-requirements = [ property-set.empty ] ; 611 _consumed = $(temp[1]) ; 612 } 613 missing-types = $(temp[2-]) ; 614 } 615 616 # No need to search for transformation if some source type has consumed 617 # source and no more source types are needed. 618 if $(only-one) && $(_consumed) 619 { 620 missing-types = ; 621 } 622 623 # TODO: we should check that only one source type is created if 624 # 'only-one' is true. 625 626 if $(missing-types) 627 { 628 local transformed = [ generators.construct-types $(project) $(name) 629 : $(missing-types) : $(property-set) : $(sources) ] ; 630 631 # Add targets of right type to 'consumed'. Add others to 'bypassed'. 632 # The 'generators.construct' rule has done its best to convert 633 # everything to the required type. There is no need to rerun it on 634 # targets of different types. 635 636 usage-requirements = $(transformed[1]) ; 637 for local t in $(transformed[2-]) 638 { 639 if [ $(t).type ] in $(missing-types) 640 { 641 _consumed += $(t) ; 642 } 643 } 644 } 645 646 return $(usage-requirements) [ sequence.unique $(_consumed) ] ; 647 } 648 649 # Converts several files to consumable types. Called for composing 650 # generators only. 651 # 652 rule convert-multiple-sources-to-consumable-types ( project : property-set : 653 sources * ) 654 { 655 local result ; 656 # We process each source one-by-one, trying to convert it to a usable 657 # type. 658 if ! $(self.source-types) 659 { 660 # Anything is acceptable 661 return [ property-set.empty ] $(sources) ; 662 } 663 else 664 { 665 local usage-requirements = [ property-set.empty ] ; 666 local acceptible-types = [ sequence.unique 667 [ sequence.transform type.all-derived : $(self.source-types) ] ] ; 668 for local source in $(sources) 669 { 670 if ! [ $(source).type ] in $(acceptible-types) 671 { 672 local transformed = [ generators.construct-types $(project) 673 : $(self.source-types) : $(property-set) : $(source) ] ; 674 for local t in $(transformed[2-]) 675 { 676 if [ $(t).type ] in $(self.source-types) 677 { 678 result += $(t) ; 679 } 680 } 681 if ! $(transformed) 682 { 683 generators.dout [ indent ] " failed to convert " $(source) ; 684 } 685 else 686 { 687 usage-requirements = [ $(usage-requirements).add $(transformed[1]) ] ; 688 } 689 } 690 else 691 { 692 result += $(source) ; 693 } 694 } 695 return $(usage-requirements) [ sequence.unique $(result) : stable ] ; 696 } 697 } 698 699 rule consume-directly ( source ) 700 { 701 local real-source-type = [ $(source).type ] ; 702 703 # If there are no source types, we can consume anything. 704 local source-types = $(self.source-types) ; 705 source-types ?= $(real-source-type) ; 706 707 local result = "" ; 708 local missing-types ; 709 710 for local st in $(source-types) 711 { 712 # The 'source' if of the right type already. 713 if $(real-source-type) = $(st) || [ type.is-derived 714 $(real-source-type) $(st) ] 715 { 716 result = $(source) ; 717 } 718 else 719 { 720 missing-types += $(st) ; 721 } 722 } 723 return $(result) $(missing-types) ; 724 } 725 726 # Returns the class to be used to actions. Default implementation returns 727 # "action". 728 # 729 rule action-class ( ) 730 { 731 return "action" ; 732 } 733} 734 735 736# Registers a new generator instance 'g'. 737# 738rule register ( g ) 739{ 740 .all-generators += $(g) ; 741 742 # A generator can produce several targets of the same type. We want unique 743 # occurrence of that generator in .generators.$(t) in that case, otherwise, 744 # it will be tried twice and we will get a false ambiguity. 745 for local t in [ sequence.unique [ $(g).target-types ] ] 746 { 747 .generators.$(t) += $(g) ; 748 } 749 750 # Update the set of generators for toolset. 751 752 # TODO: should we check that generator with this id is not already 753 # registered. For example, the fop.jam module intentionally declared two 754 # generators with the same id, so such check will break it. 755 local id = [ $(g).id ] ; 756 757 # Some generators have multiple periods in their name, so a simple $(id:S=) 758 # will not generate the right toolset name. E.g. if id = gcc.compile.c++, 759 # then .generators-for-toolset.$(id:S=) will append to 760 # .generators-for-toolset.gcc.compile, which is a separate value from 761 # .generators-for-toolset.gcc. Correcting this makes generator inheritance 762 # work properly. See also inherit-generators in the toolset module. 763 local base = $(id) ; 764 while $(base:S) 765 { 766 base = $(base:B) ; 767 } 768 .generators-for-toolset.$(base) += $(g) ; 769 770 771 # After adding a new generator that can construct new target types, we need 772 # to clear the related cached viable source target type information for 773 # constructing a specific target type or using a specific generator. Cached 774 # viable source target type lists affected by this are those containing any 775 # of the target types constructed by the new generator or any of their base 776 # target types. 777 # 778 # A more advanced alternative to clearing that cached viable source target 779 # type information would be to expand it with additional source types or 780 # even better - mark it as needing to be expanded on next use. 781 # 782 # Also see the http://thread.gmane.org/gmane.comp.lib.boost.build/19077 783 # mailing list thread for an even more advanced idea of how we could convert 784 # Boost Build's Jamfile processing, target selection and generator selection 785 # into separate steps which would prevent these caches from ever being 786 # invalidated. 787 # 788 # For now we just clear all the cached viable source target type information 789 # that does not simply state 'all types' and may implement a more detailed 790 # algorithm later on if it becomes needed. 791 792 invalidate-extendable-viable-source-target-type-cache ; 793} 794 795 796# Creates a new non-composing 'generator' class instance and registers it. 797# Returns the created instance. Rationale: the instance is returned so that it 798# is possible to first register a generator and then call its 'run' method, 799# bypassing the whole generator selection process. 800# 801rule register-standard ( id : source-types * : target-types + : requirements * ) 802{ 803 local g = [ new generator $(id) : $(source-types) : $(target-types) : 804 $(requirements) ] ; 805 register $(g) ; 806 return $(g) ; 807} 808 809 810# Creates a new composing 'generator' class instance and registers it. 811# 812rule register-composing ( id : source-types * : target-types + : requirements * 813 ) 814{ 815 local g = [ new generator $(id) true : $(source-types) : $(target-types) : 816 $(requirements) ] ; 817 register $(g) ; 818 return $(g) ; 819} 820 821 822# Returns all generators belonging to the given 'toolset', i.e. whose ids are 823# '$(toolset).<something>'. 824# 825rule generators-for-toolset ( toolset ) 826{ 827 return $(.generators-for-toolset.$(toolset)) ; 828} 829 830 831# Make generator 'overrider-id' be preferred to 'overridee-id'. If, when 832# searching for generators that could produce a target of a certain type, both 833# those generators are among viable generators, the overridden generator is 834# immediately discarded. 835# 836# The overridden generators are discarded immediately after computing the list 837# of viable generators but before running any of them. 838# 839rule override ( overrider-id : overridee-id ) 840{ 841 .override.$(overrider-id) += $(overridee-id) ; 842} 843 844 845# Returns a list of source type which can possibly be converted to 'target-type' 846# by some chain of generator invocation. 847# 848# More formally, takes all generators for 'target-type' and returns a union of 849# source types for those generators and result of calling itself recursively on 850# source types. 851# 852# Returns '*' in case any type should be considered a viable source type for the 853# given type. 854# 855local rule viable-source-types-real ( target-type ) 856{ 857 local result ; 858 859 # 't0' is the initial list of target types we need to process to get a list 860 # of their viable source target types. New target types will not be added to 861 # this list. 862 local t0 = [ type.all-bases $(target-type) ] ; 863 864 # 't' is the list of target types which have not yet been processed to get a 865 # list of their viable source target types. This list will get expanded as 866 # we locate more target types to process. 867 local t = $(t0) ; 868 869 while $(t) 870 { 871 # Find all generators for the current type. Unlike 872 # 'find-viable-generators' we do not care about the property-set. 873 local generators = $(.generators.$(t[1])) ; 874 t = $(t[2-]) ; 875 876 while $(generators) 877 { 878 local g = $(generators[1]) ; 879 generators = $(generators[2-]) ; 880 881 if ! [ $(g).source-types ] 882 { 883 # Empty source types -- everything can be accepted. 884 result = * ; 885 # This will terminate this loop. 886 generators = ; 887 # This will terminate the outer loop. 888 t = ; 889 } 890 891 for local source-type in [ $(g).source-types ] 892 { 893 if ! $(source-type) in $(result) 894 { 895 # If a generator accepts a 'source-type' it will also 896 # happily accept any type derived from it. 897 for local n in [ type.all-derived $(source-type) ] 898 { 899 if ! $(n) in $(result) 900 { 901 # Here there is no point in adding target types to 902 # the list of types to process in case they are or 903 # have already been on that list. We optimize this 904 # check by realizing that we only need to avoid the 905 # original target type's base types. Other target 906 # types that are or have been on the list of target 907 # types to process have been added to the 'result' 908 # list as well and have thus already been eliminated 909 # by the previous if. 910 if ! $(n) in $(t0) 911 { 912 t += $(n) ; 913 } 914 result += $(n) ; 915 } 916 } 917 } 918 } 919 } 920 } 921 922 return $(result) ; 923} 924 925 926# Helper rule, caches the result of 'viable-source-types-real'. 927# 928rule viable-source-types ( target-type ) 929{ 930 local key = .vst.$(target-type) ; 931 if ! $($(key)) 932 { 933 .vst-cached-types += $(target-type) ; 934 local v = [ viable-source-types-real $(target-type) ] ; 935 if ! $(v) 936 { 937 v = none ; 938 } 939 $(key) = $(v) ; 940 } 941 942 if $($(key)) != none 943 { 944 return $($(key)) ; 945 } 946} 947 948 949# Returns the list of source types, which, when passed to 'run' method of 950# 'generator', has some change of being eventually used (probably after 951# conversion by other generators). 952# 953# Returns '*' in case any type should be considered a viable source type for the 954# given generator. 955# 956rule viable-source-types-for-generator-real ( generator ) 957{ 958 local source-types = [ $(generator).source-types ] ; 959 if ! $(source-types) 960 { 961 # If generator does not specify any source types, it might be a special 962 # generator like builtin.lib-generator which just relays to other 963 # generators. Return '*' to indicate that any source type is possibly 964 # OK, since we do not know for sure. 965 return * ; 966 } 967 else 968 { 969 local result ; 970 while $(source-types) 971 { 972 local s = $(source-types[1]) ; 973 source-types = $(source-types[2-]) ; 974 local viable-sources = [ generators.viable-source-types $(s) ] ; 975 if $(viable-sources) = * 976 { 977 result = * ; 978 source-types = ; # Terminate the loop. 979 } 980 else 981 { 982 result += [ type.all-derived $(s) ] $(viable-sources) ; 983 } 984 } 985 return [ sequence.unique $(result) ] ; 986 } 987} 988 989 990# Helper rule, caches the result of 'viable-source-types-for-generator'. 991# 992local rule viable-source-types-for-generator ( generator ) 993{ 994 local key = .vstg.$(generator) ; 995 if ! $($(key)) 996 { 997 .vstg-cached-generators += $(generator) ; 998 local v = [ viable-source-types-for-generator-real $(generator) ] ; 999 if ! $(v) 1000 { 1001 v = none ; 1002 } 1003 $(key) = $(v) ; 1004 } 1005 1006 if $($(key)) != none 1007 { 1008 return $($(key)) ; 1009 } 1010} 1011 1012 1013# Returns usage requirements + list of created targets. 1014# 1015local rule try-one-generator-really ( project name ? : generator : target-type 1016 : property-set : sources * ) 1017{ 1018 local targets = 1019 [ $(generator).run $(project) $(name) : $(property-set) : $(sources) ] ; 1020 1021 local usage-requirements ; 1022 local success ; 1023 1024 generators.dout [ indent ] returned $(targets) ; 1025 1026 if $(targets) 1027 { 1028 success = true ; 1029 1030 if [ class.is-a $(targets[1]) : property-set ] 1031 { 1032 usage-requirements = $(targets[1]) ; 1033 targets = $(targets[2-]) ; 1034 } 1035 else 1036 { 1037 usage-requirements = [ property-set.empty ] ; 1038 } 1039 } 1040 1041 generators.dout [ indent ] " generator" [ $(generator).id ] " spawned " ; 1042 generators.dout [ indent ] " " $(targets) ; 1043 if $(usage-requirements) 1044 { 1045 generators.dout [ indent ] " with usage requirements:" $(usage-requirements) ; 1046 } 1047 1048 if $(success) 1049 { 1050 return $(usage-requirements) $(targets) ; 1051 } 1052} 1053 1054 1055# Checks if generator invocation can be pruned, because it is guaranteed to 1056# fail. If so, quickly returns an empty list. Otherwise, calls 1057# try-one-generator-really. 1058# 1059local rule try-one-generator ( project name ? : generator : target-type 1060 : property-set : sources * ) 1061{ 1062 local source-types ; 1063 for local s in $(sources) 1064 { 1065 source-types += [ $(s).type ] ; 1066 } 1067 local viable-source-types = [ viable-source-types-for-generator $(generator) 1068 ] ; 1069 1070 if $(source-types) && $(viable-source-types) != * && 1071 ! [ set.intersection $(source-types) : $(viable-source-types) ] 1072 { 1073 local id = [ $(generator).id ] ; 1074 generators.dout [ indent ] " ** generator '$(id)' pruned" ; 1075 #generators.dout [ indent ] "source-types" '$(source-types)' ; 1076 #generators.dout [ indent ] "viable-source-types" '$(viable-source-types)' ; 1077 } 1078 else 1079 { 1080 return [ try-one-generator-really $(project) $(name) : $(generator) : 1081 $(target-type) : $(property-set) : $(sources) ] ; 1082 } 1083} 1084 1085 1086rule construct-types ( project name ? : target-types + : property-set 1087 : sources + ) 1088{ 1089 local result ; 1090 local usage-requirements = [ property-set.empty ] ; 1091 for local t in $(target-types) 1092 { 1093 local r = [ construct $(project) $(name) : $(t) : $(property-set) : 1094 $(sources) ] ; 1095 if $(r) 1096 { 1097 usage-requirements = [ $(usage-requirements).add $(r[1]) ] ; 1098 result += $(r[2-]) ; 1099 } 1100 } 1101 # TODO: have to introduce parameter controlling if several types can be 1102 # matched and add appropriate checks. 1103 1104 # TODO: need to review the documentation for 'construct' to see if it should 1105 # return $(source) even if nothing can be done with it. Currents docs seem 1106 # to imply that, contrary to the behaviour. 1107 if $(result) 1108 { 1109 return $(usage-requirements) $(result) ; 1110 } 1111 else 1112 { 1113 return $(usage-requirements) $(sources) ; 1114 } 1115} 1116 1117 1118# Ensures all 'targets' have their type. If this is not so, exists with error. 1119# 1120local rule ensure-type ( targets * ) 1121{ 1122 for local t in $(targets) 1123 { 1124 if ! [ $(t).type ] 1125 { 1126 import errors ; 1127 errors.error "target" [ $(t).str ] "has no type" ; 1128 } 1129 } 1130} 1131 1132 1133# Returns generators which can be used to construct target of specified type 1134# with specified properties. Uses the following algorithm: 1135# - iterates over requested target-type and all its bases (in the order returned 1136# by type.all-bases). 1137# - for each type find all generators that generate that type and whose 1138# requirements are satisfied by properties. 1139# - if the set of generators is not empty, returns that set. 1140# 1141# Note: this algorithm explicitly ignores generators for base classes if there 1142# is at least one generator for the requested target-type. 1143# 1144local rule find-viable-generators-aux ( target-type : property-set ) 1145{ 1146 # Select generators that can create the required target type. 1147 local viable-generators = ; 1148 1149 import type ; 1150 local t = $(target-type) ; 1151 1152 if $(.debug) 1153 { 1154 generators.dout [ indent ] find-viable-generators target-type= $(target-type) 1155 property-set= [ $(property-set).as-path ] ; 1156 generators.dout [ indent ] "trying type" $(target-type) ; 1157 } 1158 1159 local generators = $(.generators.$(target-type)) ; 1160 if $(generators) 1161 { 1162 if $(.debug) 1163 { 1164 generators.dout [ indent ] "there are generators for this type" ; 1165 } 1166 } 1167 else 1168 { 1169 local t = [ type.base $(target-type) ] ; 1170 1171 # Get the list of generators for the requested type. If no generator is 1172 # registered, try base type, and so on. 1173 while $(t) 1174 { 1175 if $(.debug) 1176 { 1177 generators.dout [ indent ] "trying type" $(t) ; 1178 } 1179 if $(.generators.$(t)) 1180 { 1181 generators.dout [ indent ] "there are generators for this type" ; 1182 generators = $(.generators.$(t)) ; 1183 1184 # We are here because there were no generators found for 1185 # target-type but there are some generators for its base type. 1186 # We will try to use them, but they will produce targets of 1187 # base type, not of 'target-type'. So, we clone the generators 1188 # and modify the list of target types. 1189 local generators2 ; 1190 for local g in $(generators) 1191 { 1192 # generators.register adds a generator to the list of 1193 # generators for toolsets, which is a bit strange, but 1194 # should work. That list is only used when inheriting a 1195 # toolset, which should have been done before running 1196 # generators. 1197 generators2 += [ $(g).clone-and-change-target-type $(t) : 1198 $(target-type) ] ; 1199 generators.register $(generators2[-1]) ; 1200 } 1201 generators = $(generators2) ; 1202 t = ; 1203 } 1204 else 1205 { 1206 t = [ type.base $(t) ] ; 1207 } 1208 } 1209 } 1210 1211 for local g in $(generators) 1212 { 1213 if $(.debug) 1214 { 1215 generators.dout [ indent ] "trying generator" [ $(g).id ] "(" [ $(g).source-types ] -> [ $(g).target-types ] ")" ; 1216 } 1217 1218 if [ $(g).match-rank $(property-set) ] 1219 { 1220 if $(.debug) 1221 { 1222 generators.dout [ indent ] " is viable" ; 1223 } 1224 viable-generators += $(g) ; 1225 } 1226 } 1227 1228 return $(viable-generators) ; 1229} 1230 1231 1232rule find-viable-generators ( target-type : property-set ) 1233{ 1234 local key = $(target-type).$(property-set) ; 1235 local l = $(.fv.$(key)) ; 1236 if ! $(l) 1237 { 1238 l = [ find-viable-generators-aux $(target-type) : $(property-set) ] ; 1239 if ! $(l) 1240 { 1241 l = none ; 1242 } 1243 .fv.$(key) = $(l) ; 1244 } 1245 1246 if $(l) = none 1247 { 1248 l = ; 1249 } 1250 1251 local viable-generators ; 1252 for local g in $(l) 1253 { 1254 # Avoid trying the same generator twice on different levels. 1255 if ! $(g) in $(.active-generators) 1256 { 1257 viable-generators += $(g) ; 1258 } 1259 else 1260 { 1261 generators.dout [ indent ] " generator " [ $(g).id ] "is active, discaring" ; 1262 } 1263 } 1264 1265 # Generators which override 'all'. 1266 local all-overrides ; 1267 # Generators which are overridden. 1268 local overriden-ids ; 1269 for local g in $(viable-generators) 1270 { 1271 local id = [ $(g).id ] ; 1272 local this-overrides = $(.override.$(id)) ; 1273 overriden-ids += $(this-overrides) ; 1274 if all in $(this-overrides) 1275 { 1276 all-overrides += $(g) ; 1277 } 1278 } 1279 if $(all-overrides) 1280 { 1281 viable-generators = $(all-overrides) ; 1282 } 1283 local result ; 1284 for local g in $(viable-generators) 1285 { 1286 if ! [ $(g).id ] in $(overriden-ids) 1287 { 1288 result += $(g) ; 1289 } 1290 } 1291 1292 return $(result) ; 1293} 1294 1295 1296.construct-stack = ; 1297 1298 1299# Attempts to construct a target by finding viable generators, running them and 1300# selecting the dependency graph. 1301# 1302local rule construct-really ( project name ? : target-type : property-set : 1303 sources * ) 1304{ 1305 viable-generators = [ find-viable-generators $(target-type) : 1306 $(property-set) ] ; 1307 1308 generators.dout [ indent ] "*** " [ sequence.length $(viable-generators) ] 1309 " viable generators" ; 1310 1311 local result ; 1312 local generators-that-succeeded ; 1313 for local g in $(viable-generators) 1314 { 1315 # This variable will be restored on exit from this scope. 1316 local .active-generators = $(g) $(.active-generators) ; 1317 1318 local r = [ try-one-generator $(project) $(name) : $(g) : $(target-type) 1319 : $(property-set) : $(sources) ] ; 1320 1321 if $(r) 1322 { 1323 generators-that-succeeded += $(g) ; 1324 if $(result) 1325 { 1326 ECHO "Error: ambiguity found when searching for best transformation" ; 1327 ECHO "Trying to produce type '$(target-type)' from: " ; 1328 for local s in $(sources) 1329 { 1330 ECHO " - " [ $(s).str ] ; 1331 } 1332 ECHO "Generators that succeeded:" ; 1333 for local g in $(generators-that-succeeded) 1334 { 1335 ECHO " - " [ $(g).id ] ; 1336 } 1337 ECHO "First generator produced: " ; 1338 for local t in $(result[2-]) 1339 { 1340 ECHO " - " [ $(t).str ] ; 1341 } 1342 ECHO "Second generator produced: " ; 1343 for local t in $(r[2-]) 1344 { 1345 ECHO " - " [ $(t).str ] ; 1346 } 1347 EXIT ; 1348 } 1349 else 1350 { 1351 result = $(r) ; 1352 } 1353 } 1354 } 1355 1356 return $(result) ; 1357} 1358 1359 1360# Attempts to create a target of 'target-type' with 'properties' from 'sources'. 1361# The 'sources' are treated as a collection of *possible* ingridients, i.e. 1362# there is no obligation to consume them all. 1363# 1364# Returns a list of targets. When this invocation is first instance of 1365# 'construct' in stack, returns only targets of requested 'target-type', 1366# otherwise, returns also unused sources and additionally generated targets. 1367# 1368# If 'top-level' is set, does not suppress generators that are already 1369# used in the stack. This may be useful in cases where a generator 1370# has to build a metatargets -- for example a target corresponding to 1371# built tool. 1372# 1373rule construct ( project name ? : target-type : property-set * : sources * : top-level ? ) 1374{ 1375 local saved-active ; 1376 if $(top-level) 1377 { 1378 saved-active = $(.active-generators) ; 1379 .active-generators = ; 1380 } 1381 1382 # FIXME This is probably not intended be be run unconditionally, 1383 # but changing it causes no_type to fail. 1384 if "(.construct-stack)" 1385 { 1386 ensure-type $(sources) ; 1387 } 1388 1389 .construct-stack += 1 ; 1390 1391 increase-indent ; 1392 1393 if $(.debug) 1394 { 1395 generators.dout [ indent ] "*** construct" $(target-type) ; 1396 1397 for local s in $(sources) 1398 { 1399 generators.dout [ indent ] " from" $(s) ; 1400 } 1401 generators.dout [ indent ] " properties:" [ $(property-set).raw ] ; 1402 } 1403 1404 local result = [ construct-really $(project) $(name) : $(target-type) : 1405 $(property-set) : $(sources) ] ; 1406 1407 decrease-indent ; 1408 1409 .construct-stack = $(.construct-stack[2-]) ; 1410 1411 if $(top-level) 1412 { 1413 .active-generators = $(saved-active) ; 1414 } 1415 1416 return $(result) ; 1417} 1418 1419# Given 'result', obtained from some generator or generators.construct, adds 1420# 'raw-properties' as usage requirements to it. If result already contains usage 1421# requirements -- that is the first element of result of an instance of the 1422# property-set class, the existing usage requirements and 'raw-properties' are 1423# combined. 1424# 1425rule add-usage-requirements ( result * : raw-properties * ) 1426{ 1427 if $(result) 1428 { 1429 if [ class.is-a $(result[1]) : property-set ] 1430 { 1431 return [ $(result[1]).add-raw $(raw-properties) ] $(result[2-]) ; 1432 } 1433 else 1434 { 1435 return [ property-set.create $(raw-properties) ] $(result) ; 1436 } 1437 } 1438} 1439 1440rule dump ( ) 1441{ 1442 for local g in $(.all-generators) 1443 { 1444 ECHO [ $(g).id ] ":" [ $(g).source-types ] -> [ $(g).target-types ] ; 1445 } 1446} 1447 1448