import argparse
import os
import subprocess
import sys
from pathlib import Path
def run_command(cmd, description, check=True):
print(f"\n🔧 {description}")
print(f" Running: {' '.join(cmd)}")
try:
result = subprocess.run(cmd, check=check, capture_output=True, text=True)
if result.stdout:
print(f" Output: {result.stdout.strip()}")
return result.returncode == 0
except subprocess.CalledProcessError as e:
print(f" ❌ Error: {e}")
if e.stdout:
print(f" Stdout: {e.stdout}")
if e.stderr:
print(f" Stderr: {e.stderr}")
return False
def check_dependencies():
print("🔍 Checking build dependencies...")
dependencies = {
'maturin': ['maturin', '--version'],
'cargo': ['cargo', '--version'],
'python': ['python', '--version'],
}
missing = []
for name, cmd in dependencies.items():
if not run_command(cmd, f"Checking {name}", check=False):
missing.append(name)
if missing:
print(f"\n❌ Missing dependencies: {', '.join(missing)}")
print("\nTo install missing dependencies:")
if 'maturin' in missing:
print(" pip install maturin")
if 'cargo' in missing:
print(" Install Rust: https://rustup.rs/")
return False
print("✅ All dependencies available")
return True
def build_package(mode='develop', features=None, target=None):
cmd = ['maturin']
if mode == 'develop':
cmd.append('develop')
description = "Building development package"
elif mode == 'build':
cmd.append('build')
description = "Building package"
elif mode == 'release':
cmd.extend(['build', '--release'])
description = "Building release package"
else:
raise ValueError(f"Unknown build mode: {mode}")
if features:
cmd.extend(['--features', features])
description += f" with features: {features}"
if target:
cmd.extend(['--target', target])
description += f" for target: {target}"
return run_command(cmd, description)
def run_tests():
print("\n🧪 Running Python tests...")
if not run_command(['python', '-m', 'pytest', '--version'],
"Checking pytest", check=False):
print(" Installing pytest...")
if not run_command(['pip', 'install', 'pytest', 'pytest-asyncio'],
"Installing pytest"):
return False
test_cmd = [
'python', '-m', 'pytest',
'tests/python/',
'-v',
'--tb=short'
]
return run_command(test_cmd, "Running Python test suite")
def check_package():
print("\n🔍 Checking package...")
import_cmd = [
'python', '-c',
'import voirs_ffi; print(f"✅ Package imported successfully, version: {voirs_ffi.__version__}")'
]
return run_command(import_cmd, "Testing package import")
def build_wheel():
return run_command(['maturin', 'build', '--release'], "Building wheel")
def main():
parser = argparse.ArgumentParser(description="Build VoiRS FFI Python package")
parser.add_argument(
'--mode',
choices=['develop', 'build', 'release'],
default='develop',
help='Build mode (default: develop)'
)
parser.add_argument(
'--features',
help='Comma-separated list of features to enable'
)
parser.add_argument(
'--target',
help='Target platform for cross-compilation'
)
parser.add_argument(
'--test',
action='store_true',
help='Run tests after building'
)
parser.add_argument(
'--wheel',
action='store_true',
help='Build wheel distribution'
)
parser.add_argument(
'--check',
action='store_true',
help='Check package after building'
)
parser.add_argument(
'--skip-deps',
action='store_true',
help='Skip dependency checking'
)
args = parser.parse_args()
print("🏗️ VoiRS FFI Python Package Builder")
print("=" * 40)
if not args.skip_deps and not check_dependencies():
sys.exit(1)
success = True
if args.wheel:
success = build_wheel()
else:
success = build_package(
mode=args.mode,
features=args.features,
target=args.target
)
if not success:
print("\n❌ Build failed")
sys.exit(1)
if args.check:
if not check_package():
print("\n❌ Package check failed")
sys.exit(1)
if args.test:
if not run_tests():
print("\n❌ Tests failed")
sys.exit(1)
print("\n✅ Build completed successfully!")
if args.mode == 'develop':
print("\n📝 Next steps:")
print(" • Test the package: python -c 'import voirs_ffi; print(voirs_ffi.__version__)'")
print(" • Run tests: python -m pytest tests/python/")
print(" • Check examples: cd examples && python example.py")
if __name__ == '__main__':
main()