diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e439cde..1220db3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: pull_request: schedule: - - cron: "14 3 15 * *" # Runs at 03:14 UTC on the 15th of every month + - cron: "14 3 15 * *" # Runs at 03:14 UTC on the 15th of every month push: branches: - main @@ -20,80 +20,80 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - - name: Cache dependencies - id: cache-vcpkg-deps - uses: actions/cache@v4 - with: - path: vcpkg_installed - key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} - - - name: Export vcpkg host triplet for compilation - shell: bash - run: | - if [[ "$RUNNER_OS" == "Windows" ]]; then - VCPKG_HOST_TRIPLET=x64-windows-static-md - elif [[ "$RUNNER_OS" == "Linux" ]]; then - VCPKG_HOST_TRIPLET=x64-linux - elif [[ "$RUNNER_OS" == "macOS" ]]; then - VCPKG_HOST_TRIPLET=arm64-osx - fi - echo VCPKG_HOST_TRIPLET="$VCPKG_HOST_TRIPLET" >> $GITHUB_ENV - echo $VCPKG_HOST_TRIPLET - - - name: Acquire vcpkg - if: steps.cache-vcpkg-deps.outputs.cache-hit != 'true' - uses: actions/checkout@v4 - with: - repository: "Microsoft/vcpkg" - path: vcpkg - ref: c9c17dcea3016bc241df0422e82b8aea212dcb93 - - - name: Install libraries with vcpkg.json - if: steps.cache-vcpkg-deps.outputs.cache-hit != 'true' - shell: bash - run: | - ./vcpkg/bootstrap-vcpkg.sh - ./vcpkg/vcpkg install --host-triplet="$VCPKG_HOST_TRIPLET" - - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - cache-dependency-glob: "**/pyproject.toml" - - - name: Build pybmds - if: runner.os != 'Windows' - run: | - uv pip install pybind11==3.0.0 --target=./pybind11 - - uv venv --python=${{ matrix.python-version }} - - source .venv/bin/activate - - export CMAKE_PREFIX_PATH=${{ github.workspace }}/pybind11/pybind11/share/cmake - export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) - - uv pip install -v -e ".[dev]" - - py.test - - - name: Build pybmds - if: runner.os == 'Windows' - run: | - uv pip install pybind11==3.0.0 --target=./pybind11 - - uv venv --python=${{ matrix.python-version }} - - .venv/Scripts/activate - - $env:CMAKE_PREFIX_PATH="${{ github.workspace }}\pybind11\pybind11\share\cmake" - $env:CMAKE_BUILD_PARALLEL_LEVEL = [Environment]::ProcessorCount - - uv pip install -v -e ".[dev]" - - py.test + - uses: actions/checkout@v4 + + - name: Cache dependencies + id: cache-vcpkg-deps + uses: actions/cache@v4 + with: + path: vcpkg_installed + key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + + - name: Export vcpkg host triplet for compilation + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + VCPKG_HOST_TRIPLET=x64-windows-static-md + elif [[ "$RUNNER_OS" == "Linux" ]]; then + VCPKG_HOST_TRIPLET=x64-linux + elif [[ "$RUNNER_OS" == "macOS" ]]; then + VCPKG_HOST_TRIPLET=arm64-osx + fi + echo VCPKG_HOST_TRIPLET="$VCPKG_HOST_TRIPLET" >> $GITHUB_ENV + echo $VCPKG_HOST_TRIPLET + + - name: Acquire vcpkg + if: steps.cache-vcpkg-deps.outputs.cache-hit != 'true' + uses: actions/checkout@v4 + with: + repository: "Microsoft/vcpkg" + path: vcpkg + ref: c9c17dcea3016bc241df0422e82b8aea212dcb93 + + - name: Install libraries with vcpkg.json + if: steps.cache-vcpkg-deps.outputs.cache-hit != 'true' + shell: bash + run: | + ./vcpkg/bootstrap-vcpkg.sh + ./vcpkg/vcpkg install --host-triplet="$VCPKG_HOST_TRIPLET" + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + cache-dependency-glob: "**/pyproject.toml" + + - name: Build pybmds + if: runner.os != 'Windows' + run: | + uv pip install pybind11==3.0.0 --target=./pybind11 + + uv venv --python=${{ matrix.python-version }} + + source .venv/bin/activate + + export CMAKE_PREFIX_PATH=${{ github.workspace }}/pybind11/pybind11/share/cmake + export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) + + uv pip install -v -e ".[dev]" + + py.test + + - name: Build pybmds + if: runner.os == 'Windows' + run: | + uv pip install pybind11==3.0.0 --target=./pybind11 + + uv venv --python=${{ matrix.python-version }} + + .venv/Scripts/activate + + $env:CMAKE_PREFIX_PATH="${{ github.workspace }}\pybind11\pybind11\share\cmake" + $env:CMAKE_BUILD_PARALLEL_LEVEL = [Environment]::ProcessorCount + + uv pip install -v -e ".[dev]" + + py.test diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 0e192a2..489bab9 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -4,10 +4,10 @@ on: workflow_dispatch: pull_request: schedule: - - cron: "14 3 15 * *" # Runs at 03:14 UTC on the 15th of every month + - cron: "14 3 15 * *" # Runs at 03:14 UTC on the 15th of every month push: branches: - - main + - main jobs: build_wheels: @@ -19,61 +19,61 @@ jobs: os: [ubuntu-24.04, windows-2022, macos-14] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Cache dependencies - id: cache-vcpkg-deps - uses: actions/cache@v4 - with: - path: vcpkg_installed - key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} + - name: Cache dependencies + id: cache-vcpkg-deps + uses: actions/cache@v4 + with: + path: vcpkg_installed + key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json') }} - - name: Acquire vcpkg - uses: actions/checkout@v4 - with: - repository: "Microsoft/vcpkg" - path: vcpkg - ref: c9c17dcea3016bc241df0422e82b8aea212dcb93 + - name: Acquire vcpkg + uses: actions/checkout@v4 + with: + repository: "Microsoft/vcpkg" + path: vcpkg + ref: c9c17dcea3016bc241df0422e82b8aea212dcb93 - - name: Export vcpkg host triplet for compilation - shell: bash - run: | - if [[ "$RUNNER_OS" == "Windows" ]]; then - VCPKG_HOST_TRIPLET=x64-windows-static-md - elif [[ "$RUNNER_OS" == "Linux" ]]; then - VCPKG_HOST_TRIPLET=x64-linux - elif [[ "$RUNNER_OS" == "macOS" ]]; then - VCPKG_HOST_TRIPLET=arm64-osx - fi - echo VCPKG_HOST_TRIPLET="$VCPKG_HOST_TRIPLET" >> $GITHUB_ENV - echo $VCPKG_HOST_TRIPLET + - name: Export vcpkg host triplet for compilation + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + VCPKG_HOST_TRIPLET=x64-windows-static-md + elif [[ "$RUNNER_OS" == "Linux" ]]; then + VCPKG_HOST_TRIPLET=x64-linux + elif [[ "$RUNNER_OS" == "macOS" ]]; then + VCPKG_HOST_TRIPLET=arm64-osx + fi + echo VCPKG_HOST_TRIPLET="$VCPKG_HOST_TRIPLET" >> $GITHUB_ENV + echo $VCPKG_HOST_TRIPLET - - uses: pypa/cibuildwheel@v3.1.3 - env: - CIBW_BUILD: "cp313-*" - CIBW_SKIP: "*musllinux* *win32*" - CIBW_TEST_COMMAND: pytest {project}/tests - CIBW_TEST_EXTRAS: dev + - uses: pypa/cibuildwheel@v3.1.3 + env: + CIBW_BUILD: "cp313-*" + CIBW_SKIP: "*musllinux* *win32*" + CIBW_TEST_COMMAND: pytest {project}/tests + CIBW_TEST_EXTRAS: dev - CIBW_BEFORE_BUILD: rm -rf {project}/build - CIBW_BEFORE_ALL_WINDOWS: bash tools\cibw_before.sh - CIBW_ENVIRONMENT_WINDOWS: "CMAKE_PREFIX_PATH=D:/a/pycpp/pycpp/pybind11/pybind11/share/cmake" + CIBW_BEFORE_BUILD: rm -rf {project}/build + CIBW_BEFORE_ALL_WINDOWS: bash tools\cibw_before.sh + CIBW_ENVIRONMENT_WINDOWS: "CMAKE_PREFIX_PATH=D:/a/pycpp/pycpp/pybind11/pybind11/share/cmake" - CIBW_ENVIRONMENT_PASS_LINUX: "VCPKG_HOST_TRIPLET RUNNER_OS" - CIBW_BEFORE_ALL_LINUX: "source tools/cibw_before.sh" - CIBW_ENVIRONMENT_LINUX: "CMAKE_PREFIX_PATH=/project/pybind11/pybind11/share/cmake" + CIBW_ENVIRONMENT_PASS_LINUX: "VCPKG_HOST_TRIPLET RUNNER_OS" + CIBW_BEFORE_ALL_LINUX: "source tools/cibw_before.sh" + CIBW_ENVIRONMENT_LINUX: "CMAKE_PREFIX_PATH=/project/pybind11/pybind11/share/cmake" - CIBW_BEFORE_ALL_MACOS: "source tools/cibw_before.sh" - CIBW_ENVIRONMENT_MACOS: "CMAKE_PREFIX_PATH=${{ github.workspace }}/pybind11/pybind11/share/cmake" - MACOSX_DEPLOYMENT_TARGET: "14.0" + CIBW_BEFORE_ALL_MACOS: "source tools/cibw_before.sh" + CIBW_ENVIRONMENT_MACOS: "CMAKE_PREFIX_PATH=${{ github.workspace }}/pybind11/pybind11/share/cmake" + MACOSX_DEPLOYMENT_TARGET: "14.0" - - uses: actions/upload-artifact@v4 - with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl merge_wheels: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: build_wheels steps: - name: Merge Artifacts diff --git a/.gitignore b/.gitignore index 043ad1a..0151847 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ _generate/ *.egg-info *env* -cfg/ pybind11/ vcpkg/ vcpkg_installed/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 36d0b71..8ef5f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,3 @@ target_include_directories(cppcore PRIVATE ${NLOPT_DIR_INCLUDE_DIR}) target_link_libraries(cppcore PRIVATE ${NLOPT_LIB}) target_link_libraries(cppcore PRIVATE ${GSL_LIB}) target_link_libraries(cppcore PRIVATE ${GSLCBLAS_LIB}) - -target_compile_definitions( - cppcore - PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO} -) diff --git a/setup.py b/setup.py index 36a8c71..6e2b686 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ from setuptools import Extension, setup from setuptools.command.build_ext import build_ext -# Convert distutils Windows platform specifiers to CMake -A arguments PLAT_TO_CMAKE = { "win32": "Win32", "win-amd64": "x64", @@ -14,25 +13,20 @@ } -# A CMakeExtension needs a sourcedir instead of a file list. -# The name must be the _single_ output extension from the CMake build. -# If you need multiple extensions, see scikit-build. class CMakeExtension(Extension): def __init__(self, name: str, sourcedir: str = "") -> None: super().__init__(name, sources=[]) - self.sourcedir = os.fspath(Path(sourcedir).resolve()) + self.sourcedir = str(Path(sourcedir).resolve()) class CMakeBuild(build_ext): def build_extension(self, ext: CMakeExtension) -> None: - # Must be in this form due to bug in .resolve() only fixed in Python 3.10+ - ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name) - extdir = ext_fullpath.parent.resolve() + ext_fullpath = Path(self.get_ext_fullpath(ext.name)).resolve() + extdir = ext_fullpath.parent debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug cfg = "Debug" if debug else "Release" - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") cmake_args = [ f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}", f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm @@ -41,35 +35,16 @@ def build_extension(self, ext: CMakeExtension) -> None: if "CMAKE_ARGS" in os.environ: cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item] - if self.compiler.compiler_type != "msvc": - if not cmake_generator or cmake_generator == "Ninja": - try: - import ninja - - ninja_executable_path = Path(ninja.BIN_DIR) / "ninja" - cmake_args += [ - "-GNinja", - f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}", - ] - except ImportError: - pass - - else: - single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - if not single_config: - cmake_args += [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}" - ] - build_args += ["--config", cfg] + if self.compiler.compiler_type == "msvc": + cmake_args += [ + "-A", + PLAT_TO_CMAKE[self.plat_name], + f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}", + ] + build_args += ["--config", cfg] build_temp = Path(self.build_temp) / ext.name - if not build_temp.exists(): - build_temp.mkdir(parents=True) + build_temp.mkdir(parents=True, exist_ok=True) subprocess.run( ["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 38e0c98..1a859ca 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -5,18 +5,15 @@ #include #include -#define STRINGIFY(x) #x -#define MACRO_STRINGIFY(x) STRINGIFY(x) - int add(int i, int j) { return i + j; } namespace py = pybind11; -// EIGEN test +// EIGEN py::array_t eigen_matmul(py::array_t a, - py::array_t b) { + py::array_t b) { if (a.ndim() != 2 || b.ndim() != 2) throw std::runtime_error("Input must be two 2D arrays"); if (a.shape(1) != b.shape(0)) @@ -31,25 +28,24 @@ py::array_t eigen_matmul(py::array_t( {C.rows(), C.cols()}, {sizeof(double) * C.cols(), sizeof(double)}, - C.data() - ); + C.data()); } -// GSL test +// GSL double gsl_bessel(double x) { return gsl_sf_bessel_J0(x); } -// NLOPT test -double objfunc(unsigned n, const double* x, double* grad, void*) { +// NLOPT +double objfunc(unsigned n, const double *x, double *grad, void *) { if (grad) { grad[0] = 2 * (x[0] - 1); grad[1] = 2 * (x[1] - 2); } return (x[0] - 1) * (x[0] - 1) + (x[1] - 2) * (x[1] - 2); } -std::vector nlopt_demo(const std::vector& lower_bounds, - const std::vector& upper_bounds) { +std::vector nlopt_demo(const std::vector &lower_bounds, + const std::vector &upper_bounds) { if (lower_bounds.size() != 2 || upper_bounds.size() != 2) throw std::invalid_argument("Bounds must have size 2"); @@ -62,39 +58,16 @@ std::vector nlopt_demo(const std::vector& lower_bounds, double minf; int result = nlopt_optimize(opt, x, &minf); nlopt_destroy(opt); - if (result < 0) throw std::runtime_error("nlopt failed"); + if (result < 0) + throw std::runtime_error("nlopt failed"); return {x[0], x[1]}; } +// Python module PYBIND11_MODULE(cppcore, m) { - m.doc() = R"pbdoc( - Pybind11 example plugin - ----------------------- - - .. currentmodule:: cppcore - - .. autosummary:: - :toctree: _generate - - add - subtract - )pbdoc"; - - m.def("add", &add, R"pbdoc( - Add two numbers - - Some other explanation about the add function. - )pbdoc"); - - m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( - Subtract two numbers - - Some other explanation about the subtract function. - )pbdoc"); - + m.def("add", &add, "Add two numbers"); m.def("eigen_matmul", &eigen_matmul, "Matrix multiplication using Eigen"); m.def("gsl_bessel", &gsl_bessel, "Bessel function using GSL"); - m.def("nlopt_optimize", &nlopt_demo, - py::arg("lower_bounds"), py::arg("upper_bounds"), - "Run NLOPT optimization with specified bounds"); + m.def("nlopt_optimize", &nlopt_demo, py::arg("lower_bounds"), py::arg("upper_bounds"), + "Run NLOPT optimization with specified bounds"); } diff --git a/tests/test_basic.py b/tests/test_basic.py index 0592450..dec50fe 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -5,10 +5,12 @@ from demo import cppcore -def test_main(): +def test_version(): assert demo.__version__ == "0.0.1" + + +def test_basic_cpp(): assert cppcore.add(1, 2) == 3 - assert cppcore.subtract(1, 2) == -1 def test_eigen_matmul():