You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.9 KiB
210 lines
5.9 KiB
#!/usr/bin/env python
|
|
"""
|
|
Build script for pyngspice using MinGW-w64.
|
|
|
|
Single-command build that handles PATH setup, editable install,
|
|
and optional venv synchronization for pyTesla integration.
|
|
|
|
Usage:
|
|
python build_mingw.py # Build and install (editable)
|
|
python build_mingw.py --clean # Clean build directory first
|
|
python build_mingw.py --sync # Sync .pth files to other venvs
|
|
python build_mingw.py --verify # Just verify the install works
|
|
python build_mingw.py --debug # Build with debug logging
|
|
|
|
Run from cmd.exe, NOT Git Bash (to avoid MSYS path mangling).
|
|
"""
|
|
|
|
import argparse
|
|
import glob
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# ============================================================================
|
|
# Configuration
|
|
# ============================================================================
|
|
|
|
REPO_ROOT = Path(__file__).parent.resolve()
|
|
MINGW_BIN = r"C:\mingw64\bin"
|
|
CMAKE_BIN = r"C:\Program Files\CMake\bin"
|
|
PACKAGE_DIR = REPO_ROOT / "pyngspice"
|
|
BUILD_DIR = REPO_ROOT / "build"
|
|
|
|
# Other venvs that need access to pyngspice (e.g., pyTesla's venv)
|
|
# Add paths here as needed:
|
|
SYNC_VENVS = [
|
|
# r"C:\git\pytesla\.venv",
|
|
]
|
|
|
|
|
|
def get_clean_path():
|
|
"""Build a clean PATH that avoids Git MSYS conflicts."""
|
|
python_dir = Path(sys.executable).parent
|
|
python_scripts = python_dir / "Scripts"
|
|
|
|
path_parts = [
|
|
str(MINGW_BIN),
|
|
str(python_dir),
|
|
str(python_scripts),
|
|
str(CMAKE_BIN),
|
|
r"C:\Windows\System32",
|
|
r"C:\Windows",
|
|
]
|
|
|
|
return ";".join(p for p in path_parts if os.path.isdir(p))
|
|
|
|
|
|
def get_build_env(**extra):
|
|
"""Get environment variables for the build."""
|
|
env = os.environ.copy()
|
|
env["PATH"] = get_clean_path()
|
|
env["CC"] = "gcc"
|
|
env["CXX"] = "g++"
|
|
env.update(extra)
|
|
return env
|
|
|
|
|
|
def clean():
|
|
"""Remove build directory and old .pyd files."""
|
|
if BUILD_DIR.exists():
|
|
print(f"Removing {BUILD_DIR}...")
|
|
shutil.rmtree(BUILD_DIR, ignore_errors=True)
|
|
|
|
# Remove old .pyd files from package directory
|
|
for pyd in PACKAGE_DIR.glob("*.pyd"):
|
|
print(f"Removing {pyd}...")
|
|
pyd.unlink()
|
|
|
|
# Uninstall any previous install
|
|
subprocess.run(
|
|
[sys.executable, "-m", "pip", "uninstall", "-y", "pyngspice"],
|
|
capture_output=True,
|
|
)
|
|
print("Clean complete.")
|
|
|
|
|
|
def build(debug=False):
|
|
"""Build and install pyngspice in editable mode."""
|
|
env = get_build_env()
|
|
|
|
# Verify MinGW is available
|
|
gcc = shutil.which("gcc", path=env["PATH"])
|
|
if not gcc:
|
|
print(f"ERROR: gcc not found. Ensure MinGW is installed at {MINGW_BIN}")
|
|
sys.exit(1)
|
|
|
|
result = subprocess.run(["gcc", "--version"], capture_output=True, text=True, env=env)
|
|
print(f"Using GCC: {result.stdout.splitlines()[0]}")
|
|
|
|
# Build command
|
|
cmd = [
|
|
sys.executable, "-m", "pip", "install",
|
|
"--no-cache-dir",
|
|
"-e", ".",
|
|
]
|
|
|
|
if debug:
|
|
# Pass debug flag via environment (picked up by scikit-build-core)
|
|
env["CMAKE_ARGS"] = "-DCMAKE_BUILD_TYPE=Debug"
|
|
|
|
print(f"\nBuilding pyngspice...")
|
|
print(f" Command: {' '.join(cmd)}")
|
|
print(f" Working dir: {REPO_ROOT}")
|
|
print()
|
|
|
|
result = subprocess.run(cmd, cwd=str(REPO_ROOT), env=env)
|
|
if result.returncode != 0:
|
|
print("\nBuild FAILED.")
|
|
sys.exit(result.returncode)
|
|
|
|
print("\nBuild complete!")
|
|
|
|
|
|
def sync_venvs():
|
|
"""Write .pth files to other venvs so they can import pyngspice."""
|
|
if not SYNC_VENVS:
|
|
print("No venvs configured for sync. Edit SYNC_VENVS in build_mingw.py.")
|
|
return
|
|
|
|
for venv_path in SYNC_VENVS:
|
|
venv = Path(venv_path)
|
|
if not venv.exists():
|
|
print(f" SKIP (not found): {venv}")
|
|
continue
|
|
|
|
# Find site-packages
|
|
if sys.platform == "win32":
|
|
site_packages = venv / "Lib" / "site-packages"
|
|
else:
|
|
py_ver = f"python{sys.version_info.major}.{sys.version_info.minor}"
|
|
site_packages = venv / "lib" / py_ver / "site-packages"
|
|
|
|
if not site_packages.exists():
|
|
print(f" SKIP (no site-packages): {venv}")
|
|
continue
|
|
|
|
pth_file = site_packages / "pyngspice.pth"
|
|
pth_file.write_text(str(REPO_ROOT) + "\n")
|
|
print(f" Synced: {pth_file}")
|
|
|
|
print("Venv sync complete.")
|
|
|
|
|
|
def verify():
|
|
"""Verify the pyngspice installation works."""
|
|
print("\nVerifying pyngspice installation...")
|
|
|
|
# Test basic import
|
|
result = subprocess.run(
|
|
[sys.executable, "-c", (
|
|
"from pyngspice import NgspiceRunner, _cpp_available; "
|
|
"print(f' C++ extension: {_cpp_available}'); "
|
|
"print(f' NgspiceRunner available: {NgspiceRunner.detect()}'); "
|
|
"from pyngspice import __version__; "
|
|
"print(f' Version: {__version__}'); "
|
|
"print(' OK!')"
|
|
)],
|
|
cwd=str(REPO_ROOT),
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
print("\nVerification FAILED.")
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Build pyngspice with MinGW-w64")
|
|
parser.add_argument("--clean", action="store_true", help="Clean build artifacts first")
|
|
parser.add_argument("--sync", action="store_true", help="Sync .pth files to other venvs")
|
|
parser.add_argument("--verify", action="store_true", help="Verify installation only")
|
|
parser.add_argument("--debug", action="store_true", help="Build with debug configuration")
|
|
parser.add_argument("--no-verify", action="store_true", help="Skip post-build verification")
|
|
args = parser.parse_args()
|
|
|
|
if args.verify:
|
|
verify()
|
|
return
|
|
|
|
if args.clean:
|
|
clean()
|
|
|
|
if args.sync:
|
|
sync_venvs()
|
|
return
|
|
|
|
build(debug=args.debug)
|
|
|
|
if not args.no_verify:
|
|
verify()
|
|
|
|
if SYNC_VENVS:
|
|
sync_venvs()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|