ci-windows-cross.py
1 #!/usr/bin/env python3 2 # Copyright (c) The Bitcoin Core developers 3 # Distributed under the MIT software license, see the accompanying 4 # file COPYING or https://opensource.org/license/mit/. 5 6 import argparse 7 import os 8 import shlex 9 import subprocess 10 import sys 11 from pathlib import Path 12 13 sys.path.append(str(Path(__file__).resolve().parent.parent / "test")) 14 from download_utils import download_script_assets 15 16 17 def run(cmd, **kwargs): 18 print("+ " + shlex.join(cmd), flush=True) 19 kwargs.setdefault("check", True) 20 try: 21 return subprocess.run(cmd, **kwargs) 22 except Exception as e: 23 sys.exit(str(e)) 24 25 26 def print_version(): 27 bitcoind = Path.cwd() / "bin" / "bitcoind.exe" 28 run([str(bitcoind), "-version"]) 29 30 31 def check_manifests(): 32 release_dir = Path.cwd() / "bin" 33 manifest_path = release_dir / "bitcoind.manifest" 34 35 cmd_bitcoind_manifest = [ 36 "mt.exe", 37 "-nologo", 38 f"-inputresource:{release_dir / 'bitcoind.exe'}", 39 f"-out:{manifest_path}", 40 ] 41 run(cmd_bitcoind_manifest) 42 print(manifest_path.read_text()) 43 44 skipped = { # Skip as they currently do not have manifests 45 "fuzz.exe", 46 "bench_bitcoin.exe", 47 "test_kernel.exe", 48 } 49 for entry in release_dir.iterdir(): 50 if entry.suffix.lower() != ".exe": 51 continue 52 if entry.name in skipped: 53 print(f"Skipping {entry.name} (no manifest present)") 54 continue 55 print(f"Checking {entry.name}") 56 run(["mt.exe", "-nologo", f"-inputresource:{entry}", "-validate_manifest"]) 57 58 59 def prepare_tests(): 60 workspace = Path.cwd() 61 config_path = workspace / "test" / "config.ini" 62 rpcauth_path = workspace / "share" / "rpcauth" / "rpcauth.py" 63 replacements = { 64 "SRCDIR=": f"SRCDIR={workspace}", 65 "BUILDDIR=": f"BUILDDIR={workspace}", 66 "RPCAUTH=": f"RPCAUTH={rpcauth_path}", 67 } 68 lines = config_path.read_text().splitlines() 69 for index, line in enumerate(lines): 70 for prefix, new_value in replacements.items(): 71 if line.startswith(prefix): 72 lines[index] = new_value 73 break 74 content = "\n".join(lines) + "\n" 75 config_path.write_text(content) 76 print(content) 77 previous_releases_dir = Path(os.environ["PREVIOUS_RELEASES_DIR"]) 78 cmd_download_prev_rel = [ 79 sys.executable, 80 str(workspace / "test" / "get_previous_releases.py"), 81 "--target-dir", 82 str(previous_releases_dir), 83 ] 84 run(cmd_download_prev_rel) 85 run([sys.executable, "-m", "pip", "install", "pyzmq"]) 86 87 dest = workspace / "unit_test_data" 88 download_script_assets(dest) 89 90 91 def run_functional_tests(): 92 workspace = Path.cwd() 93 num_procs = str(os.process_cpu_count()) 94 test_runner_cmd = [ 95 sys.executable, 96 str(workspace / "test" / "functional" / "test_runner.py"), 97 "--jobs", 98 num_procs, 99 "--quiet", 100 f"--tmpdirprefix={workspace}", 101 "--combinedlogslen=99999999", 102 *shlex.split(os.environ.get("TEST_RUNNER_EXTRA", "").strip()), 103 # feature_unsupported_utxo_db.py fails on Windows because of emojis in the test data directory. 104 "--exclude", 105 "feature_unsupported_utxo_db.py", 106 # See https://github.com/bitcoin/bitcoin/issues/31409. 107 "--exclude", 108 "wallet_multiwallet.py", 109 ] 110 run(test_runner_cmd) 111 112 # Run feature_unsupported_utxo_db sequentially in ASCII-only tmp dir, 113 # because it is excluded above due to lack of UTF-8 support in the 114 # ancient release. 115 cmd_feature_unsupported_db = [ 116 sys.executable, 117 str(workspace / "test" / "functional" / "feature_unsupported_utxo_db.py"), 118 "--previous-releases", 119 "--tmpdir", 120 str(Path(workspace) / "test_feature_unsupported_utxo_db"), 121 ] 122 run(cmd_feature_unsupported_db) 123 124 125 def run_unit_tests(): 126 workspace = Path.cwd() 127 os.environ["DIR_UNIT_TEST_DATA"] = str(workspace / "unit_test_data") 128 # Can't use ctest here like other jobs as we don't have a CMake build tree. 129 commands = [ 130 ["./bin/test_bitcoin-qt.exe"], 131 # Intentionally run sequentially here, to catch test case failures caused by dirty global state from prior test cases: 132 ["./bin/test_bitcoin.exe", "-l", "test_suite"], 133 ["./src/secp256k1/bin/exhaustive_tests.exe"], 134 ["./src/secp256k1/bin/noverify_tests.exe"], 135 ["./src/secp256k1/bin/tests.exe"], 136 ["./src/univalue/object.exe"], 137 ["./src/univalue/unitester.exe"], 138 ] 139 for cmd in commands: 140 run(cmd) 141 142 143 def main(): 144 parser = argparse.ArgumentParser(description="Utility to run Windows CI steps.") 145 steps = list(map(lambda f: f.__name__, [ 146 print_version, 147 check_manifests, 148 prepare_tests, 149 run_unit_tests, 150 run_functional_tests, 151 ])) 152 parser.add_argument("step", choices=steps, help="CI step to perform.") 153 args = parser.parse_args() 154 155 os.environ.setdefault( 156 "PREVIOUS_RELEASES_DIR", 157 str(Path.cwd() / "previous_releases"), 158 ) 159 160 exec(f'{args.step}()') 161 162 163 if __name__ == "__main__": 164 main()