diff --git a/.travis.yml b/.travis.yml index 9413d7206..78090f94c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,9 @@ sudo: false -os: - - linux - - osx +branches: + only: + - master + - develop + - /feature\/.*/ env: matrix: - TRAVIS_EMPTY_JOB_WORKAROUND=true @@ -9,15 +11,39 @@ matrix: exclude: - env: TRAVIS_EMPTY_JOB_WORKAROUND=true include: + - os: linux + dist: precise + env: TOOLSET=gcc + - os: linux + dist: precise + env: TOOLSET=clang - os: linux dist: trusty env: TOOLSET=gcc - os: linux dist: trusty env: TOOLSET=clang + - os: osx + osx_image: xcode9.2 + env: TOOLSET=clang + - os: osx + osx_image: xcode9.1 + env: TOOLSET=clang + - os: osx + osx_image: xcode9 + env: TOOLSET=clang - os: osx osx_image: xcode8.3 env: TOOLSET=clang + - os: osx + osx_image: xcode8 + env: TOOLSET=clang + - os: osx + osx_image: xcode7.3 + env: TOOLSET=clang + - os: osx + osx_image: xcode6.4 + env: TOOLSET=clang language: cpp script: - cd src/engine diff --git a/appveyor.yml b/appveyor.yml index 4b0b6a4c2..cec088573 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,6 +4,11 @@ # # Copyright Rene Rivera 2015-2017. +branches: + only: + - master + - develop + - /feature\/.*/ image: - Visual Studio 2017 diff --git a/src/build-system.jam b/src/build-system.jam index 185526c70..c111a2312 100644 --- a/src/build-system.jam +++ b/src/build-system.jam @@ -101,16 +101,21 @@ rule set-default-toolset ( toolset : version ? ) .default-toolset-version = $(version) ; } -rule set-pre-build-hook ( function ) +rule add-pre-build-hook ( function ) { - .pre-build-hook = $(function) ; + .pre-build-hook += $(function) ; } -rule set-post-build-hook ( function ) +rule add-post-build-hook ( function ) { - .post-build-hook = $(function) ; + .post-build-hook += $(function) ; } +# Old names for backwards compatibility +IMPORT build-system : add-pre-build-hook : build-system : set-pre-build-hook ; +IMPORT build-system : add-post-build-hook : build-system : set-post-build-hook ; +EXPORT build-system : set-pre-build-hook set-post-build-hook ; + ################################################################################ # # Local rules. @@ -267,6 +272,35 @@ local rule load-config ( module-name : filename : path + : must-find ? ) return $(where) ; } +# Parses options of the form --xxx-config=path/to/config.jam +# and environmental variables of the form BOOST_BUILD_XXX_CONFIG. +# If not found, returns an empty list. The option may be +# explicitly set to the empty string, in which case, handle-config-option +# will return "". +# +local rule handle-config-option ( name : env ? ) +{ + local result = [ MATCH ^--$(name)=(.*)$ : $(.argv) ] ; + if ! $(result)-is-defined && $(env) + { + result = [ os.environ $(env) ] ; + } + # Special handling for the case when the OS does not strip the quotes + # around the file name, as is the case when using Cygwin bash. + result = [ utility.unquote $(result[-1]) ] ; + if ! $(result) + { + return $(result) ; + } + # Treat explicitly entered user paths as native OS path + # references and, if non-absolute, root them at the current + # working directory. + result = [ path.make $(result) ] ; + result = [ path.root $(result) [ path.pwd ] ] ; + result = [ path.native $(result) ] ; + return $(result) ; +} + # Loads all the configuration files used by Boost Build in the following order: # @@ -277,11 +311,21 @@ local rule load-config ( module-name : filename : path + : must-find ? ) # files will not be. If a relative path is specified, file is searched for in # the current folder. # +# -- all-config -- +# Loaded only if specified on the command-line using the --config command +# line option. If a file name is specified, it must exist and replaces all +# other configuration files. If an empty file name is passed, no configuration +# files will be loaded. +# # -- site-config -- -# Always named site-config.jam. Will only be found if located on the system -# root path (Windows), /etc (non-Windows), user's home folder or the Boost Build -# path, in that order. Not loaded in case the test-config configuration file is -# loaded or the --ignore-site-config command-line option is specified. +# Named site-config.jam by default or may be named explicitly using the +# --site-config command-line option. If named explicitly, the file is found +# relative to the current working directory and must exist. If the default one +# is used then it is searched for in the system root path (Windows), +# /etc (non-Windows), user's home folder or the Boost Build path, in that +# order. Not loaded in case the test-config configuration file is loaded, +# the file is explicitly set to the empty string or the --ignore-site-config +# command-line option is specified. # # -- user-config -- # Named user-config.jam by default or may be named explicitly using the @@ -294,8 +338,12 @@ local rule load-config ( module-name : filename : path + : must-find ? ) # file must exist. # # -- project-config -- -# Always named project-config.jam. Looked up in the current working folder and -# then upwards through its parents up to the root folder. +# Named project-config.jam. Looked up in the current working folder and +# then upwards through its parents up to the root folder. It may also be +# named explicitly using the --project-config command-line option. If a file +# is specified explicitly, it is found relative to the current working +# directory and must exist. If an empty file name is passed, project-config +# will not be loaded. # # Test configurations have been added primarily for use by Boost Build's # internal unit testing system but may be used freely in other places as well. @@ -305,14 +353,11 @@ local rule load-configuration-files # Flag indicating that site configuration should not be loaded. local ignore-site-config = [ MATCH ^(--ignore-site-config)$ : $(.argv) ] ; + local ignore-user-config ; + local ignore-project-config ; initialize-config-module test-config ; - local test-config = [ MATCH ^--test-config=(.*)$ : $(.argv) ] ; - local uq = [ MATCH \"(.*)\" : $(test-config) ] ; - if $(uq) - { - test-config = $(uq) ; - } + local test-config = [ handle-config-option test-config ] ; if $(test-config) { local where = [ load-config test-config : $(test-config:BS) : @@ -325,12 +370,32 @@ local rule load-configuration-files ECHO "notice: be ignored due to the test configuration being" "loaded." ; } + ignore-site-config = true ; + ignore-user-config = true ; } - else + } + + initialize-config-module all-config ; + local all-config = [ handle-config-option config ] ; + if $(all-config) + { + load-config all-config : $(all-config:D=) : $(all-config:D) : required ; + if $(.debug-config) { - test-config = ; + ECHO "notice: Regular configuration files will be ignored due" ; + ECHO "notice: to the global configuration being loaded." ; } } + if $(all-config)-is-defined + { + if $(.debug-config) && ! $(all-config) + { + ECHO "notice: Configuration file loading explicitly disabled." ; + } + ignore-site-config = true ; + ignore-user-config = true ; + ignore-project-config = true ; + } local user-path = [ os.home-directories ] [ os.environ BOOST_BUILD_PATH ] ; local site-path = /etc $(user-path) ; @@ -339,55 +404,52 @@ local rule load-configuration-files site-path = [ modules.peek : SystemRoot ] $(user-path) ; } - if $(.debug-config) && ! $(test-config) && $(ignore-site-config) + if $(.debug-config) && $(ignore-site-config) = --ignore-site-config { ECHO "notice: Site configuration files will be ignored due to the" ; ECHO "notice: --ignore-site-config command-line option." ; } initialize-config-module site-config ; - if ! $(test-config) && ! $(ignore-site-config) + if ! $(ignore-site-config) { - load-config site-config : site-config.jam : $(site-path) ; + local site-config = [ handle-config-option site-config ] ; + if $(site-config) + { + load-config site-config : $(site-config:D=) : $(site-config:D) + : must-exist ; + } + else if ! $(site-config)-is-defined + { + load-config site-config : site-config.jam : $(site-path) ; + } + else if $(.debug-config) + { + ECHO notice: Site configuration file loading explicitly disabled. ; + } } initialize-config-module user-config ; - if ! $(test-config) + if ! $(ignore-user-config) { - local user-config = [ MATCH ^--user-config=(.*)$ : $(.argv) ] ; - user-config = $(user-config[-1]) ; - user-config ?= [ os.environ BOOST_BUILD_USER_CONFIG ] ; - # Special handling for the case when the OS does not strip the quotes - # around the file name, as is the case when using Cygwin bash. - user-config = [ utility.unquote $(user-config) ] ; - local explicitly-requested = $(user-config) ; - user-config ?= user-config.jam ; + local user-config = + [ handle-config-option user-config : BOOST_BUILD_USER_CONFIG ] ; if $(user-config) { - if $(explicitly-requested) + if $(.debug-config) { - # Treat explicitly entered user paths as native OS path - # references and, if non-absolute, root them at the current - # working directory. - user-config = [ path.make $(user-config) ] ; - user-config = [ path.root $(user-config) [ path.pwd ] ] ; - user-config = [ path.native $(user-config) ] ; - - if $(.debug-config) - { - ECHO notice: Loading explicitly specified user configuration - file: ; - ECHO " $(user-config)" ; - } - - load-config user-config : $(user-config:BS) : $(user-config:D) - : must-exist ; - } - else - { - load-config user-config : $(user-config) : $(user-path) ; + ECHO notice: Loading explicitly specified user configuration + file: ; + ECHO " $(user-config)" ; } + + load-config user-config : $(user-config:D=) : $(user-config:D) + : must-exist ; + } + else if ! $(user-config)-is-defined + { + load-config user-config : user-config.jam : $(user-path) ; } else if $(.debug-config) { @@ -401,15 +463,33 @@ local rule load-configuration-files # - We need to load project-config.jam before Jamroot # - We probably need to load project-config.jam even if there is no Jamroot # - e.g. to implement automake-style out-of-tree builds. - local file = [ path.glob "." : project-config.jam ] ; - if ! $(file) + if ! $(ignore-project-config) { - file = [ path.glob-in-parents "." : project-config.jam ] ; - } - if $(file) - { - initialize-config-module project-config : $(file:D) ; - load-config project-config : project-config.jam : $(file:D) ; + local project-config = [ handle-config-option project-config ] ; + if $(project-config) + { + initialize-config-module project-config : $(project-config:D=) ; + load-config project-config : $(project-config:D=) + : $(project-config:D) : must-exist ; + } + else if ! $(project-config)-is-defined + { + local file = [ path.glob "." : project-config.jam ] ; + if ! $(file) + { + file = [ path.glob-in-parents "." : project-config.jam ] ; + } + if $(file) + { + initialize-config-module project-config : $(file:D) ; + load-config project-config : project-config.jam : $(file:D) ; + } + } + else if $(.debug-config) + { + ECHO notice: Project configuration file loading explicitly + disabled. ; + } } project.end-load ; @@ -458,7 +538,9 @@ local rule process-explicit-toolset-requests "previously configured; attempting to auto-configure now" ; } local t,v = [ MATCH ([^-]+)-?(.+)? : $(toolset) ] ; + project.push-current ; toolset.using $(t,v[1]) : $(t,v[2]) ; + project.pop-current ; } # Make sure we get an appropriate property into the build request in @@ -963,9 +1045,9 @@ local rule should-clean-project ( project ) { configure.print-configure-checks-summary ; - if $(.pre-build-hook) + for local function in $(.pre-build-hook) { - $(.pre-build-hook) ; + $(function) ; } DEPENDS all : $(actual-targets) ; @@ -980,9 +1062,9 @@ local rule should-clean-project ( project ) { UPDATE_NOW $(.out-xml) : : ignore-minus-n ; } - if $(.post-build-hook) + for local function in $(.post-build-hook) { - $(.post-build-hook) $(ok) ; + $(function) $(ok) ; } # Prevent automatic update of the 'all' target, now that we have # explicitly updated what we wanted. diff --git a/src/build/ac.jam b/src/build/ac.jam index c01dc5997..718765649 100644 --- a/src/build/ac.jam +++ b/src/build/ac.jam @@ -11,6 +11,7 @@ import modules ; import "class" ; import errors ; import configure ; +import feature ; import project ; import virtual-target ; import generators ; @@ -22,11 +23,21 @@ project.initialize $(__name__) ; .project = [ project.current ] ; project ac ; +feature.feature ac.print-text : : free ; + rule generate-include ( target : sources * : properties * ) { - local header = [ property.select : $(properties) ] ; print.output $(target) ; - print.text "#include <$(header:G=)>\n" : true ; + local text = [ property.select : $(properties) ] ; + if $(text) + { + print.text $(text:G=) : true ; + } + else + { + local header = [ property.select : $(properties) ] ; + print.text "#include <$(header:G=)>\n" : true ; + } } rule generate-main ( target : sources * : properties * ) @@ -35,7 +46,7 @@ rule generate-main ( target : sources * : properties * ) print.text "int main() {}" : true ; } -rule find-include-path ( properties : header : provided-path ? ) +rule find-include-path ( properties : header : provided-path ? : test-source ? ) { if $(provided-path) && [ path.exists [ path.root $(header) $(provided-path) ] ] { @@ -43,21 +54,23 @@ rule find-include-path ( properties : header : provided-path ? ) } else { - local a = [ class.new action : ac.generate-include : [ property-set.create $(header) ] ] ; + local a = [ class.new action : ac.generate-include : [ property-set.create $(header) $(test-source) ] ] ; # Create a new CPP target named after the header. # Replace dots (".") in target basename for portability. local basename = [ regex.replace $(header:D=) "[.]" "_" ] ; local header-target = $(header:S=:B=$(basename)) ; local cpp = [ class.new file-target $(header-target:S=.cpp) exact : CPP : $(.project) : $(a) ] ; cpp = [ virtual-target.register $(cpp) ] ; + $(cpp).root true ; local result = [ generators.construct $(.project) $(header-target) : OBJ : $(properties) : $(cpp) : true ] ; + configure.maybe-force-rebuild $(result[2-]) ; local jam-targets ; - for t in $(result[2-]) + for local t in $(result[2-]) { jam-targets += [ $(t).actualize ] ; } if [ UPDATE_NOW $(jam-targets) : [ modules.peek configure : .log-fd ] - : ignore-minus-n : ignore-minus-q ] + : ignore-minus-n ] { return %default ; } @@ -66,7 +79,6 @@ rule find-include-path ( properties : header : provided-path ? ) rule construct-library ( name : property-set : provided-path ? ) { - property-set = [ $(property-set).refine [ property-set.create $(link-opt) ] ] ; local lib-props = [ $(property-set).add-raw $(name) $(provided-path) ] ; return [ generators.construct $(.project) lib-$(name) : SEARCHED_LIB : $(lib-props) : : true ] ; @@ -96,16 +108,18 @@ rule find-library ( properties : names + : provided-path ? ) [ property-set.empty ] ] ; local main.cpp = [ virtual-target.register [ class.new file-target main-$(name).cpp exact : CPP : $(.project) : $(a) ] ] ; + $(main.cpp).root true ; local test = [ generators.construct $(.project) $(name) : EXE : [ $(properties).add $(lib[1]) ] : $(main.cpp) $(lib[2-]) : true ] ; + configure.maybe-force-rebuild $(test[2-]) ; local jam-targets ; for t in $(test[2-]) { jam-targets += [ $(t).actualize ] ; } if [ UPDATE_NOW $(jam-targets) : [ modules.peek configure : .log-fd ] - : ignore-minus-n : ignore-minus-q ] + : ignore-minus-n ] { result = $(name) $(link-opts[1]) ; names-iter = ; link-opts = ; # break @@ -143,6 +157,11 @@ class ac-library : basic-target self.default-names = $(names) ; } + rule set-header-test ( source ) + { + self.header-test = $(source) ; + } + rule reconfigure ( include-path ? : library-path ? : library-name ? ) { if $(include-path) || $(library-path) || $(library-name) @@ -205,10 +224,7 @@ class ac-library : basic-target library-path ?= [ modules.peek : $(name:U)_LIBPATH ] ; } - local toolset = [ $(property-set).get ] ; - local toolset-version-property = "" ; - local relevant = [ property.select - $(toolset-version-property) : + local relevant = [ property.select [ configure.get-relevant-features ] : [ $(property-set).raw ] ] ; local key = ac-library-$(name)-$(relevant:J=-) ; @@ -236,7 +252,7 @@ class ac-library : basic-target } else { - local includes = [ ac.find-include-path $(property-set) : $(self.header) : $(include-path) ] ; + local includes = [ ac.find-include-path $(property-set) : $(self.header) : $(include-path) : $(self.header-test) ] ; local library = [ ac.find-library $(property-set) : $(libnames) : $(library-path) ] ; if $(includes) && $(library) { @@ -300,5 +316,8 @@ rule check-library ( target : true-properties * : false-properties * ) { local instance = [ class.new check-library-worker $(target) : $(true-properties) : $(false-properties) ] ; - return @$(instance).check ; + return @$(instance).check + [ property.evaluate-conditional-relevance + $(true-properties) $(false-properties) + : [ configure.get-relevant-features ] ] ; } diff --git a/src/build/build-request.jam b/src/build/build-request.jam index 3110713b7..4c767c3b2 100644 --- a/src/build/build-request.jam +++ b/src/build/build-request.jam @@ -38,6 +38,23 @@ rule expand-no-defaults ( property-sets * ) } +# Update the list of expected conflicts based on the new +# features. +# +local rule remove-conflicts ( conflicts * : features * ) +{ + local result ; + for local c in $(conflicts) + { + if ! [ set.intersection [ regex.split $(c) "/" ] : $(features) ] + { + result += $(c) ; + } + } + return $(result) ; +} + + # Implementation of x-product, below. Expects all the project files to already # be loaded. # @@ -47,36 +64,54 @@ local rule x-product-aux ( property-sets + ) local p = [ feature.split $(property-sets[1]) ] ; local f = [ set.difference $(p:G) : [ feature.free-features ] ] ; local seen ; + local extra-conflicts ; + # No conflict with things used at a higher level? if ! [ set.intersection $(f) : $(x-product-used) ] { local x-product-seen ; + local x-product-conflicts = + [ remove-conflicts $(x-product-conflicts) : $(f) ] ; { # Do not mix in any conflicting features. local x-product-used = $(x-product-used) $(f) ; if $(property-sets[2]) { - local rest = [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; + local rest = [ x-product-aux $(property-sets[2-]) ] ; result = $(property-sets[1])/$(rest) ; } - - result ?= $(property-sets[1]) ; + if ! $(x-product-conflicts) + { + result ?= $(property-sets[1]) ; + } } # If we did not encounter a conflicting feature lower down, do not # recurse again. - if ! [ set.intersection $(f) : $(x-product-seen) ] + if ! [ set.intersection $(f) : $(x-product-seen) ] + || [ remove-conflicts $(x-product-conflicts) : $(x-product-seen) ] { property-sets = ; } + else + { + # A property is only allowed to be absent if it conflicts + # with either a higher or lower layer. We don't need to + # bother setting this if we already know that we don't need + # to recurse again. + extra-conflicts = $(f:J=/) ; + } seen = $(x-product-seen) ; } if $(property-sets[2]) { - result += [ x-product-aux $(property-sets[2-]) : $(feature-space) ] ; + # Lower layers expansion must conflict with this + local x-product-conflicts = $(x-product-conflicts) $(extra-conflicts) ; + + result += [ x-product-aux $(property-sets[2-]) ] ; } # Note that we have seen these features so that higher levels will recurse @@ -90,13 +125,21 @@ local rule x-product-aux ( property-sets + ) # contain conflicting values for single-valued features. Expects all the project # files to already be loaded. # +# Formal definition: +# Returns all maximum non-conflicting subsets of property-sets. +# The result is a list of all property-sets p such that +# 1. p is composed by joining a subset of property-sets without removing +# duplicates +# 2. p contains at most one instance of every single-valued feature +# 3. Adding any additional element of property-sets to p be would +# violate (2) local rule x-product ( property-sets * ) { if $(property-sets).non-empty { # Prepare some "scoped globals" that can be used by the implementation # function, x-product-aux. - local x-product-seen x-product-used ; + local x-product-seen x-product-used x-product-conflicts ; return [ x-product-aux $(property-sets) : $(feature-space) ] ; } # Otherwise return empty. diff --git a/src/build/config-cache.jam b/src/build/config-cache.jam index ff69ff6f6..0c47917e2 100644 --- a/src/build/config-cache.jam +++ b/src/build/config-cache.jam @@ -67,6 +67,9 @@ rule load ( cache-file ) cache-file = [ path.native $(cache-file) ] ; if [ path.exists $(cache-file) ] && ! ( --reconfigure in [ modules.peek : ARGV ] ) { + FILE_CONTENTS on $(cache-file) = "" ; + config-cache.write $(cache-file) ; + UPDATE_NOW $(cache-file) : [ modules.peek configure : .log-fd ] ; include $(cache-file) ; } .cache-file = $(cache-file) ; diff --git a/src/build/configure.jam b/src/build/configure.jam index 78683d6a0..b5d85c456 100644 --- a/src/build/configure.jam +++ b/src/build/configure.jam @@ -20,7 +20,9 @@ import targets ; import config-cache ; import feature ; import modules ; +import sequence ; import utility ; +import virtual-target ; # The configure feature allows external definition of what features are @@ -144,6 +146,28 @@ rule print-configure-checks-summary ( ) } } +if --reconfigure in [ modules.peek : ARGV ] +{ + .reconfigure = true ; +} + +# Handle the --reconfigure option +rule maybe-force-rebuild ( targets * ) +{ + if $(.reconfigure) + { + local all-targets ; + for local t in $(targets) + { + all-targets += [ virtual-target.traverse $(t) ] ; + } + for local t in [ sequence.unique $(all-targets) ] + { + $(t).always ; + } + } +} + # Attempts to build a set of virtual targets rule try-build ( targets * : ps : what : retry ? ) { @@ -154,6 +178,8 @@ rule try-build ( targets * : ps : what : retry ? ) local result ; local jam-targets ; + maybe-force-rebuild $(targets) ; + for local t in $(targets) { jam-targets += [ $(t).actualize ] ; @@ -206,6 +232,76 @@ rule try-build ( targets * : ps : what : retry ? ) return $(result) ; } +# Attempts to build several sets of virtual targets. Returns the +# the index of the first set that builds. +rule try-find-build ( ps : what : * ) +{ + local args = 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; + # The outer layer only needs to check $(what), but we + # also need to check the individual elements, in case + # the set of targets has changed since the last build. + local cache-name = $(what) $($(args)[1]) [ $(ps).raw ] ; + cache-name = $(cache-name:J=-) ; + local value = [ config-cache.get $(cache-name) ] ; + + local result ; + local jam-targets ; + + maybe-force-rebuild $($(args)[2-]) ; + + if $(value) + { + local none = none ; # What to show when the argument + local name = $(value) ; + if $(name) != none + { + name = [ CALC $(name) + 2 ] ; + } + local x = [ PAD " - $(what)" : $(.width) ] ; + local y = [ PAD $($(name)[1]) : 3 ] ; + result = $(value) ; + log-check-result "$(x) : $(y) (cached)" ; + } + else + { + local x = [ PAD " - $(what)" : $(.width) ] ; + for local i in $(args) + { + local jam-targets ; + for local t in $($(i)[2-]) + { + jam-targets += [ $(t).actualize ] ; + } + if [ UPDATE_NOW $(jam-targets) : + $(.log-fd) : ignore-minus-n : ignore-minus-q ] + { + result = [ CALC $(i) - 2 ] ; + log-check-result "$(x) : $($(i)[1])" ; + break ; + } + } + if ! $(result) + { + result = none ; + } + } + if ! $(value) + { + if $(result) + { + config-cache.set $(cache-name) : $(result) ; + } + else + { + config-cache.set $(cache-name) : $(result) ; + } + } + if $(result) != none + { + return $(result) ; + } +} + # Attempt to build a metatarget named by 'metatarget-reference' # in context of 'project' with properties 'ps'. # Returns non-empty value if build is OK. @@ -232,7 +328,69 @@ rule builds-raw ( metatarget-reference : project : ps : what : retry ? ) } } -local rule get-relevant-features ( ) +# Attempt to build a metatarget named by 'metatarget-reference' +# in context of 'project' with properties 'ps'. +# Returns the 1-based index of the first target +# that builds. +rule find-builds-raw ( project : ps : what : * ) +{ + local result ; + local args = 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; + + if ! $(.$(what)-tested.$(ps)) + { + .$(what)-tested.$(ps) = true ; + local targets.$(i) what.$(i) ; + for local i in $(args) + { + if ! $($(i)) + { + break ; + } + targets.$(i) = [ targets.generate-from-reference + $($(i)[1]) : $(project) : $(ps) ] ; + # ignore usage requirements + targets.$(i) = $(targets.$(i)[2-]) ; + if $($(i)[2]) + { + what.$(i) = $($(i)[2]) ; + } + else + { + local t = [ targets.resolve-reference + $($(i)[1]) : $(project) ] ; + what.$(i) = [ $(t[1]).name ] ; + } + } + + result = [ try-find-build $(ps) : $(what) + : $(what.4) $(targets.4) + : $(what.5) $(targets.5) + : $(what.6) $(targets.6) + : $(what.7) $(targets.7) + : $(what.8) $(targets.8) + : $(what.9) $(targets.9) + : $(what.10) $(targets.10) + : $(what.11) $(targets.11) + : $(what.12) $(targets.12) + : $(what.13) $(targets.13) + : $(what.14) $(targets.14) + : $(what.15) $(targets.15) + : $(what.16) $(targets.16) + : $(what.17) $(targets.17) + : $(what.18) $(targets.18) + : $(what.19) $(targets.19) ] ; + .$(what)-result.$(ps) = $(result) ; + + return $(result) ; + } + else + { + return $(.$(what)-result.$(ps)) ; + } +} + +rule get-relevant-features ( ) { local relevant = [ feature.expand ] ; local result = ; @@ -270,6 +428,19 @@ rule builds ( metatarget-reference : properties * : what ? : retry ? ) $(retry) ] ; } +rule find-builds ( what : properties * : * ) +{ + local relevant = [ property.select [ get-relevant-features ] : $(properties) ] ; + local ps = [ property-set.create $(relevant) ] ; + local t = [ targets.current ] ; + local p = [ $(t).project ] ; + + return [ find-builds-raw $(p) : $(ps) : $(what) : + $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : + $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : + $(16) : $(17) : $(18) : $(19) ] ; +} + # Called by Boost.Build startup code to specify the file to receive the # configuration check results. Should never be called by user code. @@ -314,13 +485,89 @@ class check-target-builds-worker } } +class configure-choose-worker +{ + import configure ; + import property ; + rule __init__ ( message : * ) + { + self.message = $(message) ; + for i in 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + { + local name = [ CALC $(i) - 1 ] ; + self.targets.$(name) = $($(i)[1]) ; + if ! $($(i)[2]:G) # Check whether the second argument is a property + { + self.what.$(name) = $($(i)[2]) ; + self.props.$(name) = $($(i)[3-]) ; + } + else + { + self.props.$(name) = $($(i)[2-]) ; + } + } + } + rule all-properties ( ) + { + local i = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; + return $(self.props.$(i)) ; + } + rule check ( properties * ) + { + local i = [ configure.find-builds $(self.message) : $(properties) + : $(self.targets.1) $(self.what.1) + : $(self.targets.2) $(self.what.2) + : $(self.targets.3) $(self.what.3) + : $(self.targets.4) $(self.what.4) + : $(self.targets.5) $(self.what.5) + : $(self.targets.6) $(self.what.6) + : $(self.targets.7) $(self.what.7) + : $(self.targets.8) $(self.what.8) + : $(self.targets.9) $(self.what.9) + : $(self.targets.10) $(self.what.10) + : $(self.targets.11) $(self.what.11) + : $(self.targets.12) $(self.what.12) + : $(self.targets.13) $(self.what.13) + : $(self.targets.14) $(self.what.14) + : $(self.targets.15) $(self.what.15) + : $(self.targets.16) $(self.what.16) + : $(self.targets.17) $(self.what.17) + : $(self.targets.18) $(self.what.18) + : $(self.targets.19) $(self.what.19) ] ; + if $(self.props.$(i)) + { + return [ property.evaluate-conditionals-in-context $(self.props.$(i)) : $(properties) ] ; + } + } +} + rule check-target-builds ( target message ? : true-properties * : false-properties * ) { local instance = [ new check-target-builds-worker $(target) $(message) : $(true-properties) : $(false-properties) ] ; - return @$(instance).check ; + return @$(instance).check + [ property.evaluate-conditional-relevance + $(true-properties) $(false-properties) + : [ configure.get-relevant-features ] ] ; +} + +# Usage: +# [ configure.choose "architecture" +# : /config//x86 x86 x86 +# : /config//mips mips mips +# ] +rule choose ( message : * ) +{ + local instance = [ new configure-choose-worker $(message) + : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) + : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) + : $(17) : $(18) : $(19) ] ; + return @$(instance).check + [ property.evaluate-conditional-relevance + [ $(instance).all-properties ] + : [ configure.get-relevant-features ] ] ; } diff --git a/src/build/feature.jam b/src/build/feature.jam index e58edcbed..d833d70e4 100644 --- a/src/build/feature.jam +++ b/src/build/feature.jam @@ -289,7 +289,8 @@ local rule find-implied-subfeature ( feature subvalue : value-string ? ) import errors ; errors.error invalid feature $(feature) ; } - return $($(feature)$(value-string:E="")<>$(subvalue).subfeature) ; + value-string += "" ; + return $($(feature)$(value-string)<>$(subvalue).subfeature) ; } @@ -371,12 +372,8 @@ local rule expand-subfeatures-aux ( local result = $(components[1]:G=$(feature)) ; - local subvalues = $(components[2-]) ; - while $(subvalues) + for local subvalue in $(components[2-]) { - local subvalue = $(subvalues[1]) ; # Pop the head off of subvalues. - subvalues = $(subvalues[2-]) ; - local subfeature = [ find-implied-subfeature $(feature) $(subvalue) : $(value) ] ; @@ -385,7 +382,7 @@ local rule expand-subfeatures-aux ( { result = $(components:J=-) ; result = $(result:G=$(feature)) ; - subvalues = ; # Stop looping. + break ; } else { @@ -665,6 +662,10 @@ rule subfeature ( local f = [ utility.ungrist $(feature) ] ; feature $(f)-$(subfeature-name) : $(subvalues) : $(attributes) subfeature ; + # Features and subfeatures are always relevent as a group + .feature-dependencies.$(f) += $(f)-$(subfeature-name) ; + .feature-dependencies.$(f)-$(subfeature-name) += $(f) ; + # Now make sure the subfeature values are known. extend-subfeature $(feature) $(value-string) : $(subfeature) : $(subvalues) ; } @@ -695,6 +696,10 @@ rule compose ( composite-property : component-properties * ) errors.error composite property "$(composite-property)" cannot have itself as a component ; } $(composite-property).components = $(component-properties) ; + + # A composite feature is relevant if any composed feature is relevant + local component-features = [ sequence.transform utility.ungrist : $(component-properties:G) ] ; + .feature-dependencies.$(component-features) += [ utility.ungrist $(feature) ] ; } @@ -1043,6 +1048,8 @@ local rule split-top-feature ( feature-plus ) # Given a set of properties, add default values for features not represented in # the set. # +# properties must be fully expanded and must not contain conditionals. +# # Note: if there's an ordinary feature F1 and a composite feature F2 which # includes some value for F1 and both feature have default values then the # default value of F1 will be added (as opposed to the value in F2). This might @@ -1070,27 +1077,65 @@ rule add-defaults ( properties * ) feature ; } } - # We don't add default for elements with ":" inside. This catches: - # 1. Conditional properties --- we don't want debug:DEBUG - # to be takes as specified value for - # 2. Free properties with ":" in values. We don't care, since free - # properties don't have defaults. - local xproperties = [ MATCH "^([^:]+)$" : $(properties) ] ; - local missing-top = [ set.difference $(.all-top-features) : $(xproperties:G) ] ; + local missing-top = [ set.difference $(.all-top-features) : $(properties:G) ] ; local more = [ defaults $(missing-top) ] ; - properties += $(more) ; - xproperties += $(more) ; - # Add defaults for subfeatures of features which are present. - for local p in $(xproperties) + # This is similar to property.refine, except that it + # does not remove subfeatures, because we might be adding + # the default value of a subfeature. + local to-remove ; + for local f in $(properties:G) { - local s = $($(p:G).subfeatures) ; - local f = [ utility.ungrist $(p:G) ] ; - local missing-subs = [ set.difference <$(f)-$(s)> : $(properties:G) ] ; - properties += [ defaults [ select-subfeatures $(p) : $(missing-subs) ] ] ; + if ! free in [ attributes $(f) ] + { + to-remove += $(f) ; + } } - return $(properties) ; + local worklist = $(properties) ; + local expanded-from-composite ; + local to-expand = $(more) ; + while $(worklist) + { + # Add defaults for subfeatures of features which are present. + for local p in $(worklist) + { + local s = $($(p:G).subfeatures) ; + local f = [ utility.ungrist $(p:G) ] ; + local missing-subs = [ set.difference <$(f)-$(s)> : $(properties:G) ] ; + local sd = [ defaults [ select-subfeatures $(p) : $(missing-subs) ] ] ; + to-expand += $(sd) ; + } + worklist = ; + + # Expand subfeatures of newly added properties + for local m in [ sequence.transform expand-composite : $(to-expand) ] + { + if ! $(m:G) in $(to-remove) + { + local att = [ attributes $(m:G) ] ; + if $(m:G) in $(expanded-from-composite) && + ! free in $(att) && + ! $(m) in $(more) + { + import errors ; + errors.error "default values for $(p:G) conflict" ; + } + if ! $(m) in $(to-expand) + { + expanded-from-composite += $(m:G) ; + } + more += $(m) ; + if ! subfeature in $(att) && ! free in $(att) + { + worklist += $(m) ; + } + } + } + to-expand = ; + } + + return [ sequence.unique $(properties) $(more) ] ; } @@ -1124,6 +1169,41 @@ rule split ( property-set ) return $(result) ; } +# Returns all the features that also must be relevant when these features are relevant +rule expand-relevant ( features * ) +{ + local conditional ; + local result ; + for f in $(features) + { + # This looks like a conditional, even though it isn't really. + # (Free features can never be used in conditionals) + local split = [ MATCH "^(.*):(.*)$" : $(f) ] ; + if $(split) + { + local-dependencies.$(split[1]) += $(split[2]) ; + conditional += local-dependencies.$(split[1]) ; + } + else + { + result += $(f) ; + } + } + local queue = $(result) ; + while $(queue) + { + local added = [ set.difference + $(.feature-dependencies.$(queue)) + $(local-dependencies.$(queue)) + : $(result) ] ; + result += $(added) ; + queue = $(added) ; + } + # Clean up local map + $(conditional) = ; + return $(result) ; +} + # Tests of module feature. # @@ -1202,6 +1282,9 @@ rule __test__ ( ) assert.result foo=x-y : expand-subfeatures foo=x-y ; + assert.result minus=- + : expand-subfeatures minus=- ; + assert.result gcc 3.0.1 : expand-subfeatures gcc-3.0.1 ; @@ -1234,17 +1317,18 @@ rule __test__ ( ) assert.result optional : attributes ; - assert.result static foobar on - gcc:FOO gcc debug native - dummy1 2.95.2 - : add-defaults static foobar on - gcc:FOO ; + assert.result [ SORT _DEBUG static + foobar on + gcc debug native + dummy1 2.95.2 ] + : add-defaults static foobar on ; - assert.result static foobar on - gcc:FOO fu1 gcc debug - native dummy1 q 2.95.2 + assert.result [ SORT _DEBUG static + foobar on + fu1 gcc debug + native dummy1 q 2.95.2 ] : add-defaults static foobar on - gcc:FOO fu1 ; + fu1 ; set-default : static ; assert.result static : defaults ; diff --git a/src/build/generators.jam b/src/build/generators.jam index c26db7257..d00f5242c 100644 --- a/src/build/generators.jam +++ b/src/build/generators.jam @@ -167,8 +167,10 @@ class generator import utility ; import path ; import property ; + import property-set ; import sequence ; import set ; + import toolset ; import type ; import virtual-target ; @@ -236,6 +238,10 @@ class generator # Note that 'transform' here, is the same as 'for_each'. sequence.transform type.validate : $(self.source-types) ; sequence.transform type.validate : $(self.target-types) ; + + local relevant-for-generator = + [ sequence.transform utility.ungrist : $(requirements:G) ] ; + self.relevant-features = [ property-set.create $(relevant-for-generator) ] ; } ################# End of constructor ################# @@ -406,10 +412,10 @@ class generator } local result ; - if $(consumed) + if $(consumed[2]) { - result = [ construct-result $(consumed) : $(project) $(name) : - $(property-set) ] ; + result = [ construct-result $(consumed[2-]) : $(project) $(name) : + [ $(property-set).add $(consumed[1]) ] ] ; } if $(result) @@ -421,7 +427,11 @@ class generator generators.dout [ indent ] " FAILURE" ; } generators.dout ; - return $(result) ; + if $(result) + { + # Make sure that we propagate usage-requirements up the stack. + return [ $(result[1]).add $(consumed[1]) ] $(result[2-]) ; + } } # Constructs the dependency graph to be returned by this generator. @@ -443,6 +453,11 @@ class generator ) { local result ; + + local relevant = [ toolset.relevant $(self.rule-name) ] ; + relevant = [ $(relevant).add $(self.relevant-features) ] ; + property-set = [ $(property-set).add $(relevant) ] ; + # If this is a 1->1 transformation, apply it to all consumed targets in # order. if ! $(self.source-types[2]) && ! $(self.composing) @@ -458,7 +473,10 @@ class generator result += [ generated-targets $(consumed) : $(property-set) : $(project) $(name) ] ; } - return $(result) ; + if $(result) + { + return $(relevant) $(result) ; + } } # Determine target name from fullname (maybe including path components) @@ -576,6 +594,7 @@ class generator { local _consumed ; local missing-types ; + local usage-requirements ; if $(sources[2]) { @@ -588,6 +607,7 @@ class generator local temp = [ consume-directly $(sources) ] ; if $(temp[1]) { + usage-requirements = [ property-set.empty ] ; _consumed = $(temp[1]) ; } missing-types = $(temp[2-]) ; @@ -613,7 +633,7 @@ class generator # everything to the required type. There is no need to rerun it on # targets of different types. - # NOTE: ignoring usage requirements. + usage-requirements = $(transformed[1]) ; for local t in $(transformed[2-]) { if [ $(t).type ] in $(missing-types) @@ -623,7 +643,7 @@ class generator } } - return [ sequence.unique $(_consumed) ] ; + return $(usage-requirements) [ sequence.unique $(_consumed) ] ; } # Converts several files to consumable types. Called for composing @@ -638,10 +658,11 @@ class generator if ! $(self.source-types) { # Anything is acceptible - return $(sources) ; + return [ property-set.empty ] $(sources) ; } else { + local usage-requirements = [ property-set.empty ] ; local acceptible-types = [ sequence.unique [ sequence.transform type.all-derived : $(self.source-types) ] ] ; for local source in $(sources) @@ -661,13 +682,17 @@ class generator { generators.dout [ indent ] " failed to convert " $(source) ; } + else + { + usage-requirements = [ $(usage-requirements).add $(transformed[1]) ] ; + } } else { result += $(source) ; } } - return [ sequence.unique $(result) : stable ] ; + return $(usage-requirements) [ sequence.unique $(result) : stable ] ; } } diff --git a/src/build/project.jam b/src/build/project.jam index 2b3386a13..803574052 100644 --- a/src/build/project.jam +++ b/src/build/project.jam @@ -441,7 +441,7 @@ rule initialize ( local jamroot ; local parent-module ; - if $(module-name) = test-config + if $(module-name) in test-config all-config { # No parent. } @@ -502,7 +502,7 @@ rule initialize ( } else { - local cfgs = project site test user ; + local cfgs = project site test user all ; if ! $(module-name) in $(cfgs)-config { # This is a standalone project with known location. Set its @@ -833,7 +833,7 @@ rule current ( ) # Temporarily changes the current project to 'project'. Should be followed by # 'pop-current'. # -rule push-current ( project ) +rule push-current ( project ? ) { .saved-current-project += $(.current-project) ; .current-project = $(project) ; @@ -900,9 +900,9 @@ rule get-jamroot-module ( project ) # Returns the project target corresponding to the 'project-module'. # -rule target ( project-module ) +rule target ( project-module : allow-missing ? ) { - if ! $(.target.$(project-module)) + if ! $(.target.$(project-module)) && ! $(allow-missing) { import errors ; errors.user-error Project target requested but not yet assigned for diff --git a/src/build/property-set.jam b/src/build/property-set.jam index 55cb55645..194b3e0bc 100644 --- a/src/build/property-set.jam +++ b/src/build/property-set.jam @@ -34,6 +34,7 @@ class property-set { import errors ; import feature ; + import modules ; import path ; import property ; import property-set ; @@ -86,6 +87,26 @@ class property-set return $(self.free) ; } + # Returns relevant base properties + rule base-relevant ( ) + { + if ! $(self.relevant-initialized) + { + init-relevant ; + } + return $(self.base-relevant) ; + } + + # Returns all relevant properties + rule relevant + { + if ! $(self.relevant-initialized) + { + init-relevant ; + } + return $(self.relevant) ; + } + # Returns dependency properties. # rule dependency ( ) @@ -215,7 +236,7 @@ class property-set { if ! $(self.as-path) { - self.as-path = [ property.as-path [ base ] ] ; + self.as-path = [ property.as-path [ base-relevant ] ] ; } return $(self.as-path) ; } @@ -345,6 +366,30 @@ class property-set self.base-initialized = true ; } + rule init-relevant ( ) + { + local relevant-features = [ get ] ; + relevant-features = [ feature.expand-relevant $(relevant-features) ] ; + relevant-features = <$(relevant-features)> ; + ignore-relevance = [ modules.peek property-set : .ignore-relevance ] ; + for local p in $(self.raw) + { + if $(ignore-relevance) || $(p:G) in $(relevant-features) + { + local att = [ feature.attributes $(p:G) ] ; + if ! ( incidental in $(att) ) + { + self.relevant += $(p) ; + if ! ( free in $(att) ) + { + self.base-relevant += $(p) ; + } + } + } + } + self.relevant-initialized = true ; + } + rule init-dependency ( ) { for local p in $(self.raw) @@ -369,7 +414,7 @@ class property-set # characters as well, e.g. free or indirect properties. Indirect # properties for example contain a full Jamfile path in their value # which on Windows file systems contains ':' as the drive separator. - if [ MATCH (:) : $(p:G=) ] + if ( [ MATCH (:) : $(p:G=) ] && ! ( free in [ feature.attributes $(p:G) ] ) ) || $(p:G) = { self.conditional += $(p) ; } @@ -382,6 +427,13 @@ class property-set } } +# This is a temporary measure to help users work around +# any problems. Remove it once we've verified that +# everything works. +if --ignore-relevance in [ modules.peek : ARGV ] +{ + .ignore-relevance = true ; +} # Creates a new 'property-set' instance for the given raw properties or returns # an already existing ones. diff --git a/src/build/property.jam b/src/build/property.jam index 78a9744b1..c172bd253 100644 --- a/src/build/property.jam +++ b/src/build/property.jam @@ -28,8 +28,20 @@ rule refine ( properties * : requirements * ) for local r in $(requirements) { # Do not consider conditional requirements. - if ! [ MATCH (:) : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ] + if ! [ MATCH (:<) : $(r:G=) ] && ! free in [ feature.attributes $(r:G) ] { + if ! $(r) in $(properties) + { + # Kill subfeatures of properties that we're changing + local sub = [ modules.peek feature : $(r:G).subfeatures ] ; + if $(sub) + { + # non-specific subfeatures are still valid + sub = [ MATCH "(.*:.*)" : $(sub) ] ; + local name = [ utility.ungrist $(r:G) ] ; + unset += <$(name)-$(sub)> ; + } + } unset += $(r:G) ; } } @@ -37,7 +49,7 @@ rule refine ( properties * : requirements * ) # Remove properties that are overridden by requirements for local p in $(properties) { - if [ MATCH (:) : $(p:G=) ] || ! $(p:G) in $(unset) + if [ MATCH (:<) : $(p:G=) ] || ! $(p:G) in $(unset) { result += $(p) ; } @@ -55,12 +67,17 @@ rule evaluate-conditionals-in-context ( properties * : context * ) { local base ; local conditionals ; + local indirect ; for local p in $(properties) { - if [ MATCH (:<) : $(p) ] + if [ MATCH (:<) : $(p) ] && ! free in [ feature.attributes $(p:G) ] { conditionals += $(p) ; } + else if $(p:G) = + { + indirect += $(p) ; + } else { base += $(p) ; @@ -116,10 +133,58 @@ rule evaluate-conditionals-in-context ( properties * : context * ) } } } + # Import here to avoid cyclic dependency + import project ; + for local i in [ MATCH "^@(.*)" : $(indirect:G=) ] + { + # If the rule was set in a project module, translate paths + # relative to that project's location. + local m = [ indirect.get-module $(i) ] ; + local p = [ project.target $(m) : allow-missing ] ; + local new = [ indirect.call $(i) $(context) ] ; + if $(p) && [ $(p).location ] + { + result += [ translate-paths $(new) : [ $(p).location ] ] ; + } + else + { + result += $(new) ; + } + } return $(result) ; } +# Returns properties indicating how the conditionals in +# properties affect feature relevance. If the optional argument cond +# is passed, it is treated as extra conditions for all properties. +# +rule evaluate-conditional-relevance ( properties * : cond * ) +{ + cond = [ sequence.transform utility.ungrist : $(cond:G) ] ; + local result ; + for local p in $(properties) + { + # Separate condition and property. + local s = [ MATCH ^(.*):(<.*) : $(p) ] ; + if ! $(s) || free in [ feature.attributes $(p:G) ] + { + local value = [ utility.ungrist $(p:G) ] ; + result += $(value):$(cond) ; + } + else + { + local condition = [ regex.split $(s[1]) "," ] ; + condition = [ MATCH ^!?(.*) : $(condition) ] ; + condition = [ sequence.transform utility.ungrist : $(condition:G) ] $(cond) ; + local value = [ utility.ungrist $(s[2]:G) ] ; + result += $(value):$(condition) ; + } + } + return [ sequence.unique $(result) ] ; +} + + rule expand-subfeatures-in-conditions ( properties * ) { local result ; diff --git a/src/build/targets.jam b/src/build/targets.jam index 16caaa814..6ffc082a4 100644 --- a/src/build/targets.jam +++ b/src/build/targets.jam @@ -191,6 +191,7 @@ class project-target : abstract-target import property-set ; import set ; import sequence ; + import toolset ; import "class" : new ; rule __init__ ( name : project-module parent-project ? @@ -584,6 +585,8 @@ class project-target : abstract-target IMPORT $(parent-module) : $(user-rules) : $(this-module) : $(user-rules) ; EXPORT $(this-module) : $(user-rules) ; + + toolset.inherit-flags $(this-module) : $(parent-module) ; } } @@ -621,10 +624,11 @@ class main-target : abstract-target { import assert ; import feature ; - import print ; import property-set ; import sequence ; + import set ; import targets : start-building end-building ; + import utility ; rule __init__ ( name : project ) { @@ -719,6 +723,43 @@ class main-target : abstract-target } } + # Features are relevant here if they could affect alternative + # selection. That is, base, non-conditional properties that + # are not identical in all target alternatives. + rule relevant-features ( ) + { + if $(self.alternatives[2-]) + { + if $(self.relevant-features) + { + return $(self.relevant-features) ; + } + local all-properties ; + for t in $(self.alternatives) + { + local ps = [ $(t).requirements ] ; + ps = [ property-set.create [ $(ps).non-conditional ] ] ; + all-properties += [ $(ps).base ] ; + } + all-properties = [ sequence.unique $(all-properties) ] ; + local result ; + for t in $(self.alternatives) + { + local ps = [ $(t).requirements ] ; + ps = [ property-set.create [ $(ps).non-conditional ] ] ; + local properties = [ set.difference $(all-properties) : [ $(ps).base ] ] ; + result += $(properties:G) ; + } + result = [ sequence.transform utility.ungrist : [ sequence.unique $(result) ] ] ; + self.relevant-features = [ property-set.create $(result) ] ; + return $(self.relevant-features) ; + } + else + { + return [ property-set.empty ] ; + } + } + rule apply-default-build ( property-set ) { return [ targets.apply-default-build $(property-set) : @@ -734,16 +775,13 @@ class main-target : abstract-target { start-building $(__name__) ; - # We want composite properties in the build request to act as if all the - # properties they expand to have been explicitly specified. - property-set = [ $(property-set).expand ] ; - local all-property-sets = [ apply-default-build $(property-set) ] ; + local relevant = [ relevant-features ] ; local usage-requirements = [ property-set.empty ] ; local result ; for local p in $(all-property-sets) { - local r = [ generate-really $(p) ] ; + local r = [ generate-really [ $(p).add $(relevant) ] ] ; if $(r) { usage-requirements = [ $(usage-requirements).add $(r[1]) ] ; @@ -901,8 +939,10 @@ rule apply-default-build ( property-set : default-build ) # 1. First, see what properties from default-build are already present in # property-set. + local expanded = [ $(property-set).expand ] ; local raw = [ $(property-set).raw ] ; - local specified-features = $(raw:G) ; + local specified-features = [ $(expanded).raw ] ; + specified-features = $(specified-features:G) ; local defaults-to-apply ; for local d in [ $(default-build).raw ] @@ -936,7 +976,7 @@ rule apply-default-build ( property-set : default-build ) for local p in $(properties) { result += [ property-set.create - [ feature.expand [ feature.split $(p) ] ] ] ; + [ feature.split $(p) ] ] ; } } else @@ -993,7 +1033,7 @@ rule common-properties ( build-request requirements ) # If 'what' is 'refined' returns context refined with new requirements. If # 'what' is 'added' returns just the requirements to be applied. # -rule evaluate-requirements ( requirements : context : what ) +rule evaluate-requirements ( requirements : context ) { # Apply non-conditional requirements. It is possible that further # conditional requirement change a value set by non-conditional @@ -1019,42 +1059,29 @@ rule evaluate-requirements ( requirements : context : what ) local conditionals = [ $(requirements).conditional ] ; # The 'count' variable has one element for each conditional feature and for - # each occurrence of '' feature. It is used as a loop + # each occurrence of '' feature. It is used as a loop # counter: for each iteration of the loop before we remove one element and # the property set should stabilize before we are done. It is assumed that # #conditionals iterations should be enough for properties to propagate # along conditions in any direction. - local count = $(conditionals) [ $(requirements).get ] + local count = $(conditionals) and-once-more ; local added-requirements ; local current = $(raw) ; - # It is assumed that ordinary conditional requirements can not add - # properties (a.k.a. indirect conditional properties), and - # that rules referred to by properties can not add new - # properties. So the list of indirect conditionals does not - # change. - local indirect = [ $(requirements).get ] ; - indirect = [ MATCH ^@(.*) : $(indirect) ] ; - local ok ; while $(count) { + # We need to expand composites here so that the requirements can + # safely override composite features. + current = [ feature.expand-composites $(current) ] ; + current = [ feature.add-defaults $(current) ] ; # Evaluate conditionals in context of current properties. local e = [ property.evaluate-conditionals-in-context $(conditionals) : $(current) ] ; - # Evaluate indirect conditionals. - for local i in $(indirect) - { - local t = [ current ] ; - local p = [ $(t).project ] ; - local new = [ indirect.call $(i) $(current) ] ; - e += [ property.translate-paths $(new) : [ $(p).location ] ] ; - } - if $(e) = $(added-requirements) { # If we got the same result, we have found the final properties. @@ -1077,39 +1104,13 @@ rule evaluate-requirements ( requirements : context : what ) errors.error Can not evaluate conditional properties $(conditionals) ; } - if $(what) = added - { - return [ property-set.create $(unconditional) $(added-requirements) ] ; - } - else if $(what) = refined - { - return [ property-set.create $(current) ] ; - } - else - { - import errors ; - errors.error "Invalid value of the 'what' parameter." ; - } + return [ property-set.create $(current) ] ; } rule common-properties2 ( build-request requirements ) { - # This guarantees that default properties are present in the result, unless - # they are overriden by some requirement. FIXME: There is a possibility that - # we have added bar, which is composite and expands to bar2, but - # default value of is not bar2, in which case it is not clear what to - # do. - # - build-request = [ $(build-request).add-defaults ] ; - # Features added by 'add-defaults' can be composite and expand to features - # without default values -- which therefore have not been added yet. It - # could be clearer/faster to expand only newly added properties but that is - # not critical. - build-request = [ $(build-request).expand ] ; - - return [ evaluate-requirements $(requirements) : $(build-request) : - refined ] ; + return [ evaluate-requirements $(requirements) : $(build-request) ] ; } @@ -1214,9 +1215,9 @@ class basic-target : abstract-target # rule match ( property-set debug ? ) { - # The condition is composed of all base non-conditional properties. It - # is not clear if we should expand 'self.requirements' or not. For one - # thing, it would be nice to be able to put + # The condition is composed of all base non-conditional properties. We + # only expand subfeatures in the condition. We do not expand + # composites. We want to be able to put # msvc-6.0 # in requirements. On the other hand, if we have release as a # condition it does not make sense to require full to be @@ -1224,6 +1225,7 @@ class basic-target : abstract-target local bcondition = [ $(self.requirements).base ] ; local ccondition = [ $(self.requirements).conditional ] ; local condition = [ set.difference $(bcondition) : $(ccondition) ] ; + condition = [ feature.expand-subfeatures $(condition) : unchecked ] ; if $(debug) { ECHO " next alternative: required properties:" @@ -1294,7 +1296,7 @@ class basic-target : abstract-target # define=FOO # he most likely wants this define to be set for all compiles. # Make it before check for already built. - property-set = [ $(property-set).refine + property-set = [ $(property-set).add [ build-system.command-line-free-features ] ] ; if ! $(self.generated.$(property-set)) @@ -1357,6 +1359,15 @@ class basic-target : abstract-target local gur = $(result[1]) ; result = $(result[2-]) ; + # Relevant is automatically applied to usage requirements + # and only applies for propagated features + local relevant = [ propagated-relevant + [ $(gur).get ] + [ $(rproperties).get ] ] ; + gur = [ property-set.create + [ property.change [ $(gur).raw ] : ] + $(relevant) ] ; + if $(self.always) { for local t in $(result) @@ -1445,8 +1456,25 @@ class basic-target : abstract-target rule compute-usage-requirements ( subvariant ) { local rproperties = [ $(subvariant).build-properties ] ; - xusage-requirements = [ targets.evaluate-requirements - $(self.usage-requirements) : $(rproperties) : added ] ; + xusage-requirements = + [ $(self.usage-requirements).evaluate-conditionals + $(rproperties) ] ; + + # Filter out non-propagated properties + local relevant ; + for local r in [ $(xusage-requirements).get ] + { + local check = [ MATCH "(.*):(.*)" : $(r) ] ; + if $(check) { check = $(check[2]) ; } + else { check = $(r) ; } + if propagated in [ feature.attributes <$(check)> ] + { + relevant += $(r) ; + } + } + xusage-requirements = [ property-set.create + [ property.change [ $(xusage-requirements).raw ] : ] + $(relevant) ] ; # We generate all dependency properties and add them, as well as their # usage requirements, to the result. @@ -1487,6 +1515,19 @@ class basic-target : abstract-target return [ $(result).add [ property-set.create $(raw) ] ] ; } + local rule propagated-relevant ( values * ) + { + local result ; + for local v in [ feature.expand-relevant $(values) ] + { + if propagated in [ feature.attributes <$(v)> ] + { + result += $(v) ; + } + } + return $(result) ; + } + # Creates new subvariant instances for 'targets'. # 'root-targets' - virtual targets to be returned to dependants # 'all-targets' - virtual targets created while building this main target @@ -1633,7 +1674,8 @@ rule main-target-requirements ( import errors ; errors.error "Conflicting requirements for target:" $(requirements) ; } - return [ $(requirements).add [ toolset.requirements ] ] ; + local result = [ $(requirements).add [ toolset.requirements ] ] ; + return [ $(result).add-raw [ property.evaluate-conditional-relevance [ $(result).raw ] ] ] ; } @@ -1656,7 +1698,10 @@ rule main-target-usage-requirements ( $(specification) : [ $(project).project-module ] [ $(project).get location ] ] ; - return [ $(project-usage-requirements).add $(usage-requirements) ] ; + local result = [ $(project-usage-requirements).add $(usage-requirements) ] ; + local relevant = + [ property.evaluate-conditional-relevance [ $(result).raw ] ] ; + return [ $(result).add-raw $(relevant) ] ; } diff --git a/src/build/toolset.jam b/src/build/toolset.jam index a942cd906..d5b326488 100644 --- a/src/build/toolset.jam +++ b/src/build/toolset.jam @@ -19,6 +19,7 @@ import set ; import property-set ; import order ; import "class" : new ; +import utility ; .flag-no = 1 ; @@ -76,6 +77,36 @@ rule pop-checking-for-flags-module ( ) } +# Specifies features that are referenced by the action rule. +# This is necessary in order to detect that these features +# are relevant. +# +rule uses-features ( rule-or-module : features * : unchecked ? ) +{ + local caller = [ CALLER_MODULE ] ; + if ! [ MATCH ".*([.]).*" : $(rule-or-module) ] + && [ MATCH "(Jamfile<.*)" : $(caller) ] + { + # Unqualified rule name, used inside Jamfile. Most likely used with + # 'make' or 'notfile' rules. This prevents setting flags on the entire + # Jamfile module (this will be considered as rule), but who cares? + # Probably, 'flags' rule should be split into 'flags' and + # 'flags-on-module'. + rule-or-module = $(caller).$(rule-or-module) ; + } + else + { + local module_ = [ MATCH "([^.]*).*" : $(rule-or-module) ] ; + if $(unchecked) != unchecked + && $(.flags-module-checking[1]) != unchecked + && $(module_) != $(caller) + { + errors.error "Module $(caller) attempted to set flags for module $(module_)" ; + } + } + .uses-features.$(rule-or-module) += $(features) ; +} + # Specifies the flags (variables) that must be set on targets under certain # conditions, described by arguments. # @@ -399,6 +430,34 @@ rule relevant-features ( rule-or-module ) } } +# Returns a list of all the features which were +# passed to uses-features. +local rule used-features ( rule-or-module ) +{ + if ! $(.used-features.$(rule-or-module)) + { + local result = $(.uses-features.$(rule-or-module)) ; + + # Strip away last dot separated part and recurse. + local next = [ MATCH ^(.+)\\.([^\\.])* : $(rule-or-module) ] ; + if $(next) + { + result += [ used-features $(next[1]) ] ; + } + result = [ sequence.unique $(result) ] ; + if $(result[1]) = "" + { + result = $(result) ; + } + .used-features.$(rule-or-module) = $(result) ; + return $(result) ; + } + else + { + return $(.used-features.$(rule-or-module)) ; + } +} + rule filter-property-set ( rule-or-module : property-set ) { local key = .filtered.property-set.$(rule-or-module).$(property-set) ; @@ -454,6 +513,23 @@ rule set-target-variables ( rule-or-module targets + : property-set ) } +# Returns a property-set indicating which features are relevant +# for the given rule. +# +rule relevant ( rule-name ) +{ + if ! $(.relevant-features-ps.$(rule-name)) + { + local features = [ sequence.transform utility.ungrist : + [ relevant-features $(rule-name) ] + [ used-features $(rule-name) ] ] ; + .relevant-features-ps.$(rule-name) = + [ property-set.create $(features) ] ; + } + return $(.relevant-features-ps.$(rule-name)) ; +} + + # Make toolset 'toolset', defined in a module of the same name, inherit from # 'base'. # 1. The 'init' rule from 'base' is imported into 'toolset' with full name. diff --git a/src/build/virtual-target.jam b/src/build/virtual-target.jam index 2d139bd7f..cb8128362 100644 --- a/src/build/virtual-target.jam +++ b/src/build/virtual-target.jam @@ -999,10 +999,8 @@ rule register ( target ) { local ps1 = [ $(a1).properties ] ; local ps2 = [ $(a2).properties ] ; - local p1 = [ $(ps1).base ] [ $(ps1).free ] [ set.difference - [ $(ps1).dependency ] : [ $(ps1).incidental ] ] ; - local p2 = [ $(ps2).base ] [ $(ps2).free ] [ set.difference - [ $(ps2).dependency ] : [ $(ps2).incidental ] ] ; + local p1 = [ $(ps1).relevant ] ; + local p2 = [ $(ps2).relevant ] ; if $(p1) = $(p2) { result = $(t) ; diff --git a/src/engine/compile.c b/src/engine/compile.c index 6adb83fa2..6ebb4006c 100644 --- a/src/engine/compile.c +++ b/src/engine/compile.c @@ -99,7 +99,7 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame ) /* Check traditional targets $(<) and sources $(>). */ if ( !rule->actions && !rule->procedure ) - unknown_rule( frame, NULL, frame->module, rule->name ); + unknown_rule( frame, NULL, frame->module, rulename ); /* If this rule will be executed for updating the targets then construct the * action for make(). diff --git a/src/engine/config_toolset.bat b/src/engine/config_toolset.bat index 291459e65..b0bdfbb6f 100644 --- a/src/engine/config_toolset.bat +++ b/src/engine/config_toolset.bat @@ -172,7 +172,10 @@ if "_%BOOST_JAM_TOOLSET_ROOT%_" == "__" ( if "_%BOOST_JAM_ARCH%_" == "__" set BOOST_JAM_ARCH=x86 set BOOST_JAM_ARGS=%BOOST_JAM_ARGS% %BOOST_JAM_ARCH% +REM return to current directory as vsdevcmd_end.bat switches to %USERPROFILE%\Source if it exists. +pushd %CD% if "_%VSINSTALLDIR%_" == "__" call :Call_If_Exists "%BOOST_JAM_TOOLSET_ROOT%Auxiliary\Build\vcvarsall.bat" %BOOST_JAM_ARGS% +popd set "BOOST_JAM_CC=cl /nologo /RTC1 /Zi /MTd /Fobootstrap/ /Fdbootstrap/ -DNT -DYYDEBUG -wd4996 kernel32.lib advapi32.lib user32.lib" set "BOOST_JAM_OPT_JAM=/Febootstrap\jam0" set "BOOST_JAM_OPT_MKJAMBASE=/Febootstrap\mkjambase0" diff --git a/src/engine/debugger.c b/src/engine/debugger.c index bb996031f..e6788a05f 100644 --- a/src/engine/debugger.c +++ b/src/engine/debugger.c @@ -195,8 +195,11 @@ static void debug_lol_read( FILE * in, LOL * lol ) static void debug_frame_write( FILE * out, FRAME * frame ) { - OBJECT * fullname = make_absolute_path( frame->file ); - debug_object_write( out, frame->file ); + OBJECT * fullname = constant_builtin; + OBJECT * file = frame->file; + if ( file == NULL ) file = constant_builtin; + else fullname = make_absolute_path( frame->file ); + debug_object_write( out, file ); debug_int_write( out, frame->line ); debug_object_write( out, fullname ); debug_lol_write( out, frame->args ); @@ -469,7 +472,8 @@ void debug_on_instruction( FRAME * frame, OBJECT * file, int line ) { int breakpoint_id; assert( debug_is_debugging() ); - if ( debug_state == DEBUG_NEXT && debug_depth <= 0 && debug_line != line ) + if ( debug_state == DEBUG_NEXT && + ( debug_depth < 0 || ( debug_depth == 0 && debug_line != line ) ) ) { debug_file = file; debug_line = line; @@ -483,14 +487,15 @@ void debug_on_instruction( FRAME * frame, OBJECT * file, int line ) debug_frame = frame; debug_end_stepping(); } - else if ( debug_state == DEBUG_FINISH && debug_depth <= 0 ) + else if ( debug_state == DEBUG_FINISH && debug_depth < 0 ) { debug_file = file; debug_line = line; debug_frame = frame; debug_end_stepping(); } - else if ( ( debug_file == NULL || ! object_equal( file, debug_file ) || line != debug_line ) && + else if ( ( debug_file == NULL || ! object_equal( file, debug_file ) || + line != debug_line || debug_depth != 0 ) && ( breakpoint_id = handle_line_breakpoint( file, line ) ) ) { debug_file = file; @@ -498,12 +503,19 @@ void debug_on_instruction( FRAME * frame, OBJECT * file, int line ) debug_frame = frame; debug_on_breakpoint( breakpoint_id ); } + else if ( ( debug_state == DEBUG_RUN || debug_state == DEBUG_FINISH ) && + ( debug_depth < 0 || ( debug_depth == 0 && debug_line != line ) ) ) + { + debug_file = NULL; + debug_line = 0; + } } void debug_on_enter_function( FRAME * frame, OBJECT * name, OBJECT * file, int line ) { int breakpoint_id; assert( debug_is_debugging() ); + ++debug_depth; if ( debug_state == DEBUG_STEP && file ) { debug_file = file; @@ -519,18 +531,18 @@ void debug_on_enter_function( FRAME * frame, OBJECT * name, OBJECT * file, int l debug_frame = frame; debug_on_breakpoint( breakpoint_id ); } - else if ( debug_state == DEBUG_NEXT || debug_state == DEBUG_FINISH ) - { - ++debug_depth; - } } void debug_on_exit_function( OBJECT * name ) { assert( debug_is_debugging() ); - if ( debug_state == DEBUG_NEXT || debug_state == DEBUG_FINISH ) + --debug_depth; + if ( debug_depth < 0 ) { - --debug_depth; + /* The current location is no longer valid + after we return from the containing function. */ + debug_file = NULL; + debug_line = 0; } } @@ -544,11 +556,13 @@ static int child_pid; static void debug_child_continue( int argc, const char * * argv ) { debug_state = DEBUG_RUN; + debug_depth = 0; } static void debug_child_step( int argc, const char * * argv ) { debug_state = DEBUG_STEP; + debug_depth = 0; } static void debug_child_next( int argc, const char * * argv ) @@ -560,7 +574,7 @@ static void debug_child_next( int argc, const char * * argv ) static void debug_child_finish( int argc, const char * * argv ) { debug_state = DEBUG_FINISH; - debug_depth = 1; + debug_depth = 0; } static void debug_child_kill( int argc, const char * * argv ) @@ -1102,7 +1116,7 @@ static void debug_start_child( int argc, const char * * argv ) PROCESS_INFORMATION pi = { NULL, NULL, 0, 0 }; STARTUPINFO si = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - assert( DEBUG_STATE == DEBUG_NO_CHILD ); + assert( debug_state == DEBUG_NO_CHILD ); if ( ! CreatePipe( &pipe1[ 0 ], &pipe1[ 1 ], &sa, 0 ) ) { printf("internal error\n"); @@ -1189,7 +1203,7 @@ static void debug_start_child( int argc, const char * * argv ) int read_fd; int pid; int i; - assert( DEBUG_STATE == DEBUG_NO_CHILD ); + assert( debug_state == DEBUG_NO_CHILD ); pipe(pipe1); pipe(pipe2); pid = fork(); diff --git a/src/engine/execnt.c b/src/engine/execnt.c index 029aa37c0..9137f585a 100644 --- a/src/engine/execnt.c +++ b/src/engine/execnt.c @@ -1282,7 +1282,9 @@ static void string_new_from_argv( string * result, char const * const * argv ) while ( *argv ) { string_push_back( result, ' ' ); + string_push_back( result, '"' ); string_append( result, *(argv++) ); + string_push_back( result, '"' ); } } diff --git a/src/engine/filesys.c b/src/engine/filesys.c index e9612bb2f..da0e47a2c 100644 --- a/src/engine/filesys.c +++ b/src/engine/filesys.c @@ -88,6 +88,7 @@ file_archive_info_t * file_archive_info( OBJECT * const path, int * found ) if ( !*found ) { + archive->name = path_key; archive->file = 0; archive->members = FL0; } @@ -292,6 +293,7 @@ file_info_t * file_query( OBJECT * const path ) return ff; } +#ifndef OS_NT /* * file_query_posix_() - query information about a path using POSIX stat() @@ -329,10 +331,45 @@ void file_query_posix_( file_info_t * const info ) info->is_file = statbuf.st_mode & S_IFREG ? 1 : 0; info->is_dir = statbuf.st_mode & S_IFDIR ? 1 : 0; info->exists = 1; +#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809 +#if defined(OS_MACOSX) + timestamp_init( &info->time, statbuf.st_mtimespec.tv_sec, statbuf.st_mtimespec.tv_nsec ); +#else + timestamp_init( &info->time, statbuf.st_mtim.tv_sec, statbuf.st_mtim.tv_nsec ); +#endif +#else timestamp_init( &info->time, statbuf.st_mtime, 0 ); +#endif } } +/* + * file_supported_fmt_resolution() - file modification timestamp resolution + * + * Returns the minimum file modification timestamp resolution supported by this + * Boost Jam implementation. File modification timestamp changes of less than + * the returned value might not be recognized. + * + * Does not take into consideration any OS or file system related restrictions. + * + * Return value 0 indicates that any value supported by the OS is also supported + * here. + */ + +void file_supported_fmt_resolution( timestamp * const t ) +{ +#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809 + timestamp_init( t, 0, 1 ); +#else + /* The current implementation does not support file modification timestamp + * resolution of less than one second. + */ + timestamp_init( t, 1, 0 ); +#endif +} + +#endif + /* * file_remove_atexit() - schedule a path to be removed on program exit diff --git a/src/engine/filesys.h b/src/engine/filesys.h index b13853d17..24c0556d9 100644 --- a/src/engine/filesys.h +++ b/src/engine/filesys.h @@ -53,6 +53,7 @@ typedef file_info_t * * FILELISTITER; /* also &FILEITEM equivalent */ typedef struct file_archive_info_t { + OBJECT * name; file_info_t * file; FILELIST * members; } file_archive_info_t; diff --git a/src/engine/fileunix.c b/src/engine/fileunix.c index 2d21e5149..5f6815725 100644 --- a/src/engine/fileunix.c +++ b/src/engine/fileunix.c @@ -201,27 +201,6 @@ void file_query_( file_info_t * const info ) } -/* - * file_supported_fmt_resolution() - file modification timestamp resolution - * - * Returns the minimum file modification timestamp resolution supported by this - * Boost Jam implementation. File modification timestamp changes of less than - * the returned value might not be recognized. - * - * Does not take into consideration any OS or file system related restrictions. - * - * Return value 0 indicates that any value supported by the OS is also supported - * here. - */ - -void file_supported_fmt_resolution( timestamp * const t ) -{ - /* The current implementation does not support file modification timestamp - * resolution of less than one second. - */ - timestamp_init( t, 1, 0 ); -} - int file_collect_archive_content_( file_archive_info_t * const archive ); /* diff --git a/src/engine/filevms.c b/src/engine/filevms.c index e94e2634c..ef93b0cdb 100644 --- a/src/engine/filevms.c +++ b/src/engine/filevms.c @@ -162,27 +162,6 @@ void file_query_( file_info_t * const info ) } -/* - * file_supported_fmt_resolution() - file modification timestamp resolution - * - * Returns the minimum file modification timestamp resolution supported by this - * Boost Jam implementation. File modification timestamp changes of less than - * the returned value might not be recognized. - * - * Does not take into consideration any OS or file system related restrictions. - * - * Return value 0 indicates that any value supported by the OS is also supported - * here. - */ - -void file_supported_fmt_resolution( timestamp * const t ) -{ - /* The current implementation does not support file modification timestamp - * resolution of less than one second. - */ - timestamp_init( t, 1, 0 ); -} - /*------------------------------------------------------------------------------ * VMS-specific processing: * diff --git a/src/engine/function.c b/src/engine/function.c index 46ec40fea..2244c3e30 100644 --- a/src/engine/function.c +++ b/src/engine/function.c @@ -533,7 +533,20 @@ static LIST * function_call_member_rule( JAM_FUNCTION * function, FRAME * frame, if ( module->class_module ) { rule = bindrule( rulename, module ); - real_rulename = object_copy( function_rulename( rule->procedure ) ); + if ( rule->procedure ) + { + real_rulename = object_copy( function_rulename( rule->procedure ) ); + } + else + { + string buf[ 1 ]; + string_new( buf ); + string_append( buf, object_str( module->name ) ); + string_push_back( buf, '.' ); + string_append( buf, object_str( rulename ) ); + real_rulename = object_new( buf->value ); + string_free( buf ); + } } else { @@ -2434,12 +2447,17 @@ static void compile_append_chain( PARSE * parse, compiler * c ) } } -static void compile_parse( PARSE * parse, compiler * c, int result_location ) +static void compile_emit_debug(compiler * c, int line) { #ifdef JAM_DEBUGGER if ( debug_is_debugging() ) - compile_emit( c, INSTR_DEBUG_LINE, parse->line ); + compile_emit( c, INSTR_DEBUG_LINE, line ); #endif +} + +static void compile_parse( PARSE * parse, compiler * c, int result_location ) +{ + compile_emit_debug(c, parse->line); if ( parse->type == PARSE_APPEND ) { compile_append_chain( parse, c ); @@ -2495,6 +2513,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) compile_emit( c, INSTR_FOR_INIT, 0 ); compile_set_label( c, top ); compile_emit_branch( c, INSTR_FOR_LOOP, end ); + compile_emit_debug( c, parse->line ); compile_emit( c, INSTR_SET, var ); compile_push_break_scope( c, end ); @@ -2649,6 +2668,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) group->elems, 0 ) )->s ); var_parse_group_free( group ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); compile_emit( c, INSTR_PUSH_LOCAL, name ); compile_push_cleanup( c, INSTR_POP_LOCAL, name ); compile_parse( parse->third, c, nested_result ); @@ -2660,6 +2680,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) var_parse_group_compile( group, c ); var_parse_group_free( group ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); compile_emit( c, INSTR_PUSH_LOCAL_GROUP, 0 ); compile_push_cleanup( c, INSTR_POP_LOCAL_GROUP, 0 ); compile_parse( parse->third, c, nested_result ); @@ -2671,6 +2692,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) { compile_parse( parse->left, c, RESULT_STACK ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); compile_emit( c, INSTR_PUSH_LOCAL_GROUP, 0 ); compile_push_cleanup( c, INSTR_POP_LOCAL_GROUP, 0 ); compile_parse( parse->third, c, nested_result ); @@ -2820,6 +2842,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) group->elems, 0 ) )->s ); var_parse_group_free( group ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); if ( result_location != RESULT_NONE ) { compile_emit( c, INSTR_SET_RESULT, 1 ); @@ -2831,6 +2854,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) var_parse_group_compile( group, c ); var_parse_group_free( group ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); if ( result_location != RESULT_NONE ) { compile_emit( c, INSTR_SET_RESULT, 1 ); @@ -2842,6 +2866,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) { compile_parse( parse->left, c, RESULT_STACK ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); if ( result_location != RESULT_NONE ) { compile_emit( c, INSTR_SET_RESULT, 1 ); @@ -2875,6 +2900,7 @@ static void compile_parse( PARSE * parse, compiler * c, int result_location ) compile_parse( parse->third, c, RESULT_STACK ); compile_parse( parse->right, c, RESULT_STACK ); + compile_emit_debug(c, parse->line); switch ( parse->num ) { case ASSIGN_APPEND: compile_emit( c, INSTR_APPEND_ON, 0 ); break; diff --git a/src/engine/jam.c b/src/engine/jam.c index 92686fc8d..4bca2fd7b 100644 --- a/src/engine/jam.c +++ b/src/engine/jam.c @@ -269,6 +269,7 @@ int main( int argc, char * * argv, char * * arg_environ ) #endif cwd_init(); + constants_init(); #ifdef JAM_DEBUGGER @@ -449,8 +450,6 @@ int main( int argc, char * * argv, char * * arg_environ ) /* ++globs.noexec; */ } - constants_init(); - { PROFILE_ENTER( MAIN ); diff --git a/src/engine/make1.c b/src/engine/make1.c index 0876bdc91..ecc892755 100644 --- a/src/engine/make1.c +++ b/src/engine/make1.c @@ -572,11 +572,42 @@ static void make1c( state const * const pState ) /* Tally success/failure for those we tried to update. */ if ( t->progress == T_MAKE_RUNNING ) + { + /* Invert OK/FAIL target status when FAIL_EXPECTED has been applied. */ + if ( t->flags & T_FLAG_FAIL_EXPECTED && !globs.noexec ) + { + switch ( t->status ) + { + case EXEC_CMD_FAIL: t->status = EXEC_CMD_OK; break; + case EXEC_CMD_OK: t->status = EXEC_CMD_FAIL; break; + } + + /* Printing failure has to be delayed until the last + * action is completed for FAIL_EXPECTED targets. + * Do it here. + */ + if ( t->status == EXEC_CMD_FAIL ) + { + out_printf( "...failed %s ", object_str( t->actions->action->rule->name ) ); + out_printf( "%s", object_str( t->boundname ) ); + out_printf( "...\n" ); + } + + /* Handle -q */ + if ( t->status == EXEC_CMD_FAIL && globs.quitquick ) + ++quit; + + /* Delete the target on failure. */ + if ( !( t->flags & ( T_FLAG_PRECIOUS | T_FLAG_NOTFILE ) ) && + !unlink( object_str( t->boundname ) ) ) + out_printf( "...removing %s\n", object_str( t->boundname ) ); + } switch ( t->status ) { case EXEC_CMD_OK: ++counts->made; break; case EXEC_CMD_FAIL: ++counts->failed; break; } + } /* Tell parents their dependency has been built. */ { @@ -833,16 +864,6 @@ static void make1c_closure /* Store the target's status. */ t->status = status_orig; - /* Invert OK/FAIL target status when FAIL_EXPECTED has been applied. */ - if ( t->flags & T_FLAG_FAIL_EXPECTED && !globs.noexec ) - { - switch ( t->status ) - { - case EXEC_CMD_FAIL: t->status = EXEC_CMD_OK; break; - case EXEC_CMD_OK: t->status = EXEC_CMD_FAIL; break; - } - } - /* Ignore failures for actions marked as 'ignore'. */ if ( t->status == EXEC_CMD_FAIL && cmd->rule->actions->flags & RULE_IGNORE ) @@ -873,7 +894,8 @@ static void make1c_closure } /* Print command text on failure. */ - if ( t->status == EXEC_CMD_FAIL && DEBUG_MAKE ) + if ( t->status == EXEC_CMD_FAIL && DEBUG_MAKE && + ! ( t->flags & T_FLAG_FAIL_EXPECTED ) ) { if ( !DEBUG_EXEC ) out_printf( "%s\n", cmd->buf->value ); @@ -891,7 +913,8 @@ static void make1c_closure ++intr; ++quit; } - if ( t->status == EXEC_CMD_FAIL && globs.quitquick ) + if ( t->status == EXEC_CMD_FAIL && globs.quitquick && + ! ( t->flags & T_FLAG_FAIL_EXPECTED ) ) ++quit; /* If the command was not successful remove all of its targets not marked as diff --git a/src/engine/timestamp.c b/src/engine/timestamp.c index b6fd906ef..679cf2982 100644 --- a/src/engine/timestamp.c +++ b/src/engine/timestamp.c @@ -132,6 +132,16 @@ void timestamp_current( timestamp * const t ) FILETIME ft; GetSystemTimeAsFileTime( &ft ); timestamp_from_filetime( t, &ft ); +#elif defined(_POSIX_TIMERS) && defined(CLOCK_REALTIME) && \ + (!defined(__GLIBC__) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17)) + /* Some older versions of XCode define _POSIX_TIMERS, but don't actually + * have clock_gettime. Check CLOCK_REALTIME as well. Prior to glibc 2.17, + * clock_gettime requires -lrt. This is a non-critical feature, so + * we just disable it to keep bootstrapping simple. + */ + struct timespec ts; + clock_gettime( CLOCK_REALTIME, &ts ); + timestamp_init( t, ts.tv_sec, ts.tv_nsec ); #else /* OS_NT */ timestamp_init( t, time( 0 ), 0 ); #endif /* OS_NT */ diff --git a/src/tools/bison.jam b/src/tools/bison.jam index 0689d4bd8..96b86a327 100644 --- a/src/tools/bison.jam +++ b/src/tools/bison.jam @@ -4,8 +4,8 @@ import generators ; import feature ; +import toolset : flags ; import type ; -import property ; feature.feature bison.prefix : : free ; type.register Y : y ; @@ -17,16 +17,10 @@ rule init ( ) { } -rule bison ( dst dst_header : src : properties * ) -{ - local r = [ property.select bison.prefix : $(properties) ] ; - if $(r) - { - PREFIX_OPT on $(<) = -p $(r:G=) ; - } -} +flags bison.bison PREFIX ; +_ = " " ; actions bison { - bison $(PREFIX_OPT) -d -o $(<[1]) $(>) + bison -p$(_)$(PREFIX) -d -o $(<[1]) $(>) } diff --git a/src/tools/boostbook.jam b/src/tools/boostbook.jam index 43e7366da..d99d5d04a 100644 --- a/src/tools/boostbook.jam +++ b/src/tools/boostbook.jam @@ -94,7 +94,7 @@ rule init ( find-tools $(docbook-xsl-dir) : $(docbook-dtd-dir) : $(boostbook-dir) ; # Register generators only if we were called via "using boostbook ;" - local reg-gen = generators.register-standard ; + local reg-gen = generators.register-xslt ; $(reg-gen) boostbook.dtdxml-to-boostbook : DTDXML : XML ; $(reg-gen) boostbook.boostbook-to-docbook : XML : DOCBOOK ; $(reg-gen) boostbook.boostbook-to-tests : XML : TESTS ; diff --git a/src/tools/bzip2.jam b/src/tools/bzip2.jam index e6f9dcc13..49d83badd 100644 --- a/src/tools/bzip2.jam +++ b/src/tools/bzip2.jam @@ -14,11 +14,11 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import make ; import os ; @@ -113,18 +113,12 @@ rule init ( project bzip2 ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local source-path = [ property.select : $(options) ] ; - source-path = $(source-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; - local tag = [ property.select : $(options) ] ; - tag = $(tag:G=) ; - local build-name = [ property.select : $(options) ] ; - build-name = $(build-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local source-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; + local tag = [ feature.get-values : $(options) ] ; + local build-name = [ feature.get-values : $(options) ] ; if ! $(library-path) && ! $(include-path) && ! $(source-path) && ! $(library-name) { @@ -171,7 +165,7 @@ rule init ( build-name ?= bz2 ; library-id = [ CALC $(library-id) + 1 ] ; tag = [ MATCH ^@?(.*)$ : $(tag) ] ; - if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ] + if $(tag) { tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } diff --git a/src/tools/clang-darwin.jam b/src/tools/clang-darwin.jam index 9d0197362..74a323a16 100644 --- a/src/tools/clang-darwin.jam +++ b/src/tools/clang-darwin.jam @@ -33,8 +33,7 @@ generators.register-c-compiler clang-darwin.compile.mm : OBJECTIVE_CPP : OBJ : < toolset.inherit-rules clang-darwin : gcc ; toolset.inherit-flags clang-darwin : gcc - : off on full space - off all on + : full x86/32 x86/64 ; @@ -65,8 +64,7 @@ rule init ( version ? : command * : options * ) : version $(version) ] ; common.handle-options clang-darwin : $(condition) : $(command) : $(options) ; - - gcc.init-link-flags clang darwin $(condition) ; + clang.init-cxxstd-flags clang-darwin : $(condition) : $(version) ; # - Ranlib. local ranlib = [ feature.get-values : $(options) ] ; @@ -79,42 +77,27 @@ rule init ( version ? : command * : options * ) SPACE = " " ; -toolset.flags clang-darwin.compile OPTIONS ; -toolset.flags clang-darwin.compile.c++ OPTIONS ; toolset.flags clang-darwin.compile.m OPTIONS ; toolset.flags clang-darwin.compile.mm OPTIONS ; toolset.flags clang-darwin.compile.mm OPTIONS ; -# toolset.flags clang-darwin.compile INCLUDES ; # Declare flags and action for compilation. -toolset.flags clang-darwin.compile OPTIONS off : -O0 ; -toolset.flags clang-darwin.compile OPTIONS speed : -O3 ; -toolset.flags clang-darwin.compile OPTIONS space : -Os ; # For clang, 'on' and 'full' are identical -toolset.flags clang-darwin.compile OPTIONS off : -fno-inline ; -toolset.flags clang-darwin.compile OPTIONS on : -Wno-inline ; toolset.flags clang-darwin.compile OPTIONS full : -Wno-inline ; -toolset.flags clang-darwin.compile OPTIONS off : -w ; -toolset.flags clang-darwin.compile OPTIONS on : -Wall ; -toolset.flags clang-darwin.compile OPTIONS all : -Wall -pedantic ; -toolset.flags clang-darwin.compile OPTIONS on : -Werror ; - -toolset.flags clang-darwin.compile OPTIONS on : -g ; -toolset.flags clang-darwin.compile OPTIONS on : -pg ; -toolset.flags clang-darwin.compile OPTIONS off : -fno-rtti ; - +# SJW 12/2017: Support for is widely inconsistant. +# shouldn't this be handled by the common gcc? toolset.flags clang-darwin.compile OPTIONS ; actions compile.c { - "$(CONFIG_COMMAND)" -x c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" + "$(CONFIG_COMMAND)" -x c $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } actions compile.c++ { - "$(CONFIG_COMMAND)" -x c++ $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" + "$(CONFIG_COMMAND)" -x c++ $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } actions compile.m diff --git a/src/tools/clang-linux.jam b/src/tools/clang-linux.jam index b1c2ad95b..53c5749e5 100644 --- a/src/tools/clang-linux.jam +++ b/src/tools/clang-linux.jam @@ -36,9 +36,9 @@ type.set-generated-target-suffix PCH toolset.inherit-rules clang-linux : gcc ; toolset.inherit-flags clang-linux : gcc - : off on full - space speed - off all on ; + : full + multi/windows + ; if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] { .debug-configuration = true ; @@ -60,8 +60,7 @@ rule init ( version ? : command * : options * ) { : version $(version) ] ; common.handle-options clang-linux : $(condition) : $(command) : $(options) ; - - gcc.init-link-flags clang linux $(condition) ; + clang.init-cxxstd-flags clang-linux : $(condition) : $(version) ; # - Ranlib. local ranlib = [ feature.get-values : $(options) ] ; @@ -75,27 +74,12 @@ rule init ( version ? : command * : options * ) { ############################################################################### # Flags -toolset.flags clang-linux.compile OPTIONS ; -toolset.flags clang-linux.compile.c++ OPTIONS ; - -toolset.flags clang-linux.compile OPTIONS off : ; -toolset.flags clang-linux.compile OPTIONS speed : -O3 ; -toolset.flags clang-linux.compile OPTIONS space : -Os ; - # note: clang silently ignores some of these inlining options -toolset.flags clang-linux.compile OPTIONS off : -fno-inline ; # For clang, 'on' and 'full' are identical. -toolset.flags clang-linux.compile OPTIONS on : -Wno-inline ; toolset.flags clang-linux.compile OPTIONS full : -Wno-inline ; -toolset.flags clang-linux.compile OPTIONS off : -w ; -toolset.flags clang-linux.compile OPTIONS on : -Wall ; -toolset.flags clang-linux.compile OPTIONS all : -Wall -pedantic ; -toolset.flags clang-linux.compile OPTIONS on : -Werror ; - -toolset.flags clang-linux.compile OPTIONS on : -g ; -toolset.flags clang-linux.compile OPTIONS on : -pg ; -toolset.flags clang-linux.compile OPTIONS off : -fno-rtti ; +toolset.flags clang-linux.compile OPTIONS multi/windows : -pthread ; +toolset.flags clang-linux.link OPTIONS multi/windows : -pthread ; ############################################################################### # C and C++ compilation diff --git a/src/tools/clang-vxworks.jam b/src/tools/clang-vxworks.jam index 203a3a320..b104aa7e8 100644 --- a/src/tools/clang-vxworks.jam +++ b/src/tools/clang-vxworks.jam @@ -69,8 +69,6 @@ rule init ( version ? : command * : options * ) common.handle-options clang-vxworks : $(condition) : $(command) : $(options) ; - gcc.init-link-flags clang-vxworks vxworks $(condition) ; - toolset.flags clang-vxworks.link .LD : $(linker) ; } diff --git a/src/tools/clang.jam b/src/tools/clang.jam index 2d89f3807..26c95202a 100644 --- a/src/tools/clang.jam +++ b/src/tools/clang.jam @@ -8,6 +8,9 @@ import feature ; import os ; import toolset ; +import sequence ; +import regex ; +import set ; feature.extend toolset : clang ; feature.subfeature toolset clang : platform : : propagated link-incompatible ; @@ -25,3 +28,33 @@ rule init ( * : * ) $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } } + + +local rule cxxstd-flags ( toolset : condition * : options * ) +{ + toolset.flags $(toolset).compile.c++ OPTIONS $(condition) : $(options) : unchecked ; + toolset.flags $(toolset).link OPTIONS $(condition) : $(options) : unchecked ; +} + +local rule version-ge ( lhs : rhs ) +{ + lhs = [ regex.split $(lhs) "[.]" ] ; + rhs = [ regex.split $(rhs) "[.]" ] ; + return [ sequence.compare $(rhs) : $(lhs) : numbers.less ] ; +} + +# Version specific flags +rule init-cxxstd-flags ( toolset : condition * : version ) +{ + local cxxstd = [ feature.values ] ; + local dialects = [ feature.values ] ; + dialects = [ set.difference $(dialects) : gnu iso ] ; + local std ; + if [ version-ge $(version) : 3.5 ] { std = 1z ; } + else if [ version-ge $(version) : 3.4 ] { std = 14 ; } + else if [ version-ge $(version) : 3.3 ] { std = 11 ; } + else { std = 03 ; } + cxxstd-flags $(toolset) : $(condition)/latest/iso : -std=c++$(std) ; + cxxstd-flags $(toolset) : $(condition)/latest/gnu : -std=gnu++$(std) ; + cxxstd-flags $(toolset) : $(condition)/latest/$(dialects) : -std=c++$(std) ; +} diff --git a/src/tools/common.jam b/src/tools/common.jam index 6a92d55c2..10059ea01 100644 --- a/src/tools/common.jam +++ b/src/tools/common.jam @@ -925,9 +925,9 @@ local rule toolset-tag ( name : type ? : property-set ) case edg* : tag += edg ; case gcc* : { - switch [ $(property-set).get ] + switch [ $(property-set).get ] { - case *mingw* : tag += mgw ; + case *windows* : tag += mgw ; case * : tag += gcc ; } } diff --git a/src/tools/darwin.jam b/src/tools/darwin.jam index 1760a1986..3369ac973 100644 --- a/src/tools/darwin.jam +++ b/src/tools/darwin.jam @@ -156,9 +156,6 @@ rule init ( version ? : command * : options * : requirement * ) flags darwin.compile OPTIONS $(condition)/full : -Wno-inline ; } - # - Set the link flags common with the GCC toolset. - gcc.init-link-flags darwin darwin $(condition) ; - # - The symbol strip program. local strip ; if in $(options) diff --git a/src/tools/doxygen.jam b/src/tools/doxygen.jam index 1e5362431..19de6d0ec 100644 --- a/src/tools/doxygen.jam +++ b/src/tools/doxygen.jam @@ -105,9 +105,9 @@ rule init ( name ? ) : DOXYFILE : DOXYGEN_XML_MULTIFILE ; generators.register-standard doxygen.xml-dir-to-boostbook : DOXYGEN_XML_MULTIFILE : BOOSTBOOK : doxproc ; - generators.register-standard doxygen.xml-to-boostbook + generators.register-xslt doxygen.xml-to-boostbook : DOXYGEN_XML : BOOSTBOOK : xsltproc ; - generators.register-standard doxygen.collect + generators.register-xslt doxygen.collect : DOXYGEN_XML_MULTIFILE : DOXYGEN_XML ; generators.register-standard doxygen.run : DOXYFILE : DOXYGEN_HTML_MULTIFILE ; @@ -274,6 +274,7 @@ rule translate-path ( path ) } } +toolset.uses-features doxygen.headers-to-doxyfile : "" ; # Generates a doxygen configuration file (doxyfile) given a set of C++ sources # and a property list that may contain features. @@ -321,6 +322,7 @@ rule headers-to-doxyfile ( target : sources * : properties * ) print.text $(text) : true ; } +toolset.uses-features doxygen.run : "" ; # Run Doxygen. See doxygen-action for a description of the strange properties of # this rule. @@ -406,6 +408,7 @@ rule collect ( target : source : properties * ) : doxygen.xml.path=$(native-path) ; } +toolset.uses-features doxygen.xml-to-boostbook : ; # Translate Doxygen XML into BoostBook. # diff --git a/src/tools/features/relevant-feature.jam b/src/tools/features/relevant-feature.jam new file mode 100644 index 000000000..f55bd7f4c --- /dev/null +++ b/src/tools/features/relevant-feature.jam @@ -0,0 +1,10 @@ +# Copyright 2017 Steven Watanabe +# 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) + +# Identifies relevant features. + +import feature ; + +feature.feature relevant : : incidental free ; diff --git a/src/tools/features/threadapi-feature.jam b/src/tools/features/threadapi-feature.jam index 1a2b0923d..0f9d42fa5 100644 --- a/src/tools/features/threadapi-feature.jam +++ b/src/tools/features/threadapi-feature.jam @@ -25,5 +25,5 @@ rule detect ( properties * ) local ps = [ property-set.create $(properties) ] ; local api = [ $(ps).get ] ; if ! $(api) { api = [ get-default $(ps) ] ; } - return $(api) ; + return $(api) threadapi:target-os ; } diff --git a/src/tools/gcc.jam b/src/tools/gcc.jam index e6907fade..50b9408c4 100644 --- a/src/tools/gcc.jam +++ b/src/tools/gcc.jam @@ -100,6 +100,7 @@ import property ; import property-set ; import rc ; import regex ; +import sequence ; import set ; import toolset ; import type ; @@ -115,7 +116,6 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] feature.extend toolset : gcc ; -# feature.subfeature toolset gcc : flavor : : optional ; toolset.inherit-generators gcc : unix : unix.link unix.link.dll ; toolset.inherit-flags gcc : unix ; @@ -179,8 +179,8 @@ rule init ( version ? : command * : options * : requirement * ) { local tool-command-string = \"$(tool-command)\" ; tool-command-string = $(tool-command-string:J=" ") ; - local tool-version = [ MATCH "^([0-9.]+)" : - [ SHELL "$(tool-command-string) -dumpversion" ] ] ; + local tool-version = [ dump-full-version + $(tool-command-string) ] ; if $(tool-version) != $(version) { # Permit a match betwen a two-digit version specified by the @@ -262,26 +262,20 @@ rule init ( version ? : command * : options * : requirement * ) { local machine = [ MATCH "^([^ ]+)" : [ SHELL "$(command-string) -dumpmachine" ] ] ; - version ?= [ MATCH "^([0-9.]+)" : - [ SHELL "$(command-string) -dumpversion" ] ] ; + version ?= [ dump-full-version $(command-string) ] ; switch $(machine:L) { case *mingw* : flavor ?= mingw ; + case *cygwin* : flavor ?= cygwin ; } } local condition ; - if $(flavor) - { - condition = flavor $(flavor) ; - } condition = [ common.check-init-parameters gcc $(requirement) : version $(version) : $(condition) ] ; common.handle-options gcc : $(condition) : $(command) : $(options) ; - init-link-flags gcc "" $(condition) ; - # If gcc is installed in a non-standard location, we would need to add # LD_LIBRARY_PATH when running programs created with it (for unit-test/run # rules). @@ -346,6 +340,8 @@ rule init ( version ? : command * : options * : requirement * ) rc.configure $(rc) : $(condition) : $(rc-type) ; toolset.flags gcc VERSION $(condition) : [ regex.split $(version) "[.]" ] ; + + init-cxxstd-flags $(condition) : $(version) ; } if [ os.name ] = NT @@ -356,6 +352,15 @@ if [ os.name ] = NT JAMSHELL = % ; } +local rule dump-full-version ( command-string ) +{ + # -dumpfullversion is only supported for gcc 7+. + # Passing both options works, as the first one that's + # recognized will be used. + return [ MATCH "^([0-9.]+)" : + [ SHELL "$(command-string) -dumpfullversion -dumpversion" ] ] ; +} + # Uses -print-prog-name to get the name of the tool. # Converts the path to native form if using cygwin. rule .get-prog-name ( command-string : tool : flavor ? ) @@ -363,7 +368,7 @@ rule .get-prog-name ( command-string : tool : flavor ? ) local prog-name = [ NORMALIZE_PATH [ MATCH "(.*)[\n]+" : [ SHELL "$(command-string) -print-prog-name=$(tool)" ] ] ] ; - if $(flavor) != vxworks && $(flavor) != mingw && [ os.name ] = NT + if $(flavor) = cygwin && [ os.name ] = NT { prog-name = [ cygwin.cygwin-to-windows-path $(prog-name) ] ; } @@ -374,259 +379,127 @@ rule .get-prog-name ( command-string : tool : flavor ? ) ### Functions that set options on the targets. ### -rule set-fpic-options ( targets * : sources * : properties * ) +local all-os = [ feature.values ] ; + +local rule compile-link-flags ( * ) { - local link = [ feature.get-values link : $(properties) ] ; - if $(link) = shared + toolset.flags gcc.compile OPTIONS $(1) : $(2) ; + toolset.flags gcc.link OPTIONS $(1) : $(2) ; +} + +{ + # This logic will add -fPIC for all compilations: + # + # lib a : a.cpp b ; + # obj b : b.cpp ; + # exe c : c.cpp a d ; + # obj d : d.cpp ; + # + # This all is fine, except that 'd' will be compiled with -fPIC even + # though it is not needed, as 'd' is used only in exe. However, it is + # hard to detect where a target is going to be used. Alternatively, we + # can set -fPIC only when main target type is LIB but than 'b' would be + # compiled without -fPIC which would lead to link errors on x86-64. So, + # compile everything with -fPIC. + # + # Yet another alternative would be to create a propagated + # feature and set it when building shared libraries, but that would be + # hard to implement and would increase the target path length even more. + + # On Windows, fPIC is the default, and specifying -fPIC explicitly leads + # to a warning. + local non-windows = [ set.difference $(all-os) : cygwin windows ] ; + compile-link-flags shared/$(non-windows) : -fPIC ; +} + +{ + # Handle address-model + compile-link-flags aix/32 : -maix32 ; + compile-link-flags aix/64 : -maix64 ; + + compile-link-flags hpux/32 : -milp32 ; + compile-link-flags hpux/64 : -mlp64 ; + + local generic-os = [ set.difference $(all-os) : aix hpux ] ; + local arch = power sparc x86 ; + compile-link-flags $(generic-os)/$(arch)/32 : -m32 ; + compile-link-flags $(generic-os)/$(arch)/64 : -m64 ; +} + +{ + # Handle threading + local rule threading-flags ( * ) { - local target-os = [ feature.get-values target-os : $(properties) ] ; - - # This logic will add -fPIC for all compilations: - # - # lib a : a.cpp b ; - # obj b : b.cpp ; - # exe c : c.cpp a d ; - # obj d : d.cpp ; - # - # This all is fine, except that 'd' will be compiled with -fPIC even - # though it is not needed, as 'd' is used only in exe. However, it is - # hard to detect where a target is going to be used. Alternatively, we - # can set -fPIC only when main target type is LIB but than 'b' would be - # compiled without -fPIC which would lead to link errors on x86-64. So, - # compile everything with -fPIC. - # - # Yet another alternative would be to create a propagated - # feature and set it when building shared libraries, but that would be - # hard to implement and would increase the target path length even more. - - # On Windows, fPIC is the default, and specifying -fPIC explicitly leads - # to a warning. - if ! $(target-os) in cygwin windows + compile-link-flags multi/$(1) : $(2) ; + if $(3) { - OPTIONS on $(targets) += -fPIC ; + toolset.flags gcc.link FINDLIBS-SA multi/$(1) : $(3) ; + } + } + + threading-flags windows : -mthreads ; + threading-flags cygwin : -mthreads ; + threading-flags solaris : -pthreads : rt ; + + local bsd = [ MATCH ^(.*bsd)$ : $(all-os) ] ; + threading-flags $(bsd) : -pthread ; + + local no-threading = android beos haiku sgi darwin vxworks ; + local threading-generic-os = [ set.difference $(all-os) : $(no-threading) $(bsd) windows cygwin solaris ] ; + threading-flags $(threading-generic-os) : -pthread : rt ; +} + +{ + local rule cxxstd-flags ( * ) + { + toolset.flags gcc.compile.c++ OPTIONS $(1) : $(2) ; + toolset.flags gcc.link OPTIONS $(1) : $(2) ; + } + + local cxxstd = [ feature.values ] ; + local dialects = [ feature.values ] ; + .cxxstd-dialects = [ set.difference $(dialects) : gnu iso ] ; + # C++ latest needs to be set up on a per-toolset basis + for local std in [ set.difference $(cxxstd) : latest ] + { + cxxstd-flags $(std)/iso : -std=c++$(std) ; + cxxstd-flags $(std)/gnu : -std=gnu++$(std) ; + # If we see this it's probably a mistake, but + # toolset.flags has no way to set up diagnostics. + cxxstd-flags $(std)/$(.cxxstd-dialects) : -std=c++$(std) ; + } + + local rule version-ge ( lhs : rhs ) + { + lhs = [ regex.split $(lhs) "[.]" ] ; + rhs = [ regex.split $(rhs) "[.]" ] ; + return [ sequence.compare $(rhs) : $(lhs) : numbers.less ] ; + } + # Version specific flags + local rule init-cxxstd-flags ( condition * : version ) + { + local std ; + if [ version-ge $(version) : 8.0 ] { std = 2a ; } + else if [ version-ge $(version) : 5.1 ] { std = 1z ; } + else if [ version-ge $(version) : 4.8 ] { std = 1y ; } + else if [ version-ge $(version) : 4.7 ] { std = 11 ; } + else if [ version-ge $(version) : 3.3 ] { std = 98 ; } + if $(std) + { + cxxstd-flags $(condition)/latest/iso : -std=c++$(std) ; + cxxstd-flags $(condition)/latest/gnu : -std=gnu++$(std) ; + cxxstd-flags $(condition)/latest/$(.cxxstd-dialects) : -std=c++$(std) ; } } } -rule set-address-model-options ( targets * : sources * : properties * ) -{ - local model = [ feature.get-values address-model : $(properties) ] ; - if $(model) - { - local option ; - local target-os = [ feature.get-values target-os : $(properties) ] ; - if $(target-os) = aix - { - if $(model) = 32 - { - option = -maix32 ; - } - else - { - option = -maix64 ; - } - } - else if $(target-os) = hpux - { - if $(model) = 32 - { - option = -milp32 ; - } - else - { - option = -mlp64 ; - } - } - else - { - local arch = [ feature.get-values architecture : $(properties) ] ; - if $(arch) = power || $(arch) = sparc || $(arch) = x86 - { - if $(model) = 32 - { - option = -m32 ; - } - else if $(model) = 64 - { - option = -m64 ; - } - } - # For darwin, the model can be 32_64. darwin.jam will handle that - # on its own. - } - OPTIONS on $(targets) += $(option) ; - } -} +generators.register-c-compiler gcc.compile.c++.preprocess : CPP : PREPROCESSED_CPP : gcc ; +generators.register-c-compiler gcc.compile.c.preprocess : C : PREPROCESSED_C : gcc ; +generators.register-c-compiler gcc.compile.c++ : CPP : OBJ : gcc ; +generators.register-c-compiler gcc.compile.c : C : OBJ : gcc ; +generators.register-c-compiler gcc.compile.asm : ASM : OBJ : gcc ; -rule set-threading-options ( targets * : sources * : properties * ) -{ - local threading = [ feature.get-values threading : $(properties) ] ; - if $(threading) = multi - { - local target-os = [ feature.get-values target-os : $(properties) ] ; - local host-os = [ feature.get-values host-os : $(properties) ] ; - local toolset = [ feature.get-values toolset : $(properties) ] ; - local option ; - local libs ; - - if $(toolset) = clang && $(target-os) = windows - { - option = -pthread ; - } - - switch $(target-os) - { - case android : # No threading options, everything is in already. - case windows : option ?= -mthreads ; - case cygwin : option ?= -mthreads ; - case solaris : option ?= -pthreads ; libs = rt ; - case beos : # No threading options. - case haiku : # No threading options. - case *bsd : option ?= -pthread ; # There is no -lrt on BSD. - case sgi : # gcc on IRIX does not support multi-threading. - case darwin : # No threading options. - case vxworks : # No threading options. - case * : option ?= -pthread ; libs = rt ; - } - if $(option) - { - OPTIONS on $(targets) += $(option) ; - } - if $(libs) - { - FINDLIBS-SA on $(targets) += $(libs) ; - } - } -} - -local rule zero-pad ( numbers * ) -{ - local result ; - for local n in $(numbers) - { - switch $(n) - { - case ???? : result += $(n) ; - case ??? : result += 0$(n) ; - case ?? : result += 00$(n) ; - case ? : result += 000$(n) ; - } - } - return $(result) ; -} - -rule set-cxxstd-options ( targets * : sources * : properties * : action ) -{ - local *targets = [ $(action).targets ] ; - local *sources = [ $(action).sources ] ; - local target-type = [ $(*targets[1]).type ] ; - local source-type = [ $(*sources[1]).type ] ; - local toolset = [ feature.get-values toolset : $(properties) ] ; - local version = [ zero-pad [ on $(targets[1]) return $(VERSION) ] ] ; - version = $(version[1]).$(version[2]) ; - local cxxstd = [ feature.get-values cxxstd : $(properties) ] ; - local cxxstd-dialect = [ feature.get-values cxxstd-dialect : $(properties) ] ; - cxxstd-dialect ?= iso ; - switch $(cxxstd-dialect) - { - case gnu : cxxstd-dialect = gnu++ ; - case iso : cxxstd-dialect = c++ ; - case * : - errors.warning Unknown cxxstd-dialect $(cxxstd-dialect:E=?) .. using - ISO dialect instead. ; - cxxstd-dialect = c++ ; - } - local option ; - if $(cxxstd) = latest - { - if $(toolset) = gcc - { - if $(version) >= 0008.0000 { option = 2a ; } - else if $(version) >= 0005.0001 { option = 1z ; } - else if $(version) >= 0004.0008 { option = 1y ; } - else if $(version) >= 0004.0007 { option = 11 ; } - else if $(version) >= 0003.0003 { option = 98 ; } - } - if $(toolset) = clang - { - if $(version) >= 0003.0005 { option = 1z ; } - if $(version) >= 0003.0004 { option = 14 ; } - if $(version) >= 0003.0003 { option = 11 ; } - option ?= 03 ; - } - } - else - { - option = $(cxxstd) ; - } - if $(source-type) in CPP || $(target-type) in CPP_PCH EXE SHARED_LIB - { - OPTIONS on $(targets) += -std=$(cxxstd-dialect)$(option) ; - } -} - -### -### Compiling generators and actions. -### - -class gcc-c-compiling-generator : C-compiling-generator -{ - rule action-class ( ) - { - return gcc-c-compile-action ; - } -} - -class gcc-c-compile-action : compile-action -{ - import gcc ; - - rule execute ( action-name targets + : sources * : properties * ) - { - gcc.set-threading-options $(targets) : $(sources) : $(properties) ; - gcc.set-fpic-options $(targets) : $(sources) : $(properties) ; - gcc.set-address-model-options $(targets) : $(sources) : $(properties) ; - gcc.set-cxxstd-options $(targets) : $(sources) : $(properties) : $(__name__) ; - compile-action.execute $(action-name) $(targets) : $(sources) : $(properties) ; - } -} - -local rule register-gcc-c-compiler ( id : source-types + : target-types + : requirements * - : optional-properties * ) -{ - generators.register [ new gcc-c-compiling-generator $(id) : $(source-types) : - $(target-types) : $(requirements) : $(optional-properties) ] ; -} - -register-gcc-c-compiler gcc.compile.c++.preprocess : CPP : PREPROCESSED_CPP : gcc ; -register-gcc-c-compiler gcc.compile.c.preprocess : C : PREPROCESSED_C : gcc ; -register-gcc-c-compiler gcc.compile.c++ : CPP : OBJ : gcc ; -register-gcc-c-compiler gcc.compile.c : C : OBJ : gcc ; -register-gcc-c-compiler gcc.compile.asm : ASM : OBJ : gcc ; - -class gcc-fortran-compiling-generator : fortran-compiling-generator -{ - rule action-class ( ) - { - return gcc-fortran-compile-action ; - } -} - -class gcc-fortran-compile-action : compile-action -{ - import gcc ; - - rule execute ( action-name targets + : sources * : properties * ) - { - gcc.set-threading-options $(targets) : $(sources) : $(properties) ; - gcc.set-fpic-options $(targets) : $(sources) : $(properties) ; - gcc.set-address-model-options $(targets) : $(sources) : $(properties) ; - gcc.set-cxxstd-options $(targets) : $(sources) : $(properties) : $(__name__) ; - compile-action.execute $(action-name) $(targets) : $(sources) : $(properties) ; - } -} - -generators.register [ new gcc-fortran-compiling-generator +generators.register [ new fortran-compiling-generator gcc.compile.fortran : FORTRAN FORTRAN90 : OBJ : gcc ] ; rule compile.c++.preprocess ( targets * : sources * : properties * ) @@ -763,8 +636,8 @@ class gcc-pch-generator : pch-generator # Return result of base class and pch-file property as # usage-requirements. return - [ property-set.create $(pch-file) -Winvalid-pch ] - $(pch-file) + [ $(pch-file[1]).add-raw $(pch-file[2-]) -Winvalid-pch ] + $(pch-file[2-]) ; } @@ -777,25 +650,6 @@ class gcc-pch-generator : pch-generator return [ generator.generated-targets $(sources) : $(property-set) : $(project) $(name) ] ; } - - rule action-class ( ) - { - return gcc-pch-compile-action ; - } -} - -class gcc-pch-compile-action : compile-action -{ - import gcc ; - - rule execute ( action-name targets + : sources * : properties * ) - { - gcc.set-threading-options $(targets) : $(sources) : $(properties) ; - gcc.set-fpic-options $(targets) : $(sources) : $(properties) ; - gcc.set-address-model-options $(targets) : $(sources) : $(properties) ; - gcc.set-cxxstd-options $(targets) : $(sources) : $(properties) : $(__name__) ; - compile-action.execute $(action-name) $(targets) : $(sources) : $(properties) ; - } } # Note: the 'H' source type will catch both '.h' header and '.hpp' header. The @@ -917,26 +771,6 @@ class gcc-linking-generator : unix-linking-generator $(property-set) : $(sources) ] ; } } - - rule action-class ( ) - { - return gcc-link-action ; - } -} - -class gcc-link-action : action -{ - import gcc ; - - rule execute ( action-name targets + : sources * : properties * ) - { - gcc.set-threading-options $(targets) : $(sources) : $(properties) ; - gcc.set-fpic-options $(targets) : $(sources) : $(properties) ; - gcc.set-address-model-options $(targets) : $(sources) : $(properties) ; - gcc.set-cxxstd-options $(targets) : $(sources) : $(properties) : $(__name__) ; - gcc.set-link-options $(action-name) $(targets) : $(sources) : $(properties) ; - action.execute $(action-name) $(targets) : $(sources) : $(properties) ; - } } # The set of permissible input types is different on mingw. So, define two sets @@ -1005,230 +839,163 @@ toolset.flags gcc.link LIBRARIES ; toolset.flags gcc.link.dll .IMPLIB-COMMAND windows : "-Wl,--out-implib," ; toolset.flags gcc.link.dll .IMPLIB-COMMAND cygwin : "-Wl,--out-implib," ; -# Now, the vendor specific flags. -# The parameter linker can be either aix, darwin, gnu, hpux, osf or sun. -rule init-link-flags ( toolset subtool condition ) +# target specific link flags { - ## Need to define the linker-type feature once for each toolset module. - if ! [ feature.valid ] - { - feature.subfeature toolset $(toolset) : linker-type : - gnu aix darwin hpux osf sun : propagated link-incompatible ; - } - ## The specification to add the linker-type is per toolset "instance". - toolset.add-requirements - $(condition),aix:aix - $(condition),darwin:darwin - $(condition),hpux:hpux - $(condition),osf:osf - $(condition),solaris:sun - ; -} + # aix -rule set-link-options ( action-name targets + : sources * : properties * ) -{ - local toolset = [ feature.get-values : $(properties) ] ; - local linker-type = [ feature.get-values : $(properties) ] ; - local target-os = [ feature.get-values : $(properties) ] ; + # On AIX we *have* to use the native linker. + # + # Using -brtl, the AIX linker will look for libraries with both the .a + # and .so extensions, such as libfoo.a and libfoo.so. Without -brtl, the + # AIX linker looks only for libfoo.a. Note that libfoo.a is an archived + # file that may contain shared objects and is different from static libs + # as on Linux. + # + # The -bnoipath strips the prepending (relative) path of libraries from + # the loader section in the target library or executable. Hence, during + # load-time LIBPATH (identical to LD_LIBRARY_PATH) or a hard-coded + # -blibpath (*similar* to -lrpath/-lrpath-link) is searched. Without + # this option, the prepending (relative) path + library name is + # hard-coded in the loader section, causing *only* this path to be + # searched during load-time. Note that the AIX linker does not have an + # -soname equivalent, this is as close as it gets. + # + # The -bbigtoc option instrcuts the linker to create a TOC bigger than 64k. + # This is neccesary for some submodules such as math, but it does make running + # the tests a tad slower. + # + # The above options are definately for AIX 5.x, and most likely also for + # AIX 4.x and AIX 6.x. For details about the AIX linker see: + # http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/es-aix_ll.pdf + # + toolset.flags gcc.link OPTIONS aix : -Wl,-brtl -Wl,-bnoipath -Wl,-bbigtoc ; - switch $(linker-type:G=) - { - case aix : + # See note [1] + toolset.flags gcc.link OPTIONS aix/static : -static ; - # On AIX we *have* to use the native linker. - # - # Using -brtl, the AIX linker will look for libraries with both the .a - # and .so extensions, such as libfoo.a and libfoo.so. Without -brtl, the - # AIX linker looks only for libfoo.a. Note that libfoo.a is an archived - # file that may contain shared objects and is different from static libs - # as on Linux. - # - # The -bnoipath strips the prepending (relative) path of libraries from - # the loader section in the target library or executable. Hence, during - # load-time LIBPATH (identical to LD_LIBRARY_PATH) or a hard-coded - # -blibpath (*similar* to -lrpath/-lrpath-link) is searched. Without - # this option, the prepending (relative) path + library name is - # hard-coded in the loader section, causing *only* this path to be - # searched during load-time. Note that the AIX linker does not have an - # -soname equivalent, this is as close as it gets. - # - # The -bbigtoc option instrcuts the linker to create a TOC bigger than 64k. - # This is neccesary for some submodules such as math, but it does make running - # the tests a tad slower. - # - # The above options are definately for AIX 5.x, and most likely also for - # AIX 4.x and AIX 6.x. For details about the AIX linker see: - # http://download.boulder.ibm.com/ibmdl/pub/software/dw/aix/es-aix_ll.pdf - # + # darwin - OPTIONS on $(targets) += -Wl,-brtl -Wl,-bnoipath -Wl,-bbigtoc ; + # On Darwin, the -s option to ld does not work unless we pass -static, + # and passing -static unconditionally is a bad idea. So, do not pass -s + # at all and darwin.jam will use a separate 'strip' invocation. + toolset.flags gcc.link RPATH darwin : ; + # This does not support -R. + toolset.flags gcc.link RPATH_OPTION darwin : -rpath ; + # -rpath-link is not supported at all. - # See note [1] - if static in $(properties) - { - OPTIONS on $(targets) += -static ; - } - - case darwin : - - # On Darwin, the -s option to ld does not work unless we pass -static, - # and passing -static unconditionally is a bad idea. So, do not pass -s - # at all and darwin.jam will use a separate 'strip' invocation. - RPATH on $(targets) += - [ feature.get-values : $(properties) ] ; - # This does not support -R. - RPATH_OPTION on $(targets) += -rpath ; - # -rpath-link is not supported at all. - - # See note [1] - if static in $(properties) - { - OPTIONS on $(targets) += -static ; - } + # See note [1] + toolset.flags gcc.link OPTIONS darwin/static : -static ; - case vxworks : - # On VxWorks we want to reflect what ever special flags have been set in the - # environment for the CPU we are targeting in the cross build - toolset.flags $(toolset).link OPTIONS $(condition)/on : -Wl,--strip-all : unchecked ; - toolset.flags $(toolset).link OPTIONS $(condition)/static : [ os.environ LDFLAGS_STATIC ] : unchecked ; - toolset.flags $(toolset).link.dll OPTIONS $(condition) : [ os.environ LDFLAGS_SO ] : unchecked ; - toolset.flags $(toolset).link OPTIONS $(condition)/shared : [ os.environ LDFLAGS_DYNAMIC ] : unchecked ; + # vxworks + # On VxWorks we want to reflect what ever special flags have been set in the + # environment for the CPU we are targeting in the cross build + toolset.flags gcc.link OPTIONS vxworks/on : -Wl,--strip-all ; + toolset.flags gcc.link OPTIONS vxworks/static : [ os.environ LDFLAGS_STATIC ] ; + toolset.flags gcc.link.dll OPTIONS vxworks : [ os.environ LDFLAGS_SO ] ; + toolset.flags gcc.link OPTIONS vxworks/shared : [ os.environ LDFLAGS_DYNAMIC ] ; - case gnu : + # default - # Strip the binary when no debugging is needed. We use --strip-all flag - # as opposed to -s since icc (intel's compiler) is generally - # option-compatible with and inherits from the gcc toolset, but does not - # support -s. - if on in $(properties) - { - OPTIONS on $(targets) += -Wl,--strip-all ; - } - RPATH on $(targets) += - [ feature.get-values : $(properties) ] ; - RPATH_OPTION on $(targets) += -rpath ; - RPATH_LINK on $(targets) += - [ feature.get-values : $(properties) ] ; - START-GROUP on $(targets) += -Wl,--start-group ; - END-GROUP on $(targets) += -Wl,--end-group ; + local generic-os = [ set.difference $(all-os) : aix darwin vxworks solaris osf hpux ] ; + # Strip the binary when no debugging is needed. We use --strip-all flag + # as opposed to -s since icc (intel's compiler) is generally + # option-compatible with and inherits from the gcc toolset, but does not + # support -s. + toolset.flags gcc.link OPTIONS $(generic-os)/on : + -Wl,--strip-all ; + toolset.flags gcc.link RPATH $(generic-os) : ; + toolset.flags gcc.link RPATH_OPTION $(generic-os) : -rpath ; + toolset.flags gcc.link RPATH_LINK $(generic-os) : ; + toolset.flags gcc.link START-GROUP $(generic-os) : + -Wl,--start-group ; + toolset.flags gcc.link END-GROUP $(generic-os) : -Wl,--end-group ; - # gnu ld has the ability to change the search behaviour for libraries - # referenced by the -l switch. These modifiers are -Bstatic and - # -Bdynamic and change search for -l switches that follow them. The - # following list shows the tried variants. Search stops at the first - # variant that has a match. - # - # *nix: -Bstatic -lxxx - # libxxx.a - # - # *nix: -Bdynamic -lxxx - # libxxx.so - # libxxx.a - # - # windows (mingw, cygwin) -Bstatic -lxxx - # libxxx.a - # xxx.lib - # - # windows (mingw, cygwin) -Bdynamic -lxxx - # libxxx.dll.a - # xxx.dll.a - # libxxx.a - # xxx.lib - # cygxxx.dll (*) - # libxxx.dll - # xxx.dll - # libxxx.a - # - # (*) This is for cygwin - # Please note that -Bstatic and -Bdynamic are not a guarantee that a - # static or dynamic lib indeed gets linked in. The switches only change - # search patterns! + # gnu ld has the ability to change the search behaviour for libraries + # referenced by the -l switch. These modifiers are -Bstatic and + # -Bdynamic and change search for -l switches that follow them. The + # following list shows the tried variants. Search stops at the first + # variant that has a match. + # + # *nix: -Bstatic -lxxx + # libxxx.a + # + # *nix: -Bdynamic -lxxx + # libxxx.so + # libxxx.a + # + # windows (mingw, cygwin) -Bstatic -lxxx + # libxxx.a + # xxx.lib + # + # windows (mingw, cygwin) -Bdynamic -lxxx + # libxxx.dll.a + # xxx.dll.a + # libxxx.a + # xxx.lib + # cygxxx.dll (*) + # libxxx.dll + # xxx.dll + # libxxx.a + # + # (*) This is for cygwin + # Please note that -Bstatic and -Bdynamic are not a guarantee that a + # static or dynamic lib indeed gets linked in. The switches only change + # search patterns! - # On *nix mixing shared libs with static runtime is not a good idea. - if shared in $(properties) - { - FINDLIBS-ST-PFX on $(targets) += -Wl,-Bstatic ; - FINDLIBS-SA-PFX on $(targets) += -Wl,-Bdynamic ; - } + # On *nix mixing shared libs with static runtime is not a good idea. + toolset.flags gcc.link FINDLIBS-ST-PFX $(generic-os)/shared : -Wl,-Bstatic ; + toolset.flags gcc.link FINDLIBS-SA-PFX $(generic-os)/shared : -Wl,-Bdynamic ; - # On windows allow mixing of static and dynamic libs with static - # runtime is not a good idea. - if static in $(properties) && windows in $(properties) - { - FINDLIBS-ST-PFX on $(targets) += -Wl,-Bstatic ; - FINDLIBS-SA-PFX on $(targets) += -Wl,-Bdynamic ; - OPTIONS on $(targets) += -Wl,-Bstatic ; - } + # On windows allow mixing of static and dynamic libs with static + # runtime is not a good idea. + toolset.flags gcc.link FINDLIBS-ST-PFX windows/static : -Wl,-Bstatic ; + toolset.flags gcc.link FINDLIBS-SA-PFX windows/static : -Wl,-Bdynamic ; + toolset.flags gcc.link OPTIONS windows/static : -Wl,-Bstatic ; - HAVE_SONAME on $(targets) += "" ; - SONAME_OPTION on $(targets) += -h ; + toolset.flags gcc.link HAVE_SONAME $(generic-os) : "" ; + toolset.flags gcc.link SONAME_OPTION $(generic-os) : -h ; - # See note [1] - if static in $(properties) - { - OPTIONS on $(targets) += -static ; - } + # See note [1] + toolset.flags gcc.link OPTIONS $(generic-os)/static : -static ; - case hpux : + # hpux - if on in $(properties) - { - OPTIONS on $(targets) += -Wl,-s ; - } - if shared in $(properties) - { - OPTIONS on $(targets) += -fPIC ; - } + toolset.flags gcc.link OPTIONS hpux/on : -Wl,-s ; - HAVE_SONAME on $(targets) += "" ; - SONAME_OPTION on $(targets) += +h ; + toolset.flags gcc.link HAVE_SONAME hpux : "" ; + toolset.flags gcc.link SONAME_OPTION hpux : +h ; - case osf : + # osf - # No --strip-all, just -s. - OPTIONS - osf/$(condition)/on - : -Wl,-s - : unchecked ; - RPATH on $(targets) += [ feature.get-values ] ; - # This does not support -R. - RPATH_OPTION on $(targets) += -rpath ; - # -rpath-link is not supported at all. + # No --strip-all, just -s. + toolset.flags gcc.link OPTIONS osf/on : -Wl,-s ; + toolset.flags gcc.link RPATH osf : ; + # This does not support -R. + toolset.flags gcc.link RPATH_OPTION osf : -rpath ; + # -rpath-link is not supported at all. - # See note [1] - if static in $(properties) - { - OPTIONS on $(targets) += -static ; - } + # See note [1] + toolset.flags gcc.link OPTIONS osf/static : -static ; - case sun : + # sun - if on in $(properties) - { - OPTIONS on $(targets) += -Wl,-s ; - } - RPATH on $(targets) += [ feature.get-values ] ; - # Solaris linker does not have a separate -rpath-link, but allows using - # -L for the same purpose. - LINKPATH on $(targets) += [ feature.get-values ] ; + toolset.flags gcc.link OPTIONS solaris/on : -Wl,-s ; - # This permits shared libraries with non-PIC code on Solaris. - # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the - # following is not needed. Whether -fPIC should be hardcoded, is a - # separate question. - # AH, 2004/10/16: it is still necessary because some tests link against - # static libraries that were compiled without PIC. - if shared in $(properties) - { - OPTIONS on $(targets) += -mimpure-text ; - } + toolset.flags gcc.link RPATH solaris : ; + # Solaris linker does not have a separate -rpath-link, but allows using + # -L for the same purpose. + toolset.flags gcc.link LINKPATH solaris : ; - # See note [1] - if static in $(properties) - { - OPTIONS on $(targets) += -static ; - } - } + # This permits shared libraries with non-PIC code on Solaris. + # VP, 2004/09/07: Now that we have -fPIC hardcode in link.dll, the + # following is not needed. Whether -fPIC should be hardcoded, is a + # separate question. + # AH, 2004/10/16: it is still necessary because some tests link against + # static libraries that were compiled without PIC. + toolset.flags gcc.link OPTIONS solaris : -mimpure-text ; + + # See note [1] + toolset.flags gcc.link OPTIONS solaris/static : -static ; # [1] # For static we made sure there are no dynamic libraries in the diff --git a/src/tools/generators/archive-generator.jam b/src/tools/generators/archive-generator.jam index 88ef16559..1b8721802 100644 --- a/src/tools/generators/archive-generator.jam +++ b/src/tools/generators/archive-generator.jam @@ -11,6 +11,7 @@ import generators ; # class archive-generator : generator { + import generators ; import property-set ; rule __init__ ( id composing ? : source-types + : target-types + @@ -25,6 +26,8 @@ class archive-generator : generator { sources += [ $(property-set).get ] ; + property-set = [ $(property-set).add-raw link ] ; + local result = [ generator.run $(project) $(name) : $(property-set) : $(sources) ] ; @@ -44,7 +47,7 @@ class archive-generator : generator # will link to the library, but it should not cause any harm. So, return # all LIB sources together with created targets, so that dependants link # to them. - local usage-requirements ; + local usage-requirements = link ; if [ $(property-set).get ] = static { for local t in $(sources) @@ -56,9 +59,7 @@ class archive-generator : generator } } - usage-requirements = [ property-set.create $(usage-requirements) ] ; - - return $(usage-requirements) $(result) ; + return [ generators.add-usage-requirements $(result) : $(usage-requirements) ] ; } } diff --git a/src/tools/generators/lib-generator.jam b/src/tools/generators/lib-generator.jam index f520b6102..079a8db17 100644 --- a/src/tools/generators/lib-generator.jam +++ b/src/tools/generators/lib-generator.jam @@ -52,10 +52,11 @@ class lib-generator : generator { actual-type = STATIC_LIB ; } - property-set = [ $(property-set).add-raw LIB ] ; + property-set = [ $(property-set).add-raw LIB link ] ; # Construct the target. - return [ generators.construct $(project) $(name) : $(actual-type) + local result = [ generators.construct $(project) $(name) : $(actual-type) : $(property-set) : $(sources) ] ; + return [ $(result[1]).add-raw link ] $(result[2-]) ; } } diff --git a/src/tools/generators/linking-generator.jam b/src/tools/generators/linking-generator.jam index 2184eea5a..23d0c1e8c 100644 --- a/src/tools/generators/linking-generator.jam +++ b/src/tools/generators/linking-generator.jam @@ -35,7 +35,7 @@ class linking-generator : generator sources += [ $(property-set).get ] ; # Add properties for all searched libraries. - local extra ; + local extra = link ; for local s in $(sources) { if [ $(s).type ] = SEARCHED_LIB @@ -55,15 +55,8 @@ class linking-generator : generator { if [ type.is-derived [ $(s).type ] SHARED_LIB ] && ! [ $(s).action ] { - # Unfortunately, we do not have a good way to find the path to a - # file, so use this nasty approach. - # - # TODO: This needs to be done better. One thing that is really - # broken with this is that it does not work correctly with - # projects having multiple source locations. - local p = [ $(s).project ] ; local location = [ path.root [ $(s).name ] - [ $(p).get source-location ] ] ; + [ $(s).path ] ] ; extra-xdll-paths += [ path.parent $(location) ] ; } } @@ -90,11 +83,12 @@ class linking-generator : generator local ur ; if $(result) { - ur = [ extra-usage-requirements $(result) : $(property-set) ] ; - ur = [ $(ur).add - [ property-set.create $(extra-xdll-paths) ] ] ; + ur = [ extra-usage-requirements $(result[2-]) : $(property-set) ] ; + ur = [ $(ur).add-raw + link $(extra-xdll-paths) ] ; + ur = [ $(ur).add $(result[1]) ] ; } - return $(ur) $(result) ; + return $(ur) $(result[2-]) ; } rule extra-usage-requirements ( created-targets * : property-set ) diff --git a/src/tools/generators/searched-lib-generator.jam b/src/tools/generators/searched-lib-generator.jam index 174d137e3..dc451970d 100644 --- a/src/tools/generators/searched-lib-generator.jam +++ b/src/tools/generators/searched-lib-generator.jam @@ -38,7 +38,7 @@ class searched-lib-generator : generator local search = [ feature.get-values : $(properties) ] ; - local a = [ new null-action $(property-set) ] ; + local a = [ new null-action [ $(property-set).add-raw link ] ] ; local lib-name = [ feature.get-values : $(properties) ] ; lib-name ?= $(name) ; local t = [ new searched-lib-target $(lib-name) : $(project) @@ -47,7 +47,7 @@ class searched-lib-generator : generator # lib png : z : png ; # the 'z' target should be returned, so that apps linking to 'png' # will link to 'z', too. - return [ property-set.create $(search) ] + return [ property-set.create $(search) link ] [ virtual-target.register $(t) ] $(sources) ; } } diff --git a/src/tools/intel-darwin.jam b/src/tools/intel-darwin.jam index 228793eb4..096224a6f 100644 --- a/src/tools/intel-darwin.jam +++ b/src/tools/intel-darwin.jam @@ -55,8 +55,6 @@ rule init ( version ? : command * : options * ) common.handle-options intel-darwin : $(condition) : $(command) : $(options) ; - gcc.init-link-flags intel-darwin darwin $(condition) ; - # handle # local library-path = [ feature.get-values : $(options) ] ; # flags intel-darwin.link USER_OPTIONS $(condition) : [ feature.get-values : $(options) ] ; @@ -118,6 +116,14 @@ rule init ( version ? : command * : options * ) if $(major) = "9" || ( $(major) = "10" && ( $(minor) = "0" || $(minor) = "1" ) ) { flags intel-darwin.compile DEFINES $(condition) : __WINT_TYPE__=int : unchecked ; } + + # - Ranlib. + local ranlib = [ feature.get-values : $(options) ] ; + toolset.flags intel-darwin.archive .RANLIB $(condition) : $(ranlib[1]) ; + + # - Archive builder. + local archiver = [ feature.get-values : $(options) ] ; + toolset.flags intel-darwin.archive .AR $(condition) : $(archiver[1]) ; } SPACE = " " ; @@ -168,6 +174,7 @@ flags intel-darwin ARFLAGS ; # logic in intel-linux, but that's hardly worth the trouble # as on Linux, 'ar' is always available. .AR = ar ; +.RANLIB = ranlib ; rule archive ( targets * : sources * : properties * ) { @@ -201,7 +208,7 @@ rule archive ( targets * : sources * : properties * ) actions piecemeal archive { "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" - "ranlib" -cs "$(<)" + "$(.RANLIB)" -cs "$(<)" } flags intel-darwin.link USER_OPTIONS ; diff --git a/src/tools/intel-linux.jam b/src/tools/intel-linux.jam index c743e52d2..a8b23459f 100644 --- a/src/tools/intel-linux.jam +++ b/src/tools/intel-linux.jam @@ -96,8 +96,6 @@ rule init ( version ? : command * : options * ) : $(command) : $(default_path) ] ; common.handle-options intel-linux : $(condition) : $(command) : $(options) ; - - gcc.init-link-flags intel-linux gnu $(condition) ; local root = [ feature.get-values : $(options) ] ; local bin ; diff --git a/src/tools/intel-vxworks.jam b/src/tools/intel-vxworks.jam index 8d9a3e3a0..4026ba734 100644 --- a/src/tools/intel-vxworks.jam +++ b/src/tools/intel-vxworks.jam @@ -54,8 +54,6 @@ rule init ( version ? : command * : options * ) common.handle-options intel-vxworks : $(condition) : $(command) : $(options) ; - gcc.init-link-flags intel-vxworks vxworks $(condition) ; - # handle # local library-path = [ feature.get-values : $(options) ] ; # flags intel-vxworks.link USER_OPTIONS $(condition) : [ feature.get-values : $(options) ] ; diff --git a/src/tools/lex.jam b/src/tools/lex.jam index 75d641318..e85d1d1ad 100644 --- a/src/tools/lex.jam +++ b/src/tools/lex.jam @@ -5,8 +5,7 @@ import type ; import generators ; import feature ; -import property ; - +import toolset : flags ; feature.feature flex.prefix : : free ; type.register LEX : l ; @@ -18,14 +17,7 @@ rule init ( ) { } -rule lex ( target : source : properties * ) -{ - local r = [ property.select flex.prefix : $(properties) ] ; - if $(r) - { - PREFIX on $(<) = $(r:G=) ; - } -} +flags lex.lex PREFIX ; actions lex { diff --git a/src/tools/libjpeg.jam b/src/tools/libjpeg.jam index 309b59df6..f267ecb73 100644 --- a/src/tools/libjpeg.jam +++ b/src/tools/libjpeg.jam @@ -14,17 +14,20 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import property ; import property-set ; header = jpeglib.h ; +# jpeglib.h requires stdio.h to be included first. +header-test = "#include \n#include \n" ; + names = jpeg ; sources = jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c @@ -99,7 +102,10 @@ rule init ( : is-default ? # Default configurations are only used when libjpeg - # has not yet been configured. + # has not yet been configured. This option is + # deprecated. A configuration will be treated + # as a default when none of , , + # , and are present. ) { local caller = [ project.current ] ; @@ -113,24 +119,22 @@ rule init ( project libjpeg ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local source-path = [ property.select : $(options) ] ; - source-path = $(source-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; - local tag = [ property.select : $(options) ] ; - tag = $(tag:G=) ; - local build-name = [ property.select : $(options) ] ; - build-name = $(build-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local source-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; + local tag = [ feature.get-values : $(options) ] ; + local build-name = [ feature.get-values : $(options) ] ; condition = [ property-set.create $(requirements) ] ; condition = [ property-set.create [ $(condition).base ] ] ; - local no-build-from-source ; - # Ignore environmental ZLIB_SOURCE if this initialization + if ! $(library-path) && ! $(include-path) && ! $(source-path) && ! $(library-name) + { + is-default = true ; + } + + # Ignore environmental LIBJPEG_SOURCE if this initialization # requested to search for a specific pre-built library. if $(library-path) || $(include-path) || $(library-name) { @@ -140,13 +144,11 @@ rule init ( [ property.select : $(options) ] "and" [ property.select : $(options) ] ; } - else - { - no-build-from-source = true ; - } } - - source-path ?= [ modules.peek : ZLIB_SOURCE ] ; + else + { + source-path ?= [ modules.peek : LIBJPEG_SOURCE ] ; + } if $(.configured.$(condition)) { @@ -163,12 +165,12 @@ rule init ( } return ; } - else if $(source-path) && ! $(no-build-from-source) + else if $(source-path) { build-name ?= jpeg ; library-id = [ CALC $(library-id) + 1 ] ; tag = [ MATCH ^@?(.*)$ : $(tag) ] ; - if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ] + if $(tag) { tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } @@ -199,7 +201,6 @@ rule init ( $(source-path) msvc:_CRT_SECURE_NO_DEPRECATE msvc:_SCL_SECURE_NO_DEPRECATE - shared:ZLIB_DLL : : $(source-path) ] ; } @@ -225,6 +226,7 @@ rule init ( local mt = [ new ac-library libjpeg : $(.project) : $(condition) : $(include-path) : $(library-path) : $(library-name) : $(root) ] ; $(mt).set-header $(header) ; + $(mt).set-header-test $(header-test) ; $(mt).set-default-names $(names) ; targets.main-target-alternative $(mt) ; } diff --git a/src/tools/libpng.jam b/src/tools/libpng.jam index 46e3cc9ae..43f681763 100644 --- a/src/tools/libpng.jam +++ b/src/tools/libpng.jam @@ -14,11 +14,11 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import property ; import property-set ; @@ -93,7 +93,10 @@ rule init ( : is-default ? # Default configurations are only used when libpng - # has not yet been configured. + # has not yet been configured. This option is + # deprecated. A configuration will be treated + # as a default when none of , , + # , and are present. ) { local caller = [ project.current ] ; @@ -107,23 +110,21 @@ rule init ( project libpng ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local source-path = [ property.select : $(options) ] ; - source-path = $(source-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; - local tag = [ property.select : $(options) ] ; - tag = $(tag:G=) ; - local build-name = [ property.select : $(options) ] ; - build-name = $(build-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local source-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; + local tag = [ feature.get-values : $(options) ] ; + local build-name = [ feature.get-values : $(options) ] ; + + if ! $(library-path) && ! $(include-path) && ! $(source-path) && ! $(library-name) + { + is-default = true ; + } condition = [ property-set.create $(requirements) ] ; condition = [ property-set.create [ $(condition).base ] ] ; - local no-build-from-source ; # Ignore environmental LIBPNG_SOURCE if this initialization # requested to search for a specific pre-built library. if $(library-path) || $(include-path) || $(library-name) @@ -134,13 +135,11 @@ rule init ( [ property.select : $(options) ] "and" [ property.select : $(options) ] ; } - else - { - no-build-from-source = true ; - } } - - source-path ?= [ modules.peek : LIBPNG_SOURCE ] ; + else + { + source-path ?= [ modules.peek : LIBPNG_SOURCE ] ; + } if $(.configured.$(condition)) { @@ -157,12 +156,12 @@ rule init ( } return ; } - else if $(source-path) && ! $(no-build-from-source) + else if $(source-path) { build-name ?= png ; library-id = [ CALC $(library-id) + 1 ] ; tag = [ MATCH ^@?(.*)$ : $(tag) ] ; - if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ] + if $(tag) { tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } diff --git a/src/tools/libtiff.jam b/src/tools/libtiff.jam index cbd8ad015..f31561491 100644 --- a/src/tools/libtiff.jam +++ b/src/tools/libtiff.jam @@ -14,11 +14,11 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import property ; import property-set ; @@ -96,7 +96,10 @@ rule init ( : is-default ? # Default configurations are only used when libtiff - # has not yet been configured. + # has not yet been configured. This option is + # deprecated. A configuration will be treated + # as a default when none of , , + # , and are present. ) { local caller = [ project.current ] ; @@ -110,24 +113,22 @@ rule init ( project libtiff ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local source-path = [ property.select : $(options) ] ; - source-path = $(source-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; - local tag = [ property.select : $(options) ] ; - tag = $(tag:G=) ; - local build-name = [ property.select : $(options) ] ; - build-name = $(build-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local source-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; + local tag = [ feature.get-values : $(options) ] ; + local build-name = [ feature.get-values : $(options) ] ; + + if ! $(library-path) && ! $(include-path) && ! $(source-path) && ! $(library-name) + { + is-default = true ; + } condition = [ property-set.create $(requirements) ] ; condition = [ property-set.create [ $(condition).base ] ] ; - local no-build-from-source ; - # Ignore environmental ZLIB_SOURCE if this initialization + # Ignore environmental LIBTIFF_SOURCE if this initialization # requested to search for a specific pre-built library. if $(library-path) || $(include-path) || $(library-name) { @@ -137,13 +138,11 @@ rule init ( [ property.select : $(options) ] "and" [ property.select : $(options) ] ; } - else - { - no-build-from-source = true ; - } } - - source-path ?= [ modules.peek : ZLIB_SOURCE ] ; + else + { + source-path ?= [ modules.peek : LIBTIFF_SOURCE ] ; + } if $(.configured.$(condition)) { @@ -160,12 +159,12 @@ rule init ( } return ; } - else if $(source-path) && ! $(no-build-from-source) + else if $(source-path) { build-name ?= tiff ; library-id = [ CALC $(library-id) + 1 ] ; tag = [ MATCH ^@?(.*)$ : $(tag) ] ; - if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ] + if $(tag) { tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } @@ -196,7 +195,6 @@ rule init ( $(source-path) msvc:_CRT_SECURE_NO_DEPRECATE msvc:_SCL_SECURE_NO_DEPRECATE - shared:ZLIB_DLL : : $(source-path) ] ; } diff --git a/src/tools/link.jam b/src/tools/link.jam index 50ec485c6..3cf6e46f7 100644 --- a/src/tools/link.jam +++ b/src/tools/link.jam @@ -34,7 +34,7 @@ actions touch { $(TOUCH) "$(<)" } -rule can-symlink ( project : ps ) +rule can-symlink ( project ) { if ! $(.can-symlink) { @@ -45,7 +45,7 @@ rule can-symlink ( project : ps ) local target = [ new file-target test-symlink : : $(project) : [ new action $(source-target) : link.mklink ] ] ; - if [ configure.try-build $(target) : $(ps) : "symlinks supported" ] + if [ configure.try-build $(target) : [ property-set.empty ] : "symlinks supported" ] { .can-symlink = true ; } @@ -64,7 +64,7 @@ if [ os.name ] = NT { # Test for Windows junctions (mklink /J) -rule can-junction ( project : ps ) +rule can-junction ( project ) { if ! $(.can-junction) { @@ -75,7 +75,7 @@ rule can-junction ( project : ps ) local target = [ new file-target test-junction : : $(project) : [ new action $(source-target) : link.junction ] ] ; - if [ configure.try-build $(target) : $(ps) : "junctions supported" ] + if [ configure.try-build $(target) : [ property-set.empty ] : "junctions supported" ] { .can-junction = true ; } @@ -96,13 +96,13 @@ else .can-junction = false ; -rule can-junction ( project : ps ) +rule can-junction ( project ) { } } -rule can-hardlink ( project : ps ) +rule can-hardlink ( project ) { if ! $(.can-hardlink) { @@ -119,7 +119,7 @@ rule can-hardlink ( project : ps ) : [ new property-set symlink ] ] ] ; - if [ configure.try-build $(target) : $(ps) : "hardlinks supported" ] + if [ configure.try-build $(target) : [ property-set.empty ] : "hardlinks supported" ] { .can-hardlink = true ; } @@ -192,10 +192,10 @@ class symlink-target-class : basic-target # If we have symlinks, don't bother checking # for hardlinks and junctions. - if ! [ link.can-symlink $(self.project) : $(property-set) ] + if ! [ link.can-symlink $(self.project) ] { - link.can-junction $(self.project) : $(property-set) ; - link.can-hardlink $(self.project) : $(property-set) ; + link.can-junction $(self.project) ; + link.can-hardlink $(self.project) ; } if [ $(property-set).get ] diff --git a/src/tools/lzma.jam b/src/tools/lzma.jam index 2c9c77b6f..619f309f6 100644 --- a/src/tools/lzma.jam +++ b/src/tools/lzma.jam @@ -14,11 +14,11 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import property ; import property-set ; @@ -62,12 +62,9 @@ rule init ( project lzma ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; if ! $(options) { diff --git a/src/tools/make.jam b/src/tools/make.jam index 40b59faf3..7c9a7ca68 100644 --- a/src/tools/make.jam +++ b/src/tools/make.jam @@ -17,6 +17,8 @@ import targets ; class make-target-class : basic-target { import "class" : new ; + import indirect ; + import toolset ; import type ; import virtual-target ; @@ -34,10 +36,11 @@ class make-target-class : basic-target # below. local m = [ MATCH ^@(.*) : $(action-name) ] ; - local a = [ new action $(source-targets) : $(m[1]) : $(property-set) ] ; + local relevant = [ toolset.relevant [ indirect.get-rule $(m[1]) ] ] ; + local a = [ new action $(source-targets) : $(m[1]) : [ $(property-set).add $(relevant) ] ] ; local t = [ new file-target $(self.name) exact : [ type.type $(self.name) ] : $(self.project) : $(a) ] ; - return [ property-set.empty ] [ virtual-target.register $(t) ] ; + return $(relevant) [ virtual-target.register $(t) ] ; } } diff --git a/src/tools/mpi.jam b/src/tools/mpi.jam index 1ccc54133..a4d29340e 100644 --- a/src/tools/mpi.jam +++ b/src/tools/mpi.jam @@ -595,6 +595,10 @@ feature mpi:processes : : free incidental ; # apply to mpi.capture output at the moment. # Redo this explicitly. toolset.flags mpi.capture-output ARGS ; +toolset.uses-features mpi.capture-output : + + ; + rule capture-output ( target : sources * : properties * ) { # Use the standard capture-output rule to run the tests diff --git a/src/tools/msvc.jam b/src/tools/msvc.jam index a7df23adc..2ab613e00 100644 --- a/src/tools/msvc.jam +++ b/src/tools/msvc.jam @@ -218,11 +218,14 @@ import midl ; import os ; import path ; import pch ; +import project ; import property ; +import property-set ; import rc ; import set ; import toolset ; import type ; +import virtual-target ; type.register MANIFEST : manifest ; @@ -325,7 +328,7 @@ rule init ( # # # Whether to rewrite setup scripts. New scripts will be output in - # TEMP directory and will be used instead of originals in build actions. + # build tree and will be used instead of originals in build actions. # Possible values: # * on - rewrite scripts, if they do not already exist (default) # * always - always rewrite scripts, even if they already exist @@ -512,7 +515,7 @@ if [ os.name ] in NT actions archive { if exist "$(<[1])" DEL "$(<[1])" - $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" } } else @@ -520,10 +523,14 @@ else actions archive { $(.RM) "$(<[1])" - $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) $(AROPTIONS) /out:"$(<[1])" @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" } } +rule compile.asm ( targets + : sources * : properties * ) +{ + set-setup-command $(targets) : $(properties) ; +} # For the assembler the following options are turned on by default: # @@ -533,12 +540,13 @@ else # actions compile.asm { - $(.ASM) -c -Zp4 -Cp -Cx -D$(DEFINES) $(ASMFLAGS) $(USER_ASMFLAGS) -Fo "$(<:W)" "$(>:W)" + $(.SETUP) $(.ASM) -c -Zp4 -Cp -Cx -D$(DEFINES) $(ASMFLAGS) $(USER_ASMFLAGS) -Fo "$(<:W)" "$(>:W)" } rule compile.c ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; C++FLAGS on $(targets[1]) = ; get-rspline $(targets) : -TC ; compile-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ; @@ -547,6 +555,7 @@ rule compile.c ( targets + : sources * : properties * ) rule compile.c.preprocess ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; C++FLAGS on $(targets[1]) = ; get-rspline $(targets) : -TC ; preprocess-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ; @@ -555,6 +564,7 @@ rule compile.c.preprocess ( targets + : sources * : properties * ) rule compile.c.pch ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; C++FLAGS on $(targets[1]) = ; get-rspline $(targets[1]) : -TC ; get-rspline $(targets[2]) : -TC ; @@ -592,12 +602,12 @@ toolset.flags msvc YLOPTION : "-Yl" ; # actions compile-c-c++ bind PDB_NAME { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" $(.CC.FILTER) + $(.SETUP) $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -Fo"$(<[1]:W)" $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" $(.CC.FILTER) } actions preprocess-c-c++ bind PDB_NAME { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -E $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" >"$(<[1]:W)" + $(.SETUP) $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -E $(PDB_CFLAG)"$(PDB_NAME)" -Yu"$(>[3]:D=)" -Fp"$(>[2]:W)" $(CC_RSPLINE))" >"$(<[1]:W)" } rule compile-c-c++ ( targets + : sources * ) @@ -624,7 +634,7 @@ rule preprocess-c-c++ ( targets + : sources * ) # syntax highlighting in the messy N-quoted code below. actions compile-c-c++-pch { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" "@($(<[1]:W).cpp:E=#include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote)$(.nl))" $(.CC.FILTER) + $(.SETUP) $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" "@($(<[1]:W).cpp:E=#include $(.escaped-double-quote)$(>[1]:D=)$(.escaped-double-quote)$(.nl))" $(.CC.FILTER) } @@ -633,18 +643,20 @@ actions compile-c-c++-pch # given as one of the source parameters. actions compile-c-c++-pch-s { - $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" $(.CC.FILTER) + $(.SETUP) $(.CC) @"@($(<[1]:W).rsp:E="$(>[2]:W)" -Fo"$(<[2]:W)" -Yc"$(>[1]:D=)" $(YLOPTION)"__bjam_pch_symbol_$(>[1]:D=)" -Fp"$(<[1]:W)" $(CC_RSPLINE))" $(.CC.FILTER) } rule compile.c++ ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; get-rspline $(targets) : -TP ; compile-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ; } rule compile.c++.preprocess ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; get-rspline $(targets) : -TP ; preprocess-c-c++ $(<) : $(>) [ on $(<) return $(PCH_FILE) ] [ on $(<) return $(PCH_HEADER) ] ; } @@ -652,6 +664,7 @@ rule compile.c++.preprocess ( targets + : sources * : properties * ) rule compile.c++.pch ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; get-rspline $(targets[1]) : -TP ; get-rspline $(targets[2]) : -TP ; local pch-source = [ on $(<) return $(PCH_SOURCE) ] ; @@ -666,31 +679,46 @@ rule compile.c++.pch ( targets + : sources * : properties * ) } } +rule compile.idl ( targets + : sources * : properties * ) +{ + set-setup-command $(targets) : $(properties) ; +} # See midl.jam for details. # actions compile.idl { - $(.IDL) /nologo @"@($(<[1]:W).rsp:E=$(.nl)"$(>:W)" $(.nl)-D$(DEFINES) $(.nl)"-I$(INCLUDES:W)" $(.nl)-U$(UNDEFS) $(.nl)$(MIDLFLAGS) $(.nl)/tlb "$(<[1]:W)" $(.nl)/h "$(<[2]:W)" $(.nl)/iid "$(<[3]:W)" $(.nl)/proxy "$(<[4]:W)" $(.nl)/dlldata "$(<[5]:W)")" + $(.SETUP) $(.IDL) /nologo @"@($(<[1]:W).rsp:E=$(.nl)"$(>:W)" $(.nl)-D$(DEFINES) $(.nl)"-I$(INCLUDES:W)" $(.nl)-U$(UNDEFS) $(.nl)$(MIDLFLAGS) $(.nl)/tlb "$(<[1]:W)" $(.nl)/h "$(<[2]:W)" $(.nl)/iid "$(<[3]:W)" $(.nl)/proxy "$(<[4]:W)" $(.nl)/dlldata "$(<[5]:W)")" $(.TOUCH_FILE) "$(<[4]:W)" $(.TOUCH_FILE) "$(<[5]:W)" } +rule compile.mc ( targets + : sources * : properties * ) +{ + set-setup-command $(targets) : $(properties) ; +} actions compile.mc { - $(.MC) $(MCFLAGS) -h "$(<[1]:DW)" -r "$(<[2]:DW)" "$(>:W)" + $(.SETUP) $(.MC) $(MCFLAGS) -h "$(<[1]:DW)" -r "$(<[2]:DW)" "$(>:W)" } +rule compile.rc ( targets + : sources * : properties * ) +{ + set-setup-command $(targets) : $(properties) ; +} + actions compile.rc { - $(.RC) -l 0x409 -U$(UNDEFS) -D$(DEFINES) -I"$(INCLUDES:W)" -fo "$(<:W)" "$(>:W)" + $(.SETUP) $(.RC) -l 0x409 -U$(UNDEFS) -D$(DEFINES) -I"$(INCLUDES:W)" -fo "$(<:W)" "$(>:W)" } +toolset.uses-features msvc.link : ; rule link ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; if on in $(properties) { if [ feature.get-values : $(properties) ] @@ -707,6 +735,7 @@ rule link ( targets + : sources * : properties * ) rule link.dll ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; DEPENDS $(<) : [ on $(<) return $(DEF_FILE) ] ; if on in $(properties) { @@ -736,44 +765,44 @@ if [ os.name ] in NT { actions link bind DEF_FILE LIBRARIES_MENTIONED_BY_FILE { - $(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" if %ERRORLEVEL% NEQ 0 EXIT %ERRORLEVEL% } actions manifest { if exist "$(<[1]).manifest" ( - $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);1" + $(.SETUP) $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);1" ) } actions manifest.user bind EMBED_MANIFEST_FILE { - $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);1" + $(.SETUP) $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);1" } actions link.dll bind DEF_FILE LIBRARIES_MENTIONED_BY_FILE { - $(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" if %ERRORLEVEL% NEQ 0 EXIT %ERRORLEVEL% } actions manifest.dll { if exist "$(<[1]).manifest" ( - $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);2" + $(.SETUP) $(.MT) -manifest "$(<[1]).manifest" "-outputresource:$(<[1]);2" ) } actions manifest.dll.user bind EMBED_MANIFEST_FILE { - $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);2" + $(.SETUP) $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);2" } } else { actions link bind DEF_FILE LIBRARIES_MENTIONED_BY_FILE { - $(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) $(LINKFLAGS) /out:"$(<[1]:W)" /LIBPATH:"$(LINKPATH:W)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" } actions manifest @@ -785,19 +814,19 @@ else actions link.dll bind DEF_FILE LIBRARIES_MENTIONED_BY_FILE { - $(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" + $(.SETUP) $(.LD) /DLL $(LINKFLAGS) /out:"$(<[1]:W)" /IMPLIB:"$(<[2]:W)" /LIBPATH:"$(LINKPATH:W)" /def:"$(DEF_FILE)" $(OPTIONS) @"@($(<[1]:W).rsp:E=$(.nl)"$(>)" $(.nl)$(LIBRARIES_MENTIONED_BY_FILE) $(.nl)$(LIBRARIES) $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_ST).lib" $(.nl)"$(LIBRARY_OPTION)$(FINDLIBS_SA).lib")" } actions manifest.dll { if test -e "$(<[1]).manifest"; then - $(.MT) -manifest "$(<[1]:W).manifest" "-outputresource:$(<[1]:W);2" + $(.SETUP) $(.MT) -manifest "$(<[1]:W).manifest" "-outputresource:$(<[1]:W);2" fi } actions manifest.dll.user bind EMBED_MANIFEST_FILE { - $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);2" + $(.SETUP) $(.MT) -manifest "$(EMBED_MANIFEST_FILE)" "-outputresource:$(<[1]);2" } } @@ -810,6 +839,7 @@ else # rule archive ( targets + : sources * : properties * ) { + set-setup-command $(targets) : $(properties) ; PDB_NAME on $(>) = $(<[1]:S=.pdb) ; LOCATE on $(<[1]:S=.pdb) = [ on $(<[1]) return $(LOCATE) ] ; } @@ -864,7 +894,7 @@ class msvc-pch-generator : pch-generator : $(pch-header) ] ; local pch-file ; - for local g in $(generated) + for local g in $(generated[2-]) { if [ type.is-derived [ $(g).type ] PCH ] { @@ -872,8 +902,8 @@ class msvc-pch-generator : pch-generator } } - return [ property-set.create $(pch-header) - $(pch-file) ] $(generated) ; + return [ $(generated[1]).add-raw $(pch-header) + $(pch-file) ] $(generated[2-]) ; } } @@ -924,72 +954,34 @@ local rule auto-detect-toolset-versions ( ) } } -# Helper rule to generate a faster alternative to MSVC setup scripts. -# We used to call MSVC setup scripts directly in every action, however in -# newer MSVC versions (10.0+) they make long-lasting registry queries -# which have a significant impact on build time. -rule maybe-rewrite-setup ( toolset : setup-script : setup-options : version : rewrite-setup ? ) -{ - local result = $(setup-script)" "$(setup-options) ; - # At the moment we only know how to rewrite scripts with cmd shell. - if ( [ os.name ] in NT ) && ( $(rewrite-setup) != off ) - { - setup-script-id = b2_$(toolset)_$(version)_$(setup-script:B) ; - if $(setup-options)-is-not-empty - { - setup-script-id = $(setup-script-id)_$(setup-options) ; - } - - if $(.$(setup-script-id)) - { - errors.error rewriting setup script for the second time ; - } - - local tmpdir = [ os.environ TEMP ] ; - local replacement = [ path.native $(tmpdir)/$(setup-script-id).cmd ] ; - if ( $(rewrite-setup) = always ) || ( ! [ path.exists $(replacement) ] ) - { - local original-vars = [ SPLIT_BY_CHARACTERS [ SHELL set ] : "\n" ] ; - local new-vars = [ SPLIT_BY_CHARACTERS [ SHELL "$(setup-script) $(setup-options)>nul && set" ] : "\n" ] ; - local diff-vars = [ set.difference $(new-vars) : $(original-vars) ] ; - if $(diff-vars) - { - local target = $(replacement) ; - FILE_CONTENTS on $(target) = "SET "$(diff-vars) ; - ALWAYS $(target) ; - msvc.write-setup-script $(target) ; - UPDATE_NOW $(target) : : ignore-minus-n ; - .$(setup-script-id) = $(replacement) ; - result = "\""$(replacement)"\"" ; - } - } - else - { - result = "\""$(replacement)"\"" ; - } - } - return $(result) ; -} - actions write-setup-script { @($(STDOUT):E=$(FILE_CONTENTS:J=$(.nl))) > "$(<)" } +if [ os.name ] = NT +{ + local rule call-batch-script ( command ) + { + return "call $(command) >nul$(.nl)" ; + } +} +else +{ + # On cygwin, we need to run both the batch script + # and the following command in the same instance + # of cmd.exe. + local rule call-batch-script ( command ) + { + return "cmd.exe /S /C call $(command) \">nul\" \"&&\" " ; + } +} # Local helper rule to create the vcvars setup command for given architecture # and options. # local rule generate-setup-cmd ( version : command : parent : options * : cpu : global-setup ? : default-global-setup-options : default-setup ) { - local setup-prefix = "call " ; - local setup-suffix = " >nul"$(.nl) ; - if ! [ os.name ] in NT - { - setup-prefix = "cmd.exe /S /C call " ; - setup-suffix = " \">nul\" \"&&\" " ; - } - local setup-options ; local setup = [ feature.get-values : $(options) ] ; @@ -1020,19 +1012,81 @@ local rule generate-setup-cmd ( version : command : parent : options * : cpu : g } } - # Cygwin to Windows path translation. - setup = "\""$(setup:W)"\"" ; - - # Append setup options to the setup name and add the final setup - # prefix & suffix. - setup-options ?= "" ; - local rewrite = [ feature.get-values : $(options) ] ; - setup = [ maybe-rewrite-setup msvc : $(setup:J=" ") : $(setup-options:J=" ") : $(version) : $(rewrite) ] ; - setup = $(setup-prefix)$(setup)$(setup-suffix) ; - - return $(setup) ; + return $(setup) "$(setup-options:J= )" ; } +# Worker for set-setup-command. Usable in a virtual-target.action. +rule adjust-setup-command ( new-setup : setup : properties * ) +{ + local internal = $(new-setup:S=.read) ; + NOTFILE $(internal) ; + local setup-options = [ property.select : $(properties) ] ; + setup-options = $(setup-options:G=:E=) ; + DEPENDS $(internal) : $(setup) ; + DEPENDS $(new-setup) : $(internal) ; + REBUILDS $(new-setup) : $(internal) ; + msvc.read-setup $(internal) : $(setup) ; + msvc.write-setup-script $(new-setup) : $(setup) ; + __ACTION_RULE__ on $(internal) = msvc.rewrite-setup $(setup) $(setup-options) $(new-setup) ; +} + +# This doesn't actually do anything. It's merely +# used as a trigger for __ACTION_RULE__. +actions quietly read-setup { } + +# Calculates the changes to the environment make by setup-script +# Should be used as a callback for __ACTION_RULE__ +local rule rewrite-setup ( setup-script setup-options new-setup : target : * ) +{ + local setup-path = [ on $(setup-script) return $(LOCATE) $(SEARCH) ] ; + setup-path = $(setup-path[1]) ; + local command = "\"$(setup-script:G=:R=$(setup-path))\" $(setup-options)" ; + local original-vars = [ SPLIT_BY_CHARACTERS [ SHELL set ] : "\n" ] ; + local new-vars = [ SPLIT_BY_CHARACTERS [ SHELL "$(command) >nul && set" ] : "\n" ] ; + local diff-vars = [ set.difference $(new-vars) : $(original-vars) ] ; + if $(diff-vars) + { + FILE_CONTENTS on $(new-setup) = "REM $(command)" "SET "$(diff-vars) ; + } +} + +IMPORT msvc : rewrite-setup : : msvc.rewrite-setup ; + +# Helper rule to generate a faster alternative to MSVC setup scripts. +# We used to call MSVC setup scripts directly in every action, however in +# newer MSVC versions (10.0+) they make long-lasting registry queries +# which have a significant impact on build time. +local rule set-setup-command ( targets * : properties * ) +{ + if ! [ on $(targets) return $(.SETUP) ] + { + local setup-script = [ on $(targets) return $(.SETUP-SCRIPT) ] ; + local setup-options = [ on $(targets) return $(.SETUP-OPTIONS) ] ; + local key = .setup-command-$(setup-script:E=)-$(setup-options:E=) ; + if ! $($(key)) + { + properties = [ feature.expand $(properties) ] ; + properties = [ property.select : $(properties) ] ; + local ps = [ property-set.create $(properties) $(setup-options) ] ; + local original = [ virtual-target.from-file $(setup-script) : [ path.pwd ] : $(.project) ] ; + local action = [ new non-scanning-action $(original) : msvc.adjust-setup-command : $(ps) ] ; + local new-setup = [ virtual-target.register [ new file-target msvc-setup.bat exact : : $(.project) : $(action) ] ] ; + local command = [ $(new-setup).actualize ] ; + local path = [ on $(command) return $(LOCATE) ] ; + local block-update = $(command:S=.nup) ; + NOUPDATE $(block-update) ; + NOTFILE $(block-update) ; + DEPENDS $(block-update) : $(command) ; + if [ on $(targets) return $(.REWRITE-SETUP) ] + { + ALWAYS $(command) ; + } + $(key) = [ call-batch-script "\"$(command:WG=:R=$(path))\" $(setup-options:E=)" ] $(block-update) ; + } + DEPENDS $(targets) : $($(key)[2]) ; + .SETUP on $(targets) = $($(key)[1]) ; + } +} # Worker rule for toolset version configuration. Takes an explicit version id or # nothing in case it should configure the default toolset version (the first @@ -1376,29 +1430,49 @@ local rule configure-really ( version ? : options * ) local cpu-assembler = $(assembler) ; cpu-assembler ?= $(default-assembler-$(c)) ; - toolset.flags msvc.compile .RC $(api)/$(cpu-conditions) : $(setup-$(c))$(resource-compiler) ; - toolset.flags msvc.compile .IDL $(api)/$(cpu-conditions) : $(setup-$(c))$(idl-compiler) ; - toolset.flags msvc.compile .MC $(api)/$(cpu-conditions) : $(setup-$(c))$(mc-compiler) ; - toolset.flags msvc.link .MT $(api)/$(cpu-conditions) : $(setup-$(c))$(manifest-tool) -nologo ; - - for api in desktop store phone + for local api in desktop store phone { local setup-script = $(setup-$(c)) ; if $(api) = phone { setup-script = $(setup-phone-$(c)) ; } - if $(api) = desktop + + if always in $(options) { - toolset.flags msvc.compile .CC $(api)/$(cpu-conditions) : $(setup-script)$(compiler) /Zm800 -nologo ; + toolset.flags msvc .REWRITE-SETUP $(api)/$(cpu-conditions) : true ; + } + + if ! $(setup-script) + { + # Should we try to set up some error handling or fallbacks here? + } + else if off in $(options) || [ os.name ] != NT + { + toolset.flags msvc .SETUP $(api)/$(cpu-conditions) : [ call-batch-script "\"$(setup-script[1]:W)\" $(setup-script[2-]:E=)" ] ; } else { - toolset.flags msvc.compile .CC $(api)/$(cpu-conditions) : $(setup-script)$(compiler) /Zm800 /ZW /EHsc -nologo ; + toolset.flags msvc .SETUP-SCRIPT $(api)/$(cpu-conditions) : $(setup-script[1]) ; + toolset.flags msvc .SETUP-OPTIONS $(api)/$(cpu-conditions) : $(setup-script[2-]) ; } - toolset.flags msvc.compile .ASM $(api)/$(cpu-conditions) : $(setup-script)$(cpu-assembler) -nologo ; - toolset.flags msvc.link .LD $(api)/$(cpu-conditions) : $(setup-script)$(linker) /NOLOGO /INCREMENTAL:NO ; - toolset.flags msvc.archive .LD $(api)/$(cpu-conditions) : $(setup-script)$(linker) /lib /NOLOGO ; + + toolset.flags msvc.compile .RC $(api)/$(cpu-conditions) : $(resource-compiler) ; + toolset.flags msvc.compile .IDL $(api)/$(cpu-conditions) : $(idl-compiler) ; + toolset.flags msvc.compile .MC $(api)/$(cpu-conditions) : $(mc-compiler) ; + toolset.flags msvc.link .MT $(api)/$(cpu-conditions) : $(manifest-tool) -nologo ; + + if $(api) = desktop + { + toolset.flags msvc.compile .CC $(api)/$(cpu-conditions) : $(compiler) /Zm800 -nologo ; + } + else + { + toolset.flags msvc.compile .CC $(api)/$(cpu-conditions) : $(compiler) /Zm800 /ZW /EHsc -nologo ; + } + toolset.flags msvc.compile .ASM $(api)/$(cpu-conditions) : $(cpu-assembler) -nologo ; + toolset.flags msvc.link .LD $(api)/$(cpu-conditions) : $(linker) /NOLOGO /INCREMENTAL:NO ; + toolset.flags msvc.archive .LD $(api)/$(cpu-conditions) : $(linker) /lib /NOLOGO ; } if $(cc-filter) @@ -1536,8 +1610,8 @@ class msvc-linking-generator : linking-generator if $(result) { - local name-main = [ $(result[0]).name ] ; - local action = [ $(result[0]).action ] ; + local name-main = [ $(result[1]).name ] ; + local action = [ $(result[1]).action ] ; if [ $(property-set).get ] = "on" { @@ -1742,6 +1816,13 @@ local rule register-toolset-really ( ) } toolset.flags msvc.archive AROPTIONS ; + + # Create a project to allow building the setup scripts + project.initialize $(__name__) ; + .project = [ project.current ] ; + project msvc ; + + feature.feature msvc.setup-options : : free ; } diff --git a/src/tools/pgi.jam b/src/tools/pgi.jam index 0ea025e7a..8b13d451b 100644 --- a/src/tools/pgi.jam +++ b/src/tools/pgi.jam @@ -40,8 +40,6 @@ rule init ( version ? : command * : options * ) # set link flags flags pgi.link FINDLIBS-ST : [ feature.get-values : $(options) ] : unchecked ; - - gcc.init-link-flags pgi gnu $(condition) ; } # Declare generators diff --git a/src/tools/python.jam b/src/tools/python.jam index 406d828b7..0459f8575 100644 --- a/src/tools/python.jam +++ b/src/tools/python.jam @@ -1243,6 +1243,10 @@ local rule pyd-pythonpath ( source ) toolset.flags python.capture-output ARGS ; toolset.flags python.capture-output INPUT_FILES ; +toolset.uses-features python.capture-output : + + ; + rule capture-output ( target : sources * : properties * ) { # Setup up a proper DLL search path. Here, $(sources[1]) is a python module @@ -1255,10 +1259,7 @@ rule capture-output ( target : sources * : properties * ) PYTHONPATH += [ feature.get-values pythonpath : $(properties) ] ; # After test is run, we remove the Python module, but not the Python script. - local targets-to-remove = $(sources[2-]) ; - targets-to-remove ?= none ; - testing.capture-output $(target) : $(sources[1]) : $(properties) : - $(targets-to-remove) ; + testing.capture-output $(target) : $(sources[1]) : $(properties) ; # PYTHONPATH is different; it will be interpreted by whichever Python is # invoked and so must follow path rules for the target os. The only OSes @@ -1275,7 +1276,7 @@ rule capture-output ( target : sources * : properties * ) } local path-separator = [ os.path-separator [ translate-os $(target-os) ] ] ; local set-PYTHONPATH = [ common.variable-setting-command PYTHONPATH : - $(PYTHONPATH:J=$(path-separator)) ] ; + $(PYTHONPATH:E=:J=$(path-separator)) ] ; LAUNCHER on $(target) = $(set-PYTHONPATH) [ on $(target) return \"$(PYTHON)\" ] ; } diff --git a/src/tools/qcc.jam b/src/tools/qcc.jam index faa353064..9ed92465f 100644 --- a/src/tools/qcc.jam +++ b/src/tools/qcc.jam @@ -21,7 +21,6 @@ import unix ; feature.extend toolset : qcc ; toolset.inherit-generators qcc : unix : unix.link unix.link.dll ; -generators.override builtin.lib-generator : qcc.prebuilt ; toolset.inherit-flags qcc : unix ; toolset.inherit-rules qcc : unix ; @@ -101,7 +100,7 @@ actions compile.c++ actions compile.c { - "$(CONFIG_COMMAND)" $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" + "$(CONFIG_COMMAND)" -lang-c $(OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -c -o "$(<)" "$(>)" } actions compile.asm diff --git a/src/tools/stlport.jam b/src/tools/stlport.jam index ed0947ca5..4a08c8c9b 100644 --- a/src/tools/stlport.jam +++ b/src/tools/stlport.jam @@ -89,6 +89,9 @@ class stlport-target-class : basic-target local requirements ; requirements += $(self.version) ; + requirements += runtime-debugging ; + requirements += toolset ; + requirements += runtime-link ; self.requirements = [ property-set.create $(requirements) ] ; } diff --git a/src/tools/testing.jam b/src/tools/testing.jam index 55576f136..a36258ed3 100644 --- a/src/tools/testing.jam +++ b/src/tools/testing.jam @@ -34,6 +34,7 @@ import alias ; +import build-system ; import "class" ; import common ; import errors ; @@ -213,6 +214,11 @@ rule dump-tests } } +if ( --dump-tests in [ modules.peek : ARGV ] ) +{ + IMPORT testing : dump-tests : : testing.dump-tests ; + build-system.add-pre-build-hook testing.dump-tests ; +} # Given a project location in normalized form (slashes are forward), compute the # name of the Boost library. @@ -325,6 +331,8 @@ generators.register-standard testing.capture-output : EXE : RUN_OUTPUT ; # http://article.gmane.org/gmane.comp.lib.boost.build/6353). generators.register-standard testing.unit-test : EXE : UNIT_TEST ; +toolset.uses-features testing.expect-success : ; +toolset.uses-features testing.expect-failure : ; # The action rules called by generators. @@ -333,7 +341,7 @@ generators.register-standard testing.unit-test : EXE : UNIT_TEST ; # rule expect-success ( target : dependency + : requirements * ) { - **passed** $(target) : $(dependency) ; + **passed** $(target) : $(dependency) : $(requirements) ; } @@ -350,25 +358,18 @@ rule expect-failure ( target : dependency + : properties * ) RMOLD $(marker) ; DEPENDS $(marker) : $(dependency) ; DEPENDS $(target) : $(marker) ; - **passed** $(target) : $(marker) ; + **passed** $(target) : $(marker) : $(properties) ; } # The rule/action combination used to report successful passing of a test. # -rule **passed** +rule **passed** ( target : sources * : properties * ) { - remove-test-targets $(<) ; - - # Dump all the tests, if needed. We do it here, since dump should happen - # only after all Jamfiles have been read, and there is no such place - # currently defined (but there should be). - if ! $(.dumped-tests) && ( --dump-tests in [ modules.peek : ARGV ] ) + if [ feature.get-values preserve-test-targets : $(properties) ] = off { - .dumped-tests = true ; - dump-tests ; + remove-test-targets $(<) ; } - # Force deletion of the target, in case any dependencies failed to build. RMOLD $(<) ; } @@ -457,21 +458,20 @@ toolset.flags testing.capture-output ARGS ; toolset.flags testing.capture-output INPUT_FILES ; toolset.flags testing.capture-output LAUNCHER ; -.preserve-test-targets = on ; +toolset.uses-features testing.capture-output : + ; + if --remove-test-targets in [ modules.peek : ARGV ] { - .preserve-test-targets = off ; + feature.set-default preserve-test-targets : off ; } # Runs executable 'sources' and stores stdout in file 'target'. Unless # --preserve-test-targets command line option has been specified, removes the -# executable. The 'target-to-remove' parameter controls what should be removed: -# - if 'none', does not remove anything, ever -# - if empty, removes 'source' -# - if non-empty and not 'none', contains a list of sources to remove. +# executable. # -rule capture-output ( target : source : properties * : targets-to-remove * ) +rule capture-output ( target : source : properties * ) { output-file on $(target) = $(target:S=.output) ; LOCATE on $(target:S=.output) = [ on $(target) return $(LOCATE) ] ; @@ -489,15 +489,6 @@ rule capture-output ( target : source : properties * : targets-to-remove * ) # bug). DEPENDS $(target) : [ on $(target) return $(INPUT_FILES) ] ; - if $(targets-to-remove) = none - { - targets-to-remove = ; - } - else if ! $(targets-to-remove) - { - targets-to-remove = $(source) ; - } - run-path-setup $(target) : $(source) : $(properties) ; DISABLE_TEST_EXECUTION on $(target) = 0 ; @@ -506,16 +497,6 @@ rule capture-output ( target : source : properties * : targets-to-remove * ) DISABLE_TEST_EXECUTION on $(target) = 1 ; } - if [ feature.get-values preserve-test-targets : $(properties) ] = off - || $(.preserve-test-targets) = off - { - rmtemp-sources $(target) : $(targets-to-remove) ; - for local to-remove in $(targets-to-remove) - { - rmtemp-all-sources $(to-remove) ; - } - } - if ! [ feature.get-values testing.launcher : $(properties) ] { ## On VMS set default launcher to MCR @@ -525,42 +506,19 @@ rule capture-output ( target : source : properties * : targets-to-remove * ) .types-to-remove = EXE OBJ ; -local rule remove-test-targets ( targets + ) +local rule remove-test-targets ( target ) { - if $(.preserve-test-targets) = off - { - rmtemp-all-sources $(target) ; - } -} - -local rule rmtemp-all-sources ( target ) -{ - local sources ; local action = [ on $(target) return $(.action) ] ; - if $(action) + local associated-targets = [ virtual-target.traverse [ $(action).targets ] ] ; + local targets-to-remove ; + for local t in [ sequence.unique $(associated-targets) ] { - local action-sources = [ $(action).sources ] ; - for local source in $(action-sources) + if [ $(t).type ] in $(.types-to-remove) { - local source-type = [ $(source).type ] ; - if $(source-type) in $(.types-to-remove) - { - sources += [ $(source).actual-name ] ; - } - else - { - # ECHO IGNORED: $(source) :: $(source-type) ; - } - } - if $(sources) - { - rmtemp-sources $(target) : $(sources) ; - for local source in $(sources) - { - rmtemp-all-sources $(source) ; - } + targets-to-remove += [ $(t).actual-name ] ; } } + rmtemp-sources $(target) : $(targets-to-remove) ; } local rule rmtemp-sources ( target : sources * ) diff --git a/src/tools/types/man.jam b/src/tools/types/man.jam index 7dc9cb353..4fb59a0fe 100644 --- a/src/tools/types/man.jam +++ b/src/tools/types/man.jam @@ -5,4 +5,4 @@ accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |# -type MANPAGE : man 0 1 1M 2 3 4 5 6 7 8 9 n p x ; +type MANPAGE : man 1M n p x ; diff --git a/src/tools/xsltproc.jam b/src/tools/xsltproc.jam index 56de81f36..f6bf1af98 100644 --- a/src/tools/xsltproc.jam +++ b/src/tools/xsltproc.jam @@ -6,14 +6,17 @@ # This module defines rules to apply an XSLT stylesheet to an XML file using the # xsltproc driver, part of libxslt. +import "class" : new ; import common ; import feature ; +import generators ; import modules ; import os ; import path ; import regex ; import sequence ; - +import toolset ; +import virtual-target ; feature.feature xsl:param : : free ; feature.feature xsl:path : : free ; @@ -108,12 +111,54 @@ rule .is-cygwin ( xsltproc ) } } +class xsltproc-action : action +{ + rule adjust-properties ( property-set ) + { + local s = [ $(self.targets[1]).creating-subvariant ] ; + if $(s) + { + return [ $(property-set).add-raw + [ $(s).implicit-includes "xsl:path" : XML ] ] ; + } + else + { + return $(property-set) ; + } + } +} + +class xsltproc-generator : generator +{ + rule action-class ( ) + { + return xsltproc-action ; + } +} + +rule register-generator ( id : source-types + : target-types + : requirements * ) +{ + if ! $(id) in $(.known-rules) + { + .known-rules += $(id) ; + flags $(id) ; + } + generators.register [ new xsltproc-generator $(id) : + $(source-types) : $(target-types) : $(requirements) ] ; +} + +IMPORT xsltproc : register-generator : : generators.register-xslt ; + +rule flags ( rulename ) +{ + toolset.uses-features $(rulename) : : unchecked ; + toolset.flags $(rulename) XSL-PATH : : unchecked ; + toolset.flags $(rulename) FLAGS : : unchecked ; +} rule compute-xslt-flags ( target : properties * ) { - # Raw flags. - local flags = [ feature.get-values : $(properties) ] ; - + local flags ; # Translate into command line flags. for local param in [ feature.get-values : $(properties) ] { @@ -121,31 +166,6 @@ rule compute-xslt-flags ( target : properties * ) flags += --stringparam $(namevalue[1]) \"$(namevalue[2])\" ; } - # Translate . - for local path in [ feature.get-values : $(properties) ] - { - flags += --path \"$(path:G=)\" ; - } - - # Take care of implicit dependencies. - local other-deps ; - for local dep in [ feature.get-values : $(properties) - ] - { - other-deps += [ $(dep:G=).creating-subvariant ] ; - } - - local implicit-target-directories ; - for local dep in [ sequence.unique $(other-deps) ] - { - implicit-target-directories += [ $(dep).all-target-directories ] ; - } - - for local dir in $(implicit-target-directories) - { - flags += --path \"$(dir:T)\" ; - } - return $(flags) ; } @@ -186,25 +206,27 @@ rule xslt-dir ( target : source stylesheet : properties * : dirname ) $(dirname) : xslt-xsltproc-dir ] ; } +_ = " " ; + actions xslt-xsltproc.windows { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<)" "$(STYLESHEET:W)" "$(>:W)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:W)" --xinclude -o "$(<)" "$(STYLESHEET:W)" "$(>:W)" } actions xslt-xsltproc bind STYLESHEET { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<)" "$(STYLESHEET:T)" "$(>:T)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:T)" --xinclude -o "$(<)" "$(STYLESHEET:T)" "$(>:T)" } actions xslt-xsltproc-dir.windows bind STYLESHEET { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<:D)/" "$(STYLESHEET:W)" "$(>:W)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:W)" --xinclude -o "$(<:D)/" "$(STYLESHEET:W)" "$(>:W)" } actions xslt-xsltproc-dir bind STYLESHEET { - $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --xinclude -o "$(<:D)/" "$(STYLESHEET:T)" "$(>:T)" + $(CATALOG) "$(NAME:E=xsltproc)" $(FLAGS) --path$(_)"$(XSL-PATH:T)" --xinclude -o "$(<:D)/" "$(STYLESHEET:T)" "$(>:T)" } diff --git a/src/tools/zlib.jam b/src/tools/zlib.jam index 45dc4767a..e1cc0cbcb 100644 --- a/src/tools/zlib.jam +++ b/src/tools/zlib.jam @@ -14,11 +14,11 @@ import project ; import ac ; import errors ; +import feature ; import "class" : new ; import targets ; import path ; import modules ; -import errors ; import indirect ; import property ; import property-set ; @@ -111,18 +111,12 @@ rule init ( project zlib ; } - local library-path = [ property.select : $(options) ] ; - library-path = $(library-path:G=) ; - local include-path = [ property.select : $(options) ] ; - include-path = $(include-path:G=) ; - local source-path = [ property.select : $(options) ] ; - source-path = $(source-path:G=) ; - local library-name = [ property.select : $(options) ] ; - library-name = $(library-name:G=) ; - local tag = [ property.select : $(options) ] ; - tag = $(tag:G=) ; - local build-name = [ property.select : $(options) ] ; - build-name = $(build-name:G=) ; + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local source-path = [ feature.get-values : $(options) ] ; + local library-name = [ feature.get-values : $(options) ] ; + local tag = [ feature.get-values : $(options) ] ; + local build-name = [ feature.get-values : $(options) ] ; if ! $(library-path) && ! $(include-path) && ! $(source-path) && ! $(library-name) { @@ -132,7 +126,6 @@ rule init ( condition = [ property-set.create $(requirements) ] ; condition = [ property-set.create [ $(condition).base ] ] ; - local no-build-from-source ; # Ignore environmental ZLIB_SOURCE if this initialization # requested to search for a specific pre-built library. if $(library-path) || $(include-path) || $(library-name) @@ -169,7 +162,7 @@ rule init ( build-name ?= z ; library-id = [ CALC $(library-id) + 1 ] ; tag = [ MATCH ^@?(.*)$ : $(tag) ] ; - if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ] + if $(tag) { tag = [ indirect.make $(tag) : [ $(caller).project-module ] ] ; } diff --git a/src/util/indirect.jam b/src/util/indirect.jam index 40884da96..9f5f9dedd 100644 --- a/src/util/indirect.jam +++ b/src/util/indirect.jam @@ -31,9 +31,16 @@ local rule indirect-rule ( x ) # rule make ( rulename bound-args * : context ? ) { - context ?= [ CALLER_MODULE ] ; - context ?= "" ; - return $(context)%$(rulename) $(bound-args) ; + if [ MATCH $(.pattern) : $(rulename) ] + { + return $(rulename) $(bound-args) ; + } + else + { + context ?= [ CALLER_MODULE ] ; + context ?= "" ; + return $(context)%$(rulename) $(bound-args) ; + } } diff --git a/src/util/sequence.jam b/src/util/sequence.jam index 97ddfe153..ddfd069c7 100644 --- a/src/util/sequence.jam +++ b/src/util/sequence.jam @@ -156,6 +156,42 @@ rule merge ( s1 * : s2 * : ordered * ) return $(result__) ; } +# Compares two sequences lexicagraphically +# +rule compare ( s1 * : s2 * : ordered * ) +{ + if ! $(ordered) + { + if $(s1) < $(s2) + { + return true ; + } + } + else + { + while true + { + if ! $(s2[1])-is-defined + { + return ; + } + else if ! $(s1[1])-is-defined + { + return true ; + } + else if [ $(ordered) $(s1[1]) $(s2[1]) ] + { + return true ; + } + else if [ $(ordered) $(s2[1]) $(s1[1]) ] + { + return ; + } + s1 = $(s1[2-]) ; + s2 = $(s2[2-]) ; + } + } +} # Join the elements of s into one long string. If joint is supplied, it is used # as a separator. diff --git a/test/BoostBuild.py b/test/BoostBuild.py index a6872dcbc..19b346f77 100644 --- a/test/BoostBuild.py +++ b/test/BoostBuild.py @@ -92,13 +92,19 @@ def get_toolset(): cygwin = hasattr(os, "uname") and os.uname()[0].lower().startswith("cygwin") windows = cygwin or os.environ.get("OS", "").lower().startswith("windows") +if cygwin: + default_os = "cygwin" +elif windows: + default_os = "windows" +elif hasattr(os, "uname"): + default_os = os.uname()[0].lower() -def prepare_prefixes_and_suffixes(toolset): - prepare_suffix_map(toolset) - prepare_library_prefix(toolset) +def prepare_prefixes_and_suffixes(toolset, target_os=default_os): + prepare_suffix_map(toolset, target_os) + prepare_library_prefix(toolset, target_os) -def prepare_suffix_map(toolset): +def prepare_suffix_map(toolset, target_os=default_os): """ Set up suffix translation performed by the Boost Build testing framework to accomodate different toolsets generating targets of the same type using @@ -107,11 +113,11 @@ def prepare_suffix_map(toolset): """ global suffixes suffixes = {} - if windows: + if target_os in ["windows", "cygwin"]: if toolset == "gcc": suffixes[".lib"] = ".a" # mingw static libs use suffix ".a". suffixes[".obj"] = ".o" - if cygwin: + if target_os == "cygwin": suffixes[".implib"] = ".lib.a" else: suffixes[".implib"] = ".lib" @@ -122,11 +128,11 @@ def prepare_suffix_map(toolset): suffixes[".obj"] = ".o" suffixes[".implib"] = ".no_implib_files_on_this_platform" - if hasattr(os, "uname") and os.uname()[0] == "Darwin": + if target_os == "darwin": suffixes[".dll"] = ".dylib" -def prepare_library_prefix(toolset): +def prepare_library_prefix(toolset, target_os=default_os): """ Setup whether Boost Build is expected to automatically prepend prefixes to its built library targets. @@ -136,9 +142,9 @@ def prepare_library_prefix(toolset): lib_prefix = "lib" global dll_prefix - if cygwin: + if target_os == "cygwin": dll_prefix = "cyg" - elif windows and toolset != "gcc": + elif target_os == "windows" and toolset != "gcc": dll_prefix = None else: dll_prefix = "lib" @@ -209,11 +215,11 @@ class Tester(TestCmd.TestCmd): def __init__(self, arguments=None, executable="bjam", match=TestCmd.match_exact, boost_build_path=None, translate_suffixes=True, pass_toolset=True, use_test_config=True, - ignore_toolset_requirements=False, workdir="", pass_d0=True, + ignore_toolset_requirements=False, workdir="", pass_d0=False, **keywords): assert arguments.__class__ is not str - self.original_workdir = os.getcwd() + self.original_workdir = os.path.dirname(__file__) if workdir and not os.path.isabs(workdir): raise ("Parameter workdir <%s> must point to an absolute " "directory: " % workdir) @@ -274,8 +280,9 @@ class Tester(TestCmd.TestCmd): # Find where jam_src is located. Try for the debug version if it is # lying around. - dirs = [os.path.join("..", "src", "engine", jam_build_dir + ".debug"), - os.path.join("..", "src", "engine", jam_build_dir)] + srcdir = os.path.join(os.path.dirname(__file__), "..", "src") + dirs = [os.path.join(srcdir, "engine", jam_build_dir + ".debug"), + os.path.join(srcdir, "engine", jam_build_dir)] for d in dirs: if os.path.exists(d): jam_build_dir = d @@ -289,7 +296,8 @@ class Tester(TestCmd.TestCmd): verbosity = [] if "--verbose" in sys.argv: keywords["verbose"] = True - verbosity = ["-d+2"] + verbosity = ["-d2"] + self.verbosity = verbosity if boost_build_path is None: boost_build_path = self.original_workdir + "/.." @@ -300,8 +308,6 @@ class Tester(TestCmd.TestCmd): else: program_list.append(os.path.join(jam_build_dir, executable)) program_list.append('-sBOOST_BUILD_PATH="' + boost_build_path + '"') - if verbosity: - program_list += verbosity if arguments: program_list += arguments @@ -320,6 +326,12 @@ class Tester(TestCmd.TestCmd): # this case. pass + def set_toolset(self, toolset, target_os=default_os): + self.toolset = toolset + self.pass_toolset = True + prepare_prefixes_and_suffixes(toolset, target_os) + + # # Methods that change the working directory's content. # @@ -426,6 +438,7 @@ class Tester(TestCmd.TestCmd): return self.previous_tree, dummy = tree.build_tree(self.workdir) + self.wait_for_time_change_since_last_build() if match is None: match = self.match @@ -444,6 +457,8 @@ class Tester(TestCmd.TestCmd): kw["program"] += self.program if extra_args: kw["program"] += extra_args + if stdout is None and not any(a.startswith("-d") for a in kw["program"]): + kw["program"] += self.verbosity if pass_toolset: kw["program"].append("toolset=" + self.toolset) if use_test_config: @@ -707,6 +722,7 @@ class Tester(TestCmd.TestCmd): self.ignore("*.rsp") # Response files. self.ignore("*.tds") # Borland debug symbols. self.ignore("*.manifest") # MSVC DLL manifests. + self.ignore("bin/standalone/msvc/*/msvc-setup.bat") # Debug builds of bjam built with gcc produce this profiling data. self.ignore("gmon.out") @@ -757,8 +773,8 @@ class Tester(TestCmd.TestCmd): matched = reduce( lambda x, y: x and reduce( lambda a, b: a and b, - y), - matched) + y, True), + matched, True) if not matched: print "Expected:\n" @@ -856,6 +872,22 @@ class Tester(TestCmd.TestCmd): """ self.__wait_for_time_change(path, touch, last_build_time=False) + def wait_for_time_change_since_last_build(self): + """ + Wait for newly assigned file system modification timestamps to + become large enough for the timestamp difference to be + correctly recognized by the Python based testing framework. + Does not care about Jam's timestamp resolution, since we + only need this to detect touched files. + """ + if self.last_build_timestamp: + timestamp_file = "timestamp-3df2f2317e15e4a9" + open(timestamp_file, "wb").close() + self.__wait_for_time_change_impl(timestamp_file, + self.last_build_timestamp, + self.__python_timestamp_resolution(timestamp_file, 0), 0) + os.unlink(timestamp_file) + def __build_timestamp_resolution(self): """ Returns the minimum path modification timestamp resolution supported @@ -1103,7 +1135,12 @@ class Tester(TestCmd.TestCmd): resolution = self.__python_timestamp_resolution(path, build_resolution) assert resolution >= build_resolution + self.__wait_for_time_change_impl(path, start_time, resolution, build_resolution) + if not touch: + os.utime(path, (stats_orig.st_atime, stats_orig.st_mtime)) + + def __wait_for_time_change_impl(self, path, start_time, resolution, build_resolution): # Implementation notes: # * Theoretically time.sleep() API might get interrupted too soon # (never actually encountered). @@ -1160,9 +1197,6 @@ class Tester(TestCmd.TestCmd): break _sleep(max(0.01, start_time - c)) - if not touch: - os.utime(path, (stats_orig.st_atime, stats_orig.st_mtime)) - class List: def __init__(self, s=""): diff --git a/test/Jamfile.jam b/test/Jamfile.jam new file mode 100644 index 000000000..7ec0bf303 --- /dev/null +++ b/test/Jamfile.jam @@ -0,0 +1,29 @@ +# Copyright 2018 Steven Watanabe +# 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 python ; +import testing ; + +if ! [ python.configured ] +{ + using python ; +} + +# Not quite perfect, but good enough for most purposes +local test-files = [ glob *.py ] ; + +local boost-build-files = [ glob + ../src/tools/*.jam + ../src/tools/*/*.jam + ../src/build/*.jam + ../src/util/*.jam + ../src/kernel/*.jam + ../src/options/*.jam + ../src/*.jam ] ; + +testing.make-test run-pyd : test_all.py : + $(test-files) + $(boost-build-files) + ; diff --git a/test/MockToolset.py b/test/MockToolset.py index 741959e50..bc02bf8f5 100755 --- a/test/MockToolset.py +++ b/test/MockToolset.py @@ -18,6 +18,7 @@ parser.add_option('-o', dest="output_file") parser.add_option('-x', dest="language") parser.add_option('-c', dest="compile", action="store_true") parser.add_option('-I', dest="includes", action="append") +parser.add_option('-D', dest="defines", action="append") parser.add_option('-L', dest="library_path", action="append") parser.add_option('--dll', dest="dll", action="store_true") parser.add_option('--archive', dest="archive", action="store_true") @@ -101,6 +102,16 @@ class MockInfo(object): " != ", map(adjust_path, expected_options.includes) return False + if options.defines is None: + options.defines = [] + if expected_options.defines is None: + expected_options.defines = [] + if options.defines != expected_options.defines: + if self.verbose: + print " Failed to match -I ", options.defines, \ + " != ", expected_options.defines + return False + if options.library_path is None: options.library_path = [] if expected_options.library_path is None: @@ -203,16 +214,18 @@ generators.register-linker mock.link : LIB OBJ : EXE : mock ; generators.register-linker mock.link.dll : LIB OBJ : SHARED_LIB : mock ; generators.register-archiver mock.archive : OBJ : STATIC_LIB : mock ; -toolset.flags mock.compile INCLUDES ; +toolset.flags mock.compile OPTIONS shared : -fPIC ; +toolset.flags mock.compile INCLUDES : ; +toolset.flags mock.compile DEFINES : ; actions compile.c { - $(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" "$(>)" -o "$(<)" + $(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" } actions compile.c++ { - $(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" "$(>)" -o "$(<)" + $(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" } toolset.flags mock.link USER_OPTIONS ; diff --git a/test/TestToolset.py b/test/TestToolset.py new file mode 100644 index 000000000..8915e78cc --- /dev/null +++ b/test/TestToolset.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates a toolset using a mock of the compiler + +import BoostBuild +import os +import re +import sys + +renames = {"debug": "variant=debug", "release": "variant=release"} + +def set_default_target_os(os): + global removed + global default_target_os + default_target_os = os + removed = set() + removed.add("target-os=" + default_target_os) + +def adjust_property(property): + global renames + if property in renames: + return renames[property] + else: + return property + +def adjust_properties(properties): + global removed + return [adjust_property(p) for p in properties if p not in removed] + +def has_property(name, properties): + return name in [re.sub("=.*", "", p) for p in properties] + +def get_property(name, properties): + for m in [re.match("(.*)=(.*)", p) for p in properties]: + if m and m.group(1) == name: + return m.group(2) + +def get_target_os(properties): + return get_property("target-os", properties) or default_target_os + +def expand_properties(properties): + result = properties[:] + if not has_property("variant", properties): + result += ["variant=debug"] + if not has_property("threading", properties): + result += ["threading=single"] + if not has_property("link", properties): + result += ["link=shared"] + if not has_property("runtime-link", properties): + result += ["runtime-link=shared"] + if not has_property("strip", properties): + result += ["strip=off"] + if not has_property("target-os", properties): + result += ["target-os=" + default_target_os] + return result + +def compute_path(properties, target_type): + path = "" + if "variant=release" in properties: + path += "/release" + else: + path += "/debug" + if has_property("address-model", properties): + path += "/address-model-" + get_property("address-model", properties) + if has_property("architecture", properties): + path += "/architecture-" + get_property("architecture", properties) + if "cxxstd=latest" in properties: + path += "/cxxstd-latest-iso" + if "link=static" in properties: + path += "/link-static" + if "runtime-link=static" in properties and target_type in ["exe"]: + path += "/runtime-link-static" + if "strip=on" in properties and target_type in ["dll", "exe", "obj2"]: + path += "/strip-on" + if get_target_os(properties) != default_target_os: + path += "/target-os-" + get_target_os(properties) + if "threading=multi" in properties: + path += "/threading-multi" + return path + +def test_toolset(toolset, version, property_sets): + t = BoostBuild.Tester() + + t.set_tree("toolset-mock") + + # Build necessary tools + t.run_build_system(["-sPYTHON_CMD=%s" % sys.executable], subdir="src") + set_default_target_os(t.read("src/bin/target-os.txt").strip()) + + for properties in property_sets: + t.set_toolset(toolset + "-" + version, get_target_os(properties)) + properties = adjust_properties(properties) + def path(t): + return toolset.split("-")[0] + "-*" + version + compute_path(properties, t) + os.environ["B2_PROPERTIES"] = " ".join(expand_properties(properties)) + t.run_build_system(["--user-config="] + properties) + t.expect_addition("bin/%s/lib.obj" % (path("obj"))) + if "link=static" not in properties: + t.expect_addition("bin/%s/l1.dll" % (path("dll"))) + else: + t.expect_addition("bin/%s/l1.lib" % (path("lib"))) + t.expect_addition("bin/%s/main.obj" % (path("obj2"))) + t.expect_addition("bin/%s/test.exe" % (path("exe"))) + t.expect_nothing_more() + t.rm("bin") + + t.cleanup() diff --git a/test/alias.py b/test/alias.py index 53371f4e6..132e4c390 100644 --- a/test/alias.py +++ b/test/alias.py @@ -99,9 +99,7 @@ int main() {} # ############################################################################### -# We do not pass the '-d0' option to Boost Build here to get more detailed -# information in case of failure. -t = BoostBuild.Tester(pass_d0=False, use_test_config=False) +t = BoostBuild.Tester(use_test_config=False) test_alias_rule(t) test_alias_source_usage_requirements(t) diff --git a/test/alternatives.py b/test/alternatives.py index e8f9220ff..7a52427d2 100644 --- a/test/alternatives.py +++ b/test/alternatives.py @@ -88,7 +88,7 @@ exe a : a_empty.cpp ; exe a : a.cpp ; """) t.run_build_system(["--no-error-backtrace"], status=None) -t.fail_test(string.find(t.stdout(), "No best alternative") == -1) +t.expect_output_lines("error: No best alternative for ./a") # Another ambiguity test: two matches properties in one alternative are neither # better nor worse than a single one in another alternative. @@ -98,7 +98,8 @@ exe a : a.cpp : on ; """) t.run_build_system(["--no-error-backtrace"], status=None) -t.fail_test(string.find(t.stdout(), "No best alternative") == -1) +t.expect_output_lines("error: No best alternative for ./a") +t.rm("bin") # Test that we can have alternative without sources. t.write("jamfile.jam", """\ @@ -108,7 +109,21 @@ feature.extend os : MAGIC ; alias specific-sources : b.cpp : MAGIC ; exe a : a.cpp specific-sources ; """) -t.rm("bin") t.run_build_system() +t.expect_addition("bin/$toolset/debug*/a.exe") +t.rm("bin") + +# Test that subfeatures are expanded in alternatives +# and that unknown subfeatures fail to match instead of +# causing errors. +t.write("jamfile.jam", """\ +import feature : feature subfeature ; +feature X : off on : propagated ; +subfeature X on : version : 1 : propagated ; +exe a : a.cpp : on-1 ; +exe a : a_empty.cpp ; +exe a : a_empty.cpp : on-2 ; +""") +t.run_build_system(["X=on-1"]) t.cleanup() diff --git a/test/command_line_properties.py b/test/command_line_properties.py new file mode 100644 index 000000000..ebb186ea4 --- /dev/null +++ b/test/command_line_properties.py @@ -0,0 +1,166 @@ +#!/usr/bin/python + +import BoostBuild + +def test_basic(): + '''Tests that feature=value works''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : 1 2 ; + make output.txt : : @run ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=2']) + t.expect_content("bin/*/output.txt", "2") + t.cleanup() + +def test_implicit(): + '''Tests that implicit features can be named without a feature''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : v1 v2 : implicit ; + make output.txt : : @run ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['v2']) + t.expect_content("bin/*/output.txt", "v2") + t.cleanup() + +def test_optional(): + '''Tests that feature= works for optional features''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : 1 2 : optional ; + make output.txt : : @run ; + flags run OPTIONS ; + actions run { echo b $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=']) + t.expect_content("bin/*/output.txt", "b") + t.cleanup() + +def test_free(): + '''Free features named on the command line apply to all targets + everywhere. Free features can contain any characters, even those + that have a special meaning.''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : : free ; + make output1.txt : : @run : output2.txt ; + make output2.txt : : @run ; + explicit output2.txt ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=x,/:-']) + t.expect_content("bin*/output1.txt", "x,/:-") + t.expect_content("bin*/output2.txt", "x,/:-") + t.cleanup() + +def test_subfeature(): + '''Subfeatures should be expressed as feature=value-subvalue''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature subfeature ; + import toolset : flags ; + feature f1 : 1 2 ; + subfeature f1 2 : sub : x y ; + make output.txt : : @run ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=2-y']) + t.expect_content("bin/*/output.txt", "y") + t.cleanup() + +def test_multiple_values(): + '''Multiple values of a feature can be given in a comma-separated list''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : 1 2 3 ; + make output.txt : : @run ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=2,3']) + t.expect_content("bin*/f1-2*/output.txt", "2") + t.expect_content("bin*/f1-3*/output.txt", "3") + t.cleanup() + +def test_multiple_properties(): + '''Multiple properties can be grouped with /''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + feature f1 : 1 2 ; + feature f2 : 3 4 ; + make output.txt : : @run ; + flags run OPTIONS ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=2/f2=4']) + t.expect_content("bin/*/output.txt", "2 4") + t.cleanup() + +def test_cross_product(): + '''If multiple properties are specified on the command line + we expand to every possible maximum set of non-conflicting features. + This test should be run after testing individual components in + isolation.''' + t = BoostBuild.Tester() + t.write('Jamroot.jam', ''' + import feature : feature ; + import toolset : flags ; + # Make features symmetric to make the paths easier to distingush + feature f1 : 11 12 13 14 15 : symmetric ; + feature f2 : 21 22 23 : symmetric ; + feature f3 : v1 v2 v3 v4 : implicit symmetric ; + feature f4 : : free ; + make output.txt : : @run ; + flags run OPTIONS ; + flags run OPTIONS ; + flags run OPTIONS ; + flags run OPTIONS ; + actions run { echo $(OPTIONS) > $(<) } + ''') + t.run_build_system(['f1=12,13/f2=22', 'v2', 'v3', 'f1=14', 'f2=23', + 'f4=xxx', 'f4=yyy', 'v4/f1=15/f4=zzz']) + t.expect_content("bin*/v2*/f1-12/f2-22*/output.txt", "12 22 v2 xxx yyy") + t.expect_addition("bin*/v2*/f1-12/f2-22*/output.txt") + t.expect_content("bin*/v2*/f1-13/f2-22*/output.txt", "13 22 v2 xxx yyy") + t.expect_addition("bin*/v2*/f1-13/f2-22*/output.txt") + t.expect_content("bin*/v2*/f1-14/f2-23*/output.txt", "14 23 v2 xxx yyy") + t.expect_addition("bin*/v2*/f1-14/f2-23*/output.txt") + t.expect_content("bin*/v3*/f1-12/f2-22*/output.txt", "12 22 v3 xxx yyy") + t.expect_addition("bin*/v3*/f1-12/f2-22*/output.txt") + t.expect_content("bin*/v3*/f1-13/f2-22*/output.txt", "13 22 v3 xxx yyy") + t.expect_addition("bin*/v3*/f1-13/f2-22*/output.txt") + t.expect_content("bin*/v3*/f1-14/f2-23*/output.txt", "14 23 v3 xxx yyy") + t.expect_addition("bin*/v3*/f1-14/f2-23*/output.txt") + t.expect_content("bin*/v4*/f1-15/f2-23*/output.txt", "15 23 v4 xxx yyy zzz") + t.expect_addition("bin*/v4*/f1-15/f2-23*/output.txt") + t.expect_nothing_more() + t.cleanup() + +test_basic() +test_implicit() +test_optional() +test_free() +test_subfeature() +test_multiple_values() +test_multiple_properties() +test_cross_product() diff --git a/test/configuration.py b/test/configuration.py index 724ecd7c9..0a9df6a6c 100755 --- a/test/configuration.py +++ b/test/configuration.py @@ -8,6 +8,7 @@ # Test Boost Build configuration file handling. import BoostBuild +import TestCmd import os import os.path @@ -316,6 +317,71 @@ for x in $(names) t.cleanup() +def test_site_config(): + # Ignore user-config, just in case it depends on the user's site-config.jam + t = BoostBuild.Tester(["--user-config="], use_test_config=False, + pass_toolset=0) + # We can immediately exit after we finish loading the config files + t.write("Jamroot", "EXIT Done : 0 ;") + t.write("my-site-config.jam", "ECHO Loaded my-site-config ;") + + t.run_build_system(["--site-config=my-site-config.jam"], + stdout="Loaded my-site-config\nDone\n") + + t.run_build_system(["--ignore-site-config", "--debug-configuration"]) + t.expect_output_lines("""\ +notice: Site configuration files will be ignored due to the +notice: --ignore-site-config command-line option.""") + + t.run_build_system(["--site-config=", "--debug-configuration"]) + t.expect_output_lines("""\ +notice: Site configuration file loading explicitly disabled.""") + + t.cleanup() + +def test_global_config(): + t = BoostBuild.Tester(use_test_config=False, pass_toolset=0) + t.write("my-config.jam", "ECHO Loading my-config ;") + t.write("Jamroot", "EXIT Done : 0 ;") + t.write("project-config.jam", "ECHO bad ;") + t.run_build_system(["--config=my-config.jam", "--debug-configuration"], + match=TestCmd.match_re, stdout= +r"""notice: found boost-build\.jam at .* +notice: loading Boost\.Build from .* +notice: Searching '.*' for all-config configuration file 'my-config\.jam'\. +notice: Loading all-config configuration file 'my-config\.jam' from '.*'\. +Loading my-config +notice: Regular configuration files will be ignored due +notice: to the global configuration being loaded\. +Done +""") + t.run_build_system(["--config=", "--debug-configuration"], + match=TestCmd.match_re, stdout= +r"""notice: found boost-build\.jam at .* +notice: loading Boost\.Build from .* +notice: Configuration file loading explicitly disabled. +Done +""") + t.cleanup() + +def test_project_config(): + t = BoostBuild.Tester(["--user-config=", "--site-config="], + use_test_config=False, pass_toolset=False) + t.write("Jamroot", "EXIT Done : 0 ;") + t.write("project-config.jam", "ECHO Loading Root ;") + t.write("my-project-config.jam", "ECHO Loading explicit ;") + t.write("sub/project-config.jam", "ECHO Loading subdir ;") + t.write("sub/Jamfile", "") + + t.run_build_system(stdout="Loading Root\nDone\n") + t.run_build_system(subdir="sub", stdout="Loading subdir\nDone\n") + t.rm("sub/project-config.jam") + t.run_build_system(subdir="sub", stdout="Loading Root\nDone\n") + t.run_build_system(["--project-config=my-project-config.jam"], + stdout="Loading explicit\nDone\n") + + t.cleanup() + ############################################################################### # # main() @@ -326,3 +392,6 @@ for x in $(names) canSetEmptyEnvironmentVariable = _canSetEmptyEnvironmentVariable() test_user_configuration() +test_site_config() +test_global_config() +test_project_config() diff --git a/test/configure.py b/test/configure.py new file mode 100644 index 000000000..3f0c958a0 --- /dev/null +++ b/test/configure.py @@ -0,0 +1,128 @@ +#!/usr/bin/python + +# Copyright 2017 Steven Watanabe +# 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) + +# Tests configure.check-target-builds and friends + +import BoostBuild + +def test_check_target_builds(): + t = BoostBuild.Tester(use_test_config=0) + t.write("Jamroot", """ +import configure ; +obj pass : pass.cpp ; +obj fail : fail.cpp ; +explicit pass fail ; +obj foo : foo.cpp : + [ configure.check-target-builds pass : PASS : FAIL ] ; +obj bar : foo.cpp : + [ configure.check-target-builds fail : FAIL : PASS ] ; +""") + t.write("pass.cpp", "void f() {}\n") + t.write("fail.cpp", "#error fail.cpp\n") + t.write("foo.cpp", """ +#ifndef PASS +#error PASS not defined +#endif +#ifdef FAIL +#error FAIL is defined +#endif +""") + t.run_build_system() + t.expect_output_lines([ + " - pass builds : yes", + " - fail builds : no"]) + t.expect_addition("bin/$toolset/debug*/pass.obj") + t.expect_addition("bin/$toolset/debug*/foo.obj") + t.expect_addition("bin/$toolset/debug*/bar.obj") + t.expect_nothing_more() + + # An up-to-date build should use the cache + t.run_build_system() + t.expect_output_lines([ + " - pass builds : yes (cached)", + " - fail builds : no (cached)"]) + t.expect_nothing_more() + + # -a should re-run everything, including configuration checks + t.run_build_system(["-a"]) + t.expect_output_lines([ + " - pass builds : yes", + " - fail builds : no"]) + t.expect_touch("bin/$toolset/debug*/pass.obj") + t.expect_touch("bin/$toolset/debug*/foo.obj") + t.expect_touch("bin/$toolset/debug*/bar.obj") + t.expect_nothing_more() + + # --reconfigure should re-run configuration checks only + t.run_build_system(["--reconfigure"]) + t.expect_output_lines([ + " - pass builds : yes", + " - fail builds : no"]) + t.expect_touch("bin/$toolset/debug*/pass.obj") + t.expect_nothing_more() + + # -a -n should not rebuild configuration checks + t.run_build_system(["-a", "-n"]) + t.expect_output_lines([ + " - pass builds : yes (cached)", + " - fail builds : no (cached)"]) + t.expect_nothing_more() + + t.cleanup() + +def test_choose(): + t = BoostBuild.Tester(use_test_config=0) + t.write("Jamroot", """ +import configure ; +obj pass : pass.cpp ; +obj fail : fail.cpp ; +explicit pass fail ; +obj foo : foo.cpp : + [ configure.choose "which one?" : fail FAIL : pass PASS ] ; +""") + t.write("pass.cpp", "void f() {}\n") + t.write("fail.cpp", "#error fail.cpp\n") + t.write("foo.cpp", """ +#ifndef PASS +#error PASS not defined +#endif +#ifdef FAIL +#error FAIL is defined +#endif +""") + t.run_build_system() + t.expect_output_lines([ + " - which one? : pass"]) + t.expect_addition("bin/$toolset/debug*/pass.obj") + t.expect_addition("bin/$toolset/debug*/foo.obj") + t.expect_nothing_more() + + # An up-to-date build should use the cache + t.run_build_system() + t.expect_output_lines([ + " - which one? : pass (cached)"]) + t.expect_nothing_more() + + # -a should re-run everything, including configuration checks + t.run_build_system(["-a"]) + t.expect_output_lines([ + " - which one? : pass"]) + t.expect_touch("bin/$toolset/debug*/pass.obj") + t.expect_touch("bin/$toolset/debug*/foo.obj") + t.expect_nothing_more() + + # --reconfigure should re-run configuration checks only + t.run_build_system(["--reconfigure"]) + t.expect_output_lines([ + " - which one? : pass"]) + t.expect_touch("bin/$toolset/debug*/pass.obj") + t.expect_nothing_more() + + t.cleanup() + +test_check_target_builds() +test_choose() diff --git a/test/core_action_output.py b/test/core_action_output.py index b26f0e0bd..d2734d00e 100755 --- a/test/core_action_output.py +++ b/test/core_action_output.py @@ -9,7 +9,7 @@ import BoostBuild -t = BoostBuild.Tester(["-d1"], pass_d0=False, pass_toolset=False) +t = BoostBuild.Tester(["-d1"], pass_toolset=False) t.write("file.jam", """\ prefix = "echo \\"" ; diff --git a/test/core_arguments.py b/test/core_arguments.py index a6e886ff6..7f1278214 100755 --- a/test/core_arguments.py +++ b/test/core_arguments.py @@ -30,7 +30,7 @@ def test_varargs(t, *args, **kwargs): test(t, "varargs", *args, **kwargs) -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(pass_toolset=0) t.write("echo_args.jam", """\ NOCARE all ; diff --git a/test/core_bindrule.py b/test/core_bindrule.py index 6ae4ab34c..5a8faca1e 100755 --- a/test/core_bindrule.py +++ b/test/core_bindrule.py @@ -8,7 +8,7 @@ import BoostBuild import os -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(["-d1"], pass_toolset=0) t.write("subdir1/file-to-bind", "# This file intentionally left blank") diff --git a/test/core_d12.py b/test/core_d12.py index 5488973d2..370fc4bf1 100644 --- a/test/core_d12.py +++ b/test/core_d12.py @@ -9,7 +9,7 @@ import BoostBuild -t = BoostBuild.Tester(["-ffile.jam"], pass_d0=False, pass_toolset=0) +t = BoostBuild.Tester(["-ffile.jam"], pass_toolset=0) t.write("file.jam", """\ actions a { } diff --git a/test/core_fail_expected.py b/test/core_fail_expected.py new file mode 100644 index 000000000..0865a0b7a --- /dev/null +++ b/test/core_fail_expected.py @@ -0,0 +1,139 @@ +#!/usr/bin/python + +# Copyright 2017 Steven Watanabe +# 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) + +import BoostBuild + +def test_basic(): + t = BoostBuild.Tester(pass_toolset=0) + + t.write("file.jam", """\ + actions fail + { + invalid-dd0eeb5899734622 + } + + FAIL_EXPECTED t1 ; + fail t1 ; + + UPDATE t1 ; + """) + + t.run_build_system(["-ffile.jam"]) + t.expect_output_lines("...failed*", False) + t.expect_nothing_more() + + t.cleanup() + +def test_error(): + t = BoostBuild.Tester(pass_toolset=0) + + t.write("file.jam", """\ + actions pass + { + echo okay >$(<) + } + + FAIL_EXPECTED t1 ; + pass t1 ; + + UPDATE t1 ; + """) + + t.run_build_system(["-ffile.jam"], status=1) + t.expect_output_lines("...failed pass t1...") + t.expect_nothing_more() + + t.cleanup() + +def test_multiple_actions(): + """FAIL_EXPECTED targets are considered to pass if the first + updating action fails. Further actions will be skipped.""" + t = BoostBuild.Tester(pass_toolset=0) + + t.write("file.jam", """\ + actions fail + { + invalid-dd0eeb5899734622 + } + + actions pass + { + echo okay >$(<) + } + + FAIL_EXPECTED t1 ; + fail t1 ; + pass t1 ; + + UPDATE t1 ; + """) + + t.run_build_system(["-ffile.jam", "-d1"]) + t.expect_output_lines("...failed*", False) + t.expect_output_lines("pass t1", False) + t.expect_nothing_more() + + t.cleanup() + +def test_quitquick(): + """Tests that FAIL_EXPECTED targets do not cause early exit + on failure.""" + t = BoostBuild.Tester(pass_toolset=0) + + t.write("file.jam", """\ + actions fail + { + invalid-dd0eeb5899734622 + } + + actions pass + { + echo okay >$(<) + } + + FAIL_EXPECTED t1 ; + fail t1 ; + + pass t2 ; + + UPDATE t1 t2 ; + """) + + t.run_build_system(["-ffile.jam", "-q", "-d1"]) + t.expect_output_lines("pass t2") + t.expect_addition("t2") + t.expect_nothing_more() + + t.cleanup() + +def test_quitquick_error(): + """FAIL_EXPECTED targets should cause early exit if they unexpectedly pass.""" + t = BoostBuild.Tester(pass_toolset=0) + + t.write("file.jam", """\ + actions pass + { + echo okay >$(<) + } + + FAIL_EXPECTED t1 ; + pass t1 ; + pass t2 ; + + UPDATE t1 t2 ; + """) + + t.run_build_system(["-ffile.jam", "-q", "-d1"], status=1) + t.expect_output_lines("pass t2", False) + t.expect_nothing_more() + + t.cleanup() + +test_basic() +test_error() +test_multiple_actions() +test_quitquick() +test_quitquick_error() diff --git a/test/core_import_module.py b/test/core_import_module.py index c5bbd3e63..5903dcd64 100644 --- a/test/core_import_module.py +++ b/test/core_import_module.py @@ -67,8 +67,7 @@ module c IMPORT_MODULE c : ; c.test ; -actions do-nothing { } -do-nothing all ; +EXIT : 0 ; """) t.run_build_system(["-fcode"], stdout="""\ @@ -77,6 +76,7 @@ R2 L1 A.L1 CTEST + """) t.cleanup() diff --git a/test/core_jamshell.py b/test/core_jamshell.py index 0344a5792..5b14213c3 100644 --- a/test/core_jamshell.py +++ b/test/core_jamshell.py @@ -7,7 +7,7 @@ import BoostBuild import sys -t = BoostBuild.Tester(pass_toolset=False, pass_d0=False) +t = BoostBuild.Tester(pass_toolset=False) t.write("file.jam", """ actions run { diff --git a/test/core_multifile_actions.py b/test/core_multifile_actions.py index 50bfe8339..a9c7f4790 100755 --- a/test/core_multifile_actions.py +++ b/test/core_multifile_actions.py @@ -20,7 +20,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(["-d1"], pass_toolset=0) t.write("file.jam", """ actions update diff --git a/test/core_nt_cmd_line.py b/test/core_nt_cmd_line.py index 579242d24..064f86826 100755 --- a/test/core_nt_cmd_line.py +++ b/test/core_nt_cmd_line.py @@ -49,7 +49,7 @@ def test_raw_empty(): # get an extra "\r" added in front of it on output. whitespace_out = whitespace_in.replace("\r\n", "\n").replace("\n", "\r\n") - t = BoostBuild.Tester(["-d2", "-d+4"], pass_d0=False, pass_toolset=0, + t = BoostBuild.Tester(["-d2", "-d+4"], pass_toolset=0, use_test_config=False) t.write("file.jam", """\ actions do_empty {%s} @@ -67,7 +67,7 @@ do_empty all ; def test_raw_nt(n=None, error=False): - t = BoostBuild.Tester(["-d1", "-d+4"], pass_d0=False, pass_toolset=0, + t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0, use_test_config=False) cmd_prefix = "%s -c \"print('XXX: " % executable @@ -135,7 +135,7 @@ do_echo all ; def test_raw_to_shell_fallback_nt(): - t = BoostBuild.Tester(["-d1", "-d+4"], pass_d0=False, pass_toolset=0, + t = BoostBuild.Tester(["-d1", "-d+4"], pass_toolset=0, use_test_config=False) cmd_prefix = '%s -c print(' % executable diff --git a/test/core_option_d2.py b/test/core_option_d2.py index bf809aa85..8e6b05a45 100755 --- a/test/core_option_d2.py +++ b/test/core_option_d2.py @@ -7,7 +7,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions .a. diff --git a/test/core_option_n.py b/test/core_option_n.py index 4dab3bf99..af3ee0c3e 100755 --- a/test/core_option_n.py +++ b/test/core_option_n.py @@ -7,7 +7,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions .a. diff --git a/test/core_parallel_actions.py b/test/core_parallel_actions.py index 0d44149b7..4f1627c20 100755 --- a/test/core_parallel_actions.py +++ b/test/core_parallel_actions.py @@ -7,7 +7,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(["-d1"], pass_toolset=0) t.write("sleep.bat", """\ ::@timeout /T %1 /NOBREAK >nul diff --git a/test/core_parallel_multifile_actions_1.py b/test/core_parallel_multifile_actions_1.py index 8d9448e0c..4b800a788 100755 --- a/test/core_parallel_multifile_actions_1.py +++ b/test/core_parallel_multifile_actions_1.py @@ -17,7 +17,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(["-d1"], pass_toolset=0) t.write("sleep.bat", """\ ::@timeout /T %1 /NOBREAK >nul diff --git a/test/core_parallel_multifile_actions_2.py b/test/core_parallel_multifile_actions_2.py index ea4034af0..c49e92380 100755 --- a/test/core_parallel_multifile_actions_2.py +++ b/test/core_parallel_multifile_actions_2.py @@ -20,7 +20,7 @@ import BoostBuild -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) +t = BoostBuild.Tester(pass_toolset=0) t.write("sleep.bat", """\ ::@timeout /T %1 /NOBREAK >nul diff --git a/test/core_update_now.py b/test/core_update_now.py index 819309a73..5535c3189 100755 --- a/test/core_update_now.py +++ b/test/core_update_now.py @@ -9,7 +9,7 @@ import os def basic(): - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions do-print @@ -39,7 +39,7 @@ updating target1 def ignore_minus_n(): - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions do-print @@ -72,7 +72,7 @@ updating target1 def failed_target(): - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions fail @@ -120,7 +120,7 @@ do-print target2 def missing_target(): - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions do-print @@ -155,7 +155,7 @@ def build_once(): effect. """ - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions do-print @@ -199,7 +199,7 @@ def return_status(): Make sure that UPDATE_NOW returns a failure status if the target failed in a previous call to UPDATE_NOW """ - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("file.jam", """\ actions fail @@ -237,7 +237,7 @@ update2: def save_restore(): """Tests that ignore-minus-n and ignore-minus-q are local to the call to UPDATE_NOW""" - t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) + t = BoostBuild.Tester(pass_toolset=0) t.write("actions.jam", """\ rule fail diff --git a/test/custom_generator.py b/test/custom_generator.py index f98a46878..00860f643 100644 --- a/test/custom_generator.py +++ b/test/custom_generator.py @@ -61,6 +61,6 @@ t.write("r.rcc", """ """) t.run_build_system() -t.expect_content("bin/$toolset/debug*/r.obj", "rc-object") +t.expect_content("bin/r.obj", "rc-object") t.cleanup() diff --git a/test/debugger.py b/test/debugger.py index 9edfa84c0..7b696a0fc 100644 --- a/test/debugger.py +++ b/test/debugger.py @@ -97,6 +97,9 @@ Breakpoint 1, f ( ) at test.jam:8 """) t.cleanup() +# Note: step doesn't need to worry about breakpoints, +# as it always stops at the next line executed. + def test_next(): t = make_tester() t.write("test.jam", """\ @@ -137,6 +140,51 @@ Breakpoint 1, f ( ) at test.jam:7 """) t.cleanup() +def test_next_breakpoint(): + """next should stop if it encounters a breakpoint. + If the normal end point happens to be a breakpoint, + then it should be reported as normal stepping.""" + t = make_tester() + t.write("test.jam", """\ + rule f ( recurse ? ) + { + if $(recurse) { f ; } + a = 1 ; + } + rule g ( ) + { + b = 2 ; + } + f true ; + g ; + """) + run(t, """\ +(b2db) break f +Breakpoint 1 set at f +(b2db) break g +Breakpoint 2 set at g +(b2db) break test.jam:4 +Breakpoint 3 set at test.jam:4 +(b2db) run -ftest.jam +Starting program: {{bjam}} -ftest.jam +Breakpoint 1, f ( true ) at test.jam:3 +3 if $(recurse) { f ; } +(b2db) next +Breakpoint 1, f ( ) at test.jam:3 +3 if $(recurse) { f ; } +(b2db) next +4 a = 1 ; +(b2db) next +4 a = 1 ; +(b2db) next +11 g ; +(b2db) next +Breakpoint 2, g ( ) at test.jam:8 +8 b = 2 ; +(b2db) quit +""") + t.cleanup() + def test_finish(): t = make_tester() t.write("test.jam", """\ @@ -178,7 +226,100 @@ Breakpoint 1, f ( ) at test.jam:3 (b2db) quit """) t.cleanup() - + +def test_finish_breakpoints(): + """finish should stop when it reaches a breakpoint.""" + t = make_tester() + t.write("test.jam", """\ + rule f ( recurse * ) + { + if $(recurse) + { + a = [ f $(recurse[2-]) ] ; + } + } + rule g ( list * ) + { + for local v in $(list) + { + x = $(v) ; + } + } + f 1 2 ; + g 1 2 ; + """) + run(t, """\ +(b2db) break test.jam:5 +Breakpoint 1 set at test.jam:5 +(b2db) break test.jam:12 +Breakpoint 2 set at test.jam:12 +(b2db) run -ftest.jam +Starting program: {{bjam}} -ftest.jam +Breakpoint 1, f ( 1 2 ) at test.jam:5 +5 a = [ f $(recurse[2-]) ] ; +(b2db) finish +Breakpoint 1, f ( 2 ) at test.jam:5 +5 a = [ f $(recurse[2-]) ] ; +(b2db) finish +5 a = [ f $(recurse[2-]) ] ; +(b2db) finish +16 g 1 2 ; +(b2db) finish +Breakpoint 2, g ( 1 2 ) at test.jam:12 +12 x = $(v) ; +(b2db) finish +Breakpoint 2, g ( 1 2 ) at test.jam:12 +12 x = $(v) ; +(b2db) quit +""") + t.cleanup() + +def test_continue_breakpoints(): + """continue should stop when it reaches a breakpoint""" + t = make_tester() + t.write("test.jam", """\ + rule f ( recurse * ) + { + if $(recurse) + { + a = [ f $(recurse[2-]) ] ; + } + } + rule g ( list * ) + { + for local v in $(list) + { + x = $(v) ; + } + } + f 1 2 ; + g 1 2 ; + """) + run(t, """\ +(b2db) break test.jam:5 +Breakpoint 1 set at test.jam:5 +(b2db) break test.jam:12 +Breakpoint 2 set at test.jam:12 +(b2db) run -ftest.jam +Starting program: {{bjam}} -ftest.jam +Breakpoint 1, f ( 1 2 ) at test.jam:5 +5 a = [ f $(recurse[2-]) ] ; +(b2db) continue +Breakpoint 1, f ( 2 ) at test.jam:5 +5 a = [ f $(recurse[2-]) ] ; +(b2db) continue +Breakpoint 1, f ( 1 2 ) at test.jam:5 +5 a = [ f $(recurse[2-]) ] ; +(b2db) continue +Breakpoint 2, g ( 1 2 ) at test.jam:12 +12 x = $(v) ; +(b2db) continue +Breakpoint 2, g ( 1 2 ) at test.jam:12 +12 x = $(v) ; +(b2db) quit +""") + t.cleanup() + def test_breakpoints(): """Tests the interaction between the following commands: break, clear, delete, disable, enable""" @@ -519,7 +660,10 @@ test_run() test_exit_status() test_step() test_next() +test_next_breakpoint() test_finish() +test_finish_breakpoints() +test_continue_breakpoints() test_breakpoints() test_breakpoints_running() test_backtrace() diff --git a/test/dependency_test.py b/test/dependency_test.py index a495affff..852955775 100644 --- a/test/dependency_test.py +++ b/test/dependency_test.py @@ -10,7 +10,7 @@ import BoostBuild def test_basic(): - t = BoostBuild.Tester(["-d3", "-d+12"], pass_d0=False, use_test_config=False) + t = BoostBuild.Tester(["-d3", "-d+12"], use_test_config=False) t.write("a.cpp", """ #include @@ -211,7 +211,7 @@ def test_scanned_includes_with_absolute_paths(): considered when scanning dependencies. """ - t = BoostBuild.Tester(["-d3", "-d+12"], pass_d0=False) + t = BoostBuild.Tester(["-d3", "-d+12"]) t.write("jamroot.jam", """\ path-constant TOP : . ; diff --git a/test/direct_request_test.py b/test/direct_request_test.py deleted file mode 100644 index 422276938..000000000 --- a/test/direct_request_test.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python - -import BoostBuild - -t = BoostBuild.Tester(use_test_config=False) - -# First check some startup. - -t.write("jamroot.jam", "") -t.write("jamfile.jam", """\ -exe a : a.cpp b ; -lib b : b.cpp ; -""") - -t.write("a.cpp", """\ -void -# ifdef _WIN32 -__declspec(dllimport) -# endif -foo(); -int main() { foo(); } -""") - -t.write("b.cpp", """\ -#ifdef MACROS -void -# ifdef _WIN32 -__declspec(dllexport) -# endif -foo() {} -#endif - -# ifdef _WIN32 -int __declspec(dllexport) force_implib_creation; -# endif -""") - -t.run_build_system(["define=MACROS"]) -t.expect_addition("bin/$toolset/debug*/" - * (BoostBuild.List("a.obj b.obj b.dll a.exe"))) - - -# When building a debug version, the 'define' still applies. -t.rm("bin") -t.run_build_system(["debug", "define=MACROS"]) -t.expect_addition("bin/$toolset/debug*/" - * (BoostBuild.List("a.obj b.obj b.dll a.exe"))) - - -# When building a release version, the 'define' still applies. -t.write("jamfile.jam", """\ -exe a : a.cpp b : debug ; -lib b : b.cpp ; -""") -t.rm("bin") -t.run_build_system(["release", "define=MACROS"]) - - -# Regression test: direct build request was not working when there was more -# than one level of 'build-project'. -t.rm(".") -t.write("jamroot.jam", "") -t.write("jamfile.jam", "build-project a ;") -t.write("a/jamfile.jam", "build-project b ;") -t.write("a/b/jamfile.jam", "") -t.run_build_system(["release"]) - -t.cleanup() diff --git a/test/dll_path.py b/test/dll_path.py index e71b704b9..6282fae9f 100644 --- a/test/dll_path.py +++ b/test/dll_path.py @@ -143,4 +143,21 @@ es2 = t.adjust_name("b/bin/$toolset/debug*") t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1); t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2); +t.rm("bin/$toolset/debug*/mp.pathlist") + +# Now run the same checks with pre-built libraries +adll = t.glob_file("a/bin/$toolset/debug*/a.dll") +bdll = t.glob_file("b/bin/$toolset/debug*/b.dll") +t.write("b/jamfile.jam", """ +local bdll = %s ; +# Make sure that it is found even with multiple source-locations +project : source-location c $(bdll:D) ; +lib b : ../a//a : $(bdll:D=) ; +""" % bdll.replace("\\", "\\\\")) +t.run_build_system(["hardcode-dll-paths=true"]) +t.expect_addition("bin/$toolset/debug*/mp.pathlist") + +t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es1) +t.expect_content_lines("bin/$toolset/debug*/mp.pathlist", "*" + es2) + t.cleanup() diff --git a/test/example_make.py b/test/example_make.py index e65158ec2..d72423cb2 100644 --- a/test/example_make.py +++ b/test/example_make.py @@ -13,5 +13,5 @@ import sys t = BoostBuild.Tester(['example.python.interpreter=%s' % sys.executable]) t.set_tree("../example/make") t.run_build_system() -t.expect_addition(["bin/$toolset/debug*/main.cpp"]) +t.expect_addition(["bin/main.cpp"]) t.cleanup() diff --git a/test/expansion.py b/test/expansion.py index c5bd30379..ed8690312 100644 --- a/test/expansion.py +++ b/test/expansion.py @@ -16,7 +16,7 @@ int main() {} """) t.write("b.cpp", """ -#ifdef CF_1 +#if defined(CF_1) && !defined(CF_IS_OFF) int main() {} #endif """) @@ -27,6 +27,36 @@ int main() {} #endif """) +t.write("d.cpp", """ +#ifndef CF_IS_OFF +int main() {} +#endif +""") + +t.write("e.cpp", """ +#if !defined(CF_IS_OFF) && defined(CF_2) && !defined(CF_1) +int main() {} +#endif +""") + +t.write("f.cpp", """ +#if defined(CF_1) +int main() {} +#endif +""") + +t.write("g.cpp", """ +#if defined(FOPT_2) +int main() {} +#endif +""") + +t.write("h.cpp", """ +#if defined(CX_2) +int main() {} +#endif +""") + t.write("jamfile.jam", """ # See if default value of composite feature 'cf' will be expanded to # CF_IS_OFF. @@ -38,14 +68,45 @@ exe b : b.cpp : on-1 ; # See if conditional requirements are recursively expanded. exe c : c.cpp : $toolset:release release:FOO ; + +# Composites specified in the default build should not +# be expanded if they are overridden in the the requirements. +exe d : d.cpp : on : off ; + +# Overriding a feature should clear subfeatures and +# apply default values of subfeatures. +exe e : e.cpp : always : on-1 ; + +# Subfeatures should not be changed if the parent feature doesn't change +exe f : f.cpp : on : on-1 ; + +# If a subfeature is not specific to the value of the parent feature, +# then changing the parent value should not clear the subfeature. +exe g : g.cpp : off : on-2 ; + +# If the default value of a composite feature adds an optional +# feature which has a subfeature with a default, then that +# default should be added. +exe h : h.cpp ; """) t.write("jamroot.jam", """ import feature ; -feature.feature cf : off on : composite incidental ; +feature.feature cf : off on always : composite incidental ; feature.compose off : CF_IS_OFF ; feature.subfeature cf on : version : 1 2 : composite optional incidental ; feature.compose 1 : CF_1 ; +feature.subfeature cf always : version : 1 2 : composite incidental ; +feature.compose 1 : CF_2 ; +feature.feature fopt : on off : optional incidental ; +feature.subfeature fopt : version : 1 2 : composite incidental ; +feature.compose 2 : FOPT_2 ; + +feature.feature cx1 : on : composite incidental ; +feature.feature cx2 : on : optional incidental ; +feature.subfeature cx2 on : sub : 1 : composite incidental ; +feature.compose on : on ; +feature.compose 1 : CX_2 ; """) t.expand_toolset("jamfile.jam") @@ -53,7 +114,12 @@ t.expand_toolset("jamfile.jam") t.run_build_system() t.expect_addition(["bin/$toolset/debug*/a.exe", "bin/$toolset/debug*/b.exe", - "bin/$toolset/release*/c.exe"]) + "bin/$toolset/release*/c.exe", + "bin/$toolset/debug*/d.exe", + "bin/$toolset/debug*/e.exe", + "bin/$toolset/debug*/f.exe", + "bin/$toolset/debug*/g.exe", + "bin/$toolset/debug*/h.exe"]) t.rm("bin") diff --git a/test/feature_relevant.py b/test/feature_relevant.py new file mode 100644 index 000000000..4e7a0c451 --- /dev/null +++ b/test/feature_relevant.py @@ -0,0 +1,142 @@ +#!/usr/bin/python + +# Copyright 2018 Steven Watanabe +# 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) + +# Tests the feature + +import BoostBuild + +t = BoostBuild.Tester(use_test_config=False) + +t.write("xxx.jam", """ +import type ; +import feature : feature ; +import toolset : flags ; +import generators ; +type.register XXX : xxx ; +type.register YYY : yyy ; +feature xxxflags : : free ; +generators.register-standard xxx.run : YYY : XXX ; +# xxxflags is relevant because it is used by flags +flags xxx.run OPTIONS : ; +actions run +{ + echo okay > $(<) +} +""") + +t.write("zzz.jam", """ +import xxx ; +import type ; +import feature : feature ; +import generators ; +type.register ZZZ : zzz ; +feature zzz.enabled : off on : propagated ; +# zzz.enabled is relevant because it is used in the generator's +# requirements +generators.register-standard zzz.run : XXX : ZZZ : on ; +actions run +{ + echo okay > $(<) +} +""") + +t.write("aaa.jam", """ +import zzz ; +import type ; +import feature : feature ; +import generators ; +import toolset : flags ; +type.register AAA : aaa ; +feature aaaflags : : free ; +generators.register-standard aaa.run : ZZZ : AAA ; +flags aaa.run OPTIONS : ; +actions run +{ + echo okay > $(<) +} +""") + +t.write("Jamroot.jam", """ +import xxx ; +import zzz ; +import aaa ; +import feature : feature ; + +# f1 is relevant, because it is composite and is relevant +feature f1 : n y : composite propagated ; +feature.compose y : -no1 ; +# f2 is relevant, because it is used in a conditional +feature f2 : n y : propagated ; +# f3 is relevant, because it is used to choose the target alternative +feature f3 : n y : propagated ; +# f4 is relevant, because it is marked as such explicitly +feature f4 : n y : propagated ; +# f5 is relevant because of the conditional usage-requirements +feature f5 : n y : propagated ; +# f6 is relevant because the indirect conditional indicates so +feature f6 : n y : propagated ; +# f7 is relevant because the icond7 says so +feature f7 : n y : propagated ; + +# The same as f[n], except not propagated +feature g1 : n y : composite ; +feature.compose y : -no1 ; +feature g2 : n y ; +feature g3 : n y ; +feature g4 : n y ; +feature g5 : n y ; +feature g6 : n y ; +feature g7 : n y ; + +project : default-build + y y y y y y y + y y y y y y y on ; + +rule icond6 ( properties * ) +{ + local result ; + if y in $(properties) || y in $(properties) + { + result += -yes6 ; + } + return $(result) + xxxflags:f6 + xxxflags:g6 ; +} + +rule icond7 ( properties * ) +{ + local result ; + if y in $(properties) || y in $(properties) + { + result += -yes7 ; + } + return $(result) + aaaflags:f7 + aaaflags:g7 ; +} + +zzz out : in.yyy + : y:-no2 y:-no2 f4 g4 + @icond6 + : + : y:-yes5 y:-yes5 @icond7 + ; +alias out : : n ; +alias out : : n ; +# Features that are relevant for out are also relevant for check-propagate +aaa check-propagate : out ; +""") + +t.write("in.yyy", "") + +t.run_build_system() +t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/out.xxx") +t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f6-y/g1-y/g2-y/g3-y/g4-y/g6-y/zzz.enabled-on/out.zzz") +t.expect_addition("bin/f1-y/f2-y/f3-y/f4-y/f5-y/f6-y/f7-y/zzz.enabled-on/check-propagate.aaa") + +t.cleanup() diff --git a/test/free_features_request.py b/test/free_features_request.py deleted file mode 100644 index c6bb4e9d6..000000000 --- a/test/free_features_request.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python - -# Copyright (C) Vladimir Prus 2007. -# 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) - -# Tests that a free feature specified on the command line applies to all -# targets ever built. - -import BoostBuild - -t = BoostBuild.Tester(use_test_config=False) - -t.write("jamroot.jam", """\ -exe hello : hello.cpp foo ; -lib foo : foo.cpp ; -""") - -t.write("hello.cpp", """\ -extern void foo(); -#ifdef FOO -int main() { foo(); } -#endif -""") - -t.write("foo.cpp", """\ -#ifdef FOO -#ifdef _WIN32 -__declspec(dllexport) -#endif -void foo() {} -#endif -""") - -# If FOO is not defined when compiling the 'foo' target, we will get a link -# error at this point. -t.run_build_system(["hello", "define=FOO"]) - -t.expect_addition("bin/$toolset/debug*/hello.exe") - -t.cleanup() diff --git a/test/gcc_runtime.py b/test/gcc_runtime.py index 28b6410a5..76c95e1af 100644 --- a/test/gcc_runtime.py +++ b/test/gcc_runtime.py @@ -20,9 +20,8 @@ t.expect_output_lines("warning: On gcc, DLLs can not be built with " t.expect_nothing_more() t.run_build_system(["link=static", "runtime-link=static"]) -binFolder = "bin/$toolset/debug*/link-static/runtime-link-static" -t.expect_addition("%s/hello.obj" % binFolder) -t.expect_addition("%s/hello.lib" % binFolder) +t.expect_addition("bin/$toolset/debug*/link-static/hello.obj") +t.expect_addition("bin/$toolset/debug*/link-static/hello.lib") t.expect_nothing_more() t.cleanup() diff --git a/test/generator_selection.py b/test/generator_selection.py index 64f9cc7b3..1ecca7100 100755 --- a/test/generator_selection.py +++ b/test/generator_selection.py @@ -87,9 +87,9 @@ my-obj other-obj : source.extension ; t.run_build_system() t.expect_output_lines("Generating a CPP file...") - t.expect_addition("bin/$toolset/debug*/dummy.my_obj") - t.expect_addition("Other/bin/$toolset/debug*/other-obj.cpp") - t.expect_addition("Other/bin/$toolset/debug*/other-obj.my_obj") + t.expect_addition("bin/dummy.my_obj") + t.expect_addition("Other/bin/other-obj.cpp") + t.expect_addition("Other/bin/other-obj.my_obj") t.expect_nothing_more() t.cleanup() @@ -139,8 +139,8 @@ yyy other : source.xxx2 ; """) t.run_build_system() - t.expect_addition("bin/$toolset/debug*/dummy.yyy") - t.expect_addition("Other/bin/$toolset/debug*/other.yyy") + t.expect_addition("bin/dummy.yyy") + t.expect_addition("Other/bin/other.yyy") t.expect_nothing_more() t.cleanup() diff --git a/test/generators_test.py b/test/generators_test.py index d666d46c2..a29a3a538 100644 --- a/test/generators_test.py +++ b/test/generators_test.py @@ -12,7 +12,7 @@ import re def test_basic(): - t = BoostBuild.Tester(pass_d0=False) + t = BoostBuild.Tester() __write_appender(t, "appender.jam") t.write("a.cpp", "") t.write("b.cxx", "") @@ -215,17 +215,17 @@ nm-exe e : e.cpp ; """) t.run_build_system() - t.expect_addition("bin/$toolset/debug*/" * BoostBuild.List("a.my_exe " + t.expect_addition("bin/" * BoostBuild.List("a.my_exe " "a.my_obj b.my_obj c.tui_h c.cpp c.my_obj d_parser.whl d_lexer.dlp " "d_parser.cpp d_lexer.cpp d_lexer.my_obj d_parser.lr0 d_parser.h " "d_parser.my_obj d_parser_symbols.h x.c x.my_obj y.x1 y.x2 y.cpp " "y.my_obj e.marked_cpp e.positions e.target_cpp e.my_obj e.my_exe " "f.my_exe obj_1.my_obj obj_2.my_obj")) - t.expect_addition("lib/bin/$toolset/debug*/" * BoostBuild.List("c.my_obj " + t.expect_addition("lib/bin/" * BoostBuild.List("c.my_obj " "auxilliary.my_lib")) t.expect_nothing_more() - folder = "bin/$toolset/debug*" + folder = "bin" t.expect_content_lines("%s/obj_1.my_obj" % folder, " Sources: 'z.cpp'") t.expect_content_lines("%s/obj_2.my_obj" % folder, " Sources: 'z.cpp'") t.expect_content_lines("%s/a.my_obj" % folder, " Sources: 'a.cpp'") @@ -285,7 +285,7 @@ appender.register ccc-to-ddd composing : CCC : DDD ; ddd _xxx : _xxx._a ; """ - t = BoostBuild.Tester(pass_d0=False) + t = BoostBuild.Tester() __write_appender(t, "appender.jam") t.write("_xxx._a", "") @@ -311,7 +311,7 @@ ddd _xxx : _xxx._a ; def suffix(rename): if rename: return "_x" return "" - name = "bin/$toolset/debug*/_xxx" + name = "bin/_xxx" e = t.expect_addition e("%s%s._b1" % (name, suffix(rename1))) e("%s%s._b2" % (name, suffix(rename2))) diff --git a/test/inline.py b/test/inline.py index d0ce91ae8..03d91a5d0 100644 --- a/test/inline.py +++ b/test/inline.py @@ -21,12 +21,11 @@ int main() {} t.write("helper.cpp", "void helper() {}\n") t.run_build_system() -t.expect_addition("bin/$toolset/debug/link-static*/a__helper.lib") -t.rm("bin/$toolset/debug/link-static/a__helper.lib") -t.rm("bin/$toolset/debug/link-static/*/a__helper.lib") +t.expect_addition("bin/$toolset/debug*/a__helper.lib") +t.rm("bin/$toolset/debug*/a__helper.lib") t.run_build_system(["a__helper"]) -t.expect_addition("bin/$toolset/debug/link-static*/a__helper.lib") +t.expect_addition("bin/$toolset/debug*/a__helper.lib") t.rm("bin") @@ -41,8 +40,8 @@ exe a2 : a.cpp [ lib helper : helper.cpp ] ; t.run_build_system() t.expect_addition("bin/$toolset/debug/link-static*/a.exe") -t.expect_addition("bin/$toolset/debug/link-static*/a__helper.lib") -t.expect_addition("bin/$toolset/debug/link-static*/a2__helper.lib") +t.expect_addition("bin/$toolset/debug*/a__helper.lib") +t.expect_addition("bin/$toolset/debug*/a2__helper.lib") # Check that the 'alias' target does not change the name of inline targets, and @@ -58,6 +57,6 @@ t.run_build_system() t.expect_nothing_more() t.run_build_system(["a"]) -t.expect_addition("bin/$toolset/debug/link-static*/helper.lib") +t.expect_addition("bin/$toolset/debug*/helper.lib") t.cleanup() diff --git a/test/zlib.py b/test/lib_zlib.py similarity index 78% rename from test/zlib.py rename to test/lib_zlib.py index 0bb8269ad..a3dd3a318 100755 --- a/test/zlib.py +++ b/test/lib_zlib.py @@ -25,7 +25,8 @@ alias zlib : /zlib//zlib : : static shared ; MockToolset.set_expected(t, ''' source_file('deflate.c', 'deflate') action('-c -x c -I./zlib -o $deflate.o $deflate.c') -action('--dll $deflate.o -o $deflate.so') +action('-c -x c -I./zlib -DZLIB_DLL -o $deflate-shared.o $deflate.c') +action('--dll $deflate-shared.o -o $deflate.so') action('--archive $deflate.o -o $deflate.a') ''') @@ -116,4 +117,25 @@ t.run_build_system() t.expect_addition('bin/mock/debug/test.exe') t.expect_addition('bin/mock/debug/link-static/test.exe') +# Initialization in explicit location - both static and shared libraries +t.rm('bin') +t.write("Jamroot.jam", """ +path-constant here : . ; +using zlib : : myzlib $(here)/zlib $(here)/zlib ; +exe test : test.cpp /zlib//zlib + : shared:SHARED : static shared ; +""") + +MockToolset.set_expected(t, common_stuff + ''' +action('$main.o -L./zlib --static-lib=myzlib -o $config.exe') +action('$main.o -L./zlib --shared-lib=myzlib -o $config.exe') +action('-c -x c++ $test.cpp -I./zlib -o $test-static.o') +action('-c -x c++ $test.cpp -I./zlib -DSHARED -o $test-shared.o') +action('$test-static.o -L./zlib --static-lib=myzlib -o $test') +action('$test-shared.o -L./zlib --shared-lib=myzlib -o $test') +''') +t.run_build_system() +t.expect_addition('bin/mock/debug/test.exe') +t.expect_addition('bin/mock/debug/link-static/test.exe') + t.cleanup() diff --git a/test/libjpeg.py b/test/libjpeg.py index dcf81c9b0..d98c5da90 100755 --- a/test/libjpeg.py +++ b/test/libjpeg.py @@ -13,8 +13,8 @@ t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user MockToolset.create(t) # Build from source -t.write("libjpeg/jpeg.h", 'libjpeg') -t.write("libjpeg/jpeg.c", 'jpeg') +t.write("libjpeg/jpeglib.h", 'libjpeg') +t.write("libjpeg/jerror.c", 'jpeg') t.write("Jamroot.jam", """ path-constant here : . ; @@ -39,7 +39,7 @@ t.rm('libjpeg') common_stuff = ''' source_file('test.cpp', 'test.cpp') source_file('main.cpp', 'int main() {}') -source_file('jpeg.h.cpp', '#include ') +source_file('jpeg.h.cpp', '#include \\n#include \\n') action('-c -x c++ $main.cpp -o $main.o') ''' t.write('test.cpp', 'test.cpp') @@ -88,7 +88,7 @@ using libjpeg : : mylibjpeg $(here)/libjpeg $(here)/libjp exe test : test.cpp /libjpeg//libjpeg : : static shared ; """) -t.write('libjpeg/jpeg.h', 'libjpeg') +t.write('libjpeg/jpeglib.h', 'libjpeg') MockToolset.set_expected(t, common_stuff + ''' action('$main.o -L./libjpeg --static-lib=mylibjpeg -o $config.exe') diff --git a/test/link.py b/test/link.py index 4f83e1b09..e0524ef0e 100755 --- a/test/link.py +++ b/test/link.py @@ -279,8 +279,8 @@ def test_update_file_link(params1, params2): .has-files = [ glob include/file1.h ] ; rule can-link ( properties * ) { - if ( ! [ link.can-symlink $(.project) : [ property-set.empty ] ] ) && - ( ! [ link.can-hardlink $(.project) : [ property-set.empty ] ] ) + if ( ! [ link.can-symlink $(.project) ] ) && + ( ! [ link.can-hardlink $(.project) ] ) { ECHO links unsupported ; } diff --git a/test/make_rule.py b/test/make_rule.py index 4a2e09ad9..ab994376e 100644 --- a/test/make_rule.py +++ b/test/make_rule.py @@ -28,8 +28,8 @@ make foo.bar : : creator : 12345678 ; """) t.run_build_system() -t.expect_addition("bin/$toolset/debug*/foo.bar") -t.fail_test(string.find(t.read("bin/$toolset/debug*/foo.bar"), "12345678") == -1) +t.expect_addition("bin/foo.bar") +t.fail_test(string.find(t.read("bin/foo.bar"), "12345678") == -1) # Regression test. Make sure that if a main target is requested two times, and diff --git a/test/message.py b/test/message.py index 5b4f7da81..30edced4d 100755 --- a/test/message.py +++ b/test/message.py @@ -10,7 +10,7 @@ import BoostBuild # Create a temporary working directory. -t = BoostBuild.Tester(use_test_config=False) +t = BoostBuild.Tester(["-d0"], use_test_config=False) t.write("Jamroot.jam", """ project diff --git a/test/path_features.py b/test/path_features.py index f8a3f7caa..5b23150be 100644 --- a/test/path_features.py +++ b/test/path_features.py @@ -96,11 +96,20 @@ int main() { return OK; } def test_paths_set_by_indirect_conditionals(): - t = BoostBuild.Tester(pass_d0=False, use_test_config=False) + t = BoostBuild.Tester(use_test_config=False) header = "child_dir/folder_to_include/some_header.h" - t.write("jamroot.jam", "build-project child_dir ;") + t.write("jamroot.jam", """ +build-project child_dir ; +rule attach-include-parent ( properties * ) +{ + return another_folder ; +} +# requirements inherited from a parent project will bind paths +# relative to the project that actually names the rule. +project : requirements @attach-include-parent ; +""") t.write("child_dir/jamfile.jam", """\ import remote/remote ; @@ -121,9 +130,11 @@ rule attach-include-remote ( properties * ) """) t.write("child_dir/x.cpp", """\ #include +#include int main() {} """) t.write(header, "int some_func();\n") + t.write("another_folder/header2.h", "int f2();\n") t.write("child_dir/folder_to_include/jamfile.jam", "") expected_x1 = "child_dir/bin/$toolset/debug*/x1.obj" @@ -139,7 +150,7 @@ int main() {} t.expect_touch(expected_x2) t.touch(header) - t.run_build_system(["..", "-d2"], subdir="child_dir/folder_to_include") + t.run_build_system([".."], subdir="child_dir/folder_to_include") t.expect_touch(expected_x1) t.expect_touch(expected_x2) @@ -149,4 +160,4 @@ int main() {} test_basic() test_absolute_paths() test_ordered_paths() -test_paths_set_by_indirect_conditionals() \ No newline at end of file +test_paths_set_by_indirect_conditionals() diff --git a/test/project-test3/jamroot.jam b/test/project-test3/jamroot.jam index 3d4dfa19a..d7cd490eb 100644 --- a/test/project-test3/jamroot.jam +++ b/test/project-test3/jamroot.jam @@ -6,6 +6,7 @@ import os ; import gcc ; import property ; +import toolset ; rule properties-as-path ( properties * ) { @@ -21,6 +22,9 @@ rule properties-as-path ( properties * ) [ property.remove incidental : $(r) ] ] ; } +toolset.flags yfc-compile KNOWN-PROPERTIES : ; +toolset.flags yfc-link KNOWN-PROPERTIES : ; + rule yfc-compile ( target : sources * : property-set * ) { PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ; diff --git a/test/project-test4/jamroot.jam b/test/project-test4/jamroot.jam index fbbe0abf1..d8cf571ae 100644 --- a/test/project-test4/jamroot.jam +++ b/test/project-test4/jamroot.jam @@ -5,6 +5,7 @@ import os ; import gcc ; import property ; +import toolset ; rule properties-as-path ( properties * ) { @@ -21,6 +22,10 @@ rule properties-as-path ( properties * ) } +toolset.flags yfc-compile KNOWN-PROPERTIES : ; +toolset.flags yfc-link KNOWN-PROPERTIES : ; + + rule yfc-compile ( target : sources * : property-set * ) { PROPERTIES on $(target) = [ properties-as-path $(property-set) ] ; @@ -60,4 +65,4 @@ if [ os.name ] = VMS } } -IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ; +#IMPORT $(__name__) : yfc-compile yfc-link : : yfc-compile yfc-link ; diff --git a/test/project_id.py b/test/project_id.py index 7f4070e07..6477f5957 100755 --- a/test/project_id.py +++ b/test/project_id.py @@ -98,7 +98,7 @@ project /project-a3 ; """) t.run_build_system() - t.expect_addition("bin/$toolset/b%d._b" % x for x in range(1, 8)) + t.expect_addition("bin/b%d._b" % x for x in range(1, 8)) t.expect_nothing_more() t.cleanup() @@ -280,7 +280,7 @@ bbb b-invalid-target : /foo//invalid ; """) t.run_build_system(["b1", "b2"]) - t.expect_addition("bin/$toolset/debug*/b%d._b" % x for x in range(1, 3)) + t.expect_addition("bin/b%d._b" % x for x in range(1, 3)) t.expect_nothing_more() t.run_build_system(["b-invalid"], status=1) diff --git a/test/project_root_rule.py b/test/project_root_rule.py index 956c8953b..503b3cad2 100644 --- a/test/project_root_rule.py +++ b/test/project_root_rule.py @@ -29,6 +29,6 @@ my-lib foo ; t.run_build_system(subdir="sub") -t.expect_addition("sub/bin/$toolset/debug/link-static*/foo.lib") +t.expect_addition("sub/bin/$toolset/debug*/foo.lib") t.cleanup() diff --git a/test/project_test3.py b/test/project_test3.py index 82b238f68..ddd24e848 100644 --- a/test/project_test3.py +++ b/test/project_test3.py @@ -24,7 +24,7 @@ t.run_build_system() t.expect_addition("bin/$toolset/debug*/a.obj") t.expect_content("bin/$toolset/debug*/a.obj", """\ -$toolset/debug +$toolset/debug* a.cpp """) @@ -39,43 +39,43 @@ t.expect_content("bin/$toolset/debug*/a.exe", t.expect_addition("lib/bin/$toolset/debug*/b.obj") t.expect_content("lib/bin/$toolset/debug*/b.obj", """\ -$toolset/debug +$toolset/debug* lib/b.cpp """) t.expect_addition("lib/bin/$toolset/debug*/m.exe") t.expect_content("lib/bin/$toolset/debug*/m.exe", """\ -$toolset/debug +$toolset/debug* lib/bin/$toolset/debug*/b.obj lib2/bin/$toolset/debug*/c.obj """) t.expect_addition("lib2/bin/$toolset/debug*/c.obj") t.expect_content("lib2/bin/$toolset/debug*/c.obj", """\ -$toolset/debug +$toolset/debug* lib2/c.cpp """) t.expect_addition("lib2/bin/$toolset/debug*/d.obj") t.expect_content("lib2/bin/$toolset/debug*/d.obj", """\ -$toolset/debug +$toolset/debug* lib2/d.cpp """) t.expect_addition("lib2/bin/$toolset/debug*/l.exe") t.expect_content("lib2/bin/$toolset/debug*/l.exe", """\ -$toolset/debug +$toolset/debug* lib2/bin/$toolset/debug*/c.obj bin/$toolset/debug*/a.obj """) t.expect_addition("lib2/helper/bin/$toolset/debug*/e.obj") t.expect_content("lib2/helper/bin/$toolset/debug*/e.obj", """\ -$toolset/debug +$toolset/debug* lib2/helper/e.cpp """) t.expect_addition("lib3/bin/$toolset/debug*/f.obj") t.expect_content("lib3/bin/$toolset/debug*/f.obj", """\ -$toolset/debug +$toolset/debug* lib3/f.cpp lib2/helper/bin/$toolset/debug*/e.obj """) diff --git a/test/project_test4.py b/test/project_test4.py index ee12c4d91..30aabacce 100644 --- a/test/project_test4.py +++ b/test/project_test4.py @@ -16,13 +16,13 @@ t.run_build_system() t.expect_addition("bin/$toolset/debug*/a.obj") t.expect_content("bin/$toolset/debug*/a.obj", -"""$toolset/debug*/include-everything +"""$toolset/debug*/include-everything* a.cpp """) t.expect_addition("bin/$toolset/debug*/a.exe") t.expect_content("bin/$toolset/debug*/a.exe", -"$toolset/debug*/include-everything\n" + +"$toolset/debug*/include-everything*\n" + "bin/$toolset/debug*/a.obj lib/bin/$toolset/debug/optimization-speed*/b.obj\n" ) diff --git a/test/rebuilds.py b/test/rebuilds.py index 89ad4d84f..8242e3ec0 100644 --- a/test/rebuilds.py +++ b/test/rebuilds.py @@ -21,7 +21,7 @@ def wait_for_bar(t): t.wait_for_time_change("bar", touch=False) -t = BoostBuild.Tester(["-ffile.jam", "-d+3", "-d+12", "-d+13"], pass_d0=False, +t = BoostBuild.Tester(["-ffile.jam", "-d+3", "-d+12", "-d+13"], pass_toolset=0) t.write("file.jam", """\ diff --git a/test/regression.py b/test/regression.py deleted file mode 100644 index 071e7ca54..000000000 --- a/test/regression.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/python - -# Copyright (C) Vladimir Prus 2003. -# 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) - -# Test for the regression testing framework. - -import BoostBuild - -# Create a temporary working directory. -t = BoostBuild.Tester(use_test_config=False) - -t.write("c.cpp", "\n") -t.write("r.cpp", """\ -void helper(); - -#include -int main( int ac, char * av[] ) -{ - helper(); - for ( int i = 1; i < ac; ++i ) - std::cout << av[ i ] << '\\n'; -} -""") -t.write("c-f.cpp", "int\n") -t.write("r-f.cpp", "int main() { return 1; }\n") - -t.write("jamroot.jam", "") -t.write("jamfile.jam", """\ -import testing ; -compile c.cpp ; -compile-fail c-f.cpp ; -run r.cpp libs//helper : foo bar ; -run-fail r-f.cpp ; -""") - -t.write("libs/jamfile.jam", "lib helper : helper.cpp ;") -t.write("libs/helper.cpp", """\ -void -#if defined(_WIN32) -__declspec(dllexport) -#endif -helper() {} -""") - -# First test that when outcomes are expected, all .test files are created. -t.run_build_system(["hardcode-dll-paths=false"], stderr=None, status=None) -t.expect_addition("bin/c.test/$toolset/debug*/c.test") -t.expect_addition("bin/c-f.test/$toolset/debug*/c-f.test") -t.expect_addition("bin/r.test/$toolset/debug*/r.test") -t.expect_addition("bin/r-f.test/$toolset/debug*/r-f.test") - -# Make sure args are handled. -t.expect_content("bin/r.test/$toolset/debug*/r.output", - "foo\nbar\n*\nEXIT STATUS: 0*\n", True) - -# Test that input file is handled as well. -t.write("r.cpp", """\ -#include -#include -int main( int ac, char * av[] ) -{ - for ( int i = 1; i < ac; ++i ) - { - std::ifstream ifs( av[ i ] ); - std::cout << ifs.rdbuf(); - } -} -""") - -t.write("dir/input.txt", "test input") - -t.write("jamfile.jam", """\ -import testing ; -compile c.cpp ; -obj c-obj : c.cpp ; -compile-fail c-f.cpp ; -run r.cpp : : dir/input.txt ; -run-fail r-f.cpp ; -time execution : r ; -time compilation : c-obj ; -""") - -t.run_build_system(["hardcode-dll-paths=false"]) -t.expect_content("bin/r.test/$toolset/debug*/r.output", """\ -test input -EXIT STATUS: 0 -""") - -t.expect_addition('bin/$toolset/debug*/execution.time') -t.expect_addition('bin/$toolset/debug*/compilation.time') - -# Make sure test failures are detected. Reverse expectation and see if .test -# files are created or not. -t.write("jamfile.jam", """\ -import testing ; -compile-fail c.cpp ; -compile c-f.cpp ; -run-fail r.cpp : : dir/input.txt ; -run r-f.cpp ; -""") - -t.touch(BoostBuild.List("c.cpp c-f.cpp r.cpp r-f.cpp")) - -t.run_build_system(["hardcode-dll-paths=false"], stderr=None, status=1) -t.expect_removal("bin/c.test/$toolset/debug*/c.test") -t.expect_removal("bin/c-f.test/$toolset/debug*/c-f.test") -t.expect_removal("bin/r.test/$toolset/debug*/r.test") -t.expect_removal("bin/r-f.test/$toolset/debug*/r-f.test") - -t.cleanup() diff --git a/test/remove_requirement.py b/test/remove_requirement.py index 8009e0f64..6efd44116 100644 --- a/test/remove_requirement.py +++ b/test/remove_requirement.py @@ -12,6 +12,8 @@ t = BoostBuild.Tester(use_test_config=False) t.write("jamroot.jam", """ project : requirements multi debug:static ; +# Force link to be relevant +project : requirements shared:TEST_DLL ; build-project sub ; build-project sub2 ; diff --git a/test/rescan_header.py b/test/rescan_header.py index 37b37c7d4..1257a223c 100755 --- a/test/rescan_header.py +++ b/test/rescan_header.py @@ -35,7 +35,7 @@ obj test : test.cpp : header3.h ; """) t.run_build_system(["-j2"]) -t.expect_addition("bin/$toolset/debug*/header3.h") +t.expect_addition("bin/header3.h") t.expect_addition("bin/$toolset/debug*/test.obj") t.expect_nothing_more() @@ -72,9 +72,9 @@ obj test : test.cpp : """) t.run_build_system(["-j2", "test"]) -t.expect_addition("bin/$toolset/debug*/header1.h") -t.expect_addition("bin/$toolset/debug*/header2.h") -t.expect_addition("bin/$toolset/debug*/header3.h") +t.expect_addition("bin/header1.h") +t.expect_addition("bin/header2.h") +t.expect_addition("bin/header3.h") t.expect_addition("bin/$toolset/debug*/test.obj") t.expect_nothing_more() @@ -122,9 +122,9 @@ obj test : test.cpp : """) t.run_build_system(["-j2", "test"]) -t.expect_addition("bin/$toolset/debug*/header1.h") -t.expect_addition("bin/$toolset/debug*/header2.h") -t.expect_addition("bin/$toolset/debug*/header3.h") +t.expect_addition("bin/header1.h") +t.expect_addition("bin/header2.h") +t.expect_addition("bin/header3.h") t.expect_addition("bin/$toolset/debug*/test.obj") t.expect_nothing_more() @@ -184,7 +184,7 @@ exe test : test2.cpp test1.cpp : header3.h ; """) t.run_build_system(["-j2", "test"]) -t.expect_addition("bin/$toolset/debug*/header3.h") +t.expect_addition("bin/header3.h") t.expect_addition("bin/$toolset/debug*/test1.obj") t.expect_addition("bin/$toolset/debug*/test2.obj") t.expect_addition("bin/$toolset/debug*/test.exe") @@ -192,7 +192,7 @@ t.expect_nothing_more() t.touch("header3.in") t.run_build_system(["-j2", "test"]) -t.expect_touch("bin/$toolset/debug*/header3.h") +t.expect_touch("bin/header3.h") t.expect_touch("bin/$toolset/debug*/test1.obj") t.expect_touch("bin/$toolset/debug*/test2.obj") t.expect_touch("bin/$toolset/debug*/test.exe") @@ -256,7 +256,7 @@ exe test : test2.cpp test1.cpp : header2.h . ; """) t.run_build_system(["-j2", "test"]) -t.expect_addition("bin/$toolset/debug*/header2.h") +t.expect_addition("bin/header2.h") t.expect_addition("bin/$toolset/debug*/test1.obj") t.expect_addition("bin/$toolset/debug*/test2.obj") t.expect_addition("bin/$toolset/debug*/test.exe") diff --git a/test/source_order.py b/test/source_order.py index d3cc2ab20..f21710a8c 100755 --- a/test/source_order.py +++ b/test/source_order.py @@ -74,8 +74,8 @@ t.write("file2.c", "") t.write("file3.c", "") t.run_build_system() -t.expect_addition("bin/$toolset/debug*/check.order-test") -t.expect_content("bin/$toolset/debug*/check.order-test", """\ +t.expect_addition("bin/check.order-test") +t.expect_content("bin/check.order-test", """\ file2.c file1.c file3.c diff --git a/test/space_in_path.py b/test/space_in_path.py index 4cd320c5b..7f0c041a3 100755 --- a/test/space_in_path.py +++ b/test/space_in_path.py @@ -7,15 +7,45 @@ # Test that paths containing spaces are handled correctly by actions. import BoostBuild +import os t = BoostBuild.Tester(use_test_config=False) t.write("has space/jamroot.jam", """\ import testing ; unit-test test : test.cpp ; +actions write-file +{ + @(STDOUT:E=okay) >"$(<)" +} +make test.txt : : @write-file ; """) t.write("has space/test.cpp", "int main() {}\n") -t.run_build_system(["has space"]) +tmpdir = t.workpath("has space") +try: + oldtmp = os.environ["TMP"] +except: + oldtmp = None +try: + oldtmpdir = os.environ["TMPDIR"] +except: + oldtmpdir = None +os.environ["TMP"] = tmpdir; # Windows +os.environ["TMPDIR"] = tmpdir; # *nix + +try: + t.run_build_system(["has space"]) + t.expect_addition("has space/bin/test.txt") + t.expect_addition("has space/bin/$toolset/debug*/test.passed") +finally: + if oldtmp is not None: + os.environ["TMP"] = oldtmp + else: + del os.environ["TMP"] + if oldtmpdir is not None: + os.environ["TMPDIR"] = oldtmpdir + else: + del os.environ["TMPDIR"] t.cleanup() diff --git a/test/static_and_shared_library.py b/test/static_and_shared_library.py index da010a241..5b3a6b73a 100755 --- a/test/static_and_shared_library.py +++ b/test/static_and_shared_library.py @@ -31,7 +31,7 @@ t.expect_nothing_more() reset() t.run_build_system(["link=static"], subdir="lib") -t.expect_addition("lib/bin/$toolset/debug/link-static*/" * BoostBuild.List( +t.expect_addition("lib/bin/$toolset/debug*/" * BoostBuild.List( "c.obj auxilliary1.lib auxilliary2.lib")) t.expect_nothing_more() diff --git a/test/suffix.py b/test/suffix.py index b6946a48a..b31dd1730 100644 --- a/test/suffix.py +++ b/test/suffix.py @@ -73,6 +73,6 @@ second a : a.cpp ; """) t.run_build_system() -t.expect_addition("bin/$toolset/debug*/a") +t.expect_addition("bin/a") t.cleanup() diff --git a/test/tag.py b/test/tag.py index 87f537c8c..adf2fce6f 100644 --- a/test/tag.py +++ b/test/tag.py @@ -91,11 +91,11 @@ __declspec (dllexport) void x () {} BoostBuild.List("bin/$toolset/release*/a_rs.exe") + BoostBuild.List("bin/$toolset/release*/b_rs.dll") + BoostBuild.List("c/a_rs.exe") + - BoostBuild.List("bin/$toolset/debug/link-static*/a_dt.exe") + - BoostBuild.List("bin/$toolset/debug/link-static*/b_dt.lib") + + BoostBuild.List("bin/$toolset/debug*/a_dt.exe") + + BoostBuild.List("bin/$toolset/debug*/b_dt.lib") + BoostBuild.List("c/a_dt.exe") + - BoostBuild.List("bin/$toolset/release/link-static*/a_rt.exe") + - BoostBuild.List("bin/$toolset/release/link-static*/b_rt.lib") + + BoostBuild.List("bin/$toolset/release*/a_rt.exe") + + BoostBuild.List("bin/$toolset/release*/b_rt.lib") + BoostBuild.List("c/a_rt.exe")) variants = ["debug", "release", "link=static,shared"] diff --git a/test/test_all.py b/test/test_all.py index fe97acd1a..cfb4c30c9 100644 --- a/test/test_all.py +++ b/test/test_all.py @@ -96,6 +96,7 @@ def run_tests(critical_tests, other_tests): print("PASSED") else: print("FAILED") + BoostBuild.flush_annotations() else: rs = "succeed" if not passed: @@ -175,18 +176,21 @@ tests = ["absolute_sources", "c_file", "chain", "clean", + "command_line_properties", "composite", "conditionals", "conditionals2", "conditionals3", "conditionals_multiple", "configuration", + "configure", "copy_time", "core_action_output", "core_action_status", "core_actions_quietly", "core_at_file", "core_bindrule", + "core_fail_expected", "core_jamshell", "core_multifile_actions", "core_nt_cmd_line", @@ -208,7 +212,6 @@ tests = ["absolute_sources", # "default_toolset", "dependency_property", "dependency_test", - "direct_request_test", "disambiguation", "dll_path", "double_loading", @@ -219,7 +222,7 @@ tests = ["absolute_sources", "expansion", "explicit", "feature_cxxflags", - "free_features_request", + "feature_relevant", "generator_selection", "generators_test", "implicit_dependency", @@ -227,7 +230,9 @@ tests = ["absolute_sources", "inherit_toolset", "inherited_dependency", "inline", + "libjpeg", "lib_source_property", + "lib_zlib", "library_chain", "library_property", "link", @@ -252,7 +257,6 @@ tests = ["absolute_sources", "project_test4", "property_expansion", "rebuilds", - "regression", "relative_sources", "remove_requirement", "rescan_header", @@ -269,18 +273,22 @@ tests = ["absolute_sources", "static_and_shared_library", "suffix", "tag", - "test_result_dumping", "test_rc", - "testing_support", + "testing", "timedata", + "toolset_clang_darwin", + "toolset_clang_linux", + "toolset_darwin", + "toolset_gcc", + "toolset_intel_darwin", "toolset_requirements", + "types", "unit_test", "unused", "use_requirements", "using", "wrapper", "wrong_project", - "zlib" ] if os.name == "posix": diff --git a/test/test_rc.py b/test/test_rc.py index 1ffac15b0..89976fe9a 100755 --- a/test/test_rc.py +++ b/test/test_rc.py @@ -34,7 +34,7 @@ def included_resource_newer_than_rc_script(): # affected by any local (mis)configuration.. t = BoostBuild.Tester(["-d4", "--debug-configuration", "--ignore-site-config", "--user-config=", "toolset=%s" % toolsetName], - pass_d0=False, pass_toolset=False, use_test_config=False, + pass_toolset=False, use_test_config=False, translate_suffixes=False) # Prepare a dummy toolset so we do not get errors in case the default one diff --git a/test/test_result_dumping.py b/test/test_result_dumping.py deleted file mode 100755 index bdfc41ee2..000000000 --- a/test/test_result_dumping.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/python - -# Copyright 2008 Jurko Gospodnetic -# 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) - -# Tests dumping Boost Build based testing results. - -import BoostBuild - - -############################################################################### -# -# Test that dumping Boost Build based testing results works in case test code -# is not located in a folder under the Jamroot folder. -# -############################################################################### - -t = BoostBuild.Tester(["--dump-tests"], use_test_config=False) - -t.write("TestBuild/jamroot.jam", """\ -import testing ; -test-suite testit : [ run ../TestSource/test.cpp ] ; -""") - -t.write("TestSource/test.cpp", "int main() {}\n") - -t.run_build_system(subdir="TestBuild") -t.expect_output_lines('boost-test(RUN) "*/TestBuild/test" : ' - '"../TestSource/test.cpp"') - -t.cleanup() diff --git a/test/testing.py b/test/testing.py new file mode 100755 index 000000000..c1c3d8d5f --- /dev/null +++ b/test/testing.py @@ -0,0 +1,533 @@ +#!/usr/bin/python + +# Copyright 2008 Jurko Gospodnetic +# Copyright 2017 Steven Watanabe +# 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) + +# Tests different aspects of Boost Builds automated testing support. + +import BoostBuild +import TestCmd + +def test_run(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +run pass.cpp ; +run fail-compile.cpp ; +run fail-link.cpp ; +run fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.output") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.run") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output") + + t.expect_nothing_more() + + t.cleanup() + +def test_run_fail(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +run-fail pass.cpp ; +run-fail fail-compile.cpp ; +run-fail fail-link.cpp ; +run-fail fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.output") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.run") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test") + + t.expect_nothing_more() + + t.cleanup() + +def test_run_change(): + """Tests that the test file is removed when a test fails after it + previously passed.""" + + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() { return 1; }\n") + t.write("fail-compile.cpp", "int main() {}\n") + t.write("fail-link.cpp", "int main() {}\n") + t.write("fail-run.cpp", "int main() {}\n") + + t.write("Jamroot.jam", """import testing ; +run-fail pass.cpp ; +run fail-compile.cpp ; +run fail-link.cpp ; +run fail-run.cpp ; +""") + t.run_build_system() + # Sanity check + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test") + + # Now make them fail + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + t.run_build_system(status=1) + + t.expect_removal("bin/pass.test/$toolset/debug*/pass.test") + t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + t.expect_removal("bin/fail-link.test/$toolset/debug*/fail-link.test") + t.expect_removal("bin/fail-run.test/$toolset/debug*/fail-run.test") + + t.cleanup() + +def test_run_path(): + """Tests that run can find shared libraries even without + hardcode-dll-paths. Important: The library is in neither the + current working directory, nor any system path, nor the same + directory as the executable, so it should never be found without + help from Boost.Build.""" + t = BoostBuild.Tester(["hardcode-dll-paths=false"], use_test_config=False) + + t.write("l.cpp", """ +void +#if defined(_WIN32) +__declspec(dllexport) +#endif +f() {} +""") + t.write("pass.cpp", "void f(); int main() { f(); }\n") + + t.write("Jamroot.jam", """import testing ; +lib l : l.cpp : shared ; +run pass.cpp l ; +""") + + t.run_build_system() + t.expect_addition("bin/$toolset/debug*/l.obj") + t.expect_addition("bin/$toolset/debug*/l.dll") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.output") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.run") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + t.expect_nothing_more() + + t.cleanup() + +def test_run_args(): + """Tests the handling of args and input-files""" + t = BoostBuild.Tester(use_test_config=False) + t.write("test.cpp", """ +#include +#include +int main(int argc, const char ** argv) +{ + for(int i = 1; i < argc; ++i) + { + if(argv[i][0] == '-') + { + std::cout << argv[i] << std::endl; + } + else + { + std::ifstream ifs(argv[i]); + std::cout << ifs.rdbuf(); + } + } +} +""") + t.write("input1.in", "first input\n") + t.write("input2.in", "second input\n") + t.write("Jamroot.jam", """import testing ; +import common ; +# FIXME: The order actually depends on the lexigraphical +# ordering of the virtual target objects, which is just +# crazy. Switch the order of input1.txt and input2.txt +# to make this fail. Joining the arguments with && might +# work, but might get a bit complicated to implement as +# dependency properties do not currently support &&. +make input1.txt : input1.in : @common.copy ; +make input2.txt : input2.in : @common.copy ; +run test.cpp : -y -a : input1.txt input2.txt ; +""") + t.run_build_system() + t.expect_content("bin/test.test/$toolset/debug*/test.output", """\ +-y +-a +first input +second input + +EXIT STATUS: 0 +""") + t.cleanup() + +def test_link(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +link pass.cpp ; +link fail-compile.cpp ; +link fail-link.cpp ; +link fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.exe") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.exe") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test") + + t.expect_nothing_more() + + t.cleanup() + +def test_link_fail(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +link-fail pass.cpp ; +link-fail fail-compile.cpp ; +link-fail fail-link.cpp ; +link-fail fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj") + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.exe") + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj") + + t.expect_nothing_more() + + t.cleanup() + +def test_link_change(): + """Tests that the test file is removed when a test fails after it + previously passed.""" + + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-compile.cpp", "int main() {}\n") + t.write("fail-link.cpp", "int main() {}\n") + + t.write("Jamroot.jam", """import testing ; +link-fail pass.cpp ; +link fail-compile.cpp ; +link fail-link.cpp ; +""") + t.run_build_system() + # Sanity check + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test") + + # Now make them fail + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.run_build_system(status=1) + + t.expect_removal("bin/pass.test/$toolset/debug*/pass.test") + t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + t.expect_removal("bin/fail-link.test/$toolset/debug*/fail-link.test") + + t.cleanup() + +def test_compile(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +compile pass.cpp ; +compile fail-compile.cpp ; +compile fail-link.cpp ; +compile fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/pass.test/$toolset/debug*/pass.obj") + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.obj") + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.obj") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test") + + t.expect_nothing_more() + + t.cleanup() + +def test_compile_fail(): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +compile-fail pass.cpp ; +compile-fail fail-compile.cpp ; +compile-fail fail-link.cpp ; +compile-fail fail-run.cpp ; +""") + + t.run_build_system(status=1) + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.obj") + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + + t.expect_nothing_more() + + t.cleanup() + +def test_compile_change(): + """Tests that the test file is removed when a test fails after it + previously passed.""" + + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass.cpp", "#error expected to fail\n") + t.write("fail-compile.cpp", "int main() {}\n") + + t.write("Jamroot.jam", """import testing ; +compile-fail pass.cpp ; +compile fail-compile.cpp ; +""") + t.run_build_system() + # Sanity check + t.expect_addition("bin/pass.test/$toolset/debug*/pass.test") + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + + # Now make them fail + t.write("pass.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.run_build_system(status=1) + + t.expect_removal("bin/pass.test/$toolset/debug*/pass.test") + t.expect_removal("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + + t.cleanup() + +def test_remove_test_targets(option): + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass-compile.cpp", "int main() {}\n") + t.write("pass-link.cpp", "int main() {}\n") + t.write("pass-run.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + t.write("source.cpp", "int f();\n") + + t.write("Jamroot.jam", """import testing ; +obj source.o : source.cpp ; +compile pass-compile.cpp ; +link pass-link.cpp source.o ; +run pass-run.cpp source.o ; +compile-fail fail-compile.cpp ; +link-fail fail-link.cpp ; +run-fail fail-run.cpp ; +""") + + t.run_build_system([option]) + + t.expect_addition("bin/$toolset/debug*/source.obj") + + t.expect_addition("bin/pass-compile.test/$toolset/debug*/pass-compile.test") + + t.expect_addition("bin/pass-link.test/$toolset/debug*/pass-link.test") + + t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.output") + t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.run") + t.expect_addition("bin/pass-run.test/$toolset/debug*/pass-run.test") + + t.expect_addition("bin/fail-compile.test/$toolset/debug*/fail-compile.test") + + t.expect_addition("bin/fail-link.test/$toolset/debug*/fail-link.test") + + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.output") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.run") + t.expect_addition("bin/fail-run.test/$toolset/debug*/fail-run.test") + + t.expect_nothing_more() + + t.cleanup() + +def test_dump_tests(): + """Tests the output of the --dump-tests option""" + t = BoostBuild.Tester(use_test_config=False) + + t.write("pass-compile.cpp", "int main() {}\n") + t.write("pass-link.cpp", "int main() {}\n") + t.write("pass-run.cpp", "int main() {}\n") + t.write("fail-compile.cpp", "#error expected to fail\n") + t.write("fail-link.cpp", "int f();\nint main() { return f(); }\n") + t.write("fail-run.cpp", "int main() { return 1; }\n") + + t.write("Jamroot.jam", """import testing ; +run pass-run.cpp ; +run-fail fail-run.cpp ; +link pass-link.cpp ; +link-fail fail-link.cpp ; +compile pass-compile.cpp ; +compile-fail fail-compile.cpp ; +build-project libs/any/test ; +build-project libs/any/example ; +build-project libs/any ; +build-project tools/bcp/test ; +build-project tools/bcp/example ; +build-project subdir/test ; +build-project status ; +build-project outside/project ; +""") + def write_subdir(dir): + t.write(dir + "/test.cpp", "int main() {}\n") + t.write(dir + "/Jamfile", "run test.cpp ;") + write_subdir("libs/any/test") + write_subdir("libs/any/example") + write_subdir("libs/any") + write_subdir("tools/bcp/test") + write_subdir("tools/bcp/example") + write_subdir("status") + write_subdir("subdir/test") + t.write("outside/other/test.cpp", "int main() {}\n") + t.write("outside/project/Jamroot", "run ../other/test.cpp ;") + t.run_build_system(["--dump-tests", "-n", "-d0"], + match=TestCmd.match_re, stdout= +"""boost-test\(RUN\) ".*/pass-run" : "pass-run\.cpp" +boost-test\(RUN_FAIL\) ".*/fail-run" : "fail-run\.cpp" +boost-test\(LINK\) ".*/pass-link" : "pass-link\.cpp" +boost-test\(LINK_FAIL\) ".*/fail-link" : "fail-link\.cpp" +boost-test\(COMPILE\) ".*/pass-compile" : "pass-compile\.cpp" +boost-test\(COMPILE_FAIL\) ".*/fail-compile" : "fail-compile\.cpp" +boost-test\(RUN\) "any/test" : "libs/any/test\.cpp" +boost-test\(RUN\) "any/test" : "libs/any/test/test\.cpp" +boost-test\(RUN\) "any/test" : "libs/any/example/test\.cpp" +boost-test\(RUN\) "bcp/test" : "tools/bcp/test/test\.cpp" +boost-test\(RUN\) "bcp/test" : "tools/bcp/example/test\.cpp" +boost-test\(RUN\) ".*/subdir/test/test" : "subdir/test/test\.cpp" +boost-test\(RUN\) "test" : "status/test\.cpp" +boost-test\(RUN\) ".*/outside/project/test" : "../other/test.cpp" +""") + t.cleanup() + +################################################################################ +# +# test_files_with_spaces_in_their_name() +# -------------------------------------- +# +################################################################################ + +def test_files_with_spaces_in_their_name(): + """Regression test making sure test result files get created correctly when + testing files with spaces in their name. + """ + + t = BoostBuild.Tester(use_test_config=False) + + t.write("valid source.cpp", "int main() {}\n"); + + t.write("invalid source.cpp", "this is not valid source code"); + + t.write("jamroot.jam", """ +import testing ; +testing.compile "valid source.cpp" ; +testing.compile-fail "invalid source.cpp" ; +""") + + t.run_build_system(status=0) + t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.obj") + t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.test") + t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.obj") + t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.test") + + t.expect_content("bin/valid source.test/$toolset/debug*/valid source.test", \ + "passed" ) + t.expect_content( \ + "bin/invalid source.test/$toolset/debug*/invalid source.test", \ + "passed" ) + t.expect_content( \ + "bin/invalid source.test/$toolset/debug*/invalid source.obj", \ + "failed as expected" ) + + t.cleanup() + + +################################################################################ +# +# main() +# ------ +# +################################################################################ + +test_run() +test_run_fail() +test_run_change() +test_run_path() +test_run_args() +test_link() +test_link_fail() +test_link_change() +test_compile() +test_compile_fail() +test_compile_change() +test_remove_test_targets("--remove-test-targets") +test_remove_test_targets("preserve-test-targets=off") +test_dump_tests() +test_files_with_spaces_in_their_name() diff --git a/test/testing_support.py b/test/testing_support.py deleted file mode 100755 index c76a1f2a7..000000000 --- a/test/testing_support.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python - -# Copyright 2008 Jurko Gospodnetic -# 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) - -# Tests different aspects of Boost Builds automated testing support. - -import BoostBuild - - -################################################################################ -# -# test_files_with_spaces_in_their_name() -# -------------------------------------- -# -################################################################################ - -def test_files_with_spaces_in_their_name(): - """Regression test making sure test result files get created correctly when - testing files with spaces in their name. - """ - - t = BoostBuild.Tester(use_test_config=False) - - t.write("valid source.cpp", "int main() {}\n"); - - t.write("invalid source.cpp", "this is not valid source code"); - - t.write("jamroot.jam", """ -import testing ; -testing.compile "valid source.cpp" ; -testing.compile-fail "invalid source.cpp" ; -""") - - t.run_build_system(status=0) - t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.obj") - t.expect_addition("bin/invalid source.test/$toolset/debug*/invalid source.test") - t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.obj") - t.expect_addition("bin/valid source.test/$toolset/debug*/valid source.test") - - t.expect_content("bin/valid source.test/$toolset/debug*/valid source.test", \ - "passed" ) - t.expect_content( \ - "bin/invalid source.test/$toolset/debug*/invalid source.test", \ - "passed" ) - t.expect_content( \ - "bin/invalid source.test/$toolset/debug*/invalid source.obj", \ - "failed as expected" ) - - t.cleanup() - - -################################################################################ -# -# main() -# ------ -# -################################################################################ - -test_files_with_spaces_in_their_name() diff --git a/test/toolset-mock/Jamroot.jam b/test/toolset-mock/Jamroot.jam new file mode 100644 index 000000000..fd5f7907b --- /dev/null +++ b/test/toolset-mock/Jamroot.jam @@ -0,0 +1,8 @@ +# Copyright 2017 Steven Watanabe +# +# 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) + +lib l1 : lib.cpp ; +exe test : main.cpp l1 ; diff --git a/test/toolset-mock/lib.cpp b/test/toolset-mock/lib.cpp new file mode 100644 index 000000000..1ba30e32d --- /dev/null +++ b/test/toolset-mock/lib.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2017 Steven Watanabe +// +// 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) + +int f() {} diff --git a/test/toolset-mock/main.cpp b/test/toolset-mock/main.cpp new file mode 100644 index 000000000..0fc8b9a75 --- /dev/null +++ b/test/toolset-mock/main.cpp @@ -0,0 +1,7 @@ +// Copyright (c) 2017 Steven Watanabe +// +// 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) + +int main() {} diff --git a/test/toolset-mock/project-config.jam b/test/toolset-mock/project-config.jam new file mode 100644 index 000000000..10291b71c --- /dev/null +++ b/test/toolset-mock/project-config.jam @@ -0,0 +1,35 @@ +# Copyright 2017 Steven Watanabe +# +# 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 modules ; + +path-constant here : . ; + +using gcc : 4.8.3 : python $(here)/src/gcc-4.8.3-linux.py : : linux ; +using gcc : 4.2.1 : python $(here)/src/gcc-4.2.1-darwin.py : : darwin ; + +# hard-code this to make the test work on other platforms +modules.poke darwin : .host-osx-version : 10.11.0 ; +using darwin : 4.2.1 : python $(here)/src/darwin-4.2.1.py + : $(here)/src/bin/libtool + $(here)/src/bin/strip + : darwin + ; + +using clang-darwin : 3.9.0 : python $(here)/src/clang-3.9.0-darwin.py + : $(here)/src/bin/ar + $(here)/src/bin/ranlib + ; + +using clang-linux : 3.9.0 : python $(here)/src/clang-linux-3.9.0.py + : $(here)/src/bin/ar + $(here)/src/bin/ranlib + ; + +using intel-darwin : 10.2 : python $(here)/src/intel-darwin-10.2.py + : $(here)/src/bin/ar + $(here)/src/bin/ranlib + ; diff --git a/test/toolset-mock/src/Jamroot.jam b/test/toolset-mock/src/Jamroot.jam new file mode 100644 index 000000000..901afdbf4 --- /dev/null +++ b/test/toolset-mock/src/Jamroot.jam @@ -0,0 +1,57 @@ +# Copyright 2017 Steven Watanabe +# +# 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 os ; +import print ; +import regex ; +import feature ; + +.PYTHON = [ os.environ PYTHON_CMD ] ; +path-constant .AR : ar.py ; +path-constant .RANLIB : ranlib.py ; +path-constant .LIBTOOL : libtool.py ; +path-constant .STRIP : strip.py ; + +rule c-escape ( str ) +{ + return [ regex.replace $(str) \\\\ \\\\ ] ; +} + +rule cfg-header ( target : : properties * ) +{ + local PYTHON = [ c-escape $(.PYTHON) ] ; + local AR = [ c-escape $(.AR) ] ; + local RANLIB = [ c-escape $(.RANLIB) ] ; + local LIBTOOL = [ c-escape $(.LIBTOOL) ] ; + local STRIP = [ c-escape $(.STRIP) ] ; + print.output $(target) ; + print.text "#define PYTHON_CMD "\"$(PYTHON)\" : true ; + print.text "#define AR_CMD "\"$(AR)\" : true ; + print.text "#define RANLIB_CMD "\"$(RANLIB)\" : true ; + print.text "#define LIBTOOL_CMD "\"$(LIBTOOL)\" : true ; + print.text "#define STRIP_CMD "\"$(STRIP)\" : true ; +} + +# We can only build one variant at a time and we need to have a fixed path +project : requirements bin ; + +make config.h : : @cfg-header ; + +project : requirements config.h ; + +rule write-target-os ( target : : properties * ) +{ + local target-os = [ feature.defaults ] ; + print.output $(target) ; + print.text $(target-os:G=) : true ; +} + +make target-os.txt : : @write-target-os ; + +exe ar : [ obj ar.obj : mock-program.cpp : PY_SCRIPT=AR_CMD ] ; +exe ranlib : [ obj ranlib.obj : mock-program.cpp : PY_SCRIPT=RANLIB_CMD ] ; +exe libtool : [ obj libtool.obj : mock-program.cpp : PY_SCRIPT=LIBTOOL_CMD ] ; +exe strip : [ obj strip.obj : mock-program.cpp : PY_SCRIPT=STRIP_CMD ] ; diff --git a/test/toolset-mock/src/MockProgram.py b/test/toolset-mock/src/MockProgram.py new file mode 100644 index 000000000..ce4723e1e --- /dev/null +++ b/test/toolset-mock/src/MockProgram.py @@ -0,0 +1,260 @@ +# Copyright 2017 Steven Watanabe +# +# 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 sys +import os +import re + +# Represents a sequence of arguments that must appear +# in a fixed order. +class ordered: + def __init__(self, *args): + self.args = args + def match(self, command_line, pos, outputs): + for p in self.args: + res = try_match(command_line, pos, p, outputs) + if res is None: + return + pos = res + return pos + +# Represents a sequence of arguments that can appear +# in any order. +class unordered: + def __init__(self, *args): + self.args = list(args) + def match(self, command_line, pos, outputs): + unmatched = self.args[:] + while len(unmatched) > 0: + res = try_match_one(command_line, pos, unmatched, outputs) + if res is None: + return + pos = res + return pos + +# Represents a single input file. +# If id is set, then the file must have been created +# by a prior use of output_file. +# If source is set, then the file must be that source file. +class input_file: + def __init__(self, id=None, source=None): + assert((id is None) ^ (source is None)) + self.id = id + self.source = source + def check(self, path): + if path.startswith("-"): + return + if self.id is not None: + try: + with open(path, "r") as f: + data = f.read() + if data == make_file_contents(self.id): + return True + else: + return + except: + return + elif self.source is not None: + if self.source == path: + return True + else: + return + assert(False) + def match(self, command_line, pos, outputs): + if self.check(command_line[pos]): + return pos + 1 + +# Matches an output file. +# If the full pattern is matched, The +# file will be created. +class output_file: + def __init__(self, id): + self.id = id + def match(self, command_line, pos, outputs): + if command_line[pos].startswith("-"): + return + outputs.append((command_line[pos], self.id)) + return pos + 1 + +# Matches the directory containing an input_file +class target_path(object): + def __init__(self, id): + self.tester = input_file(id=id) + def match(self, command_line, pos, outputs): + arg = command_line[pos] + if arg.startswith("-"): + return + try: + for path in os.listdir(arg): + if self.tester.check(os.path.join(arg, path)): + return pos + 1 + except: + return + +# Matches a single argument, which is composed of a prefix and a path +# for example arguments of the form -ofilename. +class arg(object): + def __init__(self, prefix, a): + # The prefix should be a string, a should be target_path or input_file. + self.prefix = prefix + self.a = a + def match(self, command_line, pos, outputs): + s = command_line[pos] + if s.startswith(self.prefix) and try_match([s[len(self.prefix):]], 0, self.a, outputs) == 1: + return pos + 1 + +# Given a file id, returns a string that will be +# written to the file to allow it to be recognized. +def make_file_contents(id): + return id + +# Matches a single pattern from a list. +# If it succeeds, the matching pattern +# is removed from the list. +# Returns the index after the end of the match +def try_match_one(command_line, pos, patterns, outputs): + for p in patterns: + tmp = outputs[:] + res = try_match(command_line, pos, p, tmp) + if res is not None: + outputs[:] = tmp + patterns.remove(p) + return res + +# returns the end of the match if any +def try_match(command_line, pos, pattern, outputs): + if pos == len(command_line): + return + elif type(pattern) is str: + if pattern == command_line[pos]: + return pos + 1 + else: + return pattern.match(command_line, pos, outputs) + +known_patterns = [] +program_name = None + +# Registers a command +# The arguments should be a sequence of: +# str, ordered, unordered, arg, input_file, output_file, target_path +# kwarg: stdout is text that will be printed on success. +def command(*args, **kwargs): + global known_patterns + global program_name + stdout = kwargs.get("stdout", None) + pattern = ordered(*args) + known_patterns += [(pattern, stdout)] + if program_name is None: + program_name = args[0] + else: + assert(program_name == args[0]) + +# Use this to filter the recognized commands, based on the properties +# passed to b2. +def allow_properties(*args): + try: + return all(a in os.environ["B2_PROPERTIES"].split(" ") for a in args) + except KeyError: + return True + +# Use this in the stdout argument of command to print the command +# for running another script. +def script(name): + return os.path.join(os.path.dirname(__file__), "bin", re.sub('\.py$', '', name)) + +def match(command_line): + for (p, stdout) in known_patterns: + outputs = [] + if try_match(command_line, 0, p, outputs) == len(command_line): + return (stdout, outputs) + +# Every mock program should call this after setting up all the commands. +def main(): + command_line = [program_name] + sys.argv[1:] + result = match(command_line) + if result is not None: + (stdout, outputs) = result + if stdout is not None: + print stdout + for (file,id) in outputs: + with open(file, "w") as f: + f.write(make_file_contents(id)) + exit(0) + else: + print command_line + exit(1) + +# file should be the name of a file in the same directory +# as this. Must be called after verify_setup +def verify_file(filename): + global known_files + if filename not in known_files: + known_files.add(filename) + srcdir = os.path.dirname(__file__) + execfile(os.path.join(srcdir, filename), {}) + +def verify_setup(): + """Override the behavior of most module components + in order to detect whether they are being used correctly.""" + global main + global allow_properties + global output_file + global input_file + global target_path + global script + global command + global verify_errors + global output_ids + global input_ids + global known_files + def allow_properties(*args): + return True + def main(): + pass + def output_file(id): + global output_ids + global verify_error + if id in output_ids: + verify_error("duplicate output_file: %s" % id) + output_ids.add(id) + def input_file(id=None, source=None): + if id is not None: + input_ids.add(id) + def target_path(id): + input_ids.add(id) + def script(filename): + verify_file(filename) + def command(*args, **kwargs): + pass + verify_errors = [] + output_ids = set() + input_ids = set() + known_files = set() + +def verify_error(message): + global verify_errors + verify_errors += [message] + +def verify_finalize(): + for id in input_ids: + if not id in output_ids: + verify_error("Input file does not exist: %s" % id) + for error in verify_errors: + print "error: %s" % error + if len(verify_errors) != 0: + return 1 + else: + return 0 + +def verify(): + srcdir = os.path.dirname(__file__) + if srcdir == '': + srcdir = '.' + verify_setup() + for f in os.listdir(srcdir): + if re.match(r"(gcc|clang|darwin|intel)-.*\.py", f): + verify_file(f) + exit(verify_finalize()) diff --git a/test/toolset-mock/src/ar.py b/test/toolset-mock/src/ar.py new file mode 100644 index 000000000..2e73c3410 --- /dev/null +++ b/test/toolset-mock/src/ar.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('ar', 'rc', output_file('bin/gcc-gnu-4.8.3/debug/link-static/libl1.a'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/lib.o')) +command('ar', 'rc', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/libl1.a'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/lib.o')) +command('ar', 'rc', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/lib.o')) +command('ar', 'rc', output_file('bin/clang-linux-3.9.0/debug/link-static/libl1.a'), input_file('bin/clang-linux-3.9.0/debug/link-static/lib.o')) +command('ar', 'rc', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/libl1.a'), input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/lib.o')) + +main() diff --git a/test/toolset-mock/src/clang-3.9.0-darwin.py b/test/toolset-mock/src/clang-3.9.0-darwin.py new file mode 100644 index 000000000..d21aad8fa --- /dev/null +++ b/test/toolset-mock/src/clang-3.9.0-darwin.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('clang++', '-print-prog-name=ar', stdout=script('ar.py')) +command('clang++', '-print-prog-name=ranlib', stdout=script('ranlib.py')) + +# all builds are multi-threaded for darwin +if allow_properties("variant=debug", "link=shared", "runtime-link=shared"): + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC')) + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC')) + +if allow_properties("variant=release", "link=shared", "runtime-link=shared"): + command('clang++', '-x', 'c++', unordered('-O3', '-Wno-inline', '-Wall', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/lib.o'), '-fPIC') + command('clang++', '-x', 'c++', unordered('-O3', '-Wno-inline', '-Wall', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/release/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/release/target-os-darwin/libl1.dylib'), '-fPIC') + +if allow_properties("variant=debug", "link=static", "runtime-link=shared"): + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/libl1.a'), '-g') + +if allow_properties("variant=debug", "link=static", "runtime-link=static"): + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', '-static')) + +if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "architecture=x86", "address-model=32"): + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/lib.o'), unordered('-g', '-march=i686', '-fPIC', '-m32')) + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/x86/target-os-darwin/libl1.dylib'), unordered('-g', '-march=i686', '-fPIC', '-m32')) + +if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "cxxstd=latest"): + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-std=c++1z'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', '@rpath/libl1.dylib', input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC', '-std=c++1z')) + command('clang++', '-x', 'c++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-std=c++1z'), '-c', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/test'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/main.o'), input_file('bin/clang-darwin-3.9.0/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC', '-std=c++1z')) + +main() diff --git a/test/toolset-mock/src/clang-linux-3.9.0.py b/test/toolset-mock/src/clang-linux-3.9.0.py new file mode 100644 index 000000000..242d25624 --- /dev/null +++ b/test/toolset-mock/src/clang-linux-3.9.0.py @@ -0,0 +1,48 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('clang++', '-print-prog-name=ar', stdout=script('ar.py')) +command('clang++', '-print-prog-name=ranlib', stdout=script('ranlib.py')) + +if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared'): + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-fPIC')) + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/main.o'), input_file(source='main.cpp')) + command('clang++', '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-o', output_file('bin/clang-linux-3.9.0/debug/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/main.o'), input_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-fPIC')) + +if allow_properties('variant=release', 'link=shared', 'threading=single', 'runtime-link=shared', 'strip=on'): + command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/release/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-fPIC', '-Wl,--strip-all')) + command('clang++', unordered(ordered('-x', 'c++'), '-O3', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG', '-c'), '-o', output_file('bin/clang-linux-3.9.0/release/main.o'), input_file(source='main.cpp')) + command('clang++', '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/release/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/release/libl1.so')), '-o', output_file('bin/clang-linux-3.9.0/release/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/release/main.o'), input_file('bin/clang-linux-3.9.0/release/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-fPIC', '-Wl,--strip-all')) + +if allow_properties('variant=debug', 'link=shared', 'threading=multi', 'runtime-link=shared'): + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/threading-multi/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC')) + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/main.o'), input_file(source='main.cpp')) + command('clang++', '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so')), '-o', output_file('bin/clang-linux-3.9.0/debug/threading-multi/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/threading-multi/main.o'), input_file('bin/clang-linux-3.9.0/debug/threading-multi/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC')) + +if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=shared'): + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/lib.o'), input_file(source='lib.cpp')) + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/main.o'), input_file('bin/clang-linux-3.9.0/debug/link-static/libl1.a'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-g') + +if allow_properties('variant=debug', 'link=static', 'threading=single', 'runtime-link=static'): + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/lib.o'), input_file(source='lib.cpp')) + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/main.o'), input_file(source='main.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/main.o'), input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/libl1.a'), '-Wl,--end-group', unordered('-g', '-static')) + +if allow_properties('variant=debug', 'link=shared', 'threading=single', 'runtime-link=shared', 'architecture=x86', 'address-model=32'): + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/lib.o'), input_file(source='lib.cpp')) + command('clang++', '-o', output_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-soname', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-fPIC', '-m32')) + command('clang++', unordered(ordered('-x', 'c++'), '-O0', '-fno-inline', '-Wall', '-g', '-march=i686', '-m32', '-fPIC', '-c'), '-o', output_file('bin/clang-linux-3.9.0/debug/main.o'), input_file(source='main.cpp')) + command('clang++', '-Wl,-R', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/clang-linux-3.9.0/debug/libl1.so')), '-o', output_file('bin/clang-linux-3.9.0/debug/test'), '-Wl,--start-group', input_file('bin/clang-linux-3.9.0/debug/main.o'), input_file('bin/clang-linux-3.9.0/debug/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', unordered('-g', '-march=i686', '-fPIC', '-m32')) + +main() diff --git a/test/toolset-mock/src/darwin-4.2.1.py b/test/toolset-mock/src/darwin-4.2.1.py new file mode 100644 index 000000000..d81359ca5 --- /dev/null +++ b/test/toolset-mock/src/darwin-4.2.1.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +script("libtool.py") + +command('g++', '-dumpversion', stdout='4.2.1') + +# all builds are multi-threaded for darwin +if allow_properties("variant=debug", "link=shared", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', '-dynamiclib', '-Wl,-single_module', '-install_name', 'libl1.dylib', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/lib.o'), '-headerpad_max_install_names', unordered('-g', '-fPIC')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/darwin-4.2.1/debug/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC')) + +if allow_properties("variant=release", "link=shared", "runtime-link=shared"): + command('g++', unordered('-O3', '-Wno-inline', '-Wall', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', '-dynamiclib', '-Wl,-single_module', '-install_name', 'libl1.dylib', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/libl1.dylib'), input_file('bin/darwin-4.2.1/release/target-os-darwin/lib.o'), '-headerpad_max_install_names', unordered(ordered('-Wl,-dead_strip', '-no_dead_strip_inits_and_terms'), '-fPIC')) + command('g++', unordered('-O3', '-Wno-inline', '-Wall', '-dynamic', '-gdwarf-2', '-fexceptions', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/darwin-4.2.1/release/target-os-darwin/test'), input_file('bin/darwin-4.2.1/release/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/release/target-os-darwin/libl1.dylib'), unordered(ordered('-Wl,-dead_strip', '-no_dead_strip_inits_and_terms'), '-fPIC')) + +if allow_properties("variant=debug", "link=static", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), '-g') + +if allow_properties("variant=debug", "link=static", "runtime-link=static"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-gdwarf-2', '-fexceptions'), '-c', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', ordered('-nodefaultlibs', '-shared-libgcc', '-lstdc++-static', '-lgcc_eh', '-lgcc', '-lSystem'), '-static')) + +main() diff --git a/test/toolset-mock/src/gcc-4.2.1-darwin.py b/test/toolset-mock/src/gcc-4.2.1-darwin.py new file mode 100644 index 000000000..76058c320 --- /dev/null +++ b/test/toolset-mock/src/gcc-4.2.1-darwin.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('g++', '-print-prog-name=ar', stdout=script('ar.py')) +command('g++', '-print-prog-name=ranlib', stdout=script('ranlib.py')) + +# all builds are multi-threaded for darwin +if allow_properties("variant=debug", "link=shared", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), '-shared', input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/lib.o'), unordered('-g', '-fPIC')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-fPIC'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib')), '-o', output_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/target-os-darwin/libl1.dylib'), unordered('-g', '-fPIC')) + +if allow_properties("variant=release", "link=shared", "runtime-link=shared"): + command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib'), '-shared', input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/lib.o'), '-fPIC') + command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib')), '-o', output_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/release/target-os-darwin/libl1.dylib'), '-fPIC') + +if allow_properties("variant=debug", "link=static", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), '-g') + +if allow_properties("variant=debug", "link=static", "runtime-link=static"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', '-static')) + +main() diff --git a/test/toolset-mock/src/gcc-4.8.3-linux.py b/test/toolset-mock/src/gcc-4.8.3-linux.py new file mode 100644 index 000000000..5604ee5d1 --- /dev/null +++ b/test/toolset-mock/src/gcc-4.8.3-linux.py @@ -0,0 +1,50 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('g++', '-print-prog-name=ar', stdout=script('ar.py')) +command('g++', '-print-prog-name=ranlib', stdout=script('ranlib.py')) + +if allow_properties("variant=debug", "link=shared", "threading=single", "runtime-link=shared"): + command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/lib.o"), input_file(source="lib.cpp")) + command("g++", "-o", output_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-h", "-Wl,libl1.so", "-shared", "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/lib.o"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC")) + command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file(source="main.cpp")) + command("g++", "-Wl,-rpath", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-Wl,-rpath-link", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-o", output_file("bin/gcc-gnu-4.8.3/debug/test"), "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC")) + +if allow_properties("variant=release", "link=shared", "threading=single", "runtime-link=shared"): + command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/release/lib.o'), input_file(source='lib.cpp')) + command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/release/libl1.so'), '-Wl,-h', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/release/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-fPIC') + command('g++', unordered('-O3', '-finline-functions', '-Wno-inline', '-Wall', '-fPIC', '-DNDEBUG'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/release/main.o'), input_file(source='main.cpp')) + command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/release/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/release/libl1.so')), '-o', output_file('bin/gcc-gnu-4.8.3/release/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/release/main.o'), input_file('bin/gcc-gnu-4.8.3/release/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-fPIC') + +if allow_properties("variant=debug", "link=shared", "threading=multi", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/lib.o'), input_file(source='lib.cpp')) + command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so'), '-Wl,-h', '-Wl,libl1.so', '-shared', '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/lib.o'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-pthread', '-fPIC'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/main.o'), input_file(source='main.cpp')) + command('g++', '-Wl,-rpath', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so')), '-Wl,-rpath-link', arg('-Wl,', target_path('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so')), '-o', output_file('bin/gcc-gnu-4.8.3/debug/threading-multi/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/threading-multi/libl1.so'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-lrt', '-Wl,--end-group', unordered('-g', '-pthread', '-fPIC')) + +if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=shared"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g'), '-c', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/link-static/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/libl1.a'), '-Wl,-Bstatic', '-Wl,-Bdynamic', '-Wl,--end-group', '-g') + +if allow_properties("variant=debug", "link=static", "threading=single", "runtime-link=static"): + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/lib.o'), input_file(source='lib.cpp')) + command('g++', unordered('-O0', '-fno-inline', '-Wall', '-g', '-c'), '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/main.o'), input_file(source='main.cpp')) + command('g++', '-o', output_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/test'), '-Wl,--start-group', input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/main.o'), input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/libl1.a'), '-Wl,--end-group', unordered('-g', '-static')) + + +if allow_properties("variant=debug", "link=shared", "threading=single", "runtime-link=shared"): + command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC", "-std=c++1y"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/lib.o"), input_file(source="lib.cpp")) + command("g++", "-o", output_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-h", "-Wl,libl1.so", "-shared", "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/lib.o"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC", "-std=c++1y")) + command("g++", unordered("-O0", "-fno-inline", "-Wall", "-g", "-fPIC", "-std=c++1y"), "-c", "-o", output_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file(source="main.cpp")) + command("g++", "-Wl,-rpath", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-Wl,-rpath-link", arg("-Wl,", target_path("bin/gcc-gnu-4.8.3/debug/libl1.so")), "-o", output_file("bin/gcc-gnu-4.8.3/debug/test"), "-Wl,--start-group", input_file("bin/gcc-gnu-4.8.3/debug/main.o"), input_file("bin/gcc-gnu-4.8.3/debug/libl1.so"), "-Wl,-Bstatic", "-Wl,-Bdynamic", "-Wl,--end-group", unordered("-g", "-fPIC", "-std=c++1y")) + + +main() diff --git a/test/toolset-mock/src/intel-darwin-10.2.py b/test/toolset-mock/src/intel-darwin-10.2.py new file mode 100644 index 000000000..314d6c458 --- /dev/null +++ b/test/toolset-mock/src/intel-darwin-10.2.py @@ -0,0 +1,43 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('icc', '-print-prog-name=ar', stdout=script('ar.py')) +command('icc', '-print-prog-name=ranlib', stdout=script('ranlib.py')) + +# all builds are multi-threaded for darwin +if allow_properties("variant=debug", "link=shared", "runtime-link=shared"): + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-fPIC'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/debug/target-os-darwin/lib.o'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC')) + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-fPIC'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/target-os-darwin/libl1.dylib'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC')) + +if allow_properties("variant=release", "link=shared", "runtime-link=shared"): + command('icc', '-xc++', unordered('-O3', '-inline-level=2', '-w1', '-vec-report0', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/release/target-os-darwin/lib.o'), unordered(ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC')) + command('icc', '-xc++', unordered('-O3', '-inline-level=2', '-w1', '-vec-report0', '-fPIC'), '-DNDEBUG', '-c', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/release/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/release/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/release/target-os-darwin/libl1.dylib'), unordered(ordered('-shared-intel', '-lstdc++', '-lpthread'), '-fPIC')) + +if allow_properties("variant=debug", "link=static", "runtime-link=shared"): + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/libl1.a'), '-g', ordered('-shared-intel', '-lstdc++', '-lpthread')) + +if allow_properties("variant=debug", "link=static", "runtime-link=static"): + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), unordered('-g', ordered('-static', '-static-intel', '-lstdc++', '-lpthread'), '-static')) + +if allow_properties("variant=debug", "link=shared", "runtime-link=shared", "architecture=x86", "address-model=32"): + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/lib.o'), input_file(source='lib.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/libl1.dylib'), '-single_module', '-dynamiclib', '-install_name', 'libl1.dylib', input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/lib.o'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-march=i686', '-fPIC', '-m32')) + command('icc', '-xc++', unordered('-O0', '-inline-level=0', '-w1', '-g', '-vec-report0', '-march=i686', '-fPIC', '-m32'), '-c', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/main.o'), input_file(source='main.cpp')) + command('icc', '-o', output_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/test'), input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/main.o'), input_file('bin/intel-darwin-10.2/debug/x86/target-os-darwin/libl1.dylib'), unordered('-g', ordered('-shared-intel', '-lstdc++', '-lpthread'), '-march=i686', '-fPIC', '-m32')) + +main() diff --git a/test/toolset-mock/src/libtool.py b/test/toolset-mock/src/libtool.py new file mode 100644 index 000000000..9f58dc96d --- /dev/null +++ b/test/toolset-mock/src/libtool.py @@ -0,0 +1,14 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('libtool', '-static', '-o', output_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a'), input_file('bin/darwin-4.2.1/debug/link-static/target-os-darwin/lib.o')) +command('libtool', '-static', '-o', output_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a'), input_file('bin/darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/lib.o')) + +main() diff --git a/test/toolset-mock/src/mock-program.cpp b/test/toolset-mock/src/mock-program.cpp new file mode 100644 index 000000000..62dd4b8a4 --- /dev/null +++ b/test/toolset-mock/src/mock-program.cpp @@ -0,0 +1,42 @@ +// mock-program.cpp +// +// Copyright (c) 2017 Steven Watanabe +// +// 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) + +// This program does nothing except to exec a python script + +#include +#include +#include +#include "config.h" + +#if defined(_WIN32) + #include + #define execv _execv +#else + #include +#endif + +#ifndef PY_SCRIPT +#error PY_SCRIPT must be defined to the absolute path to the script to run +#endif + +#ifndef PYTHON_CMD +#error PYTHON_CMD must be defined to the absolute path to the python interpreter +#endif + +int main(int argc, char ** argv) +{ + std::vector args; + char python_cmd[] = PYTHON_CMD; + char script[] = PY_SCRIPT; + args.push_back(python_cmd); + args.push_back(script); + args.insert(args.end(), argv + 1, argv + argc); + args.push_back(NULL); + execv(python_cmd, &args[0]); + perror("exec"); +} diff --git a/test/toolset-mock/src/project-config.jam b/test/toolset-mock/src/project-config.jam new file mode 100644 index 000000000..73dcf42df --- /dev/null +++ b/test/toolset-mock/src/project-config.jam @@ -0,0 +1,5 @@ +# Copyright 2017 Steven Watanabe +# +# 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) diff --git a/test/toolset-mock/src/ranlib.py b/test/toolset-mock/src/ranlib.py new file mode 100644 index 000000000..4abe21ed0 --- /dev/null +++ b/test/toolset-mock/src/ranlib.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('ranlib', input_file('bin/gcc-gnu-4.8.3/debug/link-static/libl1.a')) +command('ranlib', input_file('bin/gcc-gnu-4.8.3/debug/link-static/runtime-link-static/libl1.a')) +command('ranlib', input_file('bin/gcc-darwin-4.2.1/debug/link-static/target-os-darwin/libl1.a')) +command('ranlib', input_file('bin/gcc-darwin-4.2.1/debug/link-static/runtime-link-static/target-os-darwin/libl1.a')) +command('ranlib', input_file('bin/clang-darwin-3.9.0/debug/link-static/target-os-darwin/libl1.a')) +command('ranlib', input_file('bin/clang-darwin-3.9.0/debug/link-static/runtime-link-static/target-os-darwin/libl1.a')) +command('ranlib', '-cs', input_file('bin/intel-darwin-10.2/debug/link-static/target-os-darwin/libl1.a')) +command('ranlib', '-cs', input_file('bin/intel-darwin-10.2/debug/link-static/runtime-link-static/target-os-darwin/libl1.a')) +command('ranlib', input_file('bin/clang-linux-3.9.0/debug/link-static/libl1.a')) +command('ranlib', input_file('bin/clang-linux-3.9.0/debug/link-static/runtime-link-static/libl1.a')) + +main() diff --git a/test/toolset-mock/src/strip.py b/test/toolset-mock/src/strip.py new file mode 100644 index 000000000..6245588bf --- /dev/null +++ b/test/toolset-mock/src/strip.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +command('strip', '-S', '-x', input_file('bin/darwin-4.2.1/release/target-os-darwin/test')) + +main() diff --git a/test/toolset-mock/src/verify.py b/test/toolset-mock/src/verify.py new file mode 100644 index 000000000..6e5e0ea7b --- /dev/null +++ b/test/toolset-mock/src/verify.py @@ -0,0 +1,9 @@ +# Copyright 2017 Steven Watanabe +# +# 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) + +from MockProgram import * + +verify() diff --git a/test/toolset_clang_darwin.py b/test/toolset_clang_darwin.py new file mode 100644 index 000000000..d8c519b64 --- /dev/null +++ b/test/toolset_clang_darwin.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates the clang-darwin toolset using a mock of clang + +from TestToolset import test_toolset + +test_toolset("clang-darwin", "3.9.0", [ + ["target-os=darwin"], + ["target-os=darwin", "release", "strip=on"], + ["target-os=darwin", "threading=multi"], + ["target-os=darwin", "link=static"], + ["target-os=darwin", "link=static", "runtime-link=static"], + ["target-os=darwin", "architecture=x86", "address-model=32"], + ["target-os=darwin", "cxxstd=latest"]]) diff --git a/test/toolset_clang_linux.py b/test/toolset_clang_linux.py new file mode 100644 index 000000000..2fbf84b6d --- /dev/null +++ b/test/toolset_clang_linux.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates the clang_linux toolset using a mock of clang + +from TestToolset import test_toolset + +test_toolset("clang-linux", "3.9.0", [ + ["target-os=linux"], + ["target-os=linux", "release", "strip=on"], + ["target-os=linux", "threading=multi"], + ["target-os=linux", "link=static"], + ["target-os=linux", "link=static", "runtime-link=static"], + ["target-os=linux", "architecture=x86", "address-model=32"]]) diff --git a/test/toolset_darwin.py b/test/toolset_darwin.py new file mode 100644 index 000000000..58ecc8d2b --- /dev/null +++ b/test/toolset_darwin.py @@ -0,0 +1,21 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates the darwin toolset using a mock of gcc + +from TestToolset import test_toolset + +test_toolset("darwin", "4.2.1", [ + ["target-os=darwin"], + ["target-os=darwin", "release", "strip=on"], + ["target-os=darwin", "threading=multi"], + ["target-os=darwin", "link=static"], + ["target-os=darwin", "link=static", "runtime-link=static"], +# Address-model handling is quite broken +# ["target-os=darwin", "architecture=x86", "address-model=32"] +]) diff --git a/test/toolset_gcc.py b/test/toolset_gcc.py new file mode 100644 index 000000000..d3d65fcf9 --- /dev/null +++ b/test/toolset_gcc.py @@ -0,0 +1,26 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates the gcc toolset using a mock of gcc + +from TestToolset import test_toolset + +test_toolset("gcc", "4.8.3", [ + ["target-os=linux"], + ["target-os=linux", "release"], + ["target-os=linux", "threading=multi"], + ["target-os=linux", "link=static"], + ["target-os=linux", "link=static", "runtime-link=static"], + ["target-os=linux", "cxxstd=latest"]]) + +test_toolset("gcc", "4.2.1", [ + ["target-os=darwin"], + ["target-os=darwin", "release"], + ["target-os=darwin", "threading=multi"], + ["target-os=darwin", "link=static"], + ["target-os=darwin", "link=static", "runtime-link=static"]]) diff --git a/test/toolset_intel_darwin.py b/test/toolset_intel_darwin.py new file mode 100644 index 000000000..db0444900 --- /dev/null +++ b/test/toolset_intel_darwin.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +# +# Copyright 2017 Steven Watanabe +# +# 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) + +# validates the intel-darwin toolset using a mock of icc + +from TestToolset import test_toolset + +test_toolset("intel-darwin", "10.2", [ + ["target-os=darwin"], + ["target-os=darwin", "release", "strip=on"], + ["target-os=darwin", "threading=multi"], + ["target-os=darwin", "link=static"], + ["target-os=darwin", "link=static", "runtime-link=static"], + ["target-os=darwin", "architecture=x86", "address-model=32"]]) diff --git a/test/types.py b/test/types.py new file mode 100644 index 000000000..e6d9bf840 --- /dev/null +++ b/test/types.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# +# Copyright 2018 Steven Watanabe +# +# 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) + +# Tests the mapping of various suffixes +# In particular, .so[.version] needs to +# be mapped as a SHARED_LIB. + +import BoostBuild + +t = BoostBuild.Tester() + +t.write("Jamroot.jam", """\ +import type : type ; +ECHO [ type source.c ] ; +ECHO [ type source.cc ] ; +ECHO [ type source.cxx ] ; +ECHO [ type source.cpp ] ; +ECHO [ type source.o ] ; +ECHO [ type source.obj ] ; +ECHO [ type boost_system.lib ] ; +ECHO [ type boost_system.so ] ; +ECHO [ type boost_system.dll ] ; +EXIT [ type boost_system.so.1.66.0 ] : 0 ; +""") + +t.run_build_system(stdout="""\ +C +CPP +CPP +CPP +OBJ +OBJ +STATIC_LIB +SHARED_LIB +SHARED_LIB +SHARED_LIB +""") + +t.cleanup()