2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-17 13:42:14 +00:00
Files
build/v1/testing.jam
Rene Rivera d6f1a11e7c Fix breakage when raw input files are also other types of targets.
Fix ordering so that the input files order is kept regardless of type of file.


[SVN r27482]
2005-02-22 16:24:35 +00:00

563 lines
17 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 )
{
local forward_slash_path = "" ;
for i in [ split-path $(path) ] { forward_slash_path = $(forward_slash_path)/$(i) ; }
path = $(forward_slash_path) ;
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 $(RELATIVE_SUBDIR) { library = [ get-library-name $(RELATIVE_SUBDIR) ] ; }
# 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)
{
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)\"
;
}
#######
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 targets-to-test = $(run-target:S=$(SUF$(type-to-test))) ;
local parent = $(run-target:S=.test) ;
local main-target = $(gTARGET_SUBVARIANT($(parent))) ;
set-target-variables $(targets-to-test) ;
generate-dependencies $(main-target) : $(targets-to-test) ;
if $(sources)
{
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[1]) ] ;
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