1 2""" 3 Copyright Kristjan Kongas 2020 4 5 Boost Software License - Version 1.0 - August 17th, 2003 6 7 Permission is hereby granted, free of charge, to any person or organization 8 obtaining a copy of the software and accompanying documentation covered by 9 this license (the "Software") to use, reproduce, display, distribute, 10 execute, and transmit the Software, and to prepare derivative works of the 11 Software, and to permit third-parties to whom the Software is furnished to 12 do so, all subject to the following: 13 14 The copyright notices in the Software and this entire statement, including 15 the above license grant, this restriction and the following disclaimer, 16 must be included in all copies of the Software, in whole or in part, and 17 all derivative works of the Software, unless such copies or derivative 18 works are solely in the form of machine-executable object code generated by 19 a source language processor. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 DEALINGS IN THE SOFTWARE. 28""" 29 30from pathlib import Path 31import os, shutil, subprocess, sys, argparse, json, functools, random 32print = functools.partial(print, flush=True) 33 34description = """Batch-test combinations of compilers and cmake settings. CMake root directory needs to contain a file called `.release_tests.json`. Example contents: 35{ 36 "compiler_prefix": "/usr/bin/", 37 "compilers": [ 38 {"cc": "gcc", "cxx": "g++", "standards": [11, 14, 17, 20]}, 39 {"cc": "clang", "cxx": "clang++", "standards": [11, 14, 17, 20]}, 40 {"generator": "Unix Makefiles", "standards": [11, 14, 17, 20]} 41 ], 42 "cmake_build_types": ["Debug", "", "Release"] 43} 44 45""" 46 47def main(): 48 parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawTextHelpFormatter) 49 parser.add_argument("cmake_root") 50 args = parser.parse_args() 51 cmake_root = Path(args.cmake_root).absolute() 52 53 # A separate build directory is created to ensure clearing cache doesn't unintentionally remove other files 54 build_dir = "build_" + str(random.randint(0, 100 * 1000 * 1000)) 55 os.mkdir(build_dir) 56 os.chdir(build_dir) 57 58 json_path = cmake_root / ".release_tests.json" 59 with open(json_path, "r") as json_file: 60 setup = json.load(json_file) 61 try: 62 batch_test(cmake_root, setup.get("compiler_prefix"), setup["compilers"], setup["cmake_build_types"]) 63 except KeyboardInterrupt: 64 print("Tests interrupted") 65 66 os.chdir("..") 67 shutil.rmtree(build_dir) 68 69 70def batch_test(cmake_root, prefix, compilers, cmake_build_types): 71 for comp in compilers: 72 assert ("cc" in comp and "cxx" in comp) or "generator" in comp 73 74 print() 75 print() 76 if "cc" in comp and "cxx" in comp: 77 print("Testing {} and {}".format(comp["cc"], comp["cxx"])) 78 if "generator" in comp: 79 print("Testing {}".format(comp["generator"])) 80 print() 81 for standard in comp["standards"]: 82 for build_type in cmake_build_types: 83 print_build = "Default" if build_type == "" else build_type 84 print("Testing " + print_build + " mode, C++" + str(standard)) 85 86 clear_cmake_cache() 87 88 # Build and test 89 cmd = [ 90 "cmake", cmake_root, 91 "-D", "CMAKE_CXX_STANDARD=" + str(standard), 92 "-D", "CMAKE_BUILD_TYPE=" + build_type 93 ] 94 95 env = dict(os.environ) 96 if "cc" in comp and "cxx" in comp: 97 env.update({"CC": str(Path(prefix) / comp["cc"]), "CXX": str(Path(prefix) / comp["cxx"])}) 98 if "generator" in comp: 99 env.update({"CMAKE_GENERATOR": comp["generator"]}) 100 101 run(cmd, env=env) 102 run(["cmake", "--build", "."]) 103 run([sys.executable, "tests/run_standard_tests.py"]) 104 105 print() 106 print("++++++++++ SUCCESS ++++++++++") 107 print() 108 109 110def run(cmd, env=None): 111 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) 112 if result.returncode != 0: 113 print("STDOUT:") 114 print(str(result.stdout, "utf-8")) 115 print() 116 print("STDERR:") 117 print(str(result.stderr, "utf-8")) 118 print() 119 print() 120 print("---------- FAILURE ----------") 121 print() 122 sys.exit(1) 123 124 125def clear_cmake_cache(): 126 for dir_path, _, files in os.walk("."): 127 if dir_path[-len("CMakeLists"):] == "CMakeLists": 128 shutil.rmtree(dir_path) 129 for file in files: 130 if file == "CMakeCache.txt": 131 os.remove(Path(dir_path) / file) 132 133 134if __name__ == "__main__": 135 main() 136