1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
#!/usr/bin/env python
import argparse
import subprocess as sp
import os
# Initialization
NO_OF_THREADS = 8 # default no of threads is 8
SUPPORTED_BACKENDS = ['llvm', 'llvm2', 'llvm_rtlib', 'c', 'cpp', 'x86', 'wasm',
'gfortran', 'llvmImplicit', 'llvmStackArray', 'llvm_integer_8',
'fortran', 'c_nopragma', 'llvm_nopragma', 'llvm_wasm',
'llvm_wasm_emcc', 'llvm_omp', 'llvm_submodule', 'mlir',
'mlir_omp', 'mlir_llvm_omp', 'llvm_goc', 'target_offload',
'llvm_single_invocation']
SUPPORTED_STANDARDS = ['lf', 'f23', 'legacy']
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
LFORTRAN_PATH = f"{BASE_DIR}/../src/bin:$PATH"
fast_tests = "no"
nofast_llvm16 = "no"
separate_compilation = "no"
use_ninja = False
user_specified_threads = False
def run_cmd(cmd, cwd=None):
print(f"+ {cmd}")
process = sp.run(cmd, shell=True, cwd=cwd)
if process.returncode != 0:
print("Command failed.")
exit(1)
def run_test(backend, std, test_pattern=None):
run_cmd(f"mkdir {BASE_DIR}/test-{backend}")
if std == "f23":
std_string = "-DSTD_F23=yes"
elif std == "legacy":
std_string = "-DSTD_LEGACY=yes"
elif std == "lf":
std_string = ""
else:
raise Exception("Unsupported standard")
cwd=f"{BASE_DIR}/test-{backend}"
# Conditionally use Ninja or Make (default)
if use_ninja:
# Use Ninja generator for faster builds
# Add flags to skip Fortran compiler detection issues with CMake 3.29+
# Set CMAKE_Fortran_PREPROCESS_SOURCE which is required by Ninja but missing for lfortran
generator_flags = ("-G Ninja -DCMAKE_Fortran_COMPILER_WORKS=1 -DCMAKE_Fortran_COMPILER_FORCED=1 "
"-DCMAKE_Fortran_PREPROCESS_SOURCE=\"<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> "
"-E <SOURCE> -o <PREPROCESSED_SOURCE>\"")
else:
# Use default Make generator
generator_flags = ""
common=f" {generator_flags} -DCURRENT_BINARY_DIR={BASE_DIR}/test-{backend} -S {BASE_DIR} -B {BASE_DIR}/test-{backend}"
if backend == "gfortran":
run_cmd(f"FC=gfortran cmake" + common,
cwd=cwd)
elif backend == "cpp":
run_cmd(f"FC=lfortran FFLAGS=\"--openmp\" cmake -DLFORTRAN_BACKEND={backend} -DFAST={fast_tests} "
f"-DLLVM_GOC={separate_compilation} -DNOFAST_LLVM16={nofast_llvm16} {std_string}" + common,
cwd=cwd)
elif backend == "fortran":
run_cmd(f"FC=lfortran cmake -DLFORTRAN_BACKEND={backend} "
f"-DFAST={fast_tests} -DLLVM_GOC={separate_compilation} -DNOFAST_LLVM16={nofast_llvm16} "
f"-DCMAKE_Fortran_FLAGS=\"-fPIC\" {std_string}" + common,
cwd=cwd)
else:
run_cmd(f"FC=lfortran cmake -DLFORTRAN_BACKEND={backend} -DFAST={fast_tests} "
f"-DLLVM_GOC={separate_compilation} {std_string} -DNOFAST_LLVM16={nofast_llvm16} " + common,
cwd=cwd)
# If a test pattern is provided, find matching tests and build only those
if test_pattern:
# Query ctest to find which tests match the pattern
result = sp.run(f"ctest -N -R {test_pattern}", shell=True, cwd=cwd,
stdout=sp.PIPE, stderr=sp.PIPE, text=True)
if result.returncode != 0:
print("Failed to query tests with ctest")
exit(1)
# Parse the output to extract test names
# Output format: " Test #123: test_name"
import re
test_names = []
for line in result.stdout.split('\n'):
match = re.match(r'\s+Test\s+#\d+:\s+(\S+)', line)
if match:
test_names.append(match.group(1))
if not test_names:
print(f"No tests match pattern: {test_pattern}")
exit(1)
print(f"Building {len(test_names)} test(s): {', '.join(test_names)}")
# Build only the matching test targets
build_cmd = "ninja" if use_ninja else "make"
# Ninja uses all cores by default, so only specify -j if user provided it
# Make needs -j specified, so use default or user-provided value
if use_ninja and not user_specified_threads:
# User didn't specify -j, let ninja use all cores
j_flag = ""
else:
j_flag = f" -j{NO_OF_THREADS}"
for test_name in test_names:
run_cmd(f"{build_cmd}{j_flag} {test_name}", cwd=cwd)
else:
# Build all tests
build_cmd = "ninja" if use_ninja else "make"
if use_ninja and not user_specified_threads:
j_flag = ""
else:
j_flag = f" -j{NO_OF_THREADS}"
run_cmd(f"{build_cmd}{j_flag}", cwd=cwd)
# Build ctest command with optional test pattern filter
ctest_cmd = f"ctest -j{NO_OF_THREADS} --output-on-failure"
if test_pattern:
ctest_cmd += f" -R {test_pattern}"
run_cmd(ctest_cmd, cwd=cwd)
def test_backend(backend, std, test_pattern=None):
if backend not in SUPPORTED_BACKENDS:
raise Exception(f"Unsupported Backend: {backend}\n")
if std not in SUPPORTED_STANDARDS:
raise Exception(f"Unsupported Backend: {std}\n")
run_test(backend, std, test_pattern)
def check_module_names():
from glob import glob
import re
mod = re.compile(r"(module|MODULE)[ ]+(.*)", re.IGNORECASE)
files = glob("*.f90")
module_names = []
file_names = []
for file in files:
f = open(file).read()
s = mod.search(f)
if s:
module_names.append(s.group(2).lower())
file_names.append(file)
for i in range(len(module_names)):
name = module_names[i]
if name in module_names[i+1:]:
print("FAIL: Found a duplicate module name")
print("Name:", name)
print("Filename:", file_names[i])
raise Exception("Duplicate module names")
print("OK: All module names are unique")
def get_args():
parser = argparse.ArgumentParser(description="LFortran Integration Test Suite")
parser.add_argument("-j", "-n", "--no_of_threads", type=int,
help="Parallel testing on given number of threads")
parser.add_argument("-b", "--backends", nargs="*", default=["llvm"], type=str,
help="Test the requested backends (%s)" % \
", ".join(SUPPORTED_BACKENDS))
parser.add_argument("--std", type=str, default="lf",
help="Run tests with the requested Fortran standard: ".join(SUPPORTED_STANDARDS))
parser.add_argument("-f", "--fast", action='store_true',
help="Run supported tests with --fast")
parser.add_argument("-sc", "--separate_compilation", action='store_true',
help="Run tests with --separate-compilation")
parser.add_argument("-nf16", "--no_fast_till_llvm16", action='store_true',
help="Don't run unsupported tests with --fast when LLVM < 17")
parser.add_argument("-t", "--test", type=str,
help="Run specific tests matching pattern (regex)")
parser.add_argument("--ninja", action='store_true',
help="Use Ninja build system instead of Make (faster builds)")
parser.add_argument("-m", action='store_true',
help="Check that all module names are unique")
return parser.parse_args()
def main():
args = get_args()
if args.m:
check_module_names()
return
# Setup
global NO_OF_THREADS, fast_tests, std_f23_tests, nofast_llvm16, separate_compilation, use_ninja, user_specified_threads
os.environ["PATH"] += os.pathsep + LFORTRAN_PATH
# Set environment variable for testing
os.environ["LFORTRAN_TEST_ENV_VAR"] = "STATUS OK!"
# delete previously created directories (if any)
for backend in SUPPORTED_BACKENDS:
run_cmd(f"rm -rf {BASE_DIR}/test-{backend}")
if args.no_of_threads:
NO_OF_THREADS = args.no_of_threads
user_specified_threads = True
fast_tests = "yes" if args.fast else "no"
nofast_llvm16 = "yes" if args.no_fast_till_llvm16 else "no"
separate_compilation = "yes" if args.separate_compilation else "no"
use_ninja = args.ninja
test_pattern = args.test
for backend in args.backends:
test_backend(backend, args.std, test_pattern)
if __name__ == "__main__":
main()
|