add build to pyd
This commit is contained in:
@@ -5,3 +5,4 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
orisys.egg-info/
|
orisys.egg-info/
|
||||||
/.cursor/
|
/.cursor/
|
||||||
|
/dist_pyd/
|
||||||
+902
@@ -0,0 +1,902 @@
|
|||||||
|
"""
|
||||||
|
Build script to create wheel distributions with .pyd/.so files instead of .py files.
|
||||||
|
|
||||||
|
This script uses Cython to compile Python source files into extension modules (.pyd on Windows, .so on Linux),
|
||||||
|
making the source code completely hidden in the final distribution.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Cython
|
||||||
|
- A C compiler (Visual Studio Build Tools on Windows, GCC on Linux)
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import platform
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
# Package version
|
||||||
|
VERSION = "0.2.0"
|
||||||
|
|
||||||
|
# Python versions to build for
|
||||||
|
PYTHON_VERSIONS = ['3.8', '3.9', '3.10']
|
||||||
|
|
||||||
|
# Detect platform
|
||||||
|
IS_WINDOWS = platform.system() == 'Windows'
|
||||||
|
IS_LINUX = platform.system() == 'Linux'
|
||||||
|
PLATFORM_NAME = 'win' if IS_WINDOWS else 'linux' if IS_LINUX else 'unknown'
|
||||||
|
|
||||||
|
# Extension for compiled modules
|
||||||
|
EXT_EXTENSION = '.pyd' if IS_WINDOWS else '.so'
|
||||||
|
|
||||||
|
|
||||||
|
def clean_build_dirs():
|
||||||
|
"""Remove previous build artifacts."""
|
||||||
|
dirs_to_clean = ['build', 'dist', 'orisys.egg-info', 'dist_pyd', 'build_pyd']
|
||||||
|
for dir_name in dirs_to_clean:
|
||||||
|
if os.path.exists(dir_name):
|
||||||
|
print(f"Cleaning {dir_name}...")
|
||||||
|
try:
|
||||||
|
# Try to remove with error handling
|
||||||
|
shutil.rmtree(dir_name, ignore_errors=False, onerror=_handle_remove_error)
|
||||||
|
except (PermissionError, OSError) as e:
|
||||||
|
print(f" ⚠️ Warning: Could not fully remove {dir_name}: {e}")
|
||||||
|
print(f" Some files may be locked. Continuing anyway...")
|
||||||
|
# Try again with ignore_errors=True
|
||||||
|
try:
|
||||||
|
shutil.rmtree(dir_name, ignore_errors=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Ensure dist directory exists with platform subdirectories
|
||||||
|
os.makedirs(f'dist/{PLATFORM_NAME}', exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_remove_error(func, path, exc_info):
|
||||||
|
"""Handle errors during directory removal."""
|
||||||
|
# If it's a permission error, try to change permissions and retry
|
||||||
|
if func is os.rmdir:
|
||||||
|
try:
|
||||||
|
# Try to change permissions and remove
|
||||||
|
os.chmod(path, 0o777)
|
||||||
|
os.rmdir(path)
|
||||||
|
except Exception:
|
||||||
|
# If that fails, just skip it
|
||||||
|
pass
|
||||||
|
elif func is os.remove:
|
||||||
|
try:
|
||||||
|
os.chmod(path, 0o777)
|
||||||
|
os.remove(path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def check_cython():
|
||||||
|
"""Check if Cython is installed."""
|
||||||
|
try:
|
||||||
|
import Cython
|
||||||
|
print(f"✓ Cython {Cython.__version__} is available")
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
print("❌ Cython is not installed!")
|
||||||
|
print(" Install it with: pip install cython")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_c_compiler():
|
||||||
|
"""Check if a C compiler is available."""
|
||||||
|
if IS_WINDOWS:
|
||||||
|
# On Windows, check for cl.exe (Microsoft Visual C++)
|
||||||
|
# Note: cl.exe might not be in PATH even if installed
|
||||||
|
# We'll do a basic check but the actual compilation will provide better errors
|
||||||
|
cl_path = shutil.which('cl.exe')
|
||||||
|
if cl_path:
|
||||||
|
print("✓ C compiler (cl.exe) found in PATH")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("⚠️ C compiler (cl.exe) not found in PATH")
|
||||||
|
print(" (This is OK if Visual Studio is installed but not in PATH)")
|
||||||
|
print(" The build will attempt to use the compiler anyway...")
|
||||||
|
return True # Return True to allow build attempt - it will fail with better error if missing
|
||||||
|
else:
|
||||||
|
# On Linux/Mac, check for gcc
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
['gcc', '--version'], capture_output=True, text=True, timeout=5
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print(f"✓ C compiler (gcc) is available")
|
||||||
|
return True
|
||||||
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("❌ C compiler (gcc) not found!")
|
||||||
|
print("\n Install with:")
|
||||||
|
if IS_LINUX:
|
||||||
|
print(" sudo apt-get install build-essential # Ubuntu/Debian")
|
||||||
|
print(" sudo yum install gcc gcc-c++ # CentOS/RHEL")
|
||||||
|
else:
|
||||||
|
print(" Install Xcode Command Line Tools:")
|
||||||
|
print(" xcode-select --install")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_executable(version):
|
||||||
|
"""Get the Python executable for a specific version based on platform."""
|
||||||
|
current_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||||
|
if current_version == version:
|
||||||
|
return [sys.executable]
|
||||||
|
|
||||||
|
if IS_WINDOWS:
|
||||||
|
username = os.environ.get("USERNAME", "")
|
||||||
|
possible_paths = [
|
||||||
|
f'py -{version}',
|
||||||
|
f'python{version}',
|
||||||
|
f'C:\\Users\\{username}\\anaconda3\\envs\\py{version.replace(".", "")}\\python.exe',
|
||||||
|
f'C:\\Users\\{username}\\anaconda3\\envs\\py{version.replace(".", "")}-env\\python.exe',
|
||||||
|
f'C:\\Users\\{username}\\anaconda3\\envs\\python{version.replace(".", "")}\\python.exe',
|
||||||
|
f'C:\\Users\\{username}\\anaconda3\\envs\\orisys-py{version.replace(".", "")}\\python.exe',
|
||||||
|
f'C:\\Users\\{username}\\miniconda3\\envs\\py{version.replace(".", "")}\\python.exe',
|
||||||
|
f'C:\\Python{version.replace(".", "")}\\python.exe',
|
||||||
|
f'C:\\Program Files\\Python{version.replace(".", "")}\\python.exe',
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
possible_paths = [
|
||||||
|
f'python{version}',
|
||||||
|
f'/usr/bin/python{version}',
|
||||||
|
f'/usr/local/bin/python{version}',
|
||||||
|
f'~/.pyenv/versions/{version}/bin/python',
|
||||||
|
f'/opt/python{version}/bin/python',
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in possible_paths:
|
||||||
|
try:
|
||||||
|
if path.startswith('~'):
|
||||||
|
path = os.path.expanduser(path)
|
||||||
|
|
||||||
|
if IS_WINDOWS and path.startswith('py -'):
|
||||||
|
result = subprocess.run(
|
||||||
|
path.split() + ['-c', 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")'],
|
||||||
|
capture_output=True, text=True, shell=True
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout.strip() == version:
|
||||||
|
return path.split()
|
||||||
|
else:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
[path, '-c', 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode == 0 and result.stdout.strip() == version:
|
||||||
|
return [path]
|
||||||
|
except (FileNotFoundError, subprocess.SubprocessError, PermissionError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def create_cython_setup_files():
|
||||||
|
"""Create setup.py files for Cython compilation."""
|
||||||
|
print("\n=== Creating Cython setup files ===")
|
||||||
|
|
||||||
|
# Find all Python files to compile (excluding __init__.py and configs)
|
||||||
|
src_dir = Path('src/orisys')
|
||||||
|
py_files = []
|
||||||
|
|
||||||
|
for py_file in src_dir.rglob('*.py'):
|
||||||
|
# Skip __init__.py files (they need to stay as .py for package structure)
|
||||||
|
if py_file.name == '__init__.py':
|
||||||
|
continue
|
||||||
|
# Skip getDensity.py (excluded from compilation)
|
||||||
|
if py_file.name == 'getDensity.py':
|
||||||
|
continue
|
||||||
|
# Skip configs directory (JSON files only)
|
||||||
|
if 'configs' in py_file.parts and py_file.name != '__init__.py':
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get relative path from src/orisys
|
||||||
|
rel_path = py_file.relative_to(src_dir)
|
||||||
|
py_files.append(rel_path)
|
||||||
|
|
||||||
|
print(f"Found {len(py_files)} Python files to compile")
|
||||||
|
|
||||||
|
# Create dist_pyd directory structure
|
||||||
|
dist_dir = Path('dist_pyd/src/orisys')
|
||||||
|
dist_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Copy ALL Python source files to dist_pyd (needed for Cython compilation)
|
||||||
|
# Cython needs the source files to compile them
|
||||||
|
print("Copying Python source files for compilation...")
|
||||||
|
for py_file in src_dir.rglob('*.py'):
|
||||||
|
rel_path = py_file.relative_to(src_dir)
|
||||||
|
# dst_file should be in dist_pyd/src/orisys/ (not dist_pyd/src/)
|
||||||
|
dst_file = dist_dir / rel_path
|
||||||
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy(py_file, dst_file)
|
||||||
|
if py_file.name == '__init__.py':
|
||||||
|
print(f" Copied: {rel_path} (will remain as .py)")
|
||||||
|
else:
|
||||||
|
print(f" Copied: {rel_path} (will be compiled to .pyd/.so)")
|
||||||
|
|
||||||
|
# Copy configs directory (JSON files)
|
||||||
|
configs_src = src_dir / 'configs'
|
||||||
|
configs_dst = dist_dir / 'configs'
|
||||||
|
if configs_src.exists():
|
||||||
|
shutil.copytree(configs_src, configs_dst, dirs_exist_ok=True)
|
||||||
|
print(f" Copied: configs/ directory")
|
||||||
|
|
||||||
|
# Create setup.py for Cython compilation
|
||||||
|
# Build the py_files list as a string
|
||||||
|
py_files_str = '[\n'
|
||||||
|
for py_file in py_files:
|
||||||
|
# Convert path to module name (e.g., "sensor.py" -> "orisys.sensor")
|
||||||
|
module_path = str(py_file).replace('\\', '/').replace('.py', '').replace('/', '.')
|
||||||
|
file_path = f"src/orisys/{py_file}"
|
||||||
|
py_files_str += f' ("orisys.{module_path}", "{file_path}"),\n'
|
||||||
|
py_files_str += ']'
|
||||||
|
|
||||||
|
# Build setup.py content using string concatenation to avoid f-string escaping issues
|
||||||
|
# Note: We need to handle Windows multiprocessing properly
|
||||||
|
# The if __name__ == '__main__': guard is required for Windows multiprocessing support
|
||||||
|
setup_content = '''from setuptools import setup, Extension
|
||||||
|
from Cython.Build import cythonize
|
||||||
|
from pathlib import Path
|
||||||
|
import numpy
|
||||||
|
import sys
|
||||||
|
import platform
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Package version
|
||||||
|
VERSION = "''' + VERSION + '''"
|
||||||
|
|
||||||
|
# Get include directories
|
||||||
|
numpy_include = numpy.get_include()
|
||||||
|
|
||||||
|
# On Windows, find Python library for explicit linking if needed
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
import os
|
||||||
|
python_libs_dir = os.path.join(sys.base_prefix, 'libs')
|
||||||
|
python_version = f"{sys.version_info.major}{sys.version_info.minor}"
|
||||||
|
python_lib_name = f"python{python_version}"
|
||||||
|
# Check if library file exists
|
||||||
|
python_lib_path = os.path.join(python_libs_dir, f"{python_lib_name}.lib")
|
||||||
|
has_python_lib = os.path.exists(python_lib_path) if python_libs_dir and os.path.exists(python_libs_dir) else False
|
||||||
|
else:
|
||||||
|
python_libs_dir = None
|
||||||
|
python_lib_name = None
|
||||||
|
python_lib_path = None
|
||||||
|
has_python_lib = False
|
||||||
|
|
||||||
|
# List of Python files to compile (excluding __init__.py)
|
||||||
|
py_files = ''' + py_files_str + '''
|
||||||
|
|
||||||
|
# Create Extension objects
|
||||||
|
extensions = []
|
||||||
|
for module_name, file_path in py_files:
|
||||||
|
# Setup library directories and libraries for Windows
|
||||||
|
library_dirs = []
|
||||||
|
libraries = []
|
||||||
|
extra_link_args = []
|
||||||
|
|
||||||
|
if platform.system() == 'Windows' and has_python_lib:
|
||||||
|
# Explicitly link Python library on Windows
|
||||||
|
library_dirs = [python_libs_dir]
|
||||||
|
# Use libraries parameter to link Python library
|
||||||
|
# This is the standard way setuptools expects
|
||||||
|
libraries = [python_lib_name]
|
||||||
|
|
||||||
|
ext = Extension(
|
||||||
|
module_name,
|
||||||
|
[file_path],
|
||||||
|
include_dirs=[numpy_include],
|
||||||
|
library_dirs=library_dirs,
|
||||||
|
libraries=libraries,
|
||||||
|
# Add any additional compiler flags here
|
||||||
|
extra_compile_args=[],
|
||||||
|
extra_link_args=extra_link_args,
|
||||||
|
)
|
||||||
|
extensions.append(ext)
|
||||||
|
|
||||||
|
# Determine number of threads for parallel compilation
|
||||||
|
# On Windows, disable parallel compilation to avoid multiprocessing spawn issues
|
||||||
|
# On Linux/Mac, use parallel compilation for faster builds
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
nthreads = 1 # Single-threaded on Windows to avoid multiprocessing issues
|
||||||
|
else:
|
||||||
|
nthreads = 4 # Parallel compilation on Linux/Mac
|
||||||
|
|
||||||
|
# Windows multiprocessing requires this guard
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Required for Windows multiprocessing support
|
||||||
|
multiprocessing.freeze_support()
|
||||||
|
|
||||||
|
# On Windows, ensure 64-bit build if Python is 64-bit
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
import sysconfig
|
||||||
|
# Check if Python is 64-bit
|
||||||
|
if sys.maxsize > 2**32:
|
||||||
|
# Force 64-bit build by setting distutils environment
|
||||||
|
import distutils.util
|
||||||
|
# This should force 64-bit build
|
||||||
|
try:
|
||||||
|
# Try to set the platform to win-amd64
|
||||||
|
import distutils._msvccompiler
|
||||||
|
# The compiler should automatically detect 64-bit Python
|
||||||
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="orisys",
|
||||||
|
version=VERSION,
|
||||||
|
description="Orisys - Optical Tactile Sensor SDK (Compiled)",
|
||||||
|
author="Your Name",
|
||||||
|
ext_modules=cythonize(
|
||||||
|
extensions,
|
||||||
|
compiler_directives={
|
||||||
|
'language_level': "3",
|
||||||
|
'embedsignature': True,
|
||||||
|
# Optimize for size and speed
|
||||||
|
'optimize.use_switch': True,
|
||||||
|
'optimize.unpack_method_calls': True,
|
||||||
|
},
|
||||||
|
build_dir="build_pyd",
|
||||||
|
# Create .pyd/.so files instead of .py
|
||||||
|
nthreads=nthreads, # Parallel compilation (disabled on Windows)
|
||||||
|
),
|
||||||
|
zip_safe=False,
|
||||||
|
include_package_data=True,
|
||||||
|
package_data={
|
||||||
|
"orisys": ["configs/*.json"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
|
||||||
|
setup_file = Path('dist_pyd/setup.py')
|
||||||
|
with open(setup_file, 'w') as f:
|
||||||
|
f.write(setup_content)
|
||||||
|
|
||||||
|
print(f"Created setup.py with {len(py_files)} modules to compile")
|
||||||
|
return py_files
|
||||||
|
|
||||||
|
|
||||||
|
def compile_with_cython(python_version):
|
||||||
|
"""Compile Python files to .pyd/.so using Cython."""
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"Compiling with Cython for Python {python_version} on {PLATFORM_NAME}")
|
||||||
|
print('='*60)
|
||||||
|
|
||||||
|
python_cmd = get_python_executable(python_version)
|
||||||
|
|
||||||
|
if not python_cmd:
|
||||||
|
print(f"⚠️ Python {python_version} not found on {PLATFORM_NAME}, skipping...")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Using Python: {' '.join(python_cmd)}")
|
||||||
|
|
||||||
|
# Verify Python version
|
||||||
|
result = subprocess.run(
|
||||||
|
python_cmd + ['-c', 'import sys; print(f"Python {sys.version}")'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
print(result.stdout.strip())
|
||||||
|
|
||||||
|
# Check if Cython is installed in this Python
|
||||||
|
result = subprocess.run(
|
||||||
|
python_cmd + ['-c', 'import Cython; print(f"Cython {Cython.__version__}")'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"❌ Cython not installed for Python {python_version}")
|
||||||
|
print(f" Install with: {' '.join(python_cmd)} -m pip install cython")
|
||||||
|
return False
|
||||||
|
print(result.stdout.strip())
|
||||||
|
|
||||||
|
# Check if numpy is installed (required for compilation)
|
||||||
|
result = subprocess.run(
|
||||||
|
python_cmd + ['-c', 'import numpy; print(f"NumPy {numpy.__version__}")'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"❌ NumPy not installed for Python {python_version}")
|
||||||
|
print(f" Install with: {' '.join(python_cmd)} -m pip install numpy")
|
||||||
|
return False
|
||||||
|
print(result.stdout.strip())
|
||||||
|
|
||||||
|
os.chdir('dist_pyd')
|
||||||
|
|
||||||
|
# On Windows, ensure we use the correct architecture (64-bit)
|
||||||
|
# Check Python architecture first
|
||||||
|
if IS_WINDOWS:
|
||||||
|
arch_check = subprocess.run(
|
||||||
|
python_cmd + ['-c', 'import sys; print("64bit" if sys.maxsize > 2**32 else "32bit")'],
|
||||||
|
capture_output=True, text=True
|
||||||
|
)
|
||||||
|
if arch_check.returncode == 0:
|
||||||
|
python_arch = arch_check.stdout.strip()
|
||||||
|
print(f"Python architecture: {python_arch}")
|
||||||
|
if python_arch == "64bit":
|
||||||
|
# Force 64-bit build on Windows by setting environment variable
|
||||||
|
# This tells distutils to use the 64-bit toolchain
|
||||||
|
os.environ['DISTUTILS_USE_SDK'] = '1'
|
||||||
|
# Also try to find and use vcvars64.bat if available
|
||||||
|
# But setuptools should handle this automatically
|
||||||
|
print(" Using 64-bit build configuration")
|
||||||
|
|
||||||
|
# Build extensions
|
||||||
|
# Don't use --inplace to avoid directory issues
|
||||||
|
# Build to build/lib directory, then copy to src/orisys
|
||||||
|
cmd = python_cmd + ['setup.py', 'build_ext']
|
||||||
|
print(f"Running: {' '.join(cmd)}")
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
|
||||||
|
os.chdir('..')
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"❌ Error compiling with Cython")
|
||||||
|
|
||||||
|
# Check for common compiler errors
|
||||||
|
error_output = result.stderr + "\n" + result.stdout
|
||||||
|
if "cl.exe" in error_output and "failed" in error_output:
|
||||||
|
print("\n ⚠️ C compiler (cl.exe) error detected!")
|
||||||
|
print(" This usually means:")
|
||||||
|
print(" 1. Visual Studio Build Tools are not installed")
|
||||||
|
print(" 2. The compiler is not in your PATH")
|
||||||
|
print(" 3. The Visual Studio environment is not set up")
|
||||||
|
print("\n Solutions:")
|
||||||
|
print(" - Install Visual Studio Build Tools with C++ support")
|
||||||
|
print(" - Or open 'Developer Command Prompt for VS' and run from there")
|
||||||
|
print(" - Or run: vcvars64.bat before building")
|
||||||
|
|
||||||
|
print(f"\n Full error output:\n{result.stderr}")
|
||||||
|
if result.stdout and result.stderr != result.stdout:
|
||||||
|
print(f"\n stdout:\n{result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"✅ Compilation successful for Python {python_version}!")
|
||||||
|
|
||||||
|
# Copy compiled files from build directory to src/orisys
|
||||||
|
build_lib_dir = Path('build')
|
||||||
|
target_dir = Path('src/orisys')
|
||||||
|
|
||||||
|
# Find the build directory (it may be build/lib.win-amd64-cpython-310 or similar)
|
||||||
|
compiled_files = []
|
||||||
|
if build_lib_dir.exists():
|
||||||
|
# Find the actual build output directory
|
||||||
|
for lib_dir in build_lib_dir.glob('lib.*'):
|
||||||
|
orisys_build_dir = lib_dir / 'orisys'
|
||||||
|
if orisys_build_dir.exists():
|
||||||
|
# Copy all .pyd/.so files to src/orisys
|
||||||
|
print(f" Copying compiled files from {orisys_build_dir} to {target_dir}...")
|
||||||
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
for compiled_file in orisys_build_dir.glob(f'*{EXT_EXTENSION}'):
|
||||||
|
# Copy with original filename (e.g., sensor.cp310-win_amd64.pyd)
|
||||||
|
target_file = target_dir / compiled_file.name
|
||||||
|
shutil.copy(compiled_file, target_file)
|
||||||
|
compiled_files.append(target_file)
|
||||||
|
|
||||||
|
# Also create a symlink/copy with base name (e.g., sensor.pyd)
|
||||||
|
# Python can import from either name, but base name is more reliable
|
||||||
|
base_name = compiled_file.stem.split('.')[0] # Get 'sensor' from 'sensor.cp310-win_amd64'
|
||||||
|
base_file = target_dir / f"{base_name}{EXT_EXTENSION}"
|
||||||
|
if not base_file.exists():
|
||||||
|
shutil.copy(compiled_file, base_file)
|
||||||
|
compiled_files.append(base_file)
|
||||||
|
print(f" Copied: {compiled_file.name} -> {target_file} and {base_file.name}")
|
||||||
|
else:
|
||||||
|
print(f" Copied: {compiled_file.name} -> {target_file}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Verify files are in the target location
|
||||||
|
if not compiled_files:
|
||||||
|
compiled_files = list(target_dir.rglob(f'*{EXT_EXTENSION}'))
|
||||||
|
if compiled_files:
|
||||||
|
print(f" Found {len(compiled_files)} existing {EXT_EXTENSION} files in target directory")
|
||||||
|
|
||||||
|
print(f" Created {len(compiled_files)} {EXT_EXTENSION} files")
|
||||||
|
|
||||||
|
# List some example files
|
||||||
|
if compiled_files:
|
||||||
|
print(f" Examples: {', '.join([f.name for f in compiled_files[:3]])}")
|
||||||
|
if len(compiled_files) > 3:
|
||||||
|
print(f" ... and {len(compiled_files) - 3} more")
|
||||||
|
else:
|
||||||
|
print(" ⚠️ Warning: No compiled files found in expected locations")
|
||||||
|
print(" This may indicate a problem with the build process")
|
||||||
|
|
||||||
|
# Remove original .py files (except __init__.py) after successful compilation
|
||||||
|
# This ensures only compiled .pyd/.so files remain
|
||||||
|
print("\n=== Removing original .py source files (keeping only compiled extensions) ===")
|
||||||
|
removed_count = 0
|
||||||
|
for py_file in Path('dist_pyd/src/orisys').rglob('*.py'):
|
||||||
|
# Keep __init__.py files
|
||||||
|
if py_file.name == '__init__.py':
|
||||||
|
continue
|
||||||
|
# Check if corresponding .pyd/.so file exists
|
||||||
|
compiled_file = py_file.with_suffix(EXT_EXTENSION)
|
||||||
|
if compiled_file.exists():
|
||||||
|
try:
|
||||||
|
py_file.unlink()
|
||||||
|
removed_count += 1
|
||||||
|
print(f" Removed: {py_file.relative_to('dist_pyd')}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warning: Could not remove {py_file.name}: {e}")
|
||||||
|
|
||||||
|
if removed_count > 0:
|
||||||
|
print(f"Removed {removed_count} original .py files (compiled versions exist)")
|
||||||
|
else:
|
||||||
|
print("No .py files removed (compiled files may not have been created)")
|
||||||
|
|
||||||
|
# Return True if compilation was successful (returncode == 0)
|
||||||
|
# Even if file verification had issues, compilation itself succeeded
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def create_package_setup(python_version=None):
|
||||||
|
"""Create setup.py for packaging the compiled .pyd/.so files."""
|
||||||
|
print(f"\n=== Creating package setup.py (Python {python_version or '3.8-3.10'}) ===")
|
||||||
|
|
||||||
|
if python_version:
|
||||||
|
version_constraint = f'=={python_version}.*'
|
||||||
|
py_tag = f'py{python_version.replace(".", "")}'
|
||||||
|
else:
|
||||||
|
version_constraint = '>=3.8,<3.11'
|
||||||
|
py_tag = 'py38.py39.py310'
|
||||||
|
|
||||||
|
# Before packaging, ensure .pyd files are in src/orisys
|
||||||
|
# They might still be in build directory, so copy them if needed
|
||||||
|
print("=== Ensuring .pyd files are in src/orisys before packaging ===")
|
||||||
|
orisys_dir = Path('dist_pyd/src/orisys')
|
||||||
|
build_dir = Path('dist_pyd/build')
|
||||||
|
|
||||||
|
# Check if files are in build directory and copy them
|
||||||
|
copied_count = 0
|
||||||
|
if build_dir.exists():
|
||||||
|
for lib_dir in build_dir.glob('lib.*'):
|
||||||
|
orisys_build_dir = lib_dir / 'orisys'
|
||||||
|
if orisys_build_dir.exists():
|
||||||
|
print(f" Found .pyd files in {orisys_build_dir}, copying to src/orisys...")
|
||||||
|
orisys_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
for compiled_file in orisys_build_dir.glob(f'*{EXT_EXTENSION}'):
|
||||||
|
target_file = orisys_dir / compiled_file.name
|
||||||
|
# Always copy to ensure we have the latest version
|
||||||
|
shutil.copy(compiled_file, target_file)
|
||||||
|
copied_count += 1
|
||||||
|
print(f" Copied: {compiled_file.name} -> {target_file}")
|
||||||
|
|
||||||
|
if copied_count > 0:
|
||||||
|
print(f" Successfully copied {copied_count} .pyd files to src/orisys")
|
||||||
|
else:
|
||||||
|
print(" ⚠️ No .pyd files found in build directory to copy")
|
||||||
|
|
||||||
|
# Before packaging, ensure all .py files (except __init__.py) are removed
|
||||||
|
print("=== Final cleanup: Removing any remaining .py source files ===")
|
||||||
|
removed_count = 0
|
||||||
|
if orisys_dir.exists():
|
||||||
|
for py_file in orisys_dir.rglob('*.py'):
|
||||||
|
# Keep __init__.py files
|
||||||
|
if py_file.name == '__init__.py':
|
||||||
|
continue
|
||||||
|
# Check if corresponding .pyd/.so file exists
|
||||||
|
compiled_file = py_file.with_suffix(EXT_EXTENSION)
|
||||||
|
# Check for any .pyd file with this base name
|
||||||
|
base_name = py_file.stem
|
||||||
|
has_compiled = False
|
||||||
|
if compiled_file.exists():
|
||||||
|
has_compiled = True
|
||||||
|
else:
|
||||||
|
# Check for versioned .pyd files (e.g., sensor.cp310-win_amd64.pyd)
|
||||||
|
for pyd_file in orisys_dir.glob(f'{base_name}.*{EXT_EXTENSION}'):
|
||||||
|
has_compiled = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_compiled:
|
||||||
|
try:
|
||||||
|
py_file.unlink()
|
||||||
|
removed_count += 1
|
||||||
|
print(f" Removed: {py_file.relative_to('dist_pyd')}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warning: Could not remove {py_file.name}: {e}")
|
||||||
|
else:
|
||||||
|
# If no compiled version exists, this is a problem
|
||||||
|
print(f" ⚠️ Warning: {py_file.name} has no compiled version, removing anyway")
|
||||||
|
try:
|
||||||
|
py_file.unlink()
|
||||||
|
removed_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warning: Could not remove {py_file.name}: {e}")
|
||||||
|
|
||||||
|
if removed_count > 0:
|
||||||
|
print(f"Removed {removed_count} .py files before packaging")
|
||||||
|
|
||||||
|
# Find all compiled .pyd/.so files in src/orisys
|
||||||
|
compiled_files = list(orisys_dir.rglob(f'*{EXT_EXTENSION}')) if orisys_dir.exists() else []
|
||||||
|
print(f"Found {len(compiled_files)} compiled {EXT_EXTENSION} files in src/orisys")
|
||||||
|
|
||||||
|
if len(compiled_files) == 0:
|
||||||
|
print(" ⚠️ WARNING: No .pyd files found in src/orisys!")
|
||||||
|
print(" This will cause the wheel to be missing compiled modules.")
|
||||||
|
|
||||||
|
# Create MANIFEST.in to ensure .pyd files are included
|
||||||
|
manifest_content = f"""# Include all compiled extensions
|
||||||
|
include src/orisys/*{EXT_EXTENSION}
|
||||||
|
recursive-include src/orisys *{EXT_EXTENSION}
|
||||||
|
|
||||||
|
# Include JSON config files
|
||||||
|
recursive-include src/orisys/configs *.json
|
||||||
|
|
||||||
|
# Exclude all .py files except __init__.py
|
||||||
|
global-exclude *.py
|
||||||
|
include src/orisys/__init__.py
|
||||||
|
recursive-include src/orisys/configs __init__.py
|
||||||
|
"""
|
||||||
|
manifest_file = Path('dist_pyd/MANIFEST.in')
|
||||||
|
with open(manifest_file, 'w') as f:
|
||||||
|
f.write(manifest_content)
|
||||||
|
print("Created MANIFEST.in to ensure .pyd files are included")
|
||||||
|
|
||||||
|
setup_content = f'''from setuptools import setup, find_packages
|
||||||
|
from setuptools.command.build_py import build_py as _build_py
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# Read requirements
|
||||||
|
with open('requirements.txt', 'r') as f:
|
||||||
|
requirements = [line.strip() for line in f if line.strip() and not line.startswith('#')]
|
||||||
|
|
||||||
|
# Find all packages
|
||||||
|
packages = find_packages(where="src")
|
||||||
|
|
||||||
|
# Collect package data (JSON files and .pyd/.so files)
|
||||||
|
# IMPORTANT: .pyd/.so files with version tags (e.g., sensor.cp310-win_amd64.pyd)
|
||||||
|
# are NOT automatically included by setuptools, so we must explicitly list them
|
||||||
|
package_data = {{}}
|
||||||
|
for pkg in packages:
|
||||||
|
pkg_path = Path('src') / pkg.replace('.', '/')
|
||||||
|
if pkg_path.exists():
|
||||||
|
data_files = []
|
||||||
|
|
||||||
|
# Explicitly include compiled extensions (.pyd/.so) using glob pattern
|
||||||
|
# This ensures all .pyd files are included regardless of version tags
|
||||||
|
for pattern in ['*{EXT_EXTENSION}']:
|
||||||
|
for ext_file in pkg_path.glob(pattern):
|
||||||
|
rel_path = ext_file.relative_to(pkg_path)
|
||||||
|
data_files.append(str(rel_path))
|
||||||
|
# Also check subdirectories
|
||||||
|
for ext_file in pkg_path.rglob(pattern):
|
||||||
|
rel_path = ext_file.relative_to(pkg_path)
|
||||||
|
if str(rel_path) not in data_files:
|
||||||
|
data_files.append(str(rel_path))
|
||||||
|
|
||||||
|
# Include JSON files from configs directory
|
||||||
|
if pkg == 'orisys.configs':
|
||||||
|
for json_file in pkg_path.glob('*.json'):
|
||||||
|
rel_path = json_file.relative_to(pkg_path)
|
||||||
|
data_files.append(str(rel_path))
|
||||||
|
|
||||||
|
# For orisys package, include configs subdirectory JSON files
|
||||||
|
if pkg == 'orisys':
|
||||||
|
configs_dir = pkg_path / 'configs'
|
||||||
|
if configs_dir.exists() and configs_dir.is_dir():
|
||||||
|
for json_file in configs_dir.glob('*.json'):
|
||||||
|
rel_path = json_file.relative_to(pkg_path)
|
||||||
|
data_files.append(str(rel_path))
|
||||||
|
|
||||||
|
if data_files:
|
||||||
|
package_data[pkg] = data_files
|
||||||
|
print(f"Package {{pkg}} package_data: {{len(data_files)}} files")
|
||||||
|
|
||||||
|
# Custom build_py to ensure .pyd files are copied
|
||||||
|
class build_py(_build_py):
|
||||||
|
def run(self):
|
||||||
|
"""Override to ensure .pyd files are included"""
|
||||||
|
super().run()
|
||||||
|
# After building, verify .pyd files are in the build directory
|
||||||
|
for pkg in packages:
|
||||||
|
pkg_path = Path('src') / pkg.replace('.', '/')
|
||||||
|
build_pkg_path = Path(self.build_lib) / pkg.replace('.', '/')
|
||||||
|
if pkg_path.exists() and build_pkg_path.exists():
|
||||||
|
# Copy any .pyd files that might have been missed
|
||||||
|
for ext_file in pkg_path.glob('*{EXT_EXTENSION}'):
|
||||||
|
build_ext_file = build_pkg_path / ext_file.name
|
||||||
|
if not build_ext_file.exists():
|
||||||
|
shutil.copy(ext_file, build_ext_file)
|
||||||
|
print(f" Copied {{ext_file.name}} to build directory")
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="orisys",
|
||||||
|
version="{VERSION}",
|
||||||
|
description="Orisys - Optical Tactile Sensor SDK (Compiled)",
|
||||||
|
author="Your Name",
|
||||||
|
package_dir={{"": "src"}},
|
||||||
|
packages=packages,
|
||||||
|
package_data=package_data,
|
||||||
|
include_package_data=True,
|
||||||
|
install_requires=requirements,
|
||||||
|
python_requires="{version_constraint}",
|
||||||
|
zip_safe=False,
|
||||||
|
cmdclass={{"build_py": build_py}},
|
||||||
|
options={{
|
||||||
|
'bdist_wheel': {{
|
||||||
|
'python_tag': '{py_tag}',
|
||||||
|
}}
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
'''
|
||||||
|
|
||||||
|
with open('dist_pyd/setup.py', 'w') as f:
|
||||||
|
f.write(setup_content)
|
||||||
|
|
||||||
|
print("Package setup.py created successfully!")
|
||||||
|
|
||||||
|
|
||||||
|
def build_wheel_for_version(python_version):
|
||||||
|
"""Build the wheel distribution for a specific Python version."""
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"Building wheel for Python {python_version} on {PLATFORM_NAME}")
|
||||||
|
print('='*60)
|
||||||
|
|
||||||
|
python_cmd = get_python_executable(python_version)
|
||||||
|
|
||||||
|
if not python_cmd:
|
||||||
|
print(f"⚠️ Python {python_version} not found, skipping...")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get platform tag
|
||||||
|
plat_tag = get_platform_tag()
|
||||||
|
py_tag = f'py{python_version.replace(".", "")}'
|
||||||
|
|
||||||
|
os.chdir('dist_pyd')
|
||||||
|
|
||||||
|
# Build the wheel
|
||||||
|
cmd = python_cmd + ['setup.py', 'bdist_wheel',
|
||||||
|
'--python-tag', py_tag,
|
||||||
|
'--plat-name', plat_tag,
|
||||||
|
'--dist-dir', f'../dist/{PLATFORM_NAME}']
|
||||||
|
print(f"Running: {' '.join(cmd)}")
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
|
||||||
|
os.chdir('..')
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"❌ Error building wheel:\n{result.stderr}")
|
||||||
|
if result.stdout:
|
||||||
|
print(f"stdout:\n{result.stdout}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"✅ Wheel for Python {python_version} built successfully!")
|
||||||
|
print(f" Expected: orisys-{VERSION}-{py_tag}-none-{plat_tag}.whl")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_tag():
|
||||||
|
"""Get the platform tag for wheel naming."""
|
||||||
|
system = platform.system()
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
|
||||||
|
if system == 'Windows':
|
||||||
|
if machine in ('amd64', 'x86_64'):
|
||||||
|
return 'win_amd64'
|
||||||
|
elif machine in ('x86', 'i386', 'i686'):
|
||||||
|
return 'win32'
|
||||||
|
elif machine in ('arm64', 'aarch64'):
|
||||||
|
return 'win_arm64'
|
||||||
|
elif system == 'Linux':
|
||||||
|
if machine in ('x86_64', 'amd64'):
|
||||||
|
return 'manylinux2014_x86_64'
|
||||||
|
elif machine in ('aarch64', 'arm64'):
|
||||||
|
return 'manylinux2014_aarch64'
|
||||||
|
elif machine in ('i386', 'i686'):
|
||||||
|
return 'manylinux2014_i686'
|
||||||
|
elif system == 'Darwin':
|
||||||
|
if machine == 'arm64':
|
||||||
|
return 'macosx_11_0_arm64'
|
||||||
|
else:
|
||||||
|
return 'macosx_10_9_x86_64'
|
||||||
|
|
||||||
|
return 'any'
|
||||||
|
|
||||||
|
|
||||||
|
def copy_additional_files():
|
||||||
|
"""Copy non-Python files."""
|
||||||
|
print("\n=== Copying additional files ===")
|
||||||
|
|
||||||
|
# Copy requirements.txt
|
||||||
|
if os.path.exists('requirements.txt'):
|
||||||
|
shutil.copy('requirements.txt', 'dist_pyd/requirements.txt')
|
||||||
|
print("Copied requirements.txt")
|
||||||
|
|
||||||
|
# Copy README and LICENSE if they exist
|
||||||
|
for file in ['README.md', 'LICENSE']:
|
||||||
|
if os.path.exists(file):
|
||||||
|
shutil.copy(file, f'dist_pyd/{file}')
|
||||||
|
print(f"Copied {file}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Main build process."""
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Build orisys package with Cython (.pyd/.so files)')
|
||||||
|
parser.add_argument('--versions', nargs='+', default=PYTHON_VERSIONS,
|
||||||
|
help='Python versions to build for')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
current_py_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print("Building Orisys Package with Cython (.pyd/.so files)")
|
||||||
|
print(f"Platform: {PLATFORM_NAME} ({platform.system()})")
|
||||||
|
print(f"Current Python: {current_py_version} ({sys.executable})")
|
||||||
|
print(f"Python Versions: {', '.join(args.versions)}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Check Cython
|
||||||
|
if not check_cython():
|
||||||
|
print("\n❌ Build failed: Cython is required!")
|
||||||
|
print(" Install with: pip install cython")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Check C compiler
|
||||||
|
if not check_c_compiler():
|
||||||
|
print("\n❌ Build failed: C compiler is required!")
|
||||||
|
print(" Please install the required build tools (see above)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Ensure we're in the right directory
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
os.chdir(script_dir)
|
||||||
|
|
||||||
|
# Step 1: Clean previous builds
|
||||||
|
clean_build_dirs()
|
||||||
|
|
||||||
|
# Step 2: Create Cython setup files
|
||||||
|
py_files = create_cython_setup_files()
|
||||||
|
if not py_files:
|
||||||
|
print("\n❌ No Python files found to compile!")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Step 3: Copy additional files
|
||||||
|
copy_additional_files()
|
||||||
|
|
||||||
|
# Step 4: Compile for each Python version
|
||||||
|
success_count = 0
|
||||||
|
for version in args.versions:
|
||||||
|
# Compile with Cython
|
||||||
|
if compile_with_cython(version):
|
||||||
|
# Create package setup.py
|
||||||
|
create_package_setup(version)
|
||||||
|
# Build wheel
|
||||||
|
if build_wheel_for_version(version):
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
if success_count == 0:
|
||||||
|
print("\n❌ Build failed - no wheels created!")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n✅ Successfully built {success_count}/{len(args.versions)} wheels")
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("✅ Build completed successfully!")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"\nPlatform: {PLATFORM_NAME}")
|
||||||
|
print(f"Compiled source: dist_pyd/")
|
||||||
|
print(f"Distribution packages: dist/{PLATFORM_NAME}/")
|
||||||
|
print("\nGenerated wheels:")
|
||||||
|
for wheel in Path(f'dist/{PLATFORM_NAME}').glob('*.whl'):
|
||||||
|
print(f" - {wheel.name}")
|
||||||
|
print("\nInstall with:")
|
||||||
|
print(f" pip install dist/{PLATFORM_NAME}/orisys-{VERSION}-*.whl")
|
||||||
|
print("\nNote: The wheel contains .pyd/.so files instead of .py files!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
@@ -19,10 +19,10 @@ def main():
|
|||||||
# 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径)
|
# 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径)
|
||||||
|
|
||||||
# 方式1: 从视频文件读取
|
# 方式1: 从视频文件读取
|
||||||
# sensor = orisys.Sensor("./s1.mp4", isstitch=True, cuda=True, verbose=True,config_name="prototype")
|
sensor = orisys.Sensor("./s1.mp4", isstitch=True, cuda=True, verbose=True,config_name="prototype")
|
||||||
|
|
||||||
# 方式2: 从摄像头读取(取消注释使用)
|
# 方式2: 从摄像头读取(取消注释使用)
|
||||||
sensor = orisys.Sensor(0, cuda=True, verbose=True, config_name="prototype")
|
# sensor = orisys.Sensor(0, cuda=True, verbose=True, config_name="prototype")
|
||||||
|
|
||||||
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
|
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
|
||||||
|
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ class UnstructuredGrid(object):
|
|||||||
|
|
||||||
self.nsimplices = self.simplices.shape[0]
|
self.nsimplices = self.simplices.shape[0]
|
||||||
if self.dim != self.simplices.shape[1]-1:
|
if self.dim != self.simplices.shape[1]-1:
|
||||||
|
pdim = self.dim
|
||||||
|
sdim = self.simplices.shape[1]-1
|
||||||
raise SyntaxError("Dimension mismatch! pdim = "+str(pdim)+" and sdim = "+str(sdim)+" do not match!")
|
raise SyntaxError("Dimension mismatch! pdim = "+str(pdim)+" and sdim = "+str(sdim)+" do not match!")
|
||||||
|
|
||||||
self.adjacent_faces = []
|
self.adjacent_faces = []
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ class PoissonSolver(object):
|
|||||||
def solve(self, f, verbose=False):
|
def solve(self, f, verbose=False):
|
||||||
|
|
||||||
if not self.ready:
|
if not self.ready:
|
||||||
init(verbose)
|
self.prepare(verbose)
|
||||||
|
|
||||||
fshape = f.shape
|
fshape = f.shape
|
||||||
|
|
||||||
@@ -506,7 +506,8 @@ class PoissonSolver(object):
|
|||||||
print(' - GPU solver not optimized for unstructured points, using CPU')
|
print(' - GPU solver not optimized for unstructured points, using CPU')
|
||||||
return [self.solve(f, verbose) for f in f_list]
|
return [self.solve(f, verbose) for f in f_list]
|
||||||
|
|
||||||
return results
|
# If we reach here, it's an unsupported case
|
||||||
|
raise ValueError(f"Unsupported point type '{self.ptype}' for GPU batch solver")
|
||||||
|
|
||||||
def clear_gpu_cache(self):
|
def clear_gpu_cache(self):
|
||||||
"""Clear GPU cache to free GPU memory"""
|
"""Clear GPU cache to free GPU memory"""
|
||||||
|
|||||||
Reference in New Issue
Block a user