2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 13:02:11 +00:00
Files
build/v1/testing.jam
Dave Abrahams 14d041360c Kill off bogus targets created by Python testing; these were causing
white squares in test results.

Also, a small fix for Cygwin that doesn't seem to have hurt anything.


[SVN r29793]
2005-06-25 00:29:50 +00:00

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