|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +import shutil |
| 4 | +import subprocess |
| 5 | +import sys |
| 6 | +import os |
| 7 | +import urllib.request |
| 8 | +import json |
| 9 | +import argparse |
| 10 | +import tempfile |
| 11 | +import zipfile |
| 12 | + |
| 13 | +ENV_FILE = ".z3env" |
| 14 | + |
| 15 | +def is_command_available(cmd): |
| 16 | + return shutil.which(cmd) is not None |
| 17 | + |
| 18 | +def install_with_yum(): |
| 19 | + print("Detected yum. Installing clang...", file=sys.stderr) |
| 20 | + try: |
| 21 | + subprocess.run(['yum', 'install', '-y', 'clang'], check=True) |
| 22 | + print("clang installed successfully.", file=sys.stderr) |
| 23 | + except subprocess.CalledProcessError: |
| 24 | + print("Failed to install clang using yum.", file=sys.stderr) |
| 25 | + return |
| 26 | + |
| 27 | +def install_with_apt(): |
| 28 | + print("Detected apt. Installing llvm-dev, libclang-dev, and clang...", file=sys.stderr) |
| 29 | + try: |
| 30 | + subprocess.run(['apt', 'update'], check=True) |
| 31 | + subprocess.run(['apt', 'install', '-y', 'build-essential', 'libc6-dev', 'gcc-multilib'], check=True) |
| 32 | + print("Packages installed successfully via apt.", file=sys.stderr) |
| 33 | + except subprocess.CalledProcessError: |
| 34 | + print("Failed to install packages using apt.", file=sys.stderr) |
| 35 | + |
| 36 | +def write_env_file(z3_header_path, z3_lib_path): |
| 37 | + with open(ENV_FILE, "w") as f: |
| 38 | + f.write(f"export Z3_SYS_Z3_HEADER={z3_header_path}\n") |
| 39 | + f.write(f"export LD_LIBRARY_PATH={z3_lib_path}") |
| 40 | + f.write(f"export Z3_PATH={z3_lib_path}/libz3.so") |
| 41 | + f.write(f"export RUSTFLAGS='-L native={z3_lib_path}'") |
| 42 | + print(f"\n✅ Z3 environment variables written to `{ENV_FILE}`.") |
| 43 | + print(f"👉 To load them into your shell, run:\n") |
| 44 | + print(f" source ./{ENV_FILE}\n") |
| 45 | + |
| 46 | +def install_z3_glibc(target_platform): |
| 47 | + print(f"Fetching and installing Z3 Linux glibc build for target platform '{target_platform}'...", file=sys.stderr) |
| 48 | + |
| 49 | + # GitHub API to fetch the latest release |
| 50 | + api_url = "https://api.github.com/repos/Z3Prover/z3/releases/latest" |
| 51 | + with urllib.request.urlopen(api_url) as response: |
| 52 | + data = json.loads(response.read().decode()) |
| 53 | + |
| 54 | + assets = data.get("assets", []) |
| 55 | + |
| 56 | + # Define glibc build prefix based on target platform |
| 57 | + if target_platform == 'x86_64': # x64 architecture |
| 58 | + glibc_arch_prefix = "x64-glibc" |
| 59 | + elif target_platform == 'aarch64': # ARM64 architecture |
| 60 | + glibc_arch_prefix = "arm64-glibc" |
| 61 | + else: |
| 62 | + raise Exception(f"Unsupported target platform: {target_platform}") |
| 63 | + |
| 64 | + # Search for the correct glibc build file in the release assets |
| 65 | + glibc_url = None |
| 66 | + for asset in assets: |
| 67 | + if glibc_arch_prefix in asset["name"] and asset["name"].endswith(".zip"): |
| 68 | + glibc_url = asset["browser_download_url"] |
| 69 | + break |
| 70 | + |
| 71 | + if not glibc_url: |
| 72 | + raise Exception(f"Could not find a suitable Z3 glibc build for platform {target_platform}.") |
| 73 | + |
| 74 | + # Download and extract the glibc build |
| 75 | + print(f"Downloading glibc build from {glibc_url}...", file=sys.stderr) |
| 76 | + with tempfile.TemporaryDirectory() as tmpdir: |
| 77 | + zip_path = os.path.join(tmpdir, "z3-glibc.zip") |
| 78 | + urllib.request.urlretrieve(glibc_url, zip_path) |
| 79 | + |
| 80 | + print("Extracting Z3 glibc archive...", file=sys.stderr) |
| 81 | + with zipfile.ZipFile(zip_path, "r") as zip_ref: |
| 82 | + zip_ref.extractall(tmpdir) |
| 83 | + |
| 84 | + # Find the extracted Z3 directory |
| 85 | + extracted_dirs = [d for d in os.listdir(tmpdir) if os.path.isdir(os.path.join(tmpdir, d))] |
| 86 | + if not extracted_dirs: |
| 87 | + raise Exception("Extraction failed or directory structure unexpected.") |
| 88 | + z3_dir = os.path.join(tmpdir, extracted_dirs[0]) |
| 89 | + |
| 90 | + # Set the include and lib directories |
| 91 | + include_dir = os.path.join(z3_dir, "include") |
| 92 | + lib_dir = os.path.join(z3_dir, "bin") |
| 93 | + |
| 94 | + # Create a stable location for Z3 |
| 95 | + stable_install_dir = "/usr/local/z3" |
| 96 | + subprocess.run(['mkdir', '-p', stable_install_dir], check=True) |
| 97 | + subprocess.run(['cp', '-r', include_dir, stable_install_dir], check=True) |
| 98 | + subprocess.run(['cp', os.path.join(lib_dir, 'libz3.so'), '/usr/local/lib/'], check=True) |
| 99 | + subprocess.run(['ldconfig'], check=True) |
| 100 | + |
| 101 | + # Set paths for z3.h and libz3.so |
| 102 | + z3_header_path = os.path.join(stable_install_dir, "include", "z3.h") |
| 103 | + z3_lib_path = "/usr/local/lib" |
| 104 | + |
| 105 | + # Write the paths to the environment file |
| 106 | + write_env_file(z3_header_path, z3_lib_path) |
| 107 | + |
| 108 | +def main(): |
| 109 | + parser = argparse.ArgumentParser(description="Install Z3 glibc build for a target platform.") |
| 110 | + parser.add_argument("target_platform", choices=["x86_64", "aarch64"], |
| 111 | + help="Target platform architecture (e.g., x86_64 or aarch64).") |
| 112 | + args = parser.parse_args() |
| 113 | + |
| 114 | + # Check if 'yum' or 'apt' are available for optional package installation |
| 115 | + if is_command_available('yum'): |
| 116 | + install_with_yum() |
| 117 | + elif is_command_available('apt'): |
| 118 | + install_with_apt() |
| 119 | + else: |
| 120 | + print("❌ Neither yum nor apt found on this system.", file=sys.stderr) |
| 121 | + |
| 122 | + # Install Z3 glibc build based on the target platform |
| 123 | + install_z3_glibc(args.target_platform) |
| 124 | + |
| 125 | +if __name__ == "__main__": |
| 126 | + main() |
0 commit comments