# Boost.Build support specific for the Boost C++ Libraries. # Copyright Vladimir Prus 2002-2010. # Copyright Dave Abrahams 2005-2006. # Copyright Rene Rivera 2005-2007. # Copyright Douglas Gregor 2005. # # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) import "class" : new ; import common ; import configure ; import build-system ; import generate ; import modules ; import option ; import os ; import package ; import path ; import project ; import regex ; import sequence ; import set ; import targets ; import feature ; import property ; import version : version-less ; ############################################################################## # # 0. General setup. Parse options, check them. # ############################################################################## BOOST_ROOT = [ modules.binding $(__name__) ] ; BOOST_ROOT = $(BOOST_ROOT:D) ; rule set-version ( version ) { BOOST_VERSION = $(version) ; local version-tag = [ MATCH "^([^.]+)[.]([^.]+)[.]([^.]+)" : $(BOOST_VERSION) ] ; if $(version-tag[3]) = 0 { version-tag = $(version-tag[1-2]) ; } BOOST_VERSION_TAG = $(version-tag:J=_) ; } # Option to choose how many variants to build. The default is "minimal". build-type = [ option.get build-type ] ; build-type ?= minimal ; if ! ( $(build-type) in complete minimal ) { EXIT The value of the --build-type option should be either 'complete' or 'minimal' ; } # What kind of layout are we doing? layout = [ option.get layout : "" ] ; layout = [ MATCH (versioned|tagged|system)(-(.+))? : $(layout) ] ; if $(layout[3]) { layout-version = $(layout[3]) ; layout = $(layout[1]) ; if [ version-less [ regex.split $(layout-version) "[.]" ] : 1 66 ] { layout-version = 1.40 ; } else if [ version-less [ regex.split $(layout-version) "[.]" ] : 1 69 ] { layout-version = 1.66 ; } } layout-version ?= 1.69 ; # On Windows, we used versioned layout by default in order to be compatible with # autolink. On other systems, we use system layout which is what every other # program uses. Note that the Windows check is static, and will not be affected # by specific build properties used. if ! $(layout) { if [ os.name ] = NT { layout = versioned ; } else { layout = system ; } } layout-$(layout) = true ; if $(layout) = system && $(build-type) = complete { ECHO error\: Cannot use --layout=system with --build-type complete. ; ECHO error\: Please use either --layout=versioned or --layout=tagged ; ECHO error\: if you wish to build multiple variants. ; if [ os.name ] != NT { ECHO error\: Note that --layout=system is used by default on Unix starting with Boost 1.40. ; } EXIT ; } # Possible stage only location. stage-locate = [ option.get stagedir ] ; if $(stage-locate) { stage-locate = [ path.root [ path.make $(stage-locate) ] [ path.pwd ] ] ; } else { stage-locate = $(BOOST_ROOT)/stage ; } BOOST_STAGE_LOCATE = $(stage-locate) ; # Custom build ID. build-id = [ option.get buildid ] ; if $(build-id) { BUILD_ID = [ regex.replace $(build-id) "[*\\/:.\"\' ]" _ ] ; } # Python build id (for Python libraries only). python-id = [ option.get "python-buildid" ] ; if $(python-id) { PYTHON_ID = [ regex.replace $(python-id) "[*\\/:.\"\']" _ ] ; } if $(layout) = versioned { switch $(layout-version) { case 1.40 : .format-name-args = ; case 1.66 : .format-name-args = ; case 1.69 : .format-name-args = ; } } else if $(layout) = tagged { switch $(layout-version) { case 1.40 : .format-name-args = ; case 1.66 : .format-name-args = ; case 1.69 : .format-name-args = ; } } else if $(layout) = system { .format-name-args = ; } else { .format-name-error = true ; } ################################################################################ # # 1. 'tag' function adding decorations suitable to the properties if versioned # or tagged layout is requested. Called from Jamroot. # ################################################################################ rule tag ( name : type ? : property-set ) { if $(type) in STATIC_LIB SHARED_LIB IMPORT_LIB { local args = $(.format-name-args) ; if $(layout) = versioned { args += -$(BOOST_VERSION_TAG) ; } local result = [ common.format-name $(args) -$(BUILD_ID) : $(name) : $(type) : $(property-set) ] ; if $(.format-name-error) { EXIT error\: invalid layout '$(layout:E=)' ; } # Optionally add version suffix. On NT, library with version suffix will # not be recognized by linkers. On CYGWIN, we get strage duplicate # symbol errors when library is generated with version suffix. On OSX, # version suffix is not needed -- the linker expects the # libFoo.1.2.3.dylib format. AIX linkers do not accept version suffixes # either. Pgi compilers can not accept a library with version suffix. if $(type) = SHARED_LIB && ! [ $(property-set).get ] in windows cygwin darwin aix && ! [ $(property-set).get ] in pgi { result = $(result).$(BOOST_VERSION) ; } return $(result) ; } } # Specialized tag function to use for libraries linking to Python. # Appends value of --python-buildid if provided. rule python-tag ( name : type ? : property-set ) { local result = $(name) ; if $(type) in STATIC_LIB SHARED_LIB IMPORT_LIB { # Add Python version suffix local version = [ $(property-set).get ] ; local major-minor = [ MATCH "^([0-9]+)\.([0-9]+)" : $(version) ] ; local suffix = $(major-minor:J="") ; if $(suffix) { result = $(result)$(suffix) ; } # Add PYTHON_ID if supplied if $(PYTHON_ID) { result = $(result)-$(PYTHON_ID) ; } } # forward to the boost tagging rule return [ tag $(result) : $(type) : $(property-set) ] ; } ################################################################################ # # 2. Declare targets that build and install all libraries. Specifically: # # - 'stage-proper' that puts all libraries in stage/lib # - 'install-proper' that install libraries and headers to system location # ################################################################################ rule declare_install_and_stage_proper_targets ( libraries * ) { local p = [ project.current ] ; alias install-proper : libs/$(libraries)/build//install ; $(p).mark-target-as-explicit install-proper ; alias stage-proper : libs/$(libraries)/build//stage ; $(p).mark-target-as-explicit stage-proper ; } ################################################################################ # # 3. Declare top-level targets 'stage' and 'install'. These examine the # --build-type option and, in case it is 'complete', build the 'install-proper' # and 'stage-proper' targets with a number of property sets. # ################################################################################ rule emit-shared-static-warning ( ) { if ! $(.shared-static-warning-emitted) { ECHO "" ; ECHO "warning: The configuration link=shared, runtime-link=static is disabled" ; ECHO "warning: by default as being too dangerous to use, and will not be built." ; ECHO "warning: To enable it, use --allow-shared-static." ; ECHO "" ; .shared-static-warning-emitted = 1 ; } } class top-level-target : alias-target-class { import modules ; import boostcpp ; rule __init__ ( name : project : sources * : requirements * : default-build * : usage-requirements * ) { alias-target-class.__init__ $(name) : $(project) : $(sources) : $(requirements) : $(default-build) : $(usage-requirements) ; self.build-type = [ modules.peek boostcpp : build-type ] ; # On Linux, we build the release variant by default, since few users # will ever want to debug C++ Boost libraries, and there is no ABI # incompatibility between debug and release variants. We build shared # and static libraries since that is what most packages seem to provide # (.so in libfoo and .a in libfoo-dev). self.minimal-properties = [ property-set.create release multi shared static shared ] ; # On Windows, new IDE projects use: # # runtime-link=dynamic, threading=multi, variant=(debug|release) # # and in addition, C++ Boost's autolink defaults to static linking. self.minimal-properties-win = [ property-set.create debug release multi static shared 32 64 ] ; self.complete-properties = [ property-set.create debug release multi shared static shared static ] ; self.complete-properties-win = [ property-set.create debug release multi shared static shared static 32 64 ] ; } rule generate ( property-set ) { modules.poke : top-level-targets : [ modules.peek : top-level-targets ] $(self.name) ; local os = [ $(property-set).get ] ; # Because we completely override the parent's 'generate' we need to # check for default feature values ourselves. if ! $(os) { os = [ feature.defaults ] ; os = $(os:G=) ; } local build-type-set ; if $(self.build-type) = minimal { if $(os) = windows { build-type-set = $(self.minimal-properties-win) ; } else { build-type-set = $(self.minimal-properties) ; } } else if $(self.build-type) = complete { if $(os) = windows { build-type-set = $(self.complete-properties-win) ; } else { build-type-set = $(self.complete-properties) ; } } else { import errors ; errors.error "Unknown build type" ; } if $(build-type-set) { local expanded = [ targets.apply-default-build $(property-set) : $(build-type-set) ] ; # Filter inappopriate combinations. local filtered ; local skipped ; local argv = [ modules.peek : ARGV ] ; for local p in $(expanded) { # See comment in handle-static-runtime regarding this logic. if [ $(p).get ] = shared && [ $(p).get ] = static && [ $(p).get ] != cw && ! --allow-shared-static in $(argv) { # Skip this. skipped += $(p) ; } else { filtered += $(p) ; } } if $(expanded) = $(skipped) { boostcpp.emit-shared-static-warning ; } return [ build-multiple $(filtered) ] ; } } rule build-multiple ( property-sets * ) { local usage-requirements = [ property-set.empty ] ; local result ; for local p in $(property-sets) { local r = [ alias-target-class.generate $(p) ] ; if $(r) { usage-requirements = [ $(usage-requirements).add $(r[1]) ] ; result += $(r[2-]) ; } } return $(usage-requirements) [ sequence.unique $(result) ] ; } } rule declare_top_level_targets ( libraries * ) { declare_install_and_stage_proper_targets $(libraries) ; targets.create-metatarget top-level-target : [ project.current ] : install : install-proper ; targets.create-metatarget top-level-target : [ project.current ] : stage : stage-proper headers ; p = [ project.current ] ; $(p).mark-target-as-explicit install stage ; # This target is built by default, and will forward to 'stage' after # producing some explanations. targets.create-metatarget top-level-target : [ project.current ] : forward : explain stage ; } ################################################################################ # # 4. Add hook to report configuration before the build, and confirmation with # setup instructions after the build. # ################################################################################ message explain : "\nBuilding the Boost C++ Libraries.\n\n" ; local p = [ project.current ] ; $(p).mark-target-as-explicit explain ; rule pre-build ( ) { local tl = [ modules.peek : top-level-targets ] ; if stage in $(tl) || install in $(tl) { # FIXME: Remove 'if' when Boost regression tests start using trunk bjam. if PAD in [ RULENAMES ] { configure.print-component-configuration ; } } } IMPORT $(__name__) : pre-build : : $(__name__).pre-build ; build-system.set-pre-build-hook $(__name__).pre-build ; rule post-build ( ok ? ) { if forward in [ modules.peek : top-level-targets ] { if $(ok) { local include-path = [ path.native $(BOOST_ROOT) ] ; local stage-abs = [ path.native $(stage-locate)/lib ] ; ECHO " The Boost C++ Libraries were successfully built! The following directory should be added to compiler include paths: $(include-path) The following directory should be added to linker library paths: $(stage-abs) " ; } } } IMPORT $(__name__) : post-build : : $(__name__).post-build ; build-system.set-post-build-hook $(__name__).post-build ; ################################################################################ # # 5. Top-level setup. # ################################################################################ # Decides which libraries are to be installed by looking at --with- # --without- arguments. Returns the list of directories under "libs" # which must be built and installed. # rule libraries-to-install ( existing-libs * ) { local argv = [ modules.peek : ARGV ] ; local with-parameter = [ MATCH ^--with-(.*) : $(argv) ] ; local without-parameter = [ MATCH ^--without-(.*) : $(argv) ] ; if ! $(with-parameter) && ! $(without-parameter) { # Nothing is specified on command line. See if maybe project-config.jam # has some choices. local libs = [ modules.peek project-config : libraries ] ; with-parameter = [ MATCH ^--with-(.*) : $(libs) ] ; without-parameter = [ MATCH ^--without-(.*) : $(libs) ] ; } # Do some checks. if $(with-parameter) && $(without-parameter) { EXIT error\: both --with- and --without- specified ; } local wrong = [ set.difference $(with-parameter) : $(existing-libs) ] ; if $(wrong) { EXIT error\: wrong library name '$(wrong[1])' in the --with- option. ; } local wrong = [ set.difference $(without-parameter) : $(existing-libs) ] ; if $(wrong) { EXIT error\: wrong library name '$(wrong[1])' in the --without- option. ; } if $(with-parameter) { return [ set.intersection $(existing-libs) : $(with-parameter) ] ; } else { return [ set.difference $(existing-libs) : $(without-parameter) ] ; } } rule declare-targets ( all-libraries * ) { configure.register-components $(all-libraries) ; # Select the libraries to install. libraries = [ libraries-to-install $(all-libraries) ] ; configure.components-building $(libraries) ; if [ option.get "show-libraries" : : true ] { ECHO The following libraries require building\: ; for local l in $(libraries) { ECHO " - $(l)" ; } EXIT ; } declare_top_level_targets $(libraries) ; } # Returns the properties identifying the toolset. We'll use them # below to configure checks. These are essentially same as in # configure.builds, except we don't use address-model and # architecture - as we're trying to detect them here. # rule toolset-properties ( properties * ) { local toolset = [ property.select : $(properties) ] ; local toolset-version-property = "" ; return [ property.select $(toolset-version-property) : $(properties) ] ; } feature.feature deduced-address-model : 32 64 : propagated optional composite hidden ; feature.compose 32 : 32 ; feature.compose 64 : 64 ; rule deduce-address-model ( properties * ) { local result ; local filtered = [ toolset-properties $(properties) ] ; local names = 32 64 ; local idx = [ configure.find-builds "default address-model" : $(filtered) : /boost/architecture//32 "32-bit" : /boost/architecture//64 "64-bit" ] ; result = $(names[$(idx)]) ; if $(result) { # Normally, returning composite feature here is equivalent to forcing # consituent properties as well. But we only want to indicate toolset # deduced default, so also pick whatever address-model is explicitly # specified, if any. result = $(result) [ property.select : $(properties) ] ; } return $(result) ; } rule address-model ( ) { return @boostcpp.deduce-address-model ; } local deducable-architectures = arm mips1 power riscv s390x sparc x86 combined ; feature.feature deduced-architecture : $(deducable-architectures) : propagated optional composite hidden ; for a in $(deducable-architectures) { feature.compose $(a) : $(a) ; } rule deduce-architecture ( properties * ) { local result ; local filtered = [ toolset-properties $(properties) ] ; local names = arm mips1 power riscv s390x sparc x86 combined ; local idx = [ configure.find-builds "default architecture" : $(filtered) : /boost/architecture//arm : /boost/architecture//mips1 : /boost/architecture//power : /boost/architecture//riscv : /boost/architecture//s390x : /boost/architecture//sparc : /boost/architecture//x86 : /boost/architecture//combined ] ; result = $(names[$(idx)]) ; if $(result) { # See comment in deduce-address-model. result = $(result) [ property.select : $(properties) ] ; } return $(result) ; } rule architecture ( ) { return @boostcpp.deduce-architecture ; }