# Copyright 2003, 2005 Dave Abrahams # Copyright 2006 Rene Rivera # Copyright 2003, 2004, 2005, 2006 Vladimir Prus # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) # This file is part of Boost.Build version 2. You can think of it as # forming the main() routine. It is invoked by the bootstrapping code # in bootstrap.jam. # # The version of bootstrap.jam invoking this lives in # tools/build/kernel until BBv1 is retired, so that BBv1 can have its # bootstrap.jam in this directory. import project ; import targets ; import sequence ; import modules ; import feature ; import property-set ; import build-request ; import errors : error ; import virtual-target ; import "class" : new ; import toolset ; import regex ; import builtin ; import make ; import os ; import version ; # Returns the location of the build system. The primary use case # is building Boost, where it's sometimes needed to get location # of other components (like BoostBook files), and it's convenient # to use location relatively to Boost.Build path. rule location ( ) { local r = [ modules.binding build-system ] ; return $(r:P) ; } # Check if we can load 'test-config.jam'. If we can, load it and # ignore user configs. local argv = [ modules.peek : ARGV ] ; local test-config = [ GLOB [ os.environ BOOST_BUILD_PATH ] : test-config.jam ] ; local debug-config = [ MATCH ^(--debug-configuration)$ : [ modules.peek : ARGV ] ] ; if $(test-config) { if $(debug-config) { ECHO "notice: loading test-config.jam from" [ NORMALIZE_PATH $(test-config[1]) ] ; ECHO "notice: user-config.jam and site-config.jam will be ignored" ; } module test-config { import toolset : using : using ; } import test-config ; } local ignore-config ; if $(test-config) || --ignore-config in [ modules.peek : ARGV ] { ignore-config = true ; } local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ; # Unless ignore-config is set, load the configuration file in # $(path)/$(basename).jam local rule load-config ( basename : path + ) { if ! $(ignore-config) { if $(debug-config) { ECHO notice: searching \"$(path)\" for \"$(basename).jam\" ; local where = [ GLOB $(path) : $(basename).jam ] ; if $(where) { ECHO notice: loading $(basename).jam from [ NORMALIZE_PATH $(where[1]) ] ; } } modules.load $(basename) : : $(path) ; project.load-used-projects $(basename) ; } } # # Load site-config. # module site-config { import project : initialize ; initialize site-config ; } local site-path = /etc $(user-path) ; if [ os.name ] in NT CYGWIN { site-path = [ modules.peek : SystemRoot ] $(user-path) ; } load-config site-config : $(site-path) ; # # Load user-config. # module user-config { import project : initialize ; initialize user-config ; } local user-config-path = [ MATCH ^--user-config=(.*) : $(argv) ] ; if $(user-config-path) { if $(debug-config) { ECHO "Loading explicitly specifier user configuration file:" ; ECHO " $(user-config-path)" ; } modules.load user-config : $(user-config-path:BS) : $(user-config-path:D) ; project.load-used-projects user-config ; } else { load-config user-config : $(user-path) ; } # # Autoconfigure toolsets based on any instances of --toolset=xx,yy,...zz or # toolset=xx,yy,...zz in the command line # local option-toolsets = [ regex.split-list [ MATCH ^--toolset=(.*) : $(argv) ] : "," ] ; local feature-toolsets = [ regex.split-list [ MATCH ^toolset=(.*) : $(argv) ] : "," ] ; # if the user specified --toolset=..., we need to add toolset=... to # the build request local extra-build-request ; if ! $(ignore-config) { for local t in $(option-toolsets) $(feature-toolsets) { # Parse toolset-version/properties local (t-v,t,v) = [ MATCH (([^-/]+)-?([^/]+)?)/?.* : $(t) ] ; local toolset-version = $((t-v,t,v)[1]) ; local toolset = $((t-v,t,v)[2]) ; local version = $((t-v,t,v)[3]) ; if $(debug-config) { ECHO notice: [cmdline-cfg] Detected command-line request for $(toolset-version): toolset= \"$(toolset)\" "version= \""$(version)\" ; } local known ; # if the toolset isn't known, configure it now. if $(toolset) in [ feature.values ] { known = true ; } if $(known) && $(version) && ! [ feature.is-subvalue toolset : $(toolset) : version : $(version) ] { known = ; } if ! $(known) { if $(debug-config) { ECHO notice: [cmdline-cfg] toolset $(toolset-version) not previously configured; configuring now ; } toolset.using $(toolset) : $(version) ; } else { if $(debug-config) { ECHO notice: [cmdline-cfg] toolset $(toolset-version) already configured ; } } # make sure we get an appropriate property into the build request in # case the user used the "--toolset=..." form if ! $(t) in $(argv) && ! $(t) in $(feature-toolsets) { if $(debug-config) { ECHO notice: [cmdline-cfg] adding toolset=$(t) "to build request." ; } extra-build-request += toolset=$(t) ; } } } if USER_MODULE in [ RULENAMES ] { USER_MODULE site-config user-config ; } if --version in [ modules.peek : ARGV ] { version.print ; EXIT ; } # We always load project in "." so that 'use-project' directives has # any chance of been seen. Otherwise, we won't be able to refer to # subprojects using target ids. if [ project.find "." : "." ] { current-project = [ project.target [ project.load "." ] ] ; } if ! [ feature.values ] { local default-toolset = gcc ; if [ os.name ] = NT { default-toolset = msvc ; } ECHO "warning: No toolsets are configured." ; ECHO "warning: Configuring default toolset" \"$(default-toolset)\". ; ECHO "warning: If the default is wrong, you may not be able to build C++ programs." ; ECHO "warning: Use the \"--toolset=xxxxx\" option to override our guess." ; ECHO "warning: For more configuration options, please consult" ; ECHO "warning: http://boost.org/boost-build2/doc/html/bbv2/advanced/configuration.html" ; if ! $(ignore-config) { toolset.using $(default-toolset) ; } } build-request = [ build-request.from-command-line [ modules.peek : ARGV ] $(extra-build-request) ] ; properties = [ $(build-request).get-at 2 ] ; if $(properties) { expanded = [ build-request.expand-no-defaults $(properties) ] ; local xexpanded ; for local e in $(expanded) { xexpanded += [ property-set.create [ feature.split $(e) ] ] ; } expanded = $(xexpanded) ; } else { expanded = [ property-set.empty ] ; } local target-ids = [ $(build-request).get-at 1 ] ; local targets local clean ; if "--clean-all" in [ modules.peek : ARGV ] { cleanall = true ; } if "--clean" in [ modules.peek : ARGV ] { clean = true ; } local bjam-targets ; # Given a target it, try to find and return corresponding target. # This is only invoked when there's no Jamfile in "." # This code somewhat duplicates code in project-target.find but we can't reuse # that code without project-targets instance. rule find-target ( target-id ) { local split = [ MATCH (.*)//(.*) : $(target-id) ] ; local pm ; if $(split) { pm = [ project.find $(split[1]) : "." ] ; } else { pm = [ project.find $(target-id) : "." ] ; } local result ; if $(pm) { result = [ project.target $(pm) ] ; } if $(split) { result = [ $(result).find $(split[2]) ] ; } return $(result) ; } if ! $(current-project) { if ! $(target-ids) { ECHO "error: no Jamfile in current directory found, and no target references specified." ; EXIT ; } } for local id in $(target-ids) { if $(id) = clean { clean = true ; } else { local t ; if $(current-project) { t = [ $(current-project).find $(id) : no-error ] ; } else { t = [ find-target $(id) ] ; } if ! $(t) { ECHO "notice: could not find main target " $(id) ; ECHO "notice: assuming it's a name of file to create " ; bjam-targets += $(id) ; } else { targets += $(t) ; } } } if ! $(targets) { targets += [ project.target [ project.module-name "." ] ] ; } virtual-targets = ; # Virtual targets obtained when building main targets references on # the command line. When running # # bjam --clean main_target # # we want to clean the files that belong only to that main target, # so we need to record which targets are produced. local results-of-main-targets ; for local p in $(expanded) { for local t in $(targets) { local g = [ $(t).generate $(p) ] ; if ! [ class.is-a $(t) : project-target ] { results-of-main-targets += $(g[2-]) ; } virtual-targets += $(g[2-]) ; } } # The cleaning is tricky. Say, if # user says: # # bjam --clean foo # # where 'foo' is a directory, then we want to clean targets # which are in 'foo' or in any children Jamfiles, but not in any # unrelated Jamfiles. So, we collect the list of project under which # cleaning is allowed. # local projects-to-clean ; local targets-to-clean ; if $(clean) || $(clean-all) { for local t in $(targets) { if [ class.is-a $(t) : project-target ] { projects-to-clean += [ $(t).project-module ] ; } } local subvariants ; for local t in $(results-of-main-targets) { # Don't include roots or sources. targets-to-clean += [ virtual-target.traverse $(t) ] ; } targets-to-clean = [ sequence.unique $(targets-to-clean) ] ; } # Returns 'true' if 'project' is a child of 'current-project', # possibly indirect, or is equal to 'project'. # Returns 'false' otherwise. rule is-child ( project ) { if ! $(.is-child.$(project)) { local r = false ; if $(project) in $(projects-to-clean) { r = true ; } else { local parent = [ project.attribute $(project) parent-module ] ; if $(parent) && $(parent) != user-config { r = [ is-child $(parent) ] ; } } .is-child.$(project) = $(r) ; } return $(.is-child.$(project)) ; } actual-targets = ; for t in $(virtual-targets) { actual-targets += [ $(t).actualize ] ; } NOTFILE all ; DEPENDS all : $(actual-targets) ; if $(bjam-targets) { UPDATE $(bjam-targets:G=e) ; } else if $(cleanall) { UPDATE clean-all ; } else if $(clean) { local to-clean ; for local t in [ virtual-target.all-targets ] { local p = [ $(t).project ] ; # Remove only derived targets. if [ $(t).action ] { if $(t) in $(targets-to-clean) || [ is-child [ $(p).project-module ] ] = true { to-clean += $(t) ; } } } local to-clean-actual ; for local t in $(to-clean) { to-clean-actual += [ $(t).actualize ] ; } common.Clean clean : $(to-clean-actual) ; UPDATE clean ; } else { UPDATE all ; }