Skip to content

Commit 85e74b8

Browse files
committed
[Python] Restructure Python libraries to not require system links
Split the `libcppyy.so` shared library into two libraries: one library with the C++ API that will be installed into `CMAKE_INSTALL_LIBRARYDIR`, and a CPython extension that will be installed to `CMAKE_INSTALL_PYTHONDIR`. Note that the C++ API is also used by other ROOT libraries (TPython). Like this, we don't have to use symlinks to ensure that the libraries are found in both directories.
1 parent c37c953 commit 85e74b8

File tree

7 files changed

+117
-66
lines changed

7 files changed

+117
-66
lines changed

bindings/pyroot/cppyy/CPyCppyy/CMakeLists.txt

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,95 +15,125 @@ set(headers
1515

1616
set(sources
1717
src/API.cxx
18-
src/CallContext.cxx
19-
src/Converters.cxx
2018
src/CPPClassMethod.cxx
2119
src/CPPConstructor.cxx
2220
src/CPPDataMember.cxx
2321
src/CPPEnum.cxx
2422
src/CPPExcInstance.cxx
2523
src/CPPFunction.cxx
24+
src/CPPGetSetItem.cxx
2625
src/CPPInstance.cxx
2726
src/CPPMethod.cxx
2827
src/CPPOperator.cxx
2928
src/CPPOverload.cxx
3029
src/CPPScope.cxx
31-
src/CPPGetSetItem.cxx
3230
src/CPyCppyyModule.cxx
31+
src/CallContext.cxx
32+
src/Converters.cxx
3333
src/CustomPyTypes.cxx
34-
src/Dispatcher.cxx
3534
src/DispatchPtr.cxx
35+
src/Dispatcher.cxx
3636
src/Executors.cxx
3737
src/LowLevelViews.cxx
3838
src/MemoryRegulator.cxx
3939
src/ProxyWrappers.cxx
40+
src/PyException.cxx
41+
src/PyResult.cxx
4042
src/PyStrings.cxx
4143
src/Pythonize.cxx
4244
src/TemplateProxy.cxx
43-
src/PyException.cxx
44-
src/PyResult.cxx
4545
src/TupleOfInstances.cxx
4646
src/TypeManip.cxx
4747
src/Utility.cxx
4848
)
4949

50-
file(RELATIVE_PATH PYTHONDIR_TO_LIBDIR "${CMAKE_INSTALL_FULL_PYTHONDIR}" "${CMAKE_INSTALL_FULL_LIBDIR}")
51-
52-
set(libname cppyy)
5350

54-
add_library(${libname} SHARED ${headers} ${sources})
51+
add_library(CPyCppyy SHARED ${headers} ${sources})
5552
# Set the suffix to '.so' and the prefix to 'lib'
56-
set_target_properties(${libname} PROPERTIES ${ROOT_LIBRARY_PROPERTIES})
53+
set_target_properties(CPyCppyy PROPERTIES ${ROOT_LIBRARY_PROPERTIES})
54+
target_link_libraries(CPyCppyy PRIVATE Core)
5755
if(MSVC)
58-
target_link_libraries(${libname} PRIVATE cppyy_backend)
59-
target_link_libraries(${libname} PUBLIC Python3::Python)
60-
set_target_properties(${libname} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
61-
set_target_properties(${libname} PROPERTIES PREFIX "lib")
62-
set_target_properties(${libname} PROPERTIES SUFFIX ".pyd")
56+
target_link_libraries(CPyCppyy PRIVATE cppyy_backend)
57+
target_link_libraries(CPyCppyy PUBLIC Python3::Python)
58+
set_target_properties(CPyCppyy PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
6359
elseif(APPLE)
64-
target_link_libraries(${libname} PRIVATE -Wl,-bind_at_load -Wl,-w -Wl,-undefined -Wl,dynamic_lookup cppyy_backend)
60+
target_link_libraries(CPyCppyy PRIVATE -Wl,-bind_at_load -Wl,-w -Wl,-undefined -Wl,dynamic_lookup cppyy_backend)
6561
else()
66-
target_link_libraries(${libname} PRIVATE -Wl,--unresolved-symbols=ignore-all cppyy_backend)
62+
target_link_libraries(CPyCppyy PRIVATE -Wl,--unresolved-symbols=ignore-all cppyy_backend)
6763
endif()
6864

6965
if(NOT MSVC)
70-
target_compile_options(${libname} PRIVATE -Wno-strict-aliasing)
66+
target_compile_options(CPyCppyy PRIVATE -Wno-strict-aliasing)
7167
endif()
7268
if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND NOT MSVC)
73-
target_compile_options(${libname} PRIVATE
69+
target_compile_options(CPyCppyy PRIVATE
7470
-Wno-unused-but-set-parameter)
7571
endif()
7672

73+
74+
add_library(cppyy SHARED src/CPyCppyyPyModule.cxx)
75+
76+
# Set the suffix to '.so' and the prefix to 'lib'
77+
set_target_properties(cppyy PROPERTIES ${ROOT_LIBRARY_PROPERTIES})
78+
if(MSVC)
79+
target_link_libraries(cppyy PRIVATE CPyCppyy)
80+
set_target_properties(cppyy PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
81+
set_target_properties(cppyy PROPERTIES PREFIX "lib")
82+
set_target_properties(cppyy PROPERTIES SUFFIX ".pyd")
83+
elseif(APPLE)
84+
target_link_libraries(cppyy PRIVATE -Wl,-bind_at_load -Wl,-w -Wl,-undefined -Wl,dynamic_lookup CPyCppyy)
85+
else()
86+
target_link_libraries(cppyy PRIVATE -Wl,--unresolved-symbols=ignore-all CPyCppyy)
87+
endif()
88+
7789
# Avoid warnings due to invalid function casts from C++ functions in CPyCppyy
7890
# to CPython API function typedefs (e.g. PyCFunction). This is a common pattern
7991
# in CPython extension implementations, explicitly encouraged by the official
8092
# CPython docs for C/C++ extensions. see
8193
# https://docs.python.org/3/extending/extending.html#keyword-parameters-for-extension-functions
8294
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
83-
target_compile_options(${libname} PRIVATE -Wno-cast-function-type)
95+
target_compile_options(CPyCppyy PRIVATE -Wno-cast-function-type)
8496
endif()
8597

8698
# Disables warnings in Python 3.8 caused by the temporary extra filed for tp_print compatibility
8799
# (see https://github.com/python/cpython/blob/3.8/Include/cpython/object.h#L260).
88100
# Note that Python 3.8 is the lowers Python version that is still supported by
89101
# ROOT, so this compile option can be completely removed soon.
90102
if(NOT MSVC AND Python3_VERSION VERSION_LESS 3.9)
91-
target_compile_options(${libname} PRIVATE -Wno-missing-field-initializers)
103+
target_compile_options(CPyCppyy PRIVATE -Wno-missing-field-initializers)
92104
endif()
93105

94-
target_compile_definitions(${libname} PRIVATE NO_CPPYY_LEGACY_NAMESPACE)
106+
target_compile_definitions(CPyCppyy PRIVATE NO_CPPYY_LEGACY_NAMESPACE)
95107

96-
target_include_directories(${libname}
97-
SYSTEM PUBLIC ${Python3_INCLUDE_DIRS})
108+
target_include_directories(CPyCppyy SYSTEM PUBLIC ${Python3_INCLUDE_DIRS})
98109

99-
target_include_directories(${libname}
100-
PRIVATE
101-
${CMAKE_BINARY_DIR}/ginclude
110+
target_include_directories(CPyCppyy
102111
PUBLIC
103112
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
104113
)
105114

106-
set_property(GLOBAL APPEND PROPERTY ROOT_EXPORTED_TARGETS ${libname})
115+
if(NOT MSVC)
116+
# Make sure that relative RUNPATH to main ROOT libraries is always correct.
117+
118+
file(RELATIVE_PATH pymoduledir_to_libdir_build ${localruntimedir} "${localruntimedir}")
119+
file(RELATIVE_PATH pymoduledir_to_libdir_install ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_PYTHONDIR} "${CMAKE_INSTALL_FULL_LIBDIR}")
120+
121+
if(APPLE)
122+
set_target_properties(cppyy PROPERTIES
123+
BUILD_RPATH "@loader_path/${pymoduledir_to_libdir_build}"
124+
INSTALL_RPATH "@loader_path/${pymoduledir_to_libdir_install}"
125+
)
126+
else()
127+
set_target_properties(cppyy PROPERTIES
128+
BUILD_RPATH "$ORIGIN/${pymoduledir_to_libdir_build}"
129+
INSTALL_RPATH "$ORIGIN/${pymoduledir_to_libdir_install}"
130+
)
131+
endif()
132+
133+
endif()
134+
135+
set_property(GLOBAL APPEND PROPERTY ROOT_EXPORTED_TARGETS CPyCppyy)
136+
set_property(GLOBAL APPEND PROPERTY ROOT_EXPORTED_TARGETS cppyy)
107137

108138
if(NOT MSVC)
109139
# Make sure that relative RUNPATH to main ROOT libraries is always correct.
@@ -126,18 +156,17 @@ if(NOT MSVC)
126156
endif()
127157

128158
# Install library
129-
install(TARGETS ${libname} EXPORT ${CMAKE_PROJECT_NAME}Exports
159+
install(TARGETS CPyCppyy EXPORT ${CMAKE_PROJECT_NAME}Exports
130160
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
131161
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
132162
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries)
133-
if (NOT MSVC AND NOT CMAKE_INSTALL_LIBDIR STREQUAL CMAKE_INSTALL_PYTHONDIR)
134-
# add a symlink to ${libname} in CMAKE_INSTALL_PYTHONDIR
135-
set(LIB_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}.so)
136-
install(CODE "file(CREATE_LINK ${PYTHONDIR_TO_LIBDIR}/${LIB_FILE_NAME}
137-
\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_PYTHONDIR}/${LIB_FILE_NAME} SYMBOLIC)")
138-
endif()
139163

140-
file(COPY ${headers} DESTINATION ${CMAKE_BINARY_DIR}/include/CPyCppyy)
164+
install(TARGETS cppyy EXPORT ${CMAKE_PROJECT_NAME}Exports
165+
RUNTIME DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries
166+
LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries
167+
ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries)
168+
169+
file(COPY ${headers} DESTINATION ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}/CPyCppyy)
141170
install(FILES ${headers}
142171
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/CPyCppyy
143172
COMPONENT headers)

bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,21 +1064,18 @@ static struct PyModuleDef moduledef = {
10641064
cpycppyymodule_clear,
10651065
nullptr
10661066
};
1067+
#endif
10671068

1069+
namespace CPyCppyy {
10681070

10691071
//----------------------------------------------------------------------------
1070-
#define CPYCPPYY_INIT_ERROR return nullptr
1071-
extern "C" PyObject* PyInit_libcppyy()
1072-
#else
1073-
#define CPYCPPYY_INIT_ERROR return
1074-
extern "C" void initlibcppyy()
1075-
#endif
1072+
PyObject* Init()
10761073
{
10771074
// Initialization of extension module libcppyy.
10781075

10791076
// load commonly used python strings
10801077
if (!CPyCppyy::CreatePyStrings())
1081-
CPYCPPYY_INIT_ERROR;
1078+
return nullptr;
10821079

10831080
// setup interpreter
10841081
#if PY_VERSION_HEX < 0x03090000
@@ -1107,7 +1104,7 @@ extern "C" void initlibcppyy()
11071104
gThisModule = Py_InitModule(const_cast<char*>("libcppyy"), gCPyCppyyMethods);
11081105
#endif
11091106
if (!gThisModule)
1110-
CPYCPPYY_INIT_ERROR;
1107+
return nullptr;
11111108

11121109
// keep gThisModule, but do not increase its reference count even as it is borrowed,
11131110
// or a self-referencing cycle would be created
@@ -1121,58 +1118,58 @@ extern "C" void initlibcppyy()
11211118

11221119
// inject meta type
11231120
if (!Utility::InitProxy(gThisModule, &CPPScope_Type, "CPPScope"))
1124-
CPYCPPYY_INIT_ERROR;
1121+
return nullptr;
11251122

11261123
// inject object proxy type
11271124
if (!Utility::InitProxy(gThisModule, &CPPInstance_Type, "CPPInstance"))
1128-
CPYCPPYY_INIT_ERROR;
1125+
return nullptr;
11291126

11301127
// inject exception object proxy type
11311128
if (!Utility::InitProxy(gThisModule, &CPPExcInstance_Type, "CPPExcInstance"))
1132-
CPYCPPYY_INIT_ERROR;
1129+
return nullptr;
11331130

11341131
// inject method proxy type
11351132
if (!Utility::InitProxy(gThisModule, &CPPOverload_Type, "CPPOverload"))
1136-
CPYCPPYY_INIT_ERROR;
1133+
return nullptr;
11371134

11381135
// inject template proxy type
11391136
if (!Utility::InitProxy(gThisModule, &TemplateProxy_Type, "TemplateProxy"))
1140-
CPYCPPYY_INIT_ERROR;
1137+
return nullptr;
11411138

11421139
// inject property proxy type
11431140
if (!Utility::InitProxy(gThisModule, &CPPDataMember_Type, "CPPDataMember"))
1144-
CPYCPPYY_INIT_ERROR;
1141+
return nullptr;
11451142

11461143
// inject custom data types
11471144
#if PY_VERSION_HEX < 0x03000000
11481145
if (!Utility::InitProxy(gThisModule, &RefFloat_Type, "Double"))
1149-
CPYCPPYY_INIT_ERROR;
1146+
return nullptr;
11501147

11511148
if (!Utility::InitProxy(gThisModule, &RefInt_Type, "Long"))
1152-
CPYCPPYY_INIT_ERROR;
1149+
return nullptr;
11531150
#endif
11541151

11551152
if (!Utility::InitProxy(gThisModule, &CustomInstanceMethod_Type, "InstanceMethod"))
1156-
CPYCPPYY_INIT_ERROR;
1153+
return nullptr;
11571154

11581155
if (!Utility::InitProxy(gThisModule, &TupleOfInstances_Type, "InstanceArray"))
1159-
CPYCPPYY_INIT_ERROR;
1156+
return nullptr;
11601157

11611158
if (!Utility::InitProxy(gThisModule, &LowLevelView_Type, "LowLevelView"))
1162-
CPYCPPYY_INIT_ERROR;
1159+
return nullptr;
11631160

11641161
if (!Utility::InitProxy(gThisModule, &PyNullPtr_t_Type, "nullptr_t"))
1165-
CPYCPPYY_INIT_ERROR;
1162+
return nullptr;
11661163

11671164
// custom iterators
11681165
if (PyType_Ready(&InstanceArrayIter_Type) < 0)
1169-
CPYCPPYY_INIT_ERROR;
1166+
return nullptr;
11701167

11711168
if (PyType_Ready(&IndexIter_Type) < 0)
1172-
CPYCPPYY_INIT_ERROR;
1169+
return nullptr;
11731170

11741171
if (PyType_Ready(&VectorIter_Type) < 0)
1175-
CPYCPPYY_INIT_ERROR;
1172+
return nullptr;
11761173

11771174
// inject identifiable nullptr and default
11781175
gNullPtrObject = (PyObject*)&_CPyCppyy_NullPtrStruct;
@@ -1209,6 +1206,8 @@ extern "C" void initlibcppyy()
12091206

12101207
#if PY_VERSION_HEX >= 0x03000000
12111208
Py_INCREF(gThisModule);
1212-
return gThisModule;
12131209
#endif
1210+
return gThisModule;
12141211
}
1212+
1213+
} // namespace CPyCppyy
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef CPYCPPYY_CPYCPPYYMODULE_H
2+
#define CPYCPPYY_CPYCPPYYMODULE_H
3+
4+
#include "Python.h"
5+
6+
namespace CPyCppyy {
7+
8+
PyObject *Init();
9+
10+
}
11+
12+
#endif // !CPYCPPYY_CPYCPPYYMODULE_H
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "CPyCppyyModule.h"
2+
3+
//----------------------------------------------------------------------------
4+
#if PY_VERSION_HEX >= 0x03000000
5+
extern "C" PyObject* PyInit_libcppyy() {
6+
#else
7+
extern "C" void initlibcppyy() {
8+
#endif
9+
PyObject *thisModule = CPyCppyy::Init();
10+
#if PY_VERSION_HEX >= 0x03000000
11+
return thisModule;
12+
#endif
13+
}

bindings/pyroot/pythonizations/CMakeLists.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ add_dependencies(${libname} ROOTPythonizationsPySources)
174174

175175
# Set the suffix to '.so' and the prefix to 'lib'
176176
set_target_properties(${libname} PROPERTIES ${ROOT_LIBRARY_PROPERTIES_NO_VERSION})
177-
target_link_libraries(${libname} PUBLIC Core Tree cppyy)
177+
target_link_libraries(${libname} PUBLIC Core Tree CPyCppyy)
178178
if(MSVC)
179179
set_target_properties(${libname} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
180180
set_target_properties(${libname} PROPERTIES SUFFIX ".pyd")
@@ -184,9 +184,6 @@ else()
184184
target_link_libraries(${libname} PUBLIC -Wl,--unresolved-symbols=ignore-all)
185185
endif()
186186

187-
target_include_directories(${libname}
188-
SYSTEM PRIVATE ${Python3_INCLUDE_DIRS})
189-
190187
target_include_directories(${libname}
191188
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>)
192189

bindings/pyroot/pythonizations/test/import_load_libs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class ImportLoadLibs(unittest.TestCase):
5656
"libresolv",
5757
# cppyy and Python libraries
5858
"libcppyy.*",
59+
"libCPyCppyy.*",
5960
"libROOTPythonizations.*",
6061
"libpython.*",
6162
"libutil.*",

bindings/tpython/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTTPython
2323
DEPENDENCIES
2424
Core
2525
LIBRARIES
26-
cppyy
26+
CPyCppyy
2727
# We link libTPython against Python libraries to compensate for the fact that libcppyy
2828
# is built with unresolved symbols. If we didn't do this, invoking TPython from C++
2929
# would not work.

0 commit comments

Comments
 (0)