1# Copyright 2003, 2005, 2007 Dave Abrahams 2# Copyright 2006, 2007 Rene Rivera 3# Copyright 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# This file is part of Boost Build version 2. You can think of it as forming the 9# main() routine. It is invoked by the bootstrapping code in bootstrap.jam. 10 11import build-request ; 12import builtin ; 13import "class" : new ; 14import configure ; 15import config-cache ; 16import feature ; 17import generators ; 18import indirect ; 19import make ; 20import modules ; 21import os ; 22import path ; 23import project ; 24import property ; 25import property-set ; 26import regex ; 27import sequence ; 28import targets ; 29import toolset ; 30import utility ; 31import version ; 32import virtual-target ; 33 34 35################################################################################ 36# 37# Module global data. 38# 39################################################################################ 40 41# Shortcut used in this module for accessing used command-line parameters. 42.argv = [ modules.peek : ARGV ] ; 43 44# Flag indicating we should display additional debugging information related to 45# locating and loading Boost Build configuration files. 46.debug-config = [ MATCH ^(--debug-configuration)$ : $(.argv) ] ; 47 48# Virtual targets obtained when building main targets references on the command 49# line. When running 'bjam --clean main_target' we want to clean only files 50# belonging to that main target so we need to record which targets are produced 51# for it. 52.results-of-main-targets = ; 53 54# Was an XML dump requested? 55.out-xml = [ MATCH ^--out-xml=(.*)$ : $(.argv) ] ; 56 57# Default toolset & version to be used in case no other toolset has been used 58# explicitly by either the loaded configuration files, the loaded project build 59# scripts or an explicit toolset request on the command line. If not specified, 60# an arbitrary default will be used based on the current host OS. This value, 61# while not strictly necessary, has been added to allow testing Boost-Build's 62# default toolset usage functionality. 63.default-toolset = ; 64.default-toolset-version = ; 65 66 67################################################################################ 68# 69# Public rules. 70# 71################################################################################ 72 73# Returns the property set with the free features from the currently processed 74# build request. 75# 76rule command-line-free-features ( ) 77{ 78 return $(.command-line-free-features) ; 79} 80 81 82# Returns the location of the build system. The primary use case is building 83# Boost where it is sometimes needed to get the location of other components 84# (e.g. BoostBook files) and it is convenient to use locations relative to the 85# Boost Build path. 86# 87rule location ( ) 88{ 89 local r = [ modules.binding build-system ] ; 90 return $(r:P) ; 91} 92 93 94# Sets the default toolset & version to be used in case no other toolset has 95# been used explicitly by either the loaded configuration files, the loaded 96# project build scripts or an explicit toolset request on the command line. For 97# more detailed information see the comment related to used global variables. 98# 99rule set-default-toolset ( toolset : version ? ) 100{ 101 .default-toolset = $(toolset) ; 102 .default-toolset-version = $(version) ; 103} 104 105rule add-pre-build-hook ( function ) 106{ 107 .pre-build-hook += [ indirect.make $(function) : [ CALLER_MODULE ] ] ; 108} 109 110rule add-post-build-hook ( function ) 111{ 112 .post-build-hook += [ indirect.make $(function) : [ CALLER_MODULE ] ] ; 113} 114 115# Old names for backwards compatibility 116IMPORT build-system : add-pre-build-hook : build-system : set-pre-build-hook ; 117IMPORT build-system : add-post-build-hook : build-system : set-post-build-hook ; 118EXPORT build-system : set-pre-build-hook set-post-build-hook ; 119 120################################################################################ 121# 122# Local rules. 123# 124################################################################################ 125 126# Returns actual Jam targets to be used for executing a clean request. 127# 128local rule actual-clean-targets ( ) 129{ 130 # The cleaning is tricky. Say, if user says 'bjam --clean foo' where 'foo' 131 # is a directory, then we want to clean targets which are in 'foo' as well 132 # as those in any children Jamfiles under foo but not in any unrelated 133 # Jamfiles. To achieve this we first mark all projects explicitly detected 134 # as targets for this build system run as needing to be cleaned. 135 for local t in $(targets) 136 { 137 if [ class.is-a $(t) : project-target ] 138 { 139 local project = [ $(t).project-module ] ; 140 .should-clean-project.$(project) = true ; 141 } 142 } 143 144 # Construct a list of targets explicitly detected on this build system run 145 # as a result of building main targets. 146 local targets-to-clean ; 147 for local t in $(.results-of-main-targets) 148 { 149 # Do not include roots or sources. 150 targets-to-clean += [ virtual-target.traverse $(t) ] ; 151 } 152 targets-to-clean = [ sequence.unique $(targets-to-clean) ] ; 153 154 local to-clean ; 155 for local t in [ virtual-target.all-targets ] 156 { 157 # Remove only derived targets and only those asked to be cleaned, 158 # whether directly or by belonging to one of the removed projects. 159 local p = [ $(t).project ] ; 160 if [ $(t).action ] && ( $(t) in $(targets-to-clean) || 161 [ should-clean-project [ $(p).project-module ] ] ) 162 { 163 to-clean += $(t) ; 164 } 165 } 166 167 local to-clean-actual ; 168 for local t in $(to-clean) 169 { 170 to-clean-actual += [ $(t).actualize ] ; 171 } 172 return $(to-clean-actual) ; 173} 174 175 176# Given a target id, try to find and return the corresponding target. This is 177# only invoked when there is no Jamfile in ".". This code somewhat duplicates 178# code in project-target.find but we can not reuse that code without a 179# project-targets instance. 180# 181local rule find-target ( target-id ) 182{ 183 local split = [ MATCH (.*)//(.*) : $(target-id) ] ; 184 185 local pm ; 186 if $(split) 187 { 188 pm = [ project.find $(split[1]) : "." ] ; 189 } 190 else 191 { 192 pm = [ project.find $(target-id) : "." ] ; 193 } 194 195 local result ; 196 if $(pm) 197 { 198 result = [ project.target $(pm) ] ; 199 } 200 201 if $(split) 202 { 203 result = [ $(result).find $(split[2]) ] ; 204 } 205 206 return $(result) ; 207} 208 209 210# Initializes a new configuration module. 211# 212local rule initialize-config-module ( module-name : location ? ) 213{ 214 project.initialize $(module-name) : $(location) ; 215 if USER_MODULE in [ RULENAMES ] 216 { 217 USER_MODULE $(module-name) ; 218 } 219} 220 221 222# Helper rule used to load configuration files. Loads the first configuration 223# file with the given 'filename' at 'path' into module with name 'module-name'. 224# Not finding the requested file may or may not be treated as an error depending 225# on the must-find parameter. Returns a normalized path to the loaded 226# configuration file or nothing if no file was loaded. 227# 228local rule load-config ( module-name : filename : path + : must-find ? ) 229{ 230 if $(.debug-config) 231 { 232 local path-string = $(path) ; 233 if $(path-string) = "" { path-string = . ; } 234 ECHO "notice:" Searching '$(path-string)' for $(module-name) 235 configuration file '$(filename)'. ; 236 } 237 local where = [ GLOB $(path) : $(filename) ] ; 238 if $(where) 239 { 240 where = [ NORMALIZE_PATH $(where[1]) ] ; 241 if $(.debug-config) 242 { 243 local where-string = $(where:D) ; 244 if $(where-string) = "" { where-string = . ; } 245 where-string = '$(where-string)' ; 246 ECHO "notice:" Loading $(module-name) configuration file '$(filename)' 247 from $(where-string:J=" "). ; 248 } 249 250 # Set source location so that path-constant in config files with 251 # relative paths work. This is of most importance for 252 # project-config.jam, but may be used in other config files as well. 253 local attributes = [ project.attributes $(module-name) ] ; 254 $(attributes).set source-location : $(where:D) : exact ; 255 modules.load $(module-name) : $(filename) : $(path) ; 256 project.load-used-projects $(module-name) ; 257 } 258 else if $(must-find) || $(.debug-config) 259 { 260 local path-string = $(path) ; 261 if $(path-string) = "" { path-string = . ; } 262 path-string = '$(path-string)' ; 263 path-string = $(path-string:J=" ") ; 264 if $(must-find) 265 { 266 import errors ; 267 errors.user-error Configuration file '$(filename)' not found "in" 268 $(path-string). ; 269 } 270 ECHO "notice:" Configuration file '$(filename)' not found "in" 271 $(path-string). ; 272 } 273 return $(where) ; 274} 275 276# Parses options of the form --xxx-config=path/to/config.jam 277# and environmental variables of the form BOOST_BUILD_XXX_CONFIG. 278# If not found, returns an empty list. The option may be 279# explicitly set to the empty string, in which case, handle-config-option 280# will return "". 281# 282local rule handle-config-option ( name : env ? ) 283{ 284 local result = [ MATCH ^--$(name)=(.*)$ : $(.argv) ] ; 285 if ! $(result)-is-defined && $(env) 286 { 287 result = [ os.environ $(env) ] ; 288 } 289 # Special handling for the case when the OS does not strip the quotes 290 # around the file name, as is the case when using Cygwin bash. 291 result = [ utility.unquote $(result[-1]) ] ; 292 if ! $(result) 293 { 294 return $(result) ; 295 } 296 # Treat explicitly entered user paths as native OS path 297 # references and, if non-absolute, root them at the current 298 # working directory. 299 result = [ path.make $(result) ] ; 300 result = [ path.root $(result) [ path.pwd ] ] ; 301 result = [ path.native $(result) ] ; 302 return $(result) ; 303} 304 305 306# Loads all the configuration files used by Boost Build in the following order: 307# 308# -- test-config -- 309# Loaded only if specified on the command-line using the --test-config 310# command-line parameter. It is ok for this file not to exist even if specified. 311# If this configuration file is loaded, regular site and user configuration 312# files will not be. If a relative path is specified, file is searched for in 313# the current folder. 314# 315# -- all-config -- 316# Loaded only if specified on the command-line using the --config command 317# line option. If a file name is specified, it must exist and replaces all 318# other configuration files. If an empty file name is passed, no configuration 319# files will be loaded. 320# 321# -- site-config -- 322# Named site-config.jam by default or may be named explicitly using the 323# --site-config command-line option. If named explicitly, the file is found 324# relative to the current working directory and must exist. If the default one 325# is used then it is searched for in the system root path (Windows), 326# /etc (non-Windows), user's home folder or the Boost Build path, in that 327# order. Not loaded in case the test-config configuration file is loaded, 328# the file is explicitly set to the empty string or the --ignore-site-config 329# command-line option is specified. 330# 331# -- user-config -- 332# Named user-config.jam by default or may be named explicitly using the 333# --user-config command-line option or the BOOST_BUILD_USER_CONFIG environment 334# variable. If named explicitly the file is looked for from the current working 335# directory and if the default one is used then it is searched for in the 336# user's home directory and the Boost Build path, in that order. Not loaded in 337# case either the test-config configuration file is loaded or an empty file name 338# is explicitly specified. If the file name has been given explicitly then the 339# file must exist. 340# 341# -- project-config -- 342# Named project-config.jam. Looked up in the current working folder and 343# then upwards through its parents up to the root folder. It may also be 344# named explicitly using the --project-config command-line option. If a file 345# is specified explicitly, it is found relative to the current working 346# directory and must exist. If an empty file name is passed, project-config 347# will not be loaded. 348# 349# Test configurations have been added primarily for use by Boost Build's 350# internal unit testing system but may be used freely in other places as well. 351# 352local rule load-configuration-files 353{ 354 # Flag indicating that site configuration should not be loaded. 355 local ignore-site-config = 356 [ MATCH ^(--ignore-site-config)$ : $(.argv) ] ; 357 local ignore-user-config ; 358 local ignore-project-config ; 359 360 initialize-config-module test-config ; 361 local test-config = [ handle-config-option test-config ] ; 362 if $(test-config) 363 { 364 local where = [ load-config test-config : $(test-config:BS) : 365 $(test-config:D) ] ; 366 if $(where) 367 { 368 if $(.debug-config) 369 { 370 ECHO "notice: Regular site and user configuration files will" ; 371 ECHO "notice: be ignored due to the test configuration being" 372 "loaded." ; 373 } 374 ignore-site-config = true ; 375 ignore-user-config = true ; 376 } 377 } 378 379 initialize-config-module all-config ; 380 local all-config = [ handle-config-option config ] ; 381 if $(all-config) 382 { 383 load-config all-config : $(all-config:D=) : $(all-config:D) : required ; 384 if $(.debug-config) 385 { 386 ECHO "notice: Regular configuration files will be ignored due" ; 387 ECHO "notice: to the global configuration being loaded." ; 388 } 389 } 390 if $(all-config)-is-defined 391 { 392 if $(.debug-config) && ! $(all-config) 393 { 394 ECHO "notice: Configuration file loading explicitly disabled." ; 395 } 396 ignore-site-config = true ; 397 ignore-user-config = true ; 398 ignore-project-config = true ; 399 } 400 401 local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ; 402 local site-path = /etc $(user-path) ; 403 if [ os.name ] in NT CYGWIN 404 { 405 site-path = [ modules.peek : SystemRoot ] $(user-path) ; 406 } 407 408 if $(.debug-config) && $(ignore-site-config) = --ignore-site-config 409 { 410 ECHO "notice: Site configuration files will be ignored due to the" ; 411 ECHO "notice: --ignore-site-config command-line option." ; 412 } 413 414 initialize-config-module site-config ; 415 if ! $(ignore-site-config) 416 { 417 local site-config = [ handle-config-option site-config ] ; 418 if $(site-config) 419 { 420 load-config site-config : $(site-config:D=) : $(site-config:D) 421 : must-exist ; 422 } 423 else if ! $(site-config)-is-defined 424 { 425 load-config site-config : site-config.jam : $(site-path) ; 426 } 427 else if $(.debug-config) 428 { 429 ECHO "notice:" Site configuration file loading explicitly disabled. ; 430 } 431 } 432 433 initialize-config-module user-config ; 434 if ! $(ignore-user-config) 435 { 436 local user-config = 437 [ handle-config-option user-config : BOOST_BUILD_USER_CONFIG ] ; 438 439 if $(user-config) 440 { 441 if $(.debug-config) 442 { 443 ECHO "notice:" Loading explicitly specified user configuration 444 "file:" ; 445 ECHO " $(user-config)" ; 446 } 447 448 load-config user-config : $(user-config:D=) : $(user-config:D) 449 : must-exist ; 450 } 451 else if ! $(user-config)-is-defined 452 { 453 load-config user-config : user-config.jam : $(user-path) ; 454 } 455 else if $(.debug-config) 456 { 457 ECHO "notice:" User configuration file loading explicitly disabled. ; 458 } 459 } 460 461 # We look for project-config.jam from "." upward. I am not sure this is 100% 462 # right decision, we might as well check for it only alongside the Jamroot 463 # file. However: 464 # - We need to load project-config.jam before Jamroot 465 # - We probably need to load project-config.jam even if there is no Jamroot 466 # - e.g. to implement automake-style out-of-tree builds. 467 if ! $(ignore-project-config) 468 { 469 local project-config = [ handle-config-option project-config ] ; 470 if $(project-config) 471 { 472 initialize-config-module project-config : $(project-config:D=) ; 473 load-config project-config : $(project-config:D=) 474 : $(project-config:D) : must-exist ; 475 } 476 else if ! $(project-config)-is-defined 477 { 478 local file = [ path.glob "." : project-config.jam ] ; 479 if ! $(file) 480 { 481 file = [ path.glob-in-parents "." : project-config.jam ] ; 482 } 483 if $(file) 484 { 485 initialize-config-module project-config : $(file:D) ; 486 load-config project-config : project-config.jam : $(file:D) ; 487 } 488 } 489 else if $(.debug-config) 490 { 491 ECHO "notice:" Project configuration file loading explicitly 492 disabled. ; 493 } 494 } 495 496 project.end-load ; 497} 498 499 500# Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or 501# toolset=xx,yy,...zz in the command line. May return additional properties to 502# be processed as if they had been specified by the user. 503# 504local rule process-explicit-toolset-requests 505{ 506 local extra-properties ; 507 508 local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*)$ : $(.argv) ] : "," ] ; 509 local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*)$ : $(.argv) ] : "," ] ; 510 511 for local t in $(option-toolsets) $(feature-toolsets) 512 { 513 # Parse toolset-version/properties. 514 local toolset = [ MATCH "([^/]+)/?.*" : $(t) ] ; 515 local properties = [ feature.expand-subfeatures <toolset>$(toolset) : true ] ; 516 local toolset-property = [ property.select <toolset> : $(properties) ] ; 517 local known ; 518 if $(toolset-property:G=) in [ feature.values <toolset> ] 519 { 520 known = true ; 521 } 522 523 # If the toolset is not known, configure it now. 524 525 # TODO: we should do 'using $(toolset)' in case no version has been 526 # specified and there are no versions defined for the given toolset to 527 # allow the toolset to configure its default version. For this we need 528 # to know how to detect whether a given toolset has any versions 529 # defined. An alternative would be to do this whenever version is not 530 # specified but that would require that toolsets correctly handle the 531 # case when their default version is configured multiple times which 532 # should be checked for all existing toolsets first. 533 534 if ! $(known) 535 { 536 if $(.debug-config) 537 { 538 ECHO "notice: [cmdline-cfg] toolset $(toolset) not" 539 "previously configured; attempting to auto-configure now" ; 540 } 541 local t,v = [ MATCH "([^-]+)-?(.+)?" : $(toolset) ] ; 542 project.push-current ; 543 toolset.using $(t,v[1]) : $(t,v[2]) ; 544 project.pop-current ; 545 } 546 547 # Make sure we get an appropriate property into the build request in 548 # case toolset has been specified using the "--toolset=..." command-line 549 # option form. 550 if ! $(t) in $(.argv) $(feature-toolsets) 551 { 552 if $(.debug-config) 553 { 554 ECHO "notice:" "[cmdline-cfg]" adding toolset=$(t) to the build 555 request. ; 556 } 557 extra-properties += toolset=$(t) ; 558 } 559 } 560 561 return $(extra-properties) ; 562} 563 564 565# Returns whether the given project (identifed by its project module) should be 566# cleaned because it or any of its parent projects have already been marked as 567# needing to be cleaned in this build. As an optimization, will explicitly mark 568# all encountered project needing to be cleaned in case thay have not already 569# been marked so. 570# 571local rule should-clean-project ( project ) 572{ 573 if ! $(.should-clean-project.$(project))-is-defined 574 { 575 local r = "" ; 576 if ! [ project.is-jamroot-module $(project) ] 577 { 578 local parent = [ project.attribute $(project) parent-module ] ; 579 if $(parent) 580 { 581 r = [ should-clean-project $(parent) ] ; 582 } 583 } 584 .should-clean-project.$(project) = $(r) ; 585 } 586 587 return $(.should-clean-project.$(project)) ; 588} 589 590 591################################################################################ 592# 593# main() 594# ------ 595# 596################################################################################ 597 598{ 599 if --version in $(.argv) 600 { 601 version.print ; 602 EXIT ; 603 } 604 605 version.verify-engine-version ; 606 607 load-configuration-files ; 608 609 # Load explicitly specified toolset modules. 610 local extra-properties = [ process-explicit-toolset-requests ] ; 611 612 # Load the actual project build script modules. We always load the project 613 # in the current folder so 'use-project' directives have any chance of being 614 # seen. Otherwise, we would not be able to refer to subprojects using target 615 # ids. 616 local current-project ; 617 { 618 local current-module = [ project.find "." : "." ] ; 619 if $(current-module) 620 { 621 current-project = [ project.target $(current-module) ] ; 622 } 623 } 624 625 # Load the default toolset module if no other has already been specified. 626 if ! [ feature.values <toolset> ] 627 { 628 local default-toolset = $(.default-toolset) ; 629 local default-toolset-version = ; 630 if $(default-toolset) 631 { 632 default-toolset-version = $(.default-toolset-version) ; 633 } 634 else 635 { 636 default-toolset = gcc ; 637 if [ os.name ] = NT 638 { 639 default-toolset = msvc ; 640 } 641 else if [ os.name ] = VMS 642 { 643 default-toolset = vmsdecc ; 644 } 645 else if [ os.name ] = MACOSX 646 { 647 default-toolset = darwin ; 648 } 649 } 650 651 ECHO "warning: No toolsets are configured." ; 652 ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ; 653 ECHO "warning: If the default is wrong, your build may not work correctly." ; 654 ECHO "warning: Use the \"toolset=xxxxx\" option to override our guess." ; 655 ECHO "warning: For more configuration options, please consult" ; 656 ECHO "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ; 657 658 toolset.using $(default-toolset) : $(default-toolset-version) ; 659 } 660 661 662 # Parse command line for targets and properties. Note that this requires 663 # that all project files already be loaded. 664 # FIXME: This is not entirely true. Additional project files may be loaded 665 # only later via the project.find() rule when dereferencing encountered 666 # target ids containing explicit project references. See what to do about 667 # those as such 'lazy loading' may cause problems that are then extremely 668 # difficult to debug. 669 local build-request = [ build-request.from-command-line $(.argv) 670 $(extra-properties) ] ; 671 local target-ids = [ $(build-request).get-at 1 ] ; 672 local properties = [ $(build-request).get-at 2 ] ; 673 674 675 # Check that we actually found something to build. 676 if ! $(current-project) && ! $(target-ids) 677 { 678 import errors ; 679 errors.user-error no Jamfile "in" current directory found, and no target 680 references specified. ; 681 } 682 683 684 # Flags indicating that this build system run has been started in order to 685 # clean existing instead of create new targets. Note that these are not the 686 # final flag values as they may get changed later on due to some special 687 # targets being specified on the command line. 688 local clean ; if "--clean" in $(.argv) { clean = true ; } 689 local cleanall ; if "--clean-all" in $(.argv) { cleanall = true ; } 690 691 692 # List of explicitly requested files to build. Any target references read 693 # from the command line parameter not recognized as one of the targets 694 # defined in the loaded Jamfiles will be interpreted as an explicitly 695 # requested file to build. If any such files are explicitly requested then 696 # only those files and the targets they depend on will be built and they 697 # will be searched for among targets that would have been built had there 698 # been no explicitly requested files. 699 local explicitly-requested-files 700 701 702 # List of Boost Build meta-targets, virtual-targets and actual Jam targets 703 # constructed in this build system run. 704 local targets ; 705 local virtual-targets ; 706 local actual-targets ; 707 708 709 # Process each target specified on the command-line and convert it into 710 # internal Boost Build target objects. Detect special clean target. If no 711 # main Boost Build targets were explicitly requested use the current project 712 # as the target. 713 for local id in $(target-ids) 714 { 715 if $(id) = clean 716 { 717 clean = true ; 718 } 719 else 720 { 721 local t ; 722 if $(current-project) 723 { 724 t = [ $(current-project).find $(id) : no-error ] ; 725 } 726 else 727 { 728 t = [ find-target $(id) ] ; 729 } 730 731 if ! $(t) 732 { 733 ECHO "notice: could not find main target" $(id) ; 734 ECHO "notice: assuming it is a name of file to create." ; 735 explicitly-requested-files += $(id) ; 736 } 737 else 738 { 739 targets += $(t) ; 740 } 741 } 742 } 743 if ! $(targets) 744 { 745 targets += [ project.target [ project.module-name "." ] ] ; 746 } 747 748 if [ option.get dump-generators : : true ] 749 { 750 generators.dump ; 751 } 752 753 # We wish to put config.log in the build directory corresponding to Jamroot, 754 # so that the location does not differ depending on the directory we run the 755 # build from. The amount of indirection necessary here is scary. 756 local first-project = [ $(targets[0]).project ] ; 757 local first-project-root-location = [ $(first-project).get project-root ] ; 758 local first-project-root-module = [ project.load 759 $(first-project-root-location) ] ; 760 local first-project-root = [ project.target $(first-project-root-module) ] ; 761 local first-build-build-dir = [ $(first-project-root).build-dir ] ; 762 configure.set-log-file $(first-build-build-dir)/config.log ; 763 config-cache.load $(first-build-build-dir)/project-cache.jam ; 764 765 # Expand properties specified on the command line into multiple property 766 # sets consisting of all legal property combinations. Each expanded property 767 # set will be used for a single build run. E.g. if multiple toolsets are 768 # specified then requested targets will be built with each of them. 769 # The expansion is being performed as late as possible so that the feature 770 # validation is performed after all necessary modules (including project targets 771 # on the command line) have been loaded. 772 if $(properties) 773 { 774 expanded += [ build-request.convert-command-line-elements $(properties) ] ; 775 expanded = [ build-request.expand-no-defaults $(expanded) ] ; 776 local xexpanded ; 777 for local e in $(expanded) 778 { 779 xexpanded += [ property-set.create [ feature.split $(e) ] ] ; 780 } 781 expanded = $(xexpanded) ; 782 } 783 else 784 { 785 expanded = [ property-set.empty ] ; 786 } 787 788 # Now that we have a set of targets to build and a set of property sets to 789 # build the targets with, we can start the main build process by using each 790 # property set to generate virtual targets from all of our listed targets 791 # and any of their dependants. 792 for local p in $(expanded) 793 { 794 .command-line-free-features = [ property-set.create [ $(p).free ] ] ; 795 for local t in $(targets) 796 { 797 local g = [ $(t).generate $(p) ] ; 798 if ! [ class.is-a $(t) : project-target ] 799 { 800 .results-of-main-targets += $(g[2-]) ; 801 } 802 virtual-targets += $(g[2-]) ; 803 } 804 } 805 806 807 # Convert collected virtual targets into actual raw Jam targets. 808 for t in $(virtual-targets) 809 { 810 actual-targets += [ $(t).actualize ] ; 811 } 812 813 config-cache.save ; 814 815 816 # If XML data output has been requested prepare additional rules and targets 817 # so we can hook into Jam to collect build data while its building and have 818 # it trigger the final XML report generation after all the planned targets 819 # have been built. 820 if $(.out-xml) 821 { 822 # Get a qualified virtual target name. 823 rule full-target-name ( target ) 824 { 825 local name = [ $(target).name ] ; 826 local project = [ $(target).project ] ; 827 local project-path = [ $(project).get location ] ; 828 return $(project-path)//$(name) ; 829 } 830 831 # Generate an XML file containing build statistics for each constituent. 832 # 833 rule out-xml ( xml-file : constituents * ) 834 { 835 # Prepare valid XML header and footer with some basic info. 836 local nl = " 837" ; 838 local os = [ modules.peek : OS OSPLAT JAMUNAME ] "" ; 839 local timestamp = [ modules.peek : JAMDATE ] ; 840 local cwd = [ PWD ] ; 841 local command = $(.argv) ; 842 local bb-version = [ version.boost-build ] ; 843 .header on $(xml-file) = 844 "<?xml version=\"1.0\" encoding=\"utf-8\"?>" 845 "$(nl)<build format=\"1.0\" version=\"$(bb-version)\">" 846 "$(nl) <os name=\"$(os[1])\" platform=\"$(os[2])\"><![CDATA[$(os[3-]:J= )]]></os>" 847 "$(nl) <timestamp><![CDATA[$(timestamp)]]></timestamp>" 848 "$(nl) <directory><![CDATA[$(cwd)]]></directory>" 849 "$(nl) <command><![CDATA[\"$(command:J=\" \")\"]]></command>" 850 ; 851 .footer on $(xml-file) = 852 "$(nl)</build>" ; 853 854 # Generate the target dependency graph. 855 .contents on $(xml-file) += 856 "$(nl) <targets>" ; 857 for local t in [ virtual-target.all-targets ] 858 { 859 local action = [ $(t).action ] ; 860 if $(action) 861 # If a target has no action, it has no dependencies. 862 { 863 local name = [ full-target-name $(t) ] ; 864 local sources = [ $(action).sources ] ; 865 local dependencies ; 866 for local s in $(sources) 867 { 868 dependencies += [ full-target-name $(s) ] ; 869 } 870 871 local path = [ $(t).path ] ; 872 local jam-target = [ $(t).actual-name ] ; 873 874 .contents on $(xml-file) += 875 "$(nl) <target>" 876 "$(nl) <name><![CDATA[$(name)]]></name>" 877 "$(nl) <dependencies>" 878 "$(nl) <dependency><![CDATA[$(dependencies)]]></dependency>" 879 "$(nl) </dependencies>" 880 "$(nl) <path><![CDATA[$(path)]]></path>" 881 "$(nl) <jam-target><![CDATA[$(jam-target)]]></jam-target>" 882 "$(nl) </target>" 883 ; 884 } 885 } 886 .contents on $(xml-file) += 887 "$(nl) </targets>" ; 888 889 # Build $(xml-file) after $(constituents). Do so even if a 890 # constituent action fails and regenerate the xml on every bjam run. 891 INCLUDES $(xml-file) : $(constituents) ; 892 ALWAYS $(xml-file) ; 893 __ACTION_RULE__ on $(xml-file) = 894 build-system.out-xml.generate-action ; 895 out-xml.generate $(xml-file) ; 896 } 897 898 # The actual build actions are here; if we did this work in the actions 899 # clause we would have to form a valid command line containing the 900 # result of @(...) below (the name of the XML file). 901 # 902 rule out-xml.generate-action ( args * : xml-file 903 : command status start end user system : output ? ) 904 { 905 local contents = 906 [ on $(xml-file) return $(.header) $(.contents) $(.footer) ] ; 907 local f = @($(xml-file):E=$(contents)) ; 908 } 909 910 # Nothing to do here; the *real* actions happen in 911 # out-xml.generate-action. 912 actions quietly out-xml.generate { } 913 914 # Define the out-xml file target, which depends on all the targets so 915 # that it runs the collection after the targets have run. 916 out-xml $(.out-xml) : $(actual-targets) ; 917 918 # Set up a global __ACTION_RULE__ that records all the available 919 # statistics about each actual target in a variable "on" the --out-xml 920 # target. 921 # 922 rule out-xml.collect ( xml-file : target : command status start end user 923 system : output ? ) 924 { 925 local nl = " 926" ; 927 # Open the action with some basic info. 928 .contents on $(xml-file) += 929 "$(nl) <action status=\"$(status)\" start=\"$(start)\" end=\"$(end)\" user=\"$(user)\" system=\"$(system)\">" ; 930 931 # If we have an action object we can print out more detailed info. 932 local action = [ on $(target) return $(.action) ] ; 933 if $(action) 934 { 935 local action-name = [ $(action).action-name ] ; 936 local action-sources = [ $(action).sources ] ; 937 local action-props = [ $(action).properties ] ; 938 939 # The qualified name of the action which we created the target. 940 .contents on $(xml-file) += 941 "$(nl) <name><![CDATA[$(action-name)]]></name>" ; 942 943 # The sources that made up the target. 944 .contents on $(xml-file) += 945 "$(nl) <sources>" ; 946 for local source in $(action-sources) 947 { 948 local source-actual = [ $(source).actual-name ] ; 949 .contents on $(xml-file) += 950 "$(nl) <source><![CDATA[$(source-actual)]]></source>" ; 951 } 952 .contents on $(xml-file) += 953 "$(nl) </sources>" ; 954 955 # The properties that define the conditions under which the 956 # target was built. 957 .contents on $(xml-file) += 958 "$(nl) <properties>" ; 959 for local prop in [ $(action-props).raw ] 960 { 961 local prop-name = [ MATCH ^<(.*)>$ : $(prop:G) ] ; 962 .contents on $(xml-file) += 963 "$(nl) <property name=\"$(prop-name)\"><![CDATA[$(prop:G=)]]></property>" ; 964 } 965 .contents on $(xml-file) += 966 "$(nl) </properties>" ; 967 } 968 969 local locate = [ on $(target) return $(LOCATE) ] ; 970 locate ?= "" ; 971 .contents on $(xml-file) += 972 "$(nl) <jam-target><![CDATA[$(target)]]></jam-target>" 973 "$(nl) <path><![CDATA[$(target:G=:R=$(locate))]]></path>" 974 "$(nl) <command><![CDATA[$(command)]]></command>" 975 "$(nl) <output><![CDATA[$(output)]]></output>" ; 976 .contents on $(xml-file) += 977 "$(nl) </action>" ; 978 } 979 980 # When no __ACTION_RULE__ is set "on" a target, the search falls back to 981 # the global module. 982 module 983 { 984 __ACTION_RULE__ = build-system.out-xml.collect 985 [ modules.peek build-system : .out-xml ] ; 986 } 987 988 IMPORT 989 build-system : 990 out-xml.collect 991 out-xml.generate-action 992 : : 993 build-system.out-xml.collect 994 build-system.out-xml.generate-action 995 ; 996 } 997 998 local j = [ option.get jobs ] ; 999 if $(j) 1000 { 1001 modules.poke : PARALLELISM : $(j) ; 1002 } 1003 1004 local k = [ option.get keep-going : true : true ] ; 1005 if $(k) in "on" "yes" "true" 1006 { 1007 modules.poke : KEEP_GOING : 1 ; 1008 } 1009 else if $(k) in "off" "no" "false" 1010 { 1011 modules.poke : KEEP_GOING : 0 ; 1012 } 1013 else 1014 { 1015 EXIT "error: Invalid value for the --keep-going option" ; 1016 } 1017 1018 # The 'all' pseudo target is not strictly needed expect in the case when we 1019 # use it below but people often assume they always have this target 1020 # available and do not declare it themselves before use which may cause 1021 # build failures with an error message about not being able to build the 1022 # 'all' target. 1023 NOTFILE all ; 1024 1025 # And now that all the actual raw Jam targets and all the dependencies 1026 # between them have been prepared all that is left is to tell Jam to update 1027 # those targets. 1028 if $(explicitly-requested-files) 1029 { 1030 # Note that this case can not be joined with the regular one when only 1031 # exact Boost Build targets are requested as here we do not build those 1032 # requested targets but only use them to construct the dependency tree 1033 # needed to build the explicitly requested files. 1034 UPDATE $(explicitly-requested-files:G=e) $(.out-xml) ; 1035 } 1036 else if $(cleanall) 1037 { 1038 UPDATE clean-all ; 1039 } 1040 else if $(clean) 1041 { 1042 common.Clean clean : [ actual-clean-targets ] ; 1043 UPDATE clean ; 1044 } 1045 else 1046 { 1047 configure.print-configure-checks-summary ; 1048 1049 for local function in $(.pre-build-hook) 1050 { 1051 indirect.call $(function) ; 1052 } 1053 1054 DEPENDS all : $(actual-targets) ; 1055 if UPDATE_NOW in [ RULENAMES ] 1056 { 1057 local ok = [ UPDATE_NOW all ] ; 1058 # Force sequence updating of regular targets, then the xml 1059 # log output target. To ensure the output records all built 1060 # as otherwise if could execute out-of-sequence when 1061 # doing parallel builds. 1062 if $(.out-xml) 1063 { 1064 UPDATE_NOW $(.out-xml) : : ignore-minus-n ; 1065 } 1066 for local function in $(.post-build-hook) 1067 { 1068 indirect.call $(function) $(ok) ; 1069 } 1070 # Prevent automatic update of the 'all' target, now that we have 1071 # explicitly updated what we wanted. 1072 UPDATE ; 1073 } 1074 else 1075 { 1076 UPDATE all $(.out-xml) ; 1077 } 1078 } 1079} 1080