Skip to content

Building Native Extensions

Jeremy Wright edited this page Nov 26, 2023 · 1 revision

Credit to @whophil for figuring this out.

When building a native extension in Linux against a version of OCP installed via Anaconda/Mamba/etc, you will need to make sure that you match the versions of both Pybind11 and gcc between what OCP was compiled with, and what your application is being compiled with. If the versions are not matched, you may get a runtime error such as the following when you try to cast from an OCP type to an OCCT type.

terminate called after throwing an instance of 'pybind11::error_already_set'
  what():  RuntimeError: Unable to cast Python instance of type <class 'OCP.TopoDS.TopoDS_Solid'> to C++ type 'TopoDS_Solid'

At:
  <string>(7): <module>

Aborted

You may need to check the OCP build logs to determine which versions the OCP library was built with. An example environment file that creates a valid conda environment to build extensions is given below. Place the contents in a file named environment.yml and create the environment with a command like conda env create -f environment.yml -n my-env.

channels:
  - conda-forge
dependencies:
  - python 3.11
  - gxx_linux-64 11*
  - pybind11 2.11 
  - ocp 7.7.0.0
  - cmake
  - cadquery

Your CMakeLists.txt file might look like the following.

cmake_minimum_required(VERSION 3.15)
project(example)

set(CMAKE_CXX_STANDARD 14)

find_package( pybind11 REQUIRED )

find_package(OpenCASCADE CONFIG REQUIRED)
link_directories(${OpenCASCADE_LIBRARY_DIR})
include_directories(${OpenCASCADE_INCLUDE_DIR})

find_package (Python3 COMPONENTS
    Interpreter
    Development.Module)

add_executable(example main.cpp)
target_link_libraries(example PRIVATE
    ${OpenCASCADE_ModelingData_LIBRARIES}
    pybind11::embed)

And here is minimal source code that should compile and run properly when the Pybind11 and gcc versions are matched properly.

#include <TopoDS_Solid.hxx>

#include <pybind11/embed.h>

namespace py = pybind11;

PYBIND11_EMBEDDED_MODULE(show, m) {
    // Also tried `m.def("show_object", [](TopoDS_Solid s) {`
    m.def("show_object", [](py::object s) {
        TopoDS_Solid x = py::cast<TopoDS_Solid>(s);
    });
}

int main(int argc, char *argv[])
{
    // Start the Python interpreter
    py::scoped_interpreter guard{};

    // Module that allows us to provide show_object
    auto py_module = py::module_::import("show");

    py::exec(R"(
        import show
        show_object = show.show_object
        import cadquery as cq
        res = cq.Workplane().box(10, 10, 10)
        show_object(res.val().wrapped)
    )");
}
Clone this wiki locally