Skip to content

Commit 3f0145a

Browse files
committed
[Python] Statically link cppyy_backend inside libcppyy
Separating the libraries made sense in the past when the ROOT Python interfaces still supported PyPy, where only the backend needed to be loaded and there was no CPython extension. But nowadays, the clingwrapper in the `cppyy_backend` library is only used for the CPython extension `libcppyy`, so we might as well link statically. This avoids some overhead and boilerplate code: * we don't need to set the `CPPYY_BACKEND_LIBRARY` path * we don't need system links for `libcppyy_backend.so` if the Python install directory differs from the library install directory * we don't need Python code to load the backend library (installing the `cppyy_backend` Python code can be completely skipped, and patching it is not needed anymore) The change implies that the `rpath` code also needs to be moved from the backend to the CPyCppyy CMake code.
1 parent 4ccca77 commit 3f0145a

File tree

9 files changed

+57
-133
lines changed

9 files changed

+57
-133
lines changed

bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,15 @@ add_library(${libname} SHARED ${headers} ${sources})
5555
# Set the suffix to '.so' and the prefix to 'lib'
5656
set_target_properties(${libname} PROPERTIES ${ROOT_LIBRARY_PROPERTIES})
5757
if(MSVC)
58-
target_link_libraries(${libname} PUBLIC cppyy_backend Python3::Python)
58+
target_link_libraries(${libname} PRIVATE cppyy_backend)
59+
target_link_libraries(${libname} PUBLIC Python3::Python)
5960
set_target_properties(${libname} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
6061
set_target_properties(${libname} PROPERTIES PREFIX "lib")
6162
set_target_properties(${libname} PROPERTIES SUFFIX ".pyd")
6263
elseif(APPLE)
63-
target_link_libraries(${libname} PUBLIC -Wl,-bind_at_load -Wl,-w -Wl,-undefined -Wl,dynamic_lookup cppyy_backend)
64+
target_link_libraries(${libname} PRIVATE -Wl,-bind_at_load -Wl,-w -Wl,-undefined -Wl,dynamic_lookup cppyy_backend)
6465
else()
65-
target_link_libraries(${libname} PUBLIC -Wl,--unresolved-symbols=ignore-all cppyy_backend)
66+
target_link_libraries(${libname} PRIVATE -Wl,--unresolved-symbols=ignore-all cppyy_backend)
6667
endif()
6768

6869
if(NOT MSVC)
@@ -104,6 +105,26 @@ target_include_directories(${libname}
104105

105106
set_property(GLOBAL APPEND PROPERTY ROOT_EXPORTED_TARGETS ${libname})
106107

108+
if(NOT MSVC)
109+
# Make sure that relative RUNPATH to main ROOT libraries is always correct.
110+
111+
file(RELATIVE_PATH pymoduledir_to_libdir_build ${localruntimedir} "${localruntimedir}")
112+
file(RELATIVE_PATH pymoduledir_to_libdir_install ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_PYTHONDIR} "${CMAKE_INSTALL_FULL_LIBDIR}")
113+
114+
if(APPLE)
115+
set_target_properties(${libname} PROPERTIES
116+
BUILD_RPATH "@loader_path/${pymoduledir_to_libdir_build}"
117+
INSTALL_RPATH "@loader_path/${pymoduledir_to_libdir_install}"
118+
)
119+
else()
120+
set_target_properties(${libname} PROPERTIES
121+
BUILD_RPATH "$ORIGIN/${pymoduledir_to_libdir_build}"
122+
INSTALL_RPATH "$ORIGIN/${pymoduledir_to_libdir_install}"
123+
)
124+
endif()
125+
126+
endif()
127+
107128
# Install library
108129
install(TARGETS ${libname} EXPORT ${CMAKE_PROJECT_NAME}Exports
109130
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries

bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/CommonDefs.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,29 @@
66
#ifdef _MSC_VER
77
// Windows requires symbols to be explicitly exported
88
#define CPYCPPYY_EXPORT extern __declspec(dllexport)
9-
#define CPYCPPYY_IMPORT extern __declspec(dllimport)
109
#define CPYCPPYY_CLASS_EXPORT __declspec(dllexport)
1110

1211
// CPYCPPYY_EXTERN is dual use in the public API
1312
#ifndef CPYCPPYY_INTERNAL
1413
#define CPYCPPYY_EXTERN extern __declspec(dllexport)
1514
#define CPYCPPYY_CLASS_EXTERN __declspec(dllexport)
1615
#else
17-
#define CPYCPPYY_EXTERN extern __declspec(dllimport)
18-
#define CPYCPPYY_CLASS_EXTERN __declspec(dllimport)
16+
#define CPYCPPYY_EXTERN extern
17+
#define CPYCPPYY_CLASS_EXTERN
1918
#endif
2019

2120
#define CPYCPPYY_STATIC
2221

2322
#else
2423
// Linux, Mac, etc.
2524
#define CPYCPPYY_EXPORT extern
26-
#define CPYCPPYY_IMPORT extern
2725
#define CPYCPPYY_CLASS_EXPORT
2826
#define CPYCPPYY_EXTERN extern
2927
#define CPYCPPYY_CLASS_EXTERN
3028
#define CPYCPPYY_STATIC static
3129

3230
#endif
3331

32+
#define CPYCPPYY_IMPORT extern
33+
3434
#endif // !CPYCPPYY_COMMONDEFS_H

bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,12 @@ inline Cppyy::TCppType_t CPPInstance::ObjectIsA(bool check_smart) const
126126

127127

128128
//- object proxy type and type verification ----------------------------------
129-
CPYCPPYY_IMPORT PyTypeObject CPPInstance_Type;
129+
// Needs to be extern because the libROOTPythonizations is secretly using it
130+
#ifdef _MSC_VER
131+
extern __declspec(dllimport) PyTypeObject CPPInstance_Type;
132+
#else
133+
extern PyTypeObject CPPInstance_Type;
134+
#endif
130135

131136
template<typename T>
132137
inline bool CPPInstance_Check(T* object)

bindings/pyroot/cppyy/CPyCppyy/src/Cppyy.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@
99
#include <stdint.h>
1010

1111
// import/export (after precommondefs.h from PyPy)
12-
#ifdef _MSC_VER
13-
#define CPPYY_IMPORT extern __declspec(dllimport)
14-
#else
1512
#define CPPYY_IMPORT extern
16-
#endif
1713

1814
// some more types; assumes Cppyy.h follows Python.h
1915
#ifndef PY_LONG_LONG

bindings/pyroot/cppyy/CPyCppyy/src/SignalTryCatch.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ using CppyyExceptionContext_t = ExceptionContext_t;
7171
gException = R__old; \
7272
}
7373

74-
CPYCPPYY_IMPORT CppyyExceptionContext_t *gException;
74+
// extern, defined in ROOT Core
75+
#ifdef _MSC_VER
76+
extern __declspec(dllimport) CppyyExceptionContext_t *gException;
77+
#else
78+
extern CppyyExceptionContext_t *gException;
79+
#endif
7580

7681
#endif

bindings/pyroot/cppyy/cppyy-backend/CMakeLists.txt

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,6 @@
44
# For the licensing terms see $ROOTSYS/LICENSE.
55
# For the list of contributors see $ROOTSYS/README/CREDITS.
66

7-
set(py_sources
8-
cppyy_backend/__init__.py
9-
cppyy_backend/_cling_config.py
10-
cppyy_backend/_cppyy_generator.py
11-
cppyy_backend/_genreflex.py
12-
cppyy_backend/_rootcling.py
13-
cppyy_backend/bindings_utils.py
14-
cppyy_backend/loader.py
15-
cppyy_backend/_get_cppflags.py
16-
)
17-
18-
set(cppyy_backendPySrcDir cling/python/cppyy_backend)
19-
file(COPY ${cppyy_backendPySrcDir}
20-
DESTINATION ${localruntimedir}
21-
PATTERN "cmake" EXCLUDE
22-
PATTERN "pkg_templates" EXCLUDE)
23-
24-
file(RELATIVE_PATH PYTHONDIR_TO_LIBDIR "${CMAKE_INSTALL_FULL_PYTHONDIR}" "${CMAKE_INSTALL_FULL_LIBDIR}")
25-
26-
set(libname cppyy_backend)
27-
28-
add_library(${libname} SHARED clingwrapper/src/clingwrapper.cxx)
29-
if(MSVC)
30-
set_target_properties(${libname} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
31-
endif()
32-
# Set the suffix to '.so' and the prefix to 'lib'
33-
set_target_properties(${libname} PROPERTIES ${ROOT_LIBRARY_PROPERTIES})
34-
target_link_libraries(${libname} Core ${CMAKE_DL_LIBS})
35-
36-
# cppyy uses ROOT headers from binary directory
37-
add_dependencies(${libname} move_headers)
38-
39-
set_property(GLOBAL APPEND PROPERTY ROOT_EXPORTED_TARGETS ${libname})
40-
41-
if(NOT MSVC)
42-
# Make sure that relative RUNPATH to main ROOT libraries is always correct.
43-
44-
file(RELATIVE_PATH pymoduledir_to_libdir_build ${localruntimedir} "${localruntimedir}")
45-
file(RELATIVE_PATH pymoduledir_to_libdir_install ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_PYTHONDIR} "${CMAKE_INSTALL_FULL_LIBDIR}")
46-
47-
if(APPLE)
48-
set_target_properties(${libname} PROPERTIES
49-
BUILD_RPATH "@loader_path/${pymoduledir_to_libdir_build}"
50-
INSTALL_RPATH "@loader_path/${pymoduledir_to_libdir_install}"
51-
)
52-
else()
53-
set_target_properties(${libname} PROPERTIES
54-
BUILD_RPATH "$ORIGIN/${pymoduledir_to_libdir_build}"
55-
INSTALL_RPATH "$ORIGIN/${pymoduledir_to_libdir_install}"
56-
)
57-
endif()
58-
59-
endif()
60-
61-
# Install library
62-
install(TARGETS ${libname} EXPORT ${CMAKE_PROJECT_NAME}Exports
63-
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
64-
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
65-
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries)
66-
if (NOT MSVC AND NOT CMAKE_INSTALL_LIBDIR STREQUAL CMAKE_INSTALL_PYTHONDIR)
67-
# add a symlink to ${libname} in CMAKE_INSTALL_PYTHONDIR
68-
set(LIB_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}.so)
69-
install(CODE "file(CREATE_LINK ${PYTHONDIR_TO_LIBDIR}/${LIB_FILE_NAME}
70-
\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_PYTHONDIR}/${LIB_FILE_NAME} SYMBOLIC)")
71-
endif()
72-
73-
# Compile .py files
74-
foreach(py_source ${py_sources})
75-
install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} -m py_compile ${localruntimedir}/${py_source})")
76-
install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} -O -m py_compile ${localruntimedir}/${py_source})")
77-
endforeach()
78-
79-
80-
# Install Python sources and bytecode
81-
install(DIRECTORY ${localruntimedir}/cppyy_backend
82-
DESTINATION ${CMAKE_INSTALL_PYTHONDIR}
83-
COMPONENT libraries)
7+
add_library(cppyy_backend STATIC clingwrapper/src/clingwrapper.cxx)
8+
target_link_libraries(cppyy_backend Core)
9+
target_compile_options(cppyy_backend PRIVATE -fPIC)

bindings/pyroot/cppyy/cppyy/python/cppyy/_cpython_cppyy.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import sys
66

77
from . import _stdcpp_fix
8-
from cppyy_backend import loader
98

109
__all__ = [
1110
'gbl',
@@ -19,11 +18,19 @@
1918
'_end_capture_stderr'
2019
]
2120

22-
# first load the dependency libraries of the backend, then pull in the
23-
# libcppyy extension module
24-
c = loader.load_cpp_backend()
21+
# First load the dependency libraries of the backend, then pull in the libcppyy
22+
# extension module. If the backed can't be loaded, it was probably linked
23+
# statically into the extension module, so we don't error out at this point.
24+
try:
25+
from cppyy_backend import loader
26+
c = loader.load_cpp_backend()
27+
except ImportError:
28+
c = None
29+
2530
import libcppyy as _backend
26-
_backend._cpp_backend = c
31+
32+
if c is not None:
33+
_backend._cpp_backend = c
2734

2835
# explicitly expose APIs from libcppyy
2936
_w = ctypes.CDLL(_backend.__file__, ctypes.RTLD_GLOBAL)

bindings/pyroot/cppyy/patches/cppyy-backend-Fix-backend-loading-on-macOS.patch

Lines changed: 0 additions & 31 deletions
This file was deleted.

bindings/pyroot/pythonizations/python/ROOT/__init__.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,14 @@
2121
# Prevent cppyy from filtering ROOT libraries
2222
os.environ["CPPYY_NO_ROOT_FILTER"] = "1"
2323

24-
root_module_path = os.path.dirname(__file__) # expected to be ${CMAKE_INSTALL_PYTHONDIR}/ROOT
25-
root_install_pythondir = os.path.dirname(root_module_path) # expected to be ${CMAKE_INSTALL_PYTHONDIR}
26-
27-
# Directly tell cppyy where to find the backend library so we don't have to
28-
# rely on the LD_LIBRARY_PATH being set
29-
os.environ["CPPYY_BACKEND_LIBRARY"] = os.path.join(root_install_pythondir, "libcppyy_backend")
30-
3124
# The libROOTPythonizations CPython extension is in the same directory as the
3225
# ROOT Python module, but to find the other ROOT libraries we need to also add
3326
# the path of the ROOT library directory (only needed on Windows). For example,
3427
# if the ROOT Python module is in $ROOTSYS/bin/ROOT/__init__.py, the libraries
3528
# are usually in $ROOTSYS/bin.
3629
if 'win32' in sys.platform:
30+
root_module_path = os.path.dirname(__file__) # expected to be ${CMAKE_INSTALL_PYTHONDIR}/ROOT
31+
root_install_pythondir = os.path.dirname(root_module_path) # expected to be ${CMAKE_INSTALL_PYTHONDIR}
3732
os.add_dll_directory(root_install_pythondir)
3833

3934
# Do setup specific to AddressSanitizer environments

0 commit comments

Comments
 (0)