/ .github / ci-windows-cross.py
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()