mumps: new test suite

This commit is contained in:
Oleg A. Khlybov
2024-11-04 22:27:26 -08:00
committed by Christoph Reiter
parent 022eb158cb
commit f25ca078a8
8 changed files with 822 additions and 787 deletions

View File

@@ -30,7 +30,7 @@ _realname=mumps
pkgbase=mingw-w64-${_realname}
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}"
pkgver=5.7.3
pkgrel=1
pkgrel=2
pkgdesc="Sparse direct SLAE solver (mingw-w64)"
arch=('any')
mingw_arch=('mingw64' 'ucrt64')
@@ -52,16 +52,18 @@ source=("https://mumps-solver.org/MUMPS_${pkgver}.tar.gz"
"0001-makefile.inc.patch"
"0002-skip-examples.patch"
"0003-fix-setenv.patch"
"all.tcl"
"tclbuildtest.tcl"
"mumps.test")
"mumps.tcl"
"testme-0.tm"
"buildme-0.tm"
"xyz-0.tm")
sha256sums=('84a47f7c4231b9efdf4d4f631a2cae2bdd9adeaabc088261d15af040143ed112'
'd4e3d06c316544304ac6fefd595f20fd5865e791c9377e031fbad49b6238406c'
'bbefd4a5f841536132f32c1ffc684a58b778901cd777445ca817dbb34d983450'
'11609d173fc0df7b1d7269c19d5d81b2fd05a3abc57eff8ba12de1597242236c'
'ec5072630e1c0309fe383669e9187790cd135a393c67bc4bc35cf60b0ba396ff'
'277a71049b2d4cde76ca21253b253ccd7ea9e8bcfb654868c05c6bfb137d2690'
'58bc4cc4820cc91dc8cc67c2c95774b00c628fa56cc9c627894a5da08900b11b')
'aa8cc50e447eebb14492744c8be6bd8f731e0140c339a76cc31e54ea2f11813d'
'8d8f5670ffdf34a10b0d027bff976131824e2e6b053f8a13d207ecddd4663d6d'
'd0db2aa573c9aaf57b30cb3c2bd7c66e1677f3f468002f467da98b75943076b1'
'd52709e3663d50ed793bfe991c571ec4ea5e6eaf72fcc920e1f8ba43d0ed7050')
apply_patch_with_msg() {
for _patch in "$@"
@@ -131,7 +133,7 @@ package() {
install -m644 *.h "${pkgdir}${MINGW_PREFIX}/include"
)
(
cp ${srcdir}/{all.tcl,tclbuildtest.tcl,mumps.test} ${pkgdir}${MINGW_PREFIX}/share/test/mumps
cp ${srcdir}/{*.tcl,*.tm} ${pkgdir}${MINGW_PREFIX}/share/test/mumps
cd "$srcdir/${_realname}_${pkgver}/examples"
cp c_example.c ?simpletest.F input_simpletest_* ${pkgdir}${MINGW_PREFIX}/share/test/mumps
)

View File

@@ -1,7 +0,0 @@
# Template {all.tcl} file which is to be put into each test module's directory.
# Either [source] the bundled {tclbuildtest.tcl} to force load the specific code or
# alter the $auto_path list to add location of {pkgIndex.tcl,tclbuildtest.tcl}.
# The same scheme is to be applied to all .test files.
source [file join [file dirname [file normalize [info script]]] tclbuildtest.tcl]
package require tclbuildtest
::tclbuildtest::suite {*}$::argv

View File

@@ -0,0 +1,70 @@
# https://github.com/okhlybov/testme
package require Tcl
namespace eval ::buildme {
namespace export sandbox shell
proc MakeTempDir {args} {
upvar 1 unit unit
set roots $args
foreach t {TMPDIR TMP} {
if {![catch {set t [set ::env($t)]}]} {
lappend roots $t
}
}
lappend roots /tmp
foreach r $roots {
if {![catch {
set prefix [file rootname [file tail [dict get $unit -source]]]
set t [file join $r $prefix.[expr {int(rand()*999999)}]]
file mkdir $t
}]} {return $t}
}
error "failed to create temporary directory $t"
}
proc sandbox {code} {
upvar 1 unit unit
set dir [MakeTempDir]
try {
foreach p [glob -nocomplain -directory [dict get $unit -stage] * .*] {
set last [lindex [file split $p] end]
if {$last != "." && $last != ".."} {
file copy -force -- $p $dir
}
}
# Can't cd into stage dir in multithreaded environment where all threads have the same current directory
dict set unit -stage $dir
eval $code
} finally {
if {[dict get $unit -cleanup]} {
if {[catch {file delete -force -- $dir}]} {
puts stderr "failed to delete temporary directory $dir
}
}
}
}
proc shell {cmd} {
upvar 1 unit unit
set stage [dict get $unit -stage]
set cd "cd \"$stage\""
puts stdout "$cd && \\\n$cmd"
if {[catch {exec -ignorestderr $::env(SHELL) -c "$cd && $cmd" 2>@1} result opts]} {
puts stderr $result
} else {
puts stdout $result
}
return {*}$opts $result
}
}

48
mingw-w64-mumps/mumps.tcl Normal file
View File

@@ -0,0 +1,48 @@
tcl::tm::add [file normalize [file join [file dirname [info script]] .]]
package require xyz
foreach static {{} static} {
foreach executor {{} omp} {
xyz::unit MUMPS [concat $executor $static c double] {
package require buildme
buildme::sandbox {
buildme::shell "cc -o a c_example.c `pkgconf mumps-[dict get $unit -xyz] --cflags --libs [dict get $unit -static]` [dict get $unit -static] && ./a"
}
}
}
foreach executor {mpi} {
xyz::unit MUMPS [concat $executor $static c double] {
package require buildme
buildme::sandbox {
buildme::shell "mpicc -o a c_example.c `pkgconf mumps-[dict get $unit -xyz] --cflags --libs [dict get $unit -static]` [dict get $unit -static] && mpiexec -n 1 ./a"
}
}
}
}
foreach precision {double single} {
foreach scalar {real complex} {
if {$scalar == "complex"} {set input input_simpletest_cmplx} else {set input input_simpletest_real}
foreach static {{} static} {
foreach executor {{} omp} {
xyz::unit MUMPS [list $executor $static $precision $scalar fortran] -input $input {
package require buildme
buildme::sandbox {
buildme::shell "gfortran -o a [string index [dict get $unit -xyz] 0]simpletest.F -I/ucrt64/include `pkgconf mumps-[dict get $unit -xyz] --cflags --libs [dict get $unit -static]` [dict get $unit -static] && ./a < [dict get $unit -input]"
}
}
}
foreach executor {mpi} {
xyz::unit MUMPS [list $executor $static $precision $scalar fortran] -input $input {
package require buildme
buildme::sandbox {
buildme::shell "mpifort -o a [string index [dict get $unit -xyz] 0]simpletest.F -I/usrt64/include `pkgconf mumps-[dict get $unit -xyz] --cflags --libs [dict get $unit -static]` [dict get $unit -static] && mpiexec -n 1 ./a < [dict get $unit -input]"
}
}
}
}
}
}

View File

@@ -1,21 +0,0 @@
source [file join [file dirname [file normalize [info script]]] tclbuildtest.tcl]
package require tclbuildtest
::tclbuildtest::sandbox {
foreach t {{} openmp mpi} {
foreach b {{} static} {
test [list c $t real double $b] {
run [build [require mumps-[xyz]] c_example.c]
}
foreach s {real complex} {
foreach p {single double} {
test [list fortran $t $p $s $b] {
if {[constraint? complex]} {set input input_simpletest_cmplx} else {set input input_simpletest_real}
run [build [require mumps-[xyz]] -fallow-invalid-boz [x]simpletest.F] < $input
}
}
}
}
}
}

View File

@@ -1,751 +0,0 @@
#
# TclTest extenstion to test source code compilation & running
#
# https://github.com/okhlybov/tclbuildtest
#
package provide tclbuildtest 0.1.0
package require Tcl 8.6
package require tcltest 2.5.1
namespace eval ::tcltest {}; # Make pkg_mkIndex happy
# https://github.com/tcl2020/named-parameters
namespace eval ::np {
#
# proc_args_to_dict - given a proc name and declared
# proc arguments (variable names with optional
# default values and a -- and possible some more
# stuff, create and return a dict containing that
# info in a way that's convenient and quicker
# for us at runtime:
# * we store a list of positional parameters
# * we store a list of named parameters
# * we store a list of var-value defaults
# * we get a tricked-out error message in errmsg
#
::proc proc_args_to_dict {name procArgs} {
set seenDashes 0
dict set d defaults [list]
dict set d positional [list]
dict set d named [list]
set errmsg "wrong # args: should be \"$name "
foreach arg $procArgs {
if {$arg eq "--"} {
set seenDashes 1
append errmsg "?--? "
continue
}
set var [lindex $arg 0]
dict lappend d [expr {$seenDashes ? "positional" : "named"}] $var
if {[llength $arg] == 2} {
dict lappend d defaults $var [lindex $arg 1]
if {$seenDashes} {
append errmsg "?$var? "
} else {
append errmsg "?-$var val? "
}
} elseif {$var eq "args"} {
dict lappend d defaults $var [list]
append errmsg "?arg ...? "
} else {
if {$seenDashes} {
append errmsg "$var "
} else {
append errmsg "-$var val "
}
}
}
dict set d errmsg "[string range $errmsg 0 end-1]\""
return $d
}
#
# np_handler - look at an argument dict created by proc_args_to_dict
# and look at the real arguments to a function (args), and sort
# out the named and positional parameters to behave in the
# expected way.
#
::proc np_handler {argd realArgs} {
set named [dict get $argd named]
set positional [dict get $argd positional]
# process named parameters
while {[llength $realArgs] > 0} {
set arg [lindex $realArgs 0]
# if arg is --, flip to positional
if {$arg eq "--"} {
set realArgs [lrange $realArgs 1 end]
break
}
# if "var" doesn't start with a dash, flip to positional
if {[string index $arg 0] ne "-"} {
#puts "possible var '$arg' doesn't start with a dash, flip to positional"
break
}
# if "var" isn't known to us as a named parameter, flip to positional
set var [string range $arg 1 end]
if {[lsearch $named $var] < 0} {
#puts "'var' '$arg' not recognized, flip to positional"
break
}
# if there isn't at least one more element in the arg list,
# we are missing a value for one of our named parameters
if {[llength $realArgs] == 0} {
#puts "realArgs is empty but i expect something for $var"
error [dict get $argd errmsg] "" [list TCL WRONGARGS]
}
# we're good, set the named parameter into the variable sets
#puts [list set vsets($var) [lindex $realArgs 1]]
# but don't allow the same variable to be set twice
if {[info exists vsets($var)]} {
error [dict get $argd errmsg] "" [list TCL WRONGARGS]
}
set vsets($var) [lindex $realArgs 1]
set realArgs [lrange $realArgs 2 end]
}
# fill in defaults for all the vars with defaults that
# didn't get set to a value
foreach "var value" [dict get $argd defaults] {
if {![info exists vsets($var)]} {
set vsets($var) $value
}
}
foreach var $positional {
if {$var eq "args"} {
set vsets($var) $realArgs
set realArgs [list]
break
}
if {[llength $realArgs] > 0} {
set vsets($var) [lindex $realArgs 0]
set realArgs [lrange $realArgs 1 end]
}
# no arguments left. if this var doesn't
# have a default value, it's a wrong args error
if {![info exists vsets($var)]} {
error [dict get $argd errmsg] "" [list TCL WRONGARGS]
}
}
# make sure all the named parameters have been set, either
# by defaults or explicitly, any not set is an error
foreach var $named {
if {![info exists vsets($var)]} {
#puts "required named parameter '-$var' is not set"
error [dict get $argd errmsg] "" [list TCL WRONGARGS]
}
}
# are there too many arguments?
if {[llength $realArgs] > 0} {
#puts "leftover arguments (too many) '$realArgs'"
error [dict get $argd errmsg] "" [list TCL WRONGARGS]
}
# now iterate through the var-value pairs and set them into
# the caller's frame
foreach "var value" [array get vsets] {
#puts "set '$var' '$value'"
upvar $var myvar
set myvar $value
}
return
}
#
# np::proc - same as proc except if -- is in the argv
# then it will generate a proc that has extra code
# at the beginning to wrangle the named parameters
#
proc proc {name argv body} {
# handle the case where there are no named parameters
if {[lsearch $argv --] < 0} {
uplevel [list ::proc $name $argv $body]
return
}
if {[lsearch $argv --] == 0} {
return -code error "-- cannot be the first argument for named parameters. Use positional parameters."
}
set d [proc_args_to_dict $name $argv]
set newbody "::proc $name args {\n"
append newbody " ::np::np_handler [list $d] \$args\n"
append newbody $body
append newbody "\n}"
#puts $newbody
uplevel $newbody
}
}
namespace eval ::tclbuildtest {
variable system-count 0
variable build-count 0
# Standard predefined constraints
foreach ct {
c cxx fortran
single double
real complex
openmp thread hybrid mpi
static
debug
} {::tcltest::testConstraint $ct 1}
# Return a value of the environment variable or {} if no such variable is set
proc env {var} {
try {return [set ::env($var)]} on error {} {return {}}
}
# MpiExec detection
proc mpiexec {} {
variable mpiexec
try {set mpiexec} on error {} {
set mpiexec {}
foreach x [collect [env MPIEXEC] mpiexec mpirun] {
if {![catch {exec {*}$x}]} {
set mpiexec $x
break
}
}
}
if {$mpiexec == {}} {error {failed to detect MpiExec or equivalent}}
return $mpiexec
}
# PkgConfig detection
proc pkg-config {} {
variable pc
try {set pc} on error {} {
set pc {}
foreach x [collect [env PKG_CONFIG] pkg-config pkgconf] {
if {![catch {exec {*}$x --version}]} {
set pc $x
break
}
}
}
if {$pc == {}} {error {failed to detect PkgConfig or equivalent}}
return $pc
}
# C compiler detection
proc cc {} {
variable cc
try {set cc} on error {} {
set cc {}
foreach x [collect [env CC] gcc] {
if {![catch {exec {*}$x --version}]} {
set cc $x
break
}
}
}
if {$cc == {}} {error {failed to detect C compiler}}
return $cc
}
# C++ compiler detection
proc cxx {} {
variable cxx
try {set cxx} on error {} {
set cxx {}
foreach x [collect [env CXX] g++] {
if {![catch {exec {*}$x --version}]} {
set cxx $x
break
}
}
}
if {$cxx == {}} {error {failed to detect C++ compiler}}
return $cxx
}
# FORTRAN compiler detection
proc fc {} {
variable fc
try {set fc} on error {} {
set fc {}
foreach x [collect [env FC] gfortran] {
if {![catch {exec {*}$x --version}]} {
set fc $x
break
}
}
}
if {$fc == {}} {error {failed to detect FORTRAN compiler}}
return $fc
}
# MPI C compiler detection
proc mpicc {} {
variable mpicc
try {set mpicc} on error {} {
set mpicc {}
foreach x [collect [env MPICC] mpicc] {
if {![catch {exec {*}$x --version}]} {
set mpicc $x
break
}
}
}
if {$mpicc == {}} {error {failed to detect MPI C compiler}}
return $mpicc
}
# MPI C++ compiler detection
proc mpicxx {} {
variable mpicxx
try {set mpicxx} on error {} {
set mpicxx {}
foreach x [collect [env MPICXX] mpicxx mpic++] {
if {![catch {exec {*}$x --version}]} {
set mpicxx $x
break
}
}
}
if {$mpicxx == {}} {error {failed to detect MPI C++ compiler}}
return $mpicxx
}
# MPI FORTRAN compiler detection
proc mpifc {} {
variable mpifc
try {set mpifc} on error {} {
set mpifc {}
foreach x [collect [env MPIFC] [env MPIFORT] mpifort mpif90 mpif77] {
if {![catch {exec {*}$x --version}]} {
set mpifc $x
break
}
}
}
if {$mpifc == {}} {error {failed to detect MPI FORTRAN compiler}}
return $mpifc
}
# Create temporary directory
proc mktempdir {} {
set t [file join $::env(TEMP) [file rootname [file tail [info script]]][expr {int(rand()*9999)}]]
file mkdir $t
return $t
}
# Delete directory tree
proc rmdir {dir} {
try {
file delete -force $dir
} on error {} {
# This command fires up a background sanitizing process which does its best to delete
# staging directory in spite of executable locks or what's not which can happend on Windows
# This code should work in real UNIX environments or UNIX-like Windows environments
# such as Cygwin or MSYS(2).
exec -ignorestderr sh -c "nohup \${SHELL} -c \" while \[ -d '$dir' \]; do rm -rf '$dir' || sleep 3; done\" > /dev/null 2>&1 &"
}
}
# Execute script from a temporary location
# Script file is copied to the location along with all residing files
# The location gets deleted afterwards
proc sandbox {script} {
variable stagedir [mktempdir]
try {
::tcltest::configure {*}$::argv
::tcltest::workingDirectory $stagedir
file copy -force {*}[glob -directory [file dirname [file normalize [info script]]] -nocomplain *] $stagedir
eval $script
} finally {
::tcltest::cleanupTests
rmdir $stagedir
}
}
# Obtain compilation and linking flags for the specified packages via PkgConfig
proc require {args} {
if {[constraint? static]} {set flags --static} else {set flags {}}
xflags {*}[lindex [dict get [system [pkg-config] {*}$args --keep-system-cflags --cflags {*}$flags] stdout] 0]
ldflags {*}[lindex [dict get [system [pkg-config] {*}$args --libs {*}$flags] stdout] 0]
return
}
# Deduce source code language from command line arguments
proc deduce-language {opts} {
switch -regexp -nocase [lindex $opts [lsearch -glob -not $opts {-*}]] {
{\.c$} {return c}
{\.(cxx|cpp|cc)$} {return cxx}
{\.(f|for|f\d+)$} {return fortran}
default {error {failed to deduce source language from the command line agruments}}
}
}
# Deduce compilation command from command line arguments
proc deduce-compiler-proc {opts} {
set lang [deduce-language $opts]
if {[constraint? mpi]} {
switch $lang {
c {return mpicc}
cxx {return mpicxx}
fortran {return mpifc}
}
} else {
switch $lang {
c {return cc}
cxx {return cxx}
fortran {return fc}
}
}
}
# Deduce compilation flags command from command line arguments
proc deduce-compile-flags-proc {opts} {
switch [deduce-language $opts] {
c {return cflags}
cxx {return cxxflags}
fortran {return fflags}
}
}
# C preprocessor command line arguments
proc cppflags {args} {
variable cppflags
try {set cppflags} on error {} {
set cppflags [lsqueeze [env CPPFLAGS]]
if {![constraint? debug]} {lappend cppflags -DNDEBUG}
}
lappend cppflags {*}$args
}
# Language-agnostic compilation command line arguments
proc xflags {args} {
variable xflags
try {set xflags} on error {} {
set xflags {}
if {[constraint? openmp]} {lappend xflags -fopenmp}
if {[constraint? thread]} {lappend xflags -pthread}
if {[constraint? debug]} {lappend xflags -Og} else {lappend xflags -O2}
}
lappend xflags {*}$args
}
# C-specific compilation command line arguments
proc cflags {args} {
variable cflags
try {set cflags} on error {} {
set cflags [lsqueeze [env CFLAGS]]
}
lappend cflags {*}$args
}
# C++-specific compilation command line arguments
proc cxxflags {args} {
variable cxxflags
try {set cxxflags} on error {} {
set cxxflags [lsqueeze [env CXXFLAGS]]
}
lappend cxxflags {*}$args
}
# FORTRAN-specific compilation command line arguments
proc fflags {args} {
variable fflags
try {set fflags} on error {} {
set fflags [lsqueeze [env FFLAGS]]
}
lappend fflags {*}$args
}
# Linker command line arguments
proc ldflags {args} {
variable ldflags
try {set ldflags} on error {} {
set ldflags [lsqueeze [env LDFLAGS]]
if {[constraint? static]} {lappend ldflags -static}
if {[constraint? openmp]} {lappend ldflags -fopenmp}
if {[constraint? thread]} {lappend ldflags -pthread}
}
lappend ldflags {*}$args
}
# Linked libraries command line arguments
proc libs {args} {
variable libs
try {set libs} on error {} {
set libs [lsqueeze [env LIBS]]
if {[constraint? cxx]} {lappend libs -lstdc++}
if {[constraint? fortran]} {lappend libs -lgfortran -lquadmath}
}
lappend libs {*}$args
}
# Perform source code compilation into executable
# Returns the executable name
proc build {args} {
set args [lsqueeze $args]
set exe [executable]
system {*}[concat \
[[deduce-compiler-proc $args]] \
-o $exe \
[cppflags] \
[xflags] \
[[deduce-compile-flags-proc $args]] \
$args \
[ldflags] \
[libs] \
]
return $exe
}
# Perform running of the specified executable with supplied command line arguments
proc run {args} {
if {[constraint? mpi]} {set runner [mpiexec]} else {set runner {}}
system {*}[list $runner {*}$args]
}
# Execute command line built from command line arguments
proc system {args} {
variable system-count
incr system-count
set stdout stdout${system-count}
set stderr stderr${system-count}
set args [lsqueeze $args]
set command [join $args]
if {[lsearch [::tcltest::verbose] exec] < 0} {set verbose 0} else {set verbose 1}
if {$verbose} {::puts [::tcltest::outputChannel] "> $command"}
try {
exec -ignorestderr -- {*}$args > $stdout 2> $stderr
set options {}
set status 0
set code ok
} trap CHILDSTATUS {results options} {
set status [lindex [dict get $options -errorcode] 2]
set code error
} finally {
try {
set out [read-file $stdout]
set err [read-file $stderr]
} finally {
file delete -force $stdout $stderr
}
}
if {$verbose} {
foreach x $out {::puts [::tcltest::outputChannel] $x}
if {[llength $out] > 0 && [llength $err] > 0} {::puts [::tcltest::outputChannel] ----}
foreach x $err {::puts [::tcltest::outputChannel] $x}
}
return -code $code [dict create command $command status $status stdout $out stderr $err options $options]
}
# Construct a scalar type ID from the constraints
proc x {} {
if {[constraint? complex]} {
if {[constraint? double]} {return z}
if {[constraint? single]} {return c}
} else {
if {[constraint? double]} {return d}
if {[constraint? single]} {return s}
}
error {failed to contstruct the scalar type}
}
# Construct an execution model ID from the constraints
proc y {} {
if {[constraint? mpi]} {return m}
if {[constraint-any? openmp thread]} {return t}
if {[constraint? hybrid]} {return h}
return s
}
# Construct an build type ID from the constraints
proc z {} {
if {[constraint? debug]} {return g}
return o
}
# Construct a 3-letter XYZ build code from the constraints
proc xyz {} {
return [x][y][z]
}
# Construct the test name based on the constraints set
proc name {} {
join [list [file rootname [file tail [info script]]] {*}[constraints]] -
}
# Construct new unique executable name
proc executable {} {
variable build-count
variable executable
return [set executable [name]-[incr build-count].exe]
}
# Construct human-readable description of the test according to the contraints set
proc description {} {
set t {}
switch [intersection {c cxx fortran} [constraints]] {
c {lappend t C}
cxx {lappend t C++}
fortran {lappend t FORTRAN}
}
try {
switch [y] {
s {lappend t sequential}
m {lappend t MPI}
t {lappend t multithreaded}
h {lappend t heterogeneous}
}
} on error {} {}
try {
switch [x] {
s {lappend t "single precision"}
d {lappend t "double precision"}
c {lappend t "single precision complex"}
z {lappend t "double precision complex"}
}
} on error {} {}
switch [z] {
o {lappend t optimized}
g {lappend t debugging}
}
if {[constraint? static]} {lappend t static}
join $t
}
# Set constraints
proc constraints {args} {
variable constraints
try {set constraints} on error {} {
set constraints {}
}
lappend constraints {*}[lsqueeze $args]
}
# Return true if any of specified contraints is set
proc constraint-any? {args} {
foreach ct $args {
if {[constraint? $ct]} {return 1}
}
return 0
}
# Return true if all scpecified constraints are set
proc constraint-all? {args} {
foreach ct $args {
if {![constraint? $ct]} {return 0}
}
return 1
}
# Return true if specified constraint is set
proc constraint? {ct} {
variable constraints
expr {[lsearch $constraints $ct] >= 0}
}
# Test failure is triggered by throwing an exception
::tcltest::customMatch exception {return 1; #}
# Main test command
::np::proc test {{name {}} {description {}} {match {exception}} -- cts script} {
foreach v {executable constraints cppflags xflags cflags cxxflags fflags ldflags libs} {variable $v; catch {unset $v}}
set cts [constraints {*}$cts]
if {$name == {}} {set name [name]}
if {$description == {}} {set description "[description] build"}
::tcltest::test $name $description -constraints $cts -body $script -match $match
# Attempt to delete the created executable if any to conserve space in the stage dir
# It's OK for the operation to fail at this point as the executable may still be locked
variable executable; try {file delete -force $executable} on error {} {}
}
# To be used in {all.tcl}
proc suite {args} {
::tcltest::configure -testdir [file dirname [file normalize [info script]]] {*}$args
::tcltest::runAllTests
::tcltest::cleanupTests
}
# Quick & dirty hack to introduce extra verbosity option(s)
# Override the proc from in tcltest-*.tm
# Original code corresponds to version 2.5.1
proc ::tcltest::AcceptVerbose { level } {
set level [AcceptList $level]
set levelMap {
x exec
l list
p pass
b body
s skip
t start
e error
l line
m msec
u usec
}
set levelRegexp "^([join [dict values $levelMap] |])\$"
if {[llength $level] == 1} {
if {![regexp $levelRegexp $level]} {
# translate single characters abbreviations to expanded list
set level [string map $levelMap [split $level {}]]
}
}
set valid [list]
foreach v $level {
if {[regexp $levelRegexp $v]} {
lappend valid $v
}
}
return $valid
}
# Read text file and return list of lines
proc read-file {file} {
set f [open $file r]
try {
return [split [read -nonewline $f] \n]
} finally {
close $f
}
}
# Return a new list from the specified list entries squashing {} values
proc lsqueeze {list} {
set out [list]
foreach x $list {
if {$x != {}} {lappend out $x}
}
return $out
}
# Return a new list from the specified arguments squashing {} values
proc collect {args} {
lsqueeze $args
}
# Return intersection of two lists
proc intersection {a b} {
set x {}
foreach i $a {
if {[lsearch -exact $b $i] != -1} {lappend x $i}
}
return $x
}
}

640
mingw-w64-mumps/testme-0.tm Normal file
View File

@@ -0,0 +1,640 @@
# https://github.com/okhlybov/testme
package require Tcl
package require Thread
namespace eval ::testme {
namespace export unit
proc Import {source} {
variable executor
interp create box
try {
interp alias box ::testme::unit {} ::testme::unit
box eval set ::argv0 [file normalize $source]
box eval set ::nesting [incr $::nesting -1]
box eval {
cd [file dirname $::argv0]
source [file tail $::argv0]
}
} finally {
interp delete box
}
}
### Toplevel code
try {set ::nesting} on error {} {
package require platform
try {
### CLIP code
# package require Tcl 8.6
namespace eval ::clip {
# defs: { { opt(s) } -slot opt -default value -info "text" -section {common} -apply {script} }
proc parse {argv defs} {
set x [lsearch $argv --]
if {$x < 0} {
set opts $argv
set args {}
} else {
set opts [lrange $argv 0 [expr {$x-1}]]
set args [lrange $argv [expr {$x+1}] end]
}
set xargs {}
lassign [NormalizeDefs $defs] short_flags short_opts long_flags long_opts
set flagset [dict create]
foreach f $short_flags {
dict set flagset [dict get $f bare] $f
}
while {[llength $opts]} {
set x [Next opts]
# long option with inline value, ex. --abc=123
if {[regexp {^-+([[:alnum:]][[:alnum:]\-\_]*)=(.*)$} $x ~ tag value]} {
set found 0
foreach d $long_opts {
dict with d {
if {$tag == $bare} {
set found 1
break
}
}
}
if {$found} {
apply $apply $slot $value
continue
} else {
error "unrecognized option in $x"
}
}
# long/short option expecting separate value, ex. --abc 123
if {[regexp {^-+([[:alnum:]][[:alnum:]\-\_]*)} $x ~ tag]} {
set found 0
foreach d [concat $long_opts $short_opts] {
dict with d {
if {$tag == $bare} {
set value [Next opts]
set found 1
break
}
}
}
if {$found} {
apply $apply $slot $value
continue
}
}
# long/short separate flag, ex. -C, --create
if {[regexp {^-+([[:alnum:]][[:alnum:]\-\_]*)} $x ~ tag]} {
set found 0
foreach d [concat $long_flags $short_flags] {
dict with d {
if {$tag == $bare} {
set value $default
set found 1
break
}
}
}
if {$found} {
apply $apply $slot $value
continue
}
}
# short flag within the coalesced flag set, ex. -Sxy
if {[regexp {^-([[:alnum:]]+)$} $x ~ flags]} {
foreach flag [split $flags {}] {
try {
set d [dict get $flagset $flag]
dict with d {
apply $apply $slot $default
}
} on error {} {
error "unrecognized short flag -$flag"
}
}
continue
}
# stray -flag
if {[regexp {^-.*} $x]} {
error "unrecognized argument $x"
}
# the rest arguments are passed through
lappend xargs $x
}
concat $xargs $args
}
#
proc usage {defs {sections {}} {chan stdout}} {
set padding [ComputePadding $defs]
dict for {section info} $sections {
PrintSection [ExtractSectionDefs defs $section] $info $chan $padding
}
PrintSection $defs "Generic options" $chan $padding
}
proc ComputePadding {defs} {
set pad 0
foreach def $defs {
set x [string length [FormatOpts [Opts $def]]]
if {$x > $pad} { set pad $x }
}
return $pad
}
proc ExtractSectionDefs {defsVar section} {
upvar $defsVar defs
set rest [list]
set filtered [list]
foreach def $defs {
if {[Section $def] == $section} {
lappend filtered $def
} else {
lappend rest $def
}
}
set defs $rest
return $filtered
}
proc FormatOpts {opts} {
set arg 0
set x [lmap opt $opts {
regexp {(.*?)(=*)$} $opt ~ bare flag
if {!$arg && $flag != {}} { set arg 1 }
subst $bare
}]
set x [join $x {, }]
if {$arg} { set x "$x <arg>" }
return $x
}
proc PrintSection {defs info chan padding} {
if {[llength $defs]} {
incr padding 2
if {$info != {}} { puts $chan "\n* $info:\n" }
foreach def $defs {
set opts [format %${padding}s [FormatOpts [Opts $def]]]
puts $chan "$opts [Info $def]"
}
}
}
proc Opts {def} { lindex $def 0 }
proc Dict {def} { lrange $def 1 end }
proc Default {def} {
try { return [dict get [Dict $def] -default] } on error {} { return 1 }
}
proc Section {def} {
try { return [dict get [Dict $def] -section] } on error {} { return {} }
}
proc Info {def} {
try { return [dict get [Dict $def] -info] } on error {} { return {} }
}
proc Slot {def} {
try { return [dict get [Dict $def] -slot] } on error {} { return {} }
}
proc Apply {def} {
try { return [list {slot value} [dict get [Dict $def] -apply]] } on error {} { return {{slot value} { upvar 2 $slot x; set x $value }} }
}
proc Next {listVar} {
upvar $listVar list
if {![llength $list]} { error "not enough arguments" }
set v [lindex $list 0]
set list [lrange $list 1 end]
return $v
}
# Flags are the parameterless options which receive 1 value when set, ex. -S
# Single letter flags may be coalesced, ex. -Sxyz
# Multi letter flags must come on their own, ex. -foo
proc NormalizeDefs {defs} {
set short_flags {}
set short_opts {}
set long_flags {}
set long_opts {}
foreach def $defs {
set slot [Slot $def]
foreach opt [Opts $def] {
regexp -- {-+(.*?)=?} $opt ~ bare
if {$slot == {}} { set slot $bare }
switch -regexp $opt {
{^-[[:alnum:]]$} { set kind short_flags }
{^-+[[:alnum:]][[:alnum:]\-\_]*$} { set kind long_flags }
{^-[[:alnum:]]=$} { set kind short_opts }
{^-+[[:alnum:]][[:alnum:]\-\_]*=$} { set kind long_opts }
default { error "failed to decode option descriptor $opt" }
}
lappend $kind [dict create bare $bare slot $slot default [Default $def] apply [Apply $def] info [Info $def] section [Section $def]]
}
}
return [list $short_flags $short_opts $long_flags $long_opts]
}
}
variable verbose false
variable quiet false
variable staging false
variable premature false
variable cleanup true
variable jobs 0
set opts {
{{--jobs= -j=} -info "set maximum number of allowed threads" -default 0 -slot jobs}
{{-v --verbose} -info "dump unit output to standard error channel" -default true -slot verbose}
{{-T --staging} -info "manage staging directory in \$TMPDIR" -default true -slot staging}
{{-K --keep} -info "keep temporary directories & files" -default false -slot cleanup}
{{-e --bailout} -info "bail out on first failure" -default true -slot premature}
{{-q --quiet} -info "suppress TAP output to standard output channel" -default true -slot quiet}
{{-h --help} -info "print help" -apply {
puts stderr "usage: $::argv0 {-f --flag --opt=arg --opt arg -opt arg ...} {--} {tag +tag -tag ...}"
puts stderr {}
puts stderr " +tag | tag instruct to execute only units with specified tag(s)"
puts stderr " -tag instruct to skip units with specified tag(s)"
clip::usage $testme::opts {} stderr
exit 0
}}
{{--version} -info "print package version" -apply {
puts stderr [package require testme]
exit 0
}}
}
if {[catch {
if {[llength $::argv]} {set ::argv [clip::parse $::argv $opts]}
} return]} {
puts stderr $return
exit 1
}
# TODO
if {$jobs == 0} {
set jobs 4
switch -glob [platform::identify] {
linux-* {catch {set jobs [exec nproc --all]}}
win32-* {catch {set jobs $env(NUMBER_OF_PROCESSORS)}}
}
}
set +tags [list]
set -tags [list]
foreach arg $::argv {
switch -glob $arg {
+* {lappend +tags [string trimleft $arg +]}
-* {lappend -tags [string trimleft $arg -]}
default {lappend +tags $arg}
}
}
set ::nesting -1
set paths "eval tcl::tm::path add [tcl::tm::path list]"
set static {
### Execution thread code
proc lshift {var} {
upvar 1 $var x
set r [lindex $x 0]
set x [lrange $x 1 end]
return $r
}
proc lfront {list} {
return [lindex $list 0]
}
rename puts ::tcl::puts
proc puts {args} {
variable stdout
variable stderr
set xargs $args
if {[llength $args] > 1 && [lfront $args] == "-nonewline"} {
lshift args
set newline 0
} else {
set newline 1
}
switch [llength $args] {
1 {set chan stdout}
default {set chan [lshift args]}
}
if {$chan == "stdout" || $chan == "stderr"} {
if {$newline} {
foreach arg $args {lappend $chan $arg}
} else {
set $chan [concat [lrange [set $chan] 0 end-1] "[lindex [set $chan] end]$args"]
}
} else {
tcl::puts {*}$xargs
}
}
proc skip {{reason {}}} {return -code 1073741823 -level 0 $reason}
proc process-unit {unit} {
variable stdout [list]
variable stderr [list]
interp create unit
try {
interp alias unit puts {} puts
interp alias unit skip {} skip
unit eval tcl::tm::path add {*}[tcl::tm::path list]
catch {unit eval "apply {{unit} {[dict get $unit -code]}} {$unit}"} return opts
return [dict merge $opts [dict create -return $return -stdout $stdout -stderr $stderr -id [dict get $unit -id]]]
} finally {
interp delete unit
}
}
}
variable executor [tpool::create -maxworkers $jobs -initcmd "$paths; $static;"]
variable pending [list]
variable skipped [list]
variable units [dict create]
variable id 1
proc union {as bs} {
set result $as
foreach elem $bs {
if {[lsearch -exact $as $elem] == -1} {
lappend result $elem
}
}
return $result
}
proc intersection {as bs} {
set result {}
foreach elem $bs {
if {[lsearch -exact $as $elem] >= 0} {
lappend result $elem
}
}
return $result
}
proc unit {args} {
variable executor
variable pending
variable skipped
variable units
variable +tags
variable -tags
variable cleanup
variable id
set s [llength $args]
if {$s < 1 || $s % 2 == 0} {error "usage: testme::unit ?-name ...? ?-tags ...? {...}"}
set opts [lrange $args 0 end-1]
set code [lindex [lrange $args end end] 0]
set name unit[llength $pending]
set tags [list]
catch {set name [dict get $opts -name]}
catch {set tags [dict get $opts -tags]}
set tags [lsearch -inline -all -not -exact $tags {}]; # Squeeze out empty {} tags
set unit [dict create {*}$opts -stage [pwd] -name $name -tags $tags -code $code -id $id -source $::argv0 -cleanup $cleanup]
dict set units $id $unit
if {([llength ${+tags}] == 0 || [llength [intersection ${+tags} $tags]] > 0) && [llength [intersection ${-tags} $tags]] == 0} {
lappend pending [tpool::post $executor "process-unit {$unit}"]
} else {
lappend skipped $id
}
incr id
}
if {$staging} {
proc MakeTempDir {args} {
set roots $args
foreach t {TMPDIR TMP} {
if {![catch {set t [set ::env($t)]}]} {
lappend roots $t
}
}
lappend roots /tmp
foreach r $roots {
if {![catch {
set t [file join $r [expr {int(rand()*999999)}]]
file mkdir $t
}]} {
return $t
}
}
error "failed to create temporary directory $t"
}
set staging [set ::env(TMPDIR) [MakeTempDir]]
if {$verbose} {puts stderr "created temporary directory $staging"}
}
try {
Import $::argv0
if {!$quiet} {
puts "TAP version 14"
puts "1..[dict size $units]"
foreach s $skipped {
set u [dict get $units $s]
puts "ok [dict get $u -id] - [dict get $u -name] # SKIP due to tagging"
}
}
while {[llength $pending]} {
foreach f [tpool::wait $executor $pending pending] {
set pending [lsearch -inline -all -not -exact $pending $f]
set return [tpool::get $executor $f]
set u [dict get $units [dict get $return -id]]
set name [dict get $u -name]
set id [dict get $u -id]
if {!$quiet} {
switch [dict get $return -code] {
0 {puts "ok $id - $name"}
1073741823 {puts "ok $id - $name # SKIP [dict get $return -return]"}
default {
puts "not ok $id - $name"
puts " ---"
set lines [split [dict get $return -return] "\n"]
set r [lindex $lines 0]
if {[llength $lines] > 1} {set r "$r >>>"}
puts " tags: [dict get $u -tags]"
puts " source: [dict get $u -source]"
puts " return: $r"
puts " ..."
}
}
}
if {$verbose} {
set stdout [dict get $return -stdout]
set stderr [dict get $return -stderr]
if {[llength $stdout] + [llength $stderr] > 0} {
puts stderr {}
puts stderr "- $name"
if {[llength $stdout] > 0} {
puts stderr "-- stdout:"
foreach line $stdout {puts stderr $line}
}
if {[llength $stderr] > 0} {
puts stderr "-- stderr:"
foreach line $stderr {puts stderr $line}
}
}
}
flush stdout
flush stderr
if {$premature && [dict get $return -code] != 0} {error "bailing out on failure"}
}
}
} finally {
if {$cleanup && $staging != {false}} {
if {[catch {file delete -force -- $staging}]} {
if {$verbose} {puts stderr "failed to remove temporary directory $staging"}
} else {
if {$verbose} {puts stderr "removed temporary directory $staging"}
}
}
}
exit 0
} on error {return opts} {
if {$verbose} {
puts stderr $return
puts stderr [dict get $opts -errorinfo]
}
if {!$quiet} {puts "Bail out!"}
exit 1
}
}
### Nested code
if {$::nesting != 0} {
set wd [pwd]
foreach source [glob -nocomplain [file join * [file tail $::argv0]]] {
try {Import $source} finally {cd $wd}
}
}
### Custom code which slurped the testme package's code
proc with {var list code} {
upvar 1 $var v
if {[catch {set v}]} {set v [list]}
set list [concat $v $list]
apply [list $var $code] $list
}
}

54
mingw-w64-mumps/xyz-0.tm Normal file
View File

@@ -0,0 +1,54 @@
package require testme
namespace eval ::xyz {
proc unit {prefix tags args} {
testme::unit -tags $tags -name [DescribeXYZ $prefix $tags] -xyz [DeduceXYZ $tags] -static [DeduceStatic $tags] {*}$args
}
proc any? {tag tags} {
foreach t $tag {
if {[lsearch -exact $tags $t] >= 0} {return 1}
}
return 0
}
proc DeduceXYZ {tags} {
if {[any? debug $tags]} {set z g} else {set z o}
if {[any? mpi $tags]} {set y m} {
if {[any? {omp openmp} $tags]} {set y t} else {set y s}
}
if {[any? complex $tags]} {
if {[any? single $tags]} {set x c} else {set x z}
} else {
if {[any? single $tags]} {set x s} else {set x d}
}
return $x$y$z
}
proc DescribeXYZ {prefix tags} {
set prefix "$prefix:"
if {[any? single $tags]} {set prefix "$prefix single-precision"}
if {[any? double $tags]} {set prefix "$prefix double-precision"}
if {[any? complex $tags]} {set prefix "$prefix complex"}
if {[any? mpi $tags]} {set prefix "$prefix MPI"} else {if {[any? {omp openmp} $tags]} {set prefix "$prefix OpenMP"}}
if {[any? debug $tags]} {set prefix "$prefix debug"}
if {[any? c $tags]} {set prefix "$prefix C"}
if {[any? fortran $tags]} {set prefix "$prefix FORTRAN"}
if {[any? static $tags]} {set prefix "$prefix static"}
set prefix "$prefix build"
return $prefix
}
proc DeduceStatic {tags} {
if {[any? static $tags]} {return {-static}} else {return {}}
}
}