poetry python build
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,3 +5,7 @@ build_warnings*.log
|
||||
final_build.log
|
||||
cmake_configure.log
|
||||
compile_commands.json
|
||||
|
||||
dist/
|
||||
build_python/
|
||||
src/Python/uLib/*.so*
|
||||
47
build_python.py
Normal file
47
build_python.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
def build(setup_kwargs):
|
||||
"""
|
||||
Build the C++ extension using CMake.
|
||||
This function is called by poetry-core during the build process.
|
||||
The binary is placed directly inside the uLib directory in src/Python.
|
||||
"""
|
||||
# Root of the whole project where this build_extension.py is located
|
||||
project_root = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# Where the extension should go
|
||||
package_dir = os.path.join(project_root, "src/Python/uLib")
|
||||
|
||||
# Ensure package directory exists
|
||||
os.makedirs(package_dir, exist_ok=True)
|
||||
|
||||
# Temporary build directory
|
||||
build_temp = os.path.join(project_root, "build_python")
|
||||
os.makedirs(build_temp, exist_ok=True)
|
||||
|
||||
print(f"--- Running CMake build in {build_temp} ---")
|
||||
print(f"Project root: {project_root}")
|
||||
print(f"Target binary dir: {package_dir}")
|
||||
|
||||
# CMake configuration
|
||||
cmake_args = [
|
||||
f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={package_dir}",
|
||||
f"-DPYTHON_EXECUTABLE={sys.executable}",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DUSE_CUDA=OFF",
|
||||
"-G", "Unix Makefiles",
|
||||
]
|
||||
|
||||
# Use micromamba to ensure Boost and VTK are found during the build
|
||||
subprocess.check_call(["cmake", project_root] + cmake_args, cwd=build_temp)
|
||||
subprocess.check_call(["cmake", "--build", ".", "--parallel", "--target", "uLib_python"], cwd=build_temp)
|
||||
|
||||
# Ensure the package is found by poetry during the wheel creation process.
|
||||
# Return setup_kwargs for poetry-core.
|
||||
return setup_kwargs
|
||||
|
||||
if __name__ == "__main__":
|
||||
build({})
|
||||
7
poetry.lock
generated
Normal file
7
poetry.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Poetry 2.3.1 and should not be changed by hand.
|
||||
package = []
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.9"
|
||||
content-hash = "db9b4c08b159b17b239e26c67ead7c37b82d9f9eb06550245ae3134c095f98f7"
|
||||
@@ -1,10 +1,15 @@
|
||||
[tool.poetry]
|
||||
name = "uLib"
|
||||
version = "0.6.0"
|
||||
description = "CMT Cosmic Muon Tomography project uLib python bindings"
|
||||
authors = ["Andrea Rigoni Garola <andrea.rigoni@pd.infn.it>"]
|
||||
readme = "README.md"
|
||||
packages = [{ include = "uLib", from = "src/Python" }]
|
||||
build = "build_python.py"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9"
|
||||
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel",
|
||||
"pybind11>=2.6.0",
|
||||
"cmake>=3.12",
|
||||
"ninja"]
|
||||
|
||||
build-backend = "setuptools.build_meta"
|
||||
requires = ["poetry-core>=2.0.0", "pybind11>=2.6.0", "cmake>=3.12"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
52
setup.py
52
setup.py
@@ -1,52 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from setuptools import Extension, setup
|
||||
from setuptools.command.build_ext import build_ext
|
||||
|
||||
class CMakeExtension(Extension):
|
||||
def __init__(self, name, sourcedir=""):
|
||||
Extension.__init__(self, name, sources=[])
|
||||
self.sourcedir = os.path.abspath(sourcedir)
|
||||
|
||||
class CMakeBuild(build_ext):
|
||||
def build_extension(self, ext):
|
||||
extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
|
||||
|
||||
if not extdir.endswith(os.path.sep):
|
||||
extdir += os.path.sep
|
||||
|
||||
debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug
|
||||
cfg = "Debug" if debug else "Release"
|
||||
|
||||
cmake_args = [
|
||||
f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}",
|
||||
f"-DPYTHON_EXECUTABLE={sys.executable}",
|
||||
f"-DCMAKE_BUILD_TYPE={cfg}",
|
||||
f"-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE",
|
||||
f"-DCMAKE_INSTALL_RPATH=$ORIGIN",
|
||||
"-DUSE_CUDA=OFF",
|
||||
"-G", "Unix Makefiles",
|
||||
]
|
||||
build_args = []
|
||||
|
||||
build_temp = os.path.join(self.build_temp, ext.name)
|
||||
if not os.path.exists(build_temp):
|
||||
os.makedirs(build_temp)
|
||||
|
||||
subprocess.check_call(["cmake", ext.sourcedir] + cmake_args, cwd=build_temp)
|
||||
subprocess.check_call(["cmake", "--build", ".", "--target", "uLib_python"] + build_args, cwd=build_temp)
|
||||
|
||||
setup(
|
||||
name="ulib",
|
||||
version="0.6.0",
|
||||
author="Andrea Rigoni Garola",
|
||||
author_email="andrea.rigoni@pd.infn.it",
|
||||
description="CMT Cosmic Muon Tomography project uLib python bindings",
|
||||
ext_modules=[CMakeExtension("uLib_python")],
|
||||
cmdclass={"build_ext": CMakeBuild},
|
||||
zip_safe=False,
|
||||
python_requires=">=3.6",
|
||||
)
|
||||
@@ -3,11 +3,11 @@ import os
|
||||
import unittest
|
||||
import time
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
class TestCoreOptions(unittest.TestCase):
|
||||
def test_options(self):
|
||||
opt = uLib_python.Core.Options("Test Options")
|
||||
opt = uLib.Core.Options("Test Options")
|
||||
|
||||
# Test basic config file parsing
|
||||
with open("test_configuration.ini", "w") as f:
|
||||
@@ -18,12 +18,12 @@ class TestCoreOptions(unittest.TestCase):
|
||||
|
||||
class TestCoreObject(unittest.TestCase):
|
||||
def test_object(self):
|
||||
obj = uLib_python.Core.Object()
|
||||
obj = uLib.Core.Object()
|
||||
self.assertIsNotN one(obj)
|
||||
|
||||
class TestCoreTimer(unittest.TestCase):
|
||||
def test_timer(self):
|
||||
timer = uLib_python.Core.Timer()
|
||||
timer = uLib.Core.Timer()
|
||||
timer.Start()
|
||||
time.sleep(0.1)
|
||||
val = timer.StopWatch()
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import unittest
|
||||
import numpy as np
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
def vector4f0(v, target):
|
||||
diff = np.array(v) - np.array(target)
|
||||
@@ -12,7 +12,7 @@ def vector4f0(v, target):
|
||||
|
||||
class TestMathGeometry(unittest.TestCase):
|
||||
def test_geometry(self):
|
||||
Geo = uLib_python.Math.Geometry()
|
||||
Geo = uLib.Math.Geometry()
|
||||
|
||||
Geo.SetPosition([1, 1, 1])
|
||||
|
||||
@@ -27,7 +27,7 @@ class TestMathGeometry(unittest.TestCase):
|
||||
|
||||
class TestMathContainerBox(unittest.TestCase):
|
||||
def test_container_box_local(self):
|
||||
Cnt = uLib_python.Math.ContainerBox()
|
||||
Cnt = uLib.Math.ContainerBox()
|
||||
Cnt.SetOrigin([-1, -1, -1])
|
||||
Cnt.SetSize([2, 2, 2])
|
||||
|
||||
@@ -35,7 +35,7 @@ class TestMathContainerBox(unittest.TestCase):
|
||||
self.assertTrue(np.allclose(size, [2, 2, 2]))
|
||||
|
||||
def test_container_box_global(self):
|
||||
Box = uLib_python.Math.ContainerBox()
|
||||
Box = uLib.Math.ContainerBox()
|
||||
Box.SetPosition([1, 1, 1])
|
||||
Box.SetSize([2, 2, 2])
|
||||
|
||||
@@ -45,7 +45,7 @@ class TestMathContainerBox(unittest.TestCase):
|
||||
|
||||
class TestMathStructuredGrid(unittest.TestCase):
|
||||
def test_structured_grid(self):
|
||||
grid = uLib_python.Math.StructuredGrid([10, 10, 10])
|
||||
grid = uLib.Math.StructuredGrid([10, 10, 10])
|
||||
grid.SetSpacing([1, 1, 1])
|
||||
|
||||
spacing = grid.GetSpacing()
|
||||
@@ -53,40 +53,40 @@ class TestMathStructuredGrid(unittest.TestCase):
|
||||
|
||||
class TestMathAccumulator(unittest.TestCase):
|
||||
def test_accumulator_mean(self):
|
||||
acc = uLib_python.Math.Accumulator_Mean_f()
|
||||
acc = uLib.Math.Accumulator_Mean_f()
|
||||
acc(10.0)
|
||||
acc(20.0)
|
||||
self.assertAlmostEqual(acc(), 15.0)
|
||||
|
||||
class TestMathNewTypes(unittest.TestCase):
|
||||
def test_eigen_vectors(self):
|
||||
v1f = uLib_python.Math.Vector1f()
|
||||
v3d = uLib_python.Math.Vector3d()
|
||||
m4f = uLib_python.Math.Matrix4f()
|
||||
v1f = uLib.Math.Vector1f()
|
||||
v3d = uLib.Math.Vector3d()
|
||||
m4f = uLib.Math.Matrix4f()
|
||||
self.assertIsNotNone(v1f)
|
||||
self.assertIsNotNone(v3d)
|
||||
self.assertIsNotNone(m4f)
|
||||
|
||||
def test_ulib_vectors(self):
|
||||
vi = uLib_python.Math.Vector_i()
|
||||
vi = uLib.Math.Vector_i()
|
||||
vi.append(1)
|
||||
vi.append(2)
|
||||
self.assertEqual(len(vi), 2)
|
||||
self.assertEqual(vi[0], 1)
|
||||
self.assertEqual(vi[1], 2)
|
||||
|
||||
vf = uLib_python.Math.Vector_f()
|
||||
vf = uLib.Math.Vector_f()
|
||||
vf.append(1.5)
|
||||
self.assertAlmostEqual(vf[0], 1.5)
|
||||
|
||||
def test_homogeneous(self):
|
||||
p = uLib_python.Math.HPoint3f(1.0, 2.0, 3.0)
|
||||
v = uLib_python.Math.HVector3f(0.0, 1.0, 0.0)
|
||||
p = uLib.Math.HPoint3f(1.0, 2.0, 3.0)
|
||||
v = uLib.Math.HVector3f(0.0, 1.0, 0.0)
|
||||
self.assertIsNotNone(p)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
def test_vox_image(self):
|
||||
img = uLib_python.Math.VoxImage([2, 2, 2])
|
||||
img = uLib.Math.VoxImage([2, 2, 2])
|
||||
self.assertEqual(img.GetDims()[0], 2)
|
||||
img.SetValue([0, 0, 0], 10.5)
|
||||
# Note: GetValue returns float, and there might be internal scaling (1.E-6 observed in code)
|
||||
@@ -96,11 +96,11 @@ class TestMathNewTypes(unittest.TestCase):
|
||||
|
||||
class TestMathVoxRaytracer(unittest.TestCase):
|
||||
def test_raytracer(self):
|
||||
grid = uLib_python.Math.StructuredGrid([10, 10, 10])
|
||||
grid = uLib.Math.StructuredGrid([10, 10, 10])
|
||||
grid.SetSpacing([1, 1, 1])
|
||||
grid.SetOrigin([0, 0, 0])
|
||||
|
||||
rt = uLib_python.Math.VoxRaytracer(grid)
|
||||
rt = uLib.Math.VoxRaytracer(grid)
|
||||
self.assertIsNotNone(rt)
|
||||
|
||||
# Test TraceBetweenPoints
|
||||
@@ -118,7 +118,7 @@ class TestMathVoxRaytracer(unittest.TestCase):
|
||||
self.assertGreater(elements[i].L, 0)
|
||||
|
||||
def test_ray_data(self):
|
||||
data = uLib_python.Math.VoxRaytracerRayData()
|
||||
data = uLib.Math.VoxRaytracerRayData()
|
||||
data.SetCount(10)
|
||||
data.SetTotalLength(5.5)
|
||||
self.assertEqual(data.Count(), 10)
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
import uLib_python
|
||||
import uLib
|
||||
|
||||
def test_core():
|
||||
print("Testing Core module...")
|
||||
obj = uLib_python.Core.Object()
|
||||
obj = uLib.Core.Object()
|
||||
print("Core Object created:", obj)
|
||||
|
||||
timer = uLib_python.Core.Timer()
|
||||
timer = uLib.Core.Timer()
|
||||
timer.Start()
|
||||
print("Core Timer started")
|
||||
|
||||
options = uLib_python.Core.Options("Test Options")
|
||||
options = uLib.Core.Options("Test Options")
|
||||
print("Core Options created:", options)
|
||||
|
||||
def test_math():
|
||||
print("Testing Math module...")
|
||||
|
||||
# Test AffineTransform
|
||||
transform = uLib_python.Math.AffineTransform()
|
||||
transform = uLib.Math.AffineTransform()
|
||||
print("AffineTransform created")
|
||||
|
||||
# Test Geometry
|
||||
geom = uLib_python.Math.Geometry()
|
||||
geom = uLib.Math.Geometry()
|
||||
print("Geometry created")
|
||||
|
||||
# Test StructuredData
|
||||
data = uLib_python.Math.StructuredData([10, 10, 10])
|
||||
data = uLib.Math.StructuredData([10, 10, 10])
|
||||
print("StructuredData created with dims:", data.GetDims())
|
||||
|
||||
# Test Structured2DGrid
|
||||
grid2d = uLib_python.Math.Structured2DGrid()
|
||||
grid2d = uLib.Math.Structured2DGrid()
|
||||
grid2d.SetDims([100, 100])
|
||||
print("Structured2DGrid created with dims:", grid2d.GetDims())
|
||||
|
||||
# Test TriangleMesh
|
||||
mesh = uLib_python.Math.TriangleMesh()
|
||||
mesh = uLib.Math.TriangleMesh()
|
||||
print("TriangleMesh created")
|
||||
|
||||
print("All tests passed successfully!")
|
||||
|
||||
7
src/Python/uLib/__init__.py
Normal file
7
src/Python/uLib/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
try:
|
||||
from .uLib_python import Core, Math
|
||||
except ImportError:
|
||||
# Handle cases where the binary extension is not yet built
|
||||
pass
|
||||
|
||||
__all__ = ["Core", "Math"]
|
||||
Reference in New Issue
Block a user