-
Notifications
You must be signed in to change notification settings - Fork 31
Building Native Extensions
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)
)");
}