taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

configure.py (3403B)


      1 #!/usr/bin/env python3
      2 
      3 """Configure the taler-rust installation prefix and Rust toolchain."""
      4 
      5 from __future__ import annotations
      6 
      7 import argparse
      8 import os
      9 from pathlib import Path
     10 import re
     11 import shutil
     12 import subprocess
     13 import sys
     14 
     15 
     16 # Supported Rust baseline from trixie-backports; keep in sync with Cargo.toml.
     17 RUST_MIN_VERSION = "1.95"
     18 BUILD_VARIABLE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*=.*", re.DOTALL)
     19 
     20 
     21 def version_tuple(version: str) -> tuple[int, int, int]:
     22     parts = [int(part) for part in version.split(".")[:3]]
     23     return tuple(parts + [0] * (3 - len(parts)))
     24 
     25 
     26 def tool_version(path: str) -> str | None:
     27     try:
     28         output = subprocess.run(
     29             [path, "--version"],
     30             check=True,
     31             stdout=subprocess.PIPE,
     32             stderr=subprocess.PIPE,
     33             text=True,
     34         ).stdout
     35     except (OSError, subprocess.SubprocessError):
     36         return None
     37     found = re.search(r"\d+(?:\.\d+)+", output)
     38     return found.group(0) if found else None
     39 
     40 
     41 def find_tool(name: str) -> tuple[str, str | None]:
     42     requested = os.environ.get(name.upper()) or name
     43     path = shutil.which(requested)
     44     if path is None:
     45         print(f"configure: error: tool '{name}' not available", file=sys.stderr)
     46         print(
     47             f"configure: hint: install '{name}' or point {name.upper()}= at it",
     48             file=sys.stderr,
     49         )
     50         raise SystemExit(1)
     51     return path, tool_version(path)
     52 
     53 
     54 def parse_args() -> argparse.Namespace:
     55     parser = argparse.ArgumentParser(
     56         usage="./configure [--prefix=DIR] [VARIABLE=VALUE]...",
     57         description="Configure the installation prefix and Rust toolchain.",
     58     )
     59     parser.add_argument("--prefix", default="/usr/local", help="installation prefix")
     60     parser.add_argument("build_variables", nargs="*", metavar="VARIABLE=VALUE")
     61     args = parser.parse_args()
     62     if not args.prefix:
     63         parser.error("installation prefix must not be empty")
     64     for argument in args.build_variables:
     65         if not BUILD_VARIABLE.fullmatch(argument):
     66             parser.error(f"unrecognized argument: {argument}")
     67         variable = argument.partition("=")[0]
     68         print(
     69             f"configure: WARNING: unsupported variable '{variable}' is ignored",
     70             file=sys.stderr,
     71         )
     72     return args
     73 
     74 
     75 def main() -> int:
     76     args = parse_args()
     77     cargo_path, cargo_version = find_tool("cargo")
     78     rustc_path, rustc_version = find_tool("rustc")
     79 
     80     for name, path, version in (
     81         ("cargo", cargo_path, cargo_version),
     82         ("rustc", rustc_path, rustc_version),
     83     ):
     84         suffix = f" (version {version})" if version else ""
     85         print(f"found {name} as {path}{suffix}")
     86 
     87     if rustc_version and version_tuple(rustc_version) < version_tuple(RUST_MIN_VERSION):
     88         print(
     89             f"configure: WARNING: rustc {rustc_version} is older than"
     90             f" {RUST_MIN_VERSION}; this tree will not compile with it."
     91             " On Debian stable: apt install -t trixie-backports rustc cargo",
     92             file=sys.stderr,
     93         )
     94 
     95     config = (
     96         "# This makefile fragment is generated by configure.\n"
     97         f"prefix = {args.prefix}\n"
     98         f"cargo = {cargo_path}\n"
     99         f"rustc = {rustc_path}\n"
    100     )
    101     Path(".config.mk").write_text(config, encoding="utf-8")
    102     print("writing .config.mk")
    103     return 0
    104 
    105 
    106 if __name__ == "__main__":
    107     sys.exit(main())