import argparse
import os
import re
import shutil
import subprocess
import sys
from pathlib import Path
from multiprocessing import cpu_count
RUST_DIR = Path(__file__).parent.absolute()
PROJECT_ROOT = RUST_DIR.parent
OUTPUT_DIR = RUST_DIR / "dist"
TARGETS = [
("x86_64-unknown-linux-musl", "winload", "winload-linux-x86_64"),
]
def robust_rmtree(path: Path):
if not path.exists():
return
if sys.platform == "win32":
shutil.rmtree(path)
return
ret = subprocess.run(["rm", "-rf", str(path)], check=False)
if ret.returncode == 0 and not path.exists():
return
str_path = str(path)
if str_path.startswith("/mnt/"):
parts = str_path.split("/") drive = parts[2].upper() + ":" win_path = drive + "\\" + "\\".join(parts[3:])
ret = subprocess.run(
["cmd.exe", "/c", "rmdir", "/s", "/q", win_path],
check=False,
)
if ret.returncode == 0 and not path.exists():
return
try:
shutil.rmtree(path)
except Exception:
print(f" ⚠️ Warning: Could not fully remove {path}, continuing...")
def extract_version_from_cargo_toml():
cargo_toml = RUST_DIR / "Cargo.toml"
if not cargo_toml.exists():
print("❌ Cargo.toml not found")
return None
with open(cargo_toml, 'r', encoding='utf-8') as f:
content = f.read()
match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
if match:
raw_version = match.group(1)
version = raw_version.split("+")[0]
if version != raw_version:
print(f"📦 Extracted version from Cargo.toml: v{raw_version}")
print(f" (stripped build metadata for filename: v{version})")
else:
print(f"📦 Extracted version from Cargo.toml: v{version}")
return f"v{version}"
print("⚠️ Could not extract version from Cargo.toml")
return None
def run_command(cmd, cwd=None, check=True):
print(f"\n▶ {' '.join(cmd)}")
result = subprocess.run(
cmd,
cwd=cwd,
capture_output=False,
text=True,
check=check,
)
return result.returncode == 0
def ensure_target_installed(target):
print(f"\n📦 Checking target: {target}")
result = subprocess.run(
["rustup", "target", "list", "--installed"],
capture_output=True,
text=True,
check=True,
)
if target not in result.stdout:
print(f" → Installing {target}...")
run_command(["rustup", "target", "add", target])
else:
print(f" ✓ {target} already installed")
def build_target(target, binary_name, output_name, version=None):
print(f"\n🔨 Building {target}...")
target_dir = RUST_DIR / "target" / target
if target_dir.exists():
print(f" → Cleaning {target} artifacts...")
try:
robust_rmtree(target_dir)
except Exception as e:
print(f" ⚠️ Warning: Could not clean {target_dir}: {e}")
parallel_jobs = max(1, cpu_count() // 2) success = run_command(
["cargo", "build", "--release", "--target", target, "-j", str(parallel_jobs)],
cwd=RUST_DIR,
)
if not success:
print(f"❌ Build failed for {target}")
return False
if version:
base_name = output_name
ext = ""
if "." in output_name:
base_name, ext = output_name.rsplit(".", 1)
ext = "." + ext
output_name_versioned = f"{base_name}-{version}{ext}"
else:
output_name_versioned = output_name
source = RUST_DIR / "target" / target / "release" / binary_name
dest = OUTPUT_DIR / output_name_versioned
if not source.exists():
print(f"❌ Binary not found: {source}")
return False
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, dest)
size_mb = dest.stat().st_size / 1024 / 1024
print(f"✓ {output_name_versioned} ({size_mb:.2f} MB)")
return True
def main():
parser = argparse.ArgumentParser(
description="Cross-compile winload for multiple platforms"
)
parser.add_argument(
"--clean",
action="store_true",
help="Run 'cargo clean' before building",
)
args = parser.parse_args()
print("=" * 60)
print("🚀 Building winload for multiple platforms")
print("=" * 60)
version = extract_version_from_cargo_toml()
if not version:
print("⚠️ Building without version number in filename")
if not Path("/proc/version").exists():
print("❌ This script must be run in WSL")
sys.exit(1)
with open("/proc/version") as f:
if "microsoft" not in f.read().lower():
print("⚠️ Warning: This doesn't look like WSL")
if args.clean:
print("\n🧹 Running cargo clean...")
if run_command(["cargo", "clean"], cwd=RUST_DIR, check=False):
print(" ✓ Cleaned successfully")
else:
print(" ⚠️ cargo clean failed, continuing anyway...")
for target, _, _ in TARGETS:
ensure_target_installed(target)
if OUTPUT_DIR.exists():
print(f"\n🧹 Cleaning {OUTPUT_DIR}...")
robust_rmtree(OUTPUT_DIR)
success_count = 0
for target, binary, output in TARGETS:
if build_target(target, binary, output, version):
success_count += 1
print("\n" + "=" * 60)
print(f"📊 Build Summary: {success_count}/{len(TARGETS)} succeeded")
print("=" * 60)
if OUTPUT_DIR.exists():
print(f"\n📦 Output directory: {OUTPUT_DIR}")
for item in sorted(OUTPUT_DIR.iterdir()):
size_mb = item.stat().st_size / 1024 / 1024
print(f" • {item.name} ({size_mb:.2f} MB)")
sys.exit(0 if success_count == len(TARGETS) else 1)
if __name__ == "__main__":
main()