mirror of
https://github.com/boostorg/build.git
synced 2026-02-15 13:02:11 +00:00
white squares in test results. Also, a small fix for Cygwin that doesn't seem to have hurt anything. [SVN r29793]
587 lines
18 KiB
Plaintext
Executable File
587 lines
18 KiB
Plaintext
Executable File
# (C) Copyright David Abrahams 2002.
|
|
# 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)
|
|
|
|
if ! $(.testing.jam-included)
|
|
{
|
|
.testing.jam-included = "}" ; # The brace makes emacs indentation happy
|
|
|
|
# Decide which toolsets should be treated like an ordinary (unix) GCC installation
|
|
gcc-compilers = [ MATCH ^(gcc.*)$ : $(TOOLS) ] ;
|
|
mingw-compilers = [ MATCH ^(mingw.*)$ ^(gcc-nocygwin.*) : $(TOOLS) ] ;
|
|
gcc-compilers = [ difference $(gcc-compilers) : $(mingw-compilers) ] ;
|
|
|
|
local rule get-var-value ( name + )
|
|
{
|
|
return $($(name)) ;
|
|
}
|
|
|
|
local rule get-library-name ( path_or_tokens + )
|
|
{
|
|
local path = $(path_or_tokens) ;
|
|
if ! $(path[1]) { path = [ split-path $(path) ] ; }
|
|
path = /$(path:J=/) ;
|
|
|
|
local match1 = [ MATCH /libs/(.*)/(test|example) : $(path) ] ;
|
|
local match2 = [ MATCH /libs/(.*)$ : $(path) ] ;
|
|
local match3 = [ MATCH (/status$) : $(path) ] ;
|
|
|
|
if $(match1) { return $(match1[0]) ; }
|
|
else if $(match2) { return $(match2[0]) ; }
|
|
else if $(match3) { return "" ; }
|
|
else if --dump-tests in $(ARGV)
|
|
{
|
|
EXIT Cannot extract library name from path $(path) ;
|
|
}
|
|
}
|
|
|
|
# Declares a test target. If name is not supplied, it is taken from the name of
|
|
# the first source file, sans extension and directory path.
|
|
#
|
|
# type should be a target type (e.g. OBJ, DLL, LIB, EXE)
|
|
#
|
|
# RETURNS the name(s) of the generated test target(s).
|
|
rule boost-test ( sources + : target-type : requirements * : test-name ? : default-build * )
|
|
{
|
|
if ! $(gIN_LIB_INCLUDE) # No need to execute this code twice
|
|
{
|
|
local result ;
|
|
{
|
|
# manufacture a test name if none supplied explicitly
|
|
test-name ?= $(sources[1]:D=:S=) ;
|
|
|
|
local library = "" ;
|
|
if $(SUBDIR_TOKENS) { library = [ get-library-name $(SUBDIR_TOKENS) ] ; }
|
|
|
|
# Make sure that targets don't become part of "all"
|
|
local gSUPPRESS_FAKE_TARGETS = true ;
|
|
|
|
result = [
|
|
declare-local-target $(test-name)
|
|
: $(sources)
|
|
: $(requirements) <sysinclude>$(BOOST_ROOT)
|
|
: $(default-build)
|
|
: $(target-type)
|
|
] ;
|
|
|
|
if $(result) && $(result) in $(.all-boost-tests)
|
|
{
|
|
EXIT boost-test \"$(result)\" declared twice ;
|
|
.all-boost-tests += $(result) ;
|
|
}
|
|
|
|
if --dump-tests in $(ARGV) && $(result)
|
|
{
|
|
dump-test $(library) : $(result) : $(requirements) ;
|
|
}
|
|
}
|
|
|
|
Clean clean : $(result) ;
|
|
|
|
# make NOTFILE targets of the same base name as the sources which can
|
|
# be used to build a single test.
|
|
type-DEPENDS $(sources:B:S=) : $(result) ;
|
|
|
|
# The NOTFILE target called "test" builds all tests
|
|
type-DEPENDS test : $(result) ;
|
|
|
|
# Make sure the test result doesn't hang around if the test fails
|
|
RMOLD $(result) ;
|
|
|
|
return $(result) ;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
# Helper for boost-test above. Uses dynamic scoping to access
|
|
# boost-test's locals.
|
|
local rule dump-test ( library ? : targets + : requirements * )
|
|
{
|
|
local srcs = [ on $(targets) get-var-value source-files ] ;
|
|
|
|
# Pick any source file names off the associated main target as well.
|
|
srcs += [ unique [ on $(gTARGET_SUBVARIANT($(targets))) get-var-value source-files ] ] ;
|
|
|
|
local pwd = [ PWD ] ;
|
|
|
|
# locate each source file
|
|
local source-files ;
|
|
for local s in $(srcs)
|
|
{
|
|
# find out where to look for the file
|
|
local paths = [ on $(s) get-var-value LOCATE SEARCH ] ;
|
|
|
|
s = $(s:G=) ; # strip grist
|
|
|
|
# build all the potential full paths of the file
|
|
local full-paths = $(s:R=$(paths)) ;
|
|
|
|
# look there
|
|
local files = [ GLOB $(full-paths:D) : $(s:D=) ] ;
|
|
|
|
if $(files)
|
|
{
|
|
# make relative to the project root instead of "."
|
|
local file = $(files[1]:R=$(pwd)) ;
|
|
|
|
# try to undo absolute paths
|
|
source-files += [ relative-path $(file) ] ;
|
|
}
|
|
}
|
|
|
|
# Extract values of the <test-info> feature
|
|
local dump-test-info = [ get-values <test-info> : $(requirements) ] ;
|
|
|
|
# Format them into a single string of quoted strings
|
|
dump-test-info = \"$(dump-test-info:J=\"\ \")\" ;
|
|
|
|
local test-id ; # unique among all tests
|
|
|
|
if $(library)
|
|
{
|
|
test-id = $(library)/$(test-name) ;
|
|
}
|
|
else
|
|
{
|
|
test-id = $(test-name) ;
|
|
}
|
|
|
|
ECHO boost-test($(target-type)) \"$(test-id)\"
|
|
[$(dump-test-info)]
|
|
":" \"$(source-files)\"
|
|
;
|
|
|
|
if --dump-test-targets in $(ARGV)
|
|
{
|
|
ECHO boost-test(TARGET)
|
|
\"$(test-id)\" [$(dump-test-info)] ":"
|
|
\"$(targets)\"
|
|
;
|
|
}
|
|
}
|
|
|
|
|
|
#######
|
|
|
|
BOOST_TEST_SUFFIX ?= .test ;
|
|
|
|
# Set a variable which says how to dump a file to stdout
|
|
if $(NT)
|
|
{
|
|
CATENATE = type ;
|
|
}
|
|
else
|
|
{
|
|
CATENATE = cat ;
|
|
}
|
|
|
|
actions **passed** bind source-files
|
|
{
|
|
echo $(source-files) > $(<)
|
|
}
|
|
|
|
actions (failed-as-expected)
|
|
{
|
|
echo failed as expected > $(<)
|
|
}
|
|
|
|
# a utility rule which causes test-file to be built successfully only if
|
|
# dependency fails to build. Used for expected-failure tests.
|
|
rule failed-test-file ( test-file : dependency + )
|
|
{
|
|
local grist = [ MATCH ^<(.*)> : $(dependency:G) ] ;
|
|
local marker = $(dependency:G=$(grist)*fail) ;
|
|
(failed-as-expected) $(marker) ;
|
|
FAIL_EXPECTED $(dependency) ;
|
|
MakeLocate $(marker) : $(LOCATE_TARGET) ;
|
|
RMOLD $(marker) ;
|
|
DEPENDS $(marker) : $(dependency) ;
|
|
|
|
succeeded-test-file $(test-file) : $(marker) ;
|
|
}
|
|
|
|
# a utility rule which causes test-file to be built successfully only if
|
|
# dependency builds. Used for expected-success tests.
|
|
rule succeeded-test-file ( test-file : dependency + )
|
|
{
|
|
**passed** $(test-file) ;
|
|
DEPENDS $(test-file) : $(dependency) ;
|
|
}
|
|
|
|
rule declare-build-succeed-test ( test-type : dependency-type )
|
|
{
|
|
gGENERATOR_FUNCTION($(test-type)) = build-test succeeded-test-file ;
|
|
gDEPENDENCY_TYPE($(test-type)) = $(dependency-type) ;
|
|
SUF$(test-type) = $(BOOST_TEST_SUFFIX) ;
|
|
}
|
|
|
|
# A utility rule which declares test-type to be a target type which
|
|
# depends on the /failed/ construction of a target of type
|
|
# dependency-type.
|
|
rule declare-build-fail-test ( test-type : dependency-type )
|
|
{
|
|
gGENERATOR_FUNCTION($(test-type)) = build-test failed-test-file ;
|
|
gDEPENDENCY_TYPE($(test-type)) = $(dependency-type) ;
|
|
SUF$(test-type) = $(BOOST_TEST_SUFFIX) ;
|
|
}
|
|
|
|
# When the appropriate generator function is bound to the
|
|
# test-file-generator argument, this is a target generator function
|
|
# for target types declared with declare-build-succeed-test and
|
|
# declare-build-fail-test, above.
|
|
rule build-test ( test-file-generator test-file + : sources * : requirements * )
|
|
{
|
|
# Get the target type of the current target out of the build properties
|
|
local target-type = [ get-values <target-type> : $(gBUILD_PROPERTIES) ] ;
|
|
|
|
# Get the type of target to attempt to build; the outcome of this
|
|
# attempt determines the result of the test.
|
|
local dependency-type = $(gDEPENDENCY_TYPE($(target-type))) ;
|
|
|
|
# Get the actual name of the target to attempt to build
|
|
local dependency = $(test-file[1]:S=$(SUF$(dependency-type))) ;
|
|
|
|
# record the source file names so we can put them in the .test
|
|
# file.
|
|
source-files on $(test-file) = $(sources) ;
|
|
|
|
# Make sure that the test-file is erased upon failure, so as not
|
|
# to give a false indication of success.
|
|
RMOLD $(test-file) ;
|
|
|
|
# Call dependency-type's generator function to attempt the build
|
|
$(gGENERATOR_FUNCTION($(dependency-type))) $(dependency) : $(sources) : $(requirements) ;
|
|
|
|
# Generator functions don't handle this job for us; perhaps they should.
|
|
set-target-variables $(dependency) ;
|
|
|
|
if $(test-file:S) != $(BOOST_TEST_SUFFIX)
|
|
{
|
|
EXIT unexpected test file suffix. Filename: $(test-file) ;
|
|
}
|
|
|
|
# The test files go with the other subvariant targets
|
|
MakeLocate $(test-file) : $(LOCATE_TARGET) ;
|
|
Clean clean : $(test-file) ;
|
|
|
|
# Generate the test file
|
|
$(test-file-generator) $(test-file) : $(dependency) ;
|
|
}
|
|
|
|
### Rules for testing whether a file compiles ###
|
|
|
|
# Establish the rule which generates targets of type "OBJ". Should really go
|
|
# into basic build system, but wasn't needed 'till now.
|
|
gGENERATOR_FUNCTION(OBJ) = Object ;
|
|
declare-build-fail-test COMPILE_FAIL : OBJ ;
|
|
declare-build-succeed-test COMPILE : OBJ ;
|
|
|
|
# Test that the given source-file(s) compile
|
|
rule compile ( sources + : requirements * : test-name ? )
|
|
{
|
|
return [ boost-test $(sources) : COMPILE : $(requirements) : $(test-name) ] ;
|
|
}
|
|
|
|
# Test that the given source-file(s) fail to compile
|
|
rule compile-fail ( sources + : requirements * : test-name ? )
|
|
{
|
|
return [ boost-test $(sources) : COMPILE_FAIL : $(requirements) : $(test-name) ] ;
|
|
}
|
|
|
|
|
|
### Rules for testing whether a program runs ###
|
|
|
|
gGENERATOR_FUNCTION(TEST_EXE) = run-test EXE ;
|
|
SUFTEST_EXE = .run ;
|
|
|
|
rule test-executable(EXE) ( target-to-test )
|
|
{
|
|
return $(target-to-test) ;
|
|
}
|
|
|
|
rule nt-paths-to-cygwin ( paths * )
|
|
{
|
|
local result ;
|
|
for local p in $(paths)
|
|
{
|
|
# if already rooted...
|
|
if [ MATCH ^([A-Za-z]:[/\\]) : $(p) ]
|
|
{
|
|
p = [ split-path $(p) ] ;
|
|
p = [ join-path /cygdrive [ MATCH ^(.).* : $(p[1]) ] $(p[2-]) ] ;
|
|
}
|
|
result += $(p:T) ;
|
|
}
|
|
return $(result) ;
|
|
}
|
|
|
|
rule run-test ( type-to-test run-target : sources * )
|
|
{
|
|
local parent = $(run-target:S=.test) ;
|
|
local targets-to-test ;
|
|
local main-target = $(gTARGET_SUBVARIANT($(parent))) ;
|
|
|
|
# If no sources, assume someone else already built the targets,
|
|
# and they are the library dependencies of the test target that
|
|
# match the type-to-test. This should perhaps be _all_
|
|
# dependencies, but hopefully we'll throw this code out soon!
|
|
if ! $(sources)
|
|
{
|
|
for local t in [ on $(parent) return $(library-dependencies) ]
|
|
{
|
|
if $(gTARGET_TYPE($(t))) = $(type-to-test)
|
|
{
|
|
targets-to-test += $(t) ;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
targets-to-test = $(run-target:S=$(SUF$(type-to-test))) ;
|
|
|
|
set-target-variables $(targets-to-test) ;
|
|
|
|
generate-dependencies $(main-target) : $(targets-to-test) ;
|
|
|
|
declare-basic-target $(targets-to-test) : $(sources) : : : $(type-to-test) ;
|
|
$(gGENERATOR_FUNCTION($(type-to-test))) $(targets-to-test) : $(sources) ;
|
|
}
|
|
|
|
# The .test file goes with the other subvariant targets
|
|
# normalization is a hack to get the slashes going the right way on Windoze
|
|
local LOCATE_TARGET = [ FDirName [ split-path $(LOCATE_TARGET) ] ] ;
|
|
MakeLocate $(run-target) : $(LOCATE_TARGET) ;
|
|
|
|
local executable = [ test-executable($(type-to-test)) $(targets-to-test) ] ;
|
|
local x-input-files = [ expand-source-names $(gRUN_TEST_INPUT_FILES) ] ;
|
|
local x-input-deps = ;
|
|
for local x-input-file in $(x-input-files)
|
|
{
|
|
local input-file-type = [ ungrist $(x-input-file:G:L) ] ;
|
|
local input-file-type-id = $(gTARGET_TYPE_ID($(input-file-type))) ;
|
|
local input-file-typed = $(x-input-file:G=$(input-file-type-id)) ;
|
|
|
|
if $(input-file-typed)
|
|
{
|
|
dependent-include $(input-file-typed) ;
|
|
|
|
local input-file-path = [ target-path-of $(input-file-typed) ] ;
|
|
local input-file-target = [ target-id-of $(input-file-path) ] ;
|
|
|
|
local [ protect-subproject ] ;
|
|
enter-subproject [ directory-of $(input-file-path) ] ;
|
|
|
|
local p = $(gBUILD_PROPERTIES) ;
|
|
segregate-free-properties p ;
|
|
local input-file-subvariant = [
|
|
find-compatible-subvariant $(input-file-target)
|
|
: $(gCURRENT_TOOLSET) $(variant)
|
|
: $(p) ] ;
|
|
local input-file-dep = [
|
|
subvariant-target $(input-file-target)
|
|
: $(input-file-subvariant)
|
|
: $(gCURRENT_TOOLSET) $(variant) ] ;
|
|
x-input-deps += $(input-file-dep) ;
|
|
}
|
|
else
|
|
{
|
|
x-input-deps += $(x-input-file) ;
|
|
}
|
|
}
|
|
DEPENDS $(run-target) : $(executable) $(targets-to-test) $(x-input-deps) ;
|
|
INPUT_FILES on $(run-target) = $(x-input-deps) ;
|
|
ARGS on $(run-target) = $(gRUN_TEST_ARGS) ;
|
|
ARGS2 on $(run-target) = $(gRUN_TEST_ARGS2) ;
|
|
|
|
#
|
|
# Prepare path setup
|
|
#
|
|
local path-sources = $(parent) $(executable) ; # where do we get paths from?
|
|
local $(.run-path-shell-vars) ; # declare local path variables
|
|
|
|
# initialize path variables from the path-sources
|
|
for local v in $(.run-path-vars)
|
|
{
|
|
local shell-var = $(.shell-var($(v))) ;
|
|
$(shell-var) = [ unique $($(shell-var)) $(gRUN_$(v)($(path-sources))) ] ;
|
|
}
|
|
|
|
local nt-cygwin ;
|
|
if $(NT) && $(gCURRENT_TOOLSET) in $(gcc-compilers)
|
|
{
|
|
nt-cygwin = true ;
|
|
}
|
|
|
|
# build up a fragment of shell command
|
|
local path-setup ;
|
|
for local shell-var in $(.run-path-shell-vars)
|
|
{
|
|
if $($(shell-var))
|
|
{
|
|
local dirs = $($(shell-var)) ;
|
|
local splitpath = $(SPLITPATH) ;
|
|
|
|
# PATH gets translated automatically; the rest must be
|
|
# cygwin-native when running with Cygwin GCC under NT
|
|
if $(nt-cygwin) && $(shell-var) != PATH
|
|
{
|
|
dirs = [ nt-paths-to-cygwin $(dirs) ] ;
|
|
splitpath = ":" ;
|
|
}
|
|
|
|
if $(.run-path-shell-var-value($(shell-var)))
|
|
{
|
|
dirs += $(.env-prefix)$(shell-var)$(.env-suffix) ;
|
|
}
|
|
|
|
path-setup += $(SHELL_SET)$(shell-var)=$(dirs:J=$(splitpath)) ;
|
|
path-setup += $(SHELL_EXPORT)$(shell-var) ;
|
|
}
|
|
}
|
|
|
|
local debugger = [ MATCH ^--debugger=(.*) : $(ARGV) ] ;
|
|
debugger = $(debugger)" " ;
|
|
local newline = "
|
|
" ;
|
|
PATH_SETUP on $(run-target) = $(path-setup:J=$(newline):E=)$(newline)$(debugger:E=) ;
|
|
local verbose-test = 1 ;
|
|
if --verbose-test in $(ARGV)
|
|
{
|
|
verbose-test = 0 ;
|
|
}
|
|
|
|
VERBOSE_TEST on $(run-target) = $(verbose-test) ;
|
|
if $(NT)
|
|
{
|
|
STATUS on $(run-target) = %status% ;
|
|
SET_STATUS on $(run-target) = "set status=%ERRORLEVEL%" ;
|
|
RUN_OUTPUT_NL on $(run-target) = "echo." ;
|
|
STATUS_0 on $(run-target) = "%status% EQU 0 (" ;
|
|
STATUS_NOT_0 on $(run-target) = "%status% NEQ 0 (" ;
|
|
VERBOSE on $(run-target) = "%verbose% EQU 0 (" ;
|
|
ENDIF on $(run-target) = ")" ;
|
|
}
|
|
else
|
|
{
|
|
STATUS on $(run-target) = "$status" ;
|
|
SET_STATUS on $(run-target) = "status=$?" ;
|
|
RUN_OUTPUT_NL on $(run-target) = "echo" ;
|
|
STATUS_0 on $(run-target) = "test $status -eq 0 ; then" ;
|
|
STATUS_NOT_0 on $(run-target) = "test $status -ne 0 ; then" ;
|
|
VERBOSE on $(run-target) = "test $verbose -eq 0 ; then" ;
|
|
ENDIF on $(run-target) = "fi" ;
|
|
}
|
|
|
|
capture-run-output $(run-target) : $(executable) : $(debugger) ;
|
|
|
|
if ! ( --preserve-test-targets in $(ARGV) ) && ! [ MATCH ^--debugger=(.*) : $(ARGV) ]
|
|
{
|
|
RmTemps $(run-target) : $(targets-to-test) ;
|
|
}
|
|
}
|
|
|
|
# The rule is just used for argument checking
|
|
rule capture-run-output ( target : executable + : debugger ? )
|
|
{
|
|
gTEST_OUTPUT_FILE($(target)) = $(target:S=.output) ;
|
|
INCLUDES $(target) : $(target:S=.output) ;
|
|
MakeLocate $(test-file:S=.output) : $(LOCATE_TARGET) ;
|
|
Clean clean : $(test-file:S=.output) ;
|
|
output-file on $(target) = $(target:S=.output) ;
|
|
if $(debugger)
|
|
{
|
|
debug-test $(target) : $(executable) ;
|
|
}
|
|
else
|
|
{
|
|
execute-test $(target) : $(executable) ;
|
|
}
|
|
|
|
if --run-all-tests in $(ARGV)
|
|
{
|
|
ALWAYS $(target) ;
|
|
}
|
|
}
|
|
|
|
if $(NT)
|
|
{
|
|
CATENATE = type ;
|
|
}
|
|
else
|
|
{
|
|
CATENATE = cat ;
|
|
}
|
|
|
|
actions debug-test bind INPUT_FILES
|
|
{
|
|
$(PATH_SETUP)$(>) $(ARGS) "$(INPUT_FILES)" $(ARGS2)
|
|
}
|
|
|
|
actions execute-test bind INPUT_FILES output-file
|
|
{
|
|
$(PATH_SETUP)$(>) $(ARGS) "$(INPUT_FILES)" $(ARGS2) > $(output-file) 2>&1
|
|
$(SET_STATUS)
|
|
$(RUN_OUTPUT_NL) >> $(output-file)
|
|
echo EXIT STATUS: $(STATUS) >> $(output-file)
|
|
if $(STATUS_0)
|
|
$(CP) $(output-file) $(<)
|
|
$(ENDIF)
|
|
$(SHELL_SET)verbose=$(VERBOSE_TEST)
|
|
if $(STATUS_NOT_0)
|
|
$(SHELL_SET)verbose=0
|
|
$(ENDIF)
|
|
if $(VERBOSE)
|
|
echo ====== BEGIN OUTPUT ======
|
|
$(CATENATE) $(output-file)
|
|
echo ====== END OUTPUT ======
|
|
$(ENDIF)
|
|
exit $(STATUS)
|
|
}
|
|
|
|
|
|
declare-build-fail-test RUN_FAIL : TEST_EXE ;
|
|
declare-build-succeed-test RUN : TEST_EXE ;
|
|
rule run ( sources + : args * : input-files * : requirements * : name ? : default-build * : args2 * )
|
|
{
|
|
local gRUN_TEST_ARGS = $(args) ;
|
|
local gRUN_TEST_ARGS2 = $(args2) ;
|
|
local gRUN_TEST_INPUT_FILES = $(input-files) ;
|
|
SEARCH on $(input-files) = $(SEARCH_SOURCE) ;
|
|
return [ boost-test $(sources) : RUN : $(requirements) : $(name) : $(default-build) ] ;
|
|
}
|
|
|
|
rule run-fail ( sources + : args * : input-files * : requirements * : name ? )
|
|
{
|
|
local gRUN_TEST_ARGS = $(2) ;
|
|
local gRUN_TEST_INPUT_FILES = $(3) ;
|
|
SEARCH on $(3) = $(SEARCH_SOURCE) ;
|
|
return [ boost-test $(<) : RUN_FAIL : $(4) : $(name) ] ;
|
|
}
|
|
|
|
### Rules for testing whether a program links
|
|
|
|
declare-build-fail-test LINK_FAIL : EXE ;
|
|
rule link-fail ( sources + : requirements * : name ? )
|
|
{
|
|
return [ boost-test $(<) : LINK_FAIL : $(2) : $(name) ] ;
|
|
}
|
|
|
|
declare-build-succeed-test LINK : EXE ;
|
|
rule link ( sources + : requirements * : name ? )
|
|
{
|
|
return [ boost-test $(<) : LINK : $(2) : $(name) ] ;
|
|
}
|
|
|
|
### Rules for grouping tests into suites:
|
|
|
|
rule test-suite # pseudotarget-name : test-targets...
|
|
{
|
|
NOTFILE $(<) ;
|
|
type-DEPENDS $(<) : $(>) ;
|
|
}
|
|
|
|
} # include guard
|
|
|