Skip to content

Commit cbc12fb

Browse files
committed
cmake: rework python support
1 parent 0c7f7e8 commit cbc12fb

File tree

4 files changed

+238
-74
lines changed

4 files changed

+238
-74
lines changed

cmake/python.cmake

Lines changed: 129 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -107,30 +107,49 @@ if(BUILD_TESTING)
107107
endif()
108108
endif()
109109

110-
if(BUILD_TESTING)
111-
# add_python_test()
112-
# CMake function to generate and build python test.
113-
# Parameters:
114-
# the python filename
115-
# e.g.:
116-
# add_python_test(foo.py)
117-
function(add_python_test FILE_NAME)
118-
message(STATUS "Configuring test ${FILE_NAME} ...")
119-
get_filename_component(TEST_NAME ${FILE_NAME} NAME_WE)
110+
# add_python_test()
111+
# CMake function to generate and build python test.
112+
# Parameters:
113+
# FILE_NAME: the python filename
114+
# COMPONENT_NAME: name of the ortools/ subdir where the test is located
115+
# note: automatically determined if located in ortools/<component>/python/
116+
# e.g.:
117+
# add_python_test(
118+
# FILE_NAME
119+
# ${PROJECT_SOURCE_DIR}/ortools/foo/python/bar_test.py
120+
# COMPONENT_NAME
121+
# foo
122+
# )
123+
function(add_python_test)
124+
set(options "")
125+
set(oneValueArgs FILE_NAME)
126+
set(multiValueArgs "")
127+
cmake_parse_arguments(TEST
128+
"${options}"
129+
"${oneValueArgs}"
130+
"${multiValueArgs}"
131+
${ARGN}
132+
)
133+
if(NOT TEST_FILE_NAME)
134+
message(FATAL_ERROR "no FILE_NAME provided")
135+
endif()
136+
get_filename_component(TEST_NAME ${TEST_FILE_NAME} NAME_WE)
137+
138+
message(STATUS "Configuring test ${TEST_FILE_NAME} ...")
139+
140+
if(BUILD_TESTING)
120141
add_test(
121142
NAME python_test_${TEST_NAME}
122-
COMMAND ${VENV_Python3_EXECUTABLE} -m pytest ${FILE_NAME}
143+
COMMAND ${VENV_Python3_EXECUTABLE} -m pytest ${TEST_FILE_NAME}
123144
WORKING_DIRECTORY ${VENV_DIR})
124-
message(STATUS "Configuring test ${FILE_NAME} done")
125-
endfunction()
126-
endif()
145+
endif()
146+
message(STATUS "Configuring test ${TEST_FILE_NAME} ...DONE")
147+
endfunction()
127148

128149
#######################
129150
## PYTHON WRAPPERS ##
130151
#######################
131-
list(APPEND CMAKE_SWIG_FLAGS "-I${PROJECT_SOURCE_DIR}")
132-
133-
set(PYTHON_PROJECT cmakepybind11)
152+
set(PYTHON_PROJECT ${PROJECT_NAMESPACE})
134153
message(STATUS "Python project: ${PYTHON_PROJECT}")
135154
set(PYTHON_PROJECT_DIR ${PROJECT_BINARY_DIR}/python/${PYTHON_PROJECT})
136155
message(STATUS "Python project build path: ${PYTHON_PROJECT_DIR}")
@@ -139,11 +158,28 @@ message(STATUS "Python project build path: ${PYTHON_PROJECT_DIR}")
139158
## Python Packaging ##
140159
#######################
141160
#file(MAKE_DIRECTORY python/${PYTHON_PROJECT})
142-
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/__init__.py CONTENT "__version__ = \"${PROJECT_VERSION}\"\n")
161+
configure_file(
162+
${PROJECT_SOURCE_DIR}/python/__init__.py.in
163+
${PROJECT_BINARY_DIR}/python/__init__.py.in
164+
@ONLY)
165+
file(GENERATE
166+
OUTPUT ${PYTHON_PROJECT_DIR}/__init__.py
167+
INPUT ${PROJECT_BINARY_DIR}/python/__init__.py.in)
143168

144169
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foo/__init__.py CONTENT "")
170+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foo/python/__init__.py CONTENT "")
145171
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/bar/__init__.py CONTENT "")
172+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/bar/python/__init__.py CONTENT "")
146173
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foobar/__init__.py CONTENT "")
174+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foobar/python/__init__.py CONTENT "")
175+
176+
# Adds py.typed to make typed packages.
177+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foo/py.typed CONTENT "")
178+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foo/python/py.typed CONTENT "")
179+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/bar/py.typed CONTENT "")
180+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/bar/python/py.typed CONTENT "")
181+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foobar/py.typed CONTENT "")
182+
file(GENERATE OUTPUT ${PYTHON_PROJECT_DIR}/foobar/python/py.typed CONTENT "")
147183

148184
# setup.py.in contains cmake variable e.g. @PYTHON_PROJECT@ and
149185
# generator expression e.g. $<TARGET_FILE_NAME:pyFoo>
@@ -161,7 +197,76 @@ file(GENERATE
161197
# COMMAND ${CMAKE_COMMAND} -E copy setup.py setup.py
162198
# WORKING_DIRECTORY python)
163199

164-
# Look for python modules
200+
set(is_windows "$<PLATFORM_ID:Windows>")
201+
set(is_not_windows "$<NOT:$<PLATFORM_ID:Windows>>")
202+
203+
set(is_foo_shared "$<STREQUAL:$<TARGET_PROPERTY:Foo,TYPE>,SHARED_LIBRARY>")
204+
set(need_unix_foo_lib "$<AND:${is_not_windows},${is_foo_shared}>")
205+
set(need_windows_foo_lib "$<AND:${is_windows},${is_foo_shared}>")
206+
207+
set(is_bar_shared "$<STREQUAL:$<TARGET_PROPERTY:Bar,TYPE>,SHARED_LIBRARY>")
208+
set(need_unix_bar_lib "$<AND:${is_not_windows},${is_bar_shared}>")
209+
set(need_windows_bar_lib "$<AND:${is_windows},${is_bar_shared}>")
210+
211+
set(is_foobar_shared "$<STREQUAL:$<TARGET_PROPERTY:FooBar,TYPE>,SHARED_LIBRARY>")
212+
set(need_unix_foobar_lib "$<AND:${is_not_windows},${is_foobar_shared}>")
213+
set(need_windows_foobar_lib "$<AND:${is_windows},${is_foobar_shared}>")
214+
215+
add_custom_command(
216+
OUTPUT python/libs_timestamp
217+
COMMAND ${CMAKE_COMMAND} -E remove -f libs_timestamp
218+
COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_PROJECT}/.libs
219+
220+
COMMAND ${CMAKE_COMMAND} -E
221+
$<IF:${is_foo_shared},copy,true>
222+
$<${need_unix_foo_lib}:$<TARGET_SONAME_FILE:Foo>>
223+
$<${need_windows_foo_lib}:$<TARGET_FILE:Foo>>
224+
${PYTHON_PROJECT}/.libs
225+
226+
COMMAND ${CMAKE_COMMAND} -E
227+
$<IF:${is_bar_shared},copy,true>
228+
$<${need_unix_bar_lib}:$<TARGET_SONAME_FILE:Bar>>
229+
$<${need_windows_bar_lib}:$<TARGET_FILE:Bar>>
230+
${PYTHON_PROJECT}/.libs
231+
232+
COMMAND ${CMAKE_COMMAND} -E
233+
$<IF:${is_foobar_shared},copy,true>
234+
$<${need_unix_foobar_lib}:$<TARGET_SONAME_FILE:FooBar>>
235+
$<${need_windows_foobar_lib}:$<TARGET_FILE:FooBar>>
236+
${PYTHON_PROJECT}/.libs
237+
238+
COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/libs_timestamp
239+
MAIN_DEPENDENCY
240+
python/setup.py.in
241+
DEPENDS
242+
python/setup.py
243+
${PROJECT_NAMESPACE}::Foo
244+
${PROJECT_NAMESPACE}::Bar
245+
${PROJECT_NAMESPACE}::FooBar
246+
WORKING_DIRECTORY python
247+
COMMAND_EXPAND_LISTS)
248+
249+
add_custom_command(
250+
OUTPUT python/pybind11_timestamp
251+
COMMAND ${CMAKE_COMMAND} -E remove -f pybind11_timestamp
252+
COMMAND ${CMAKE_COMMAND} -E copy
253+
$<TARGET_FILE:pyFoo> ${PYTHON_PROJECT}/foo/python
254+
COMMAND ${CMAKE_COMMAND} -E copy
255+
$<TARGET_FILE:pyBar> ${PYTHON_PROJECT}/bar/python
256+
COMMAND ${CMAKE_COMMAND} -E copy
257+
$<TARGET_FILE:pyFooBar> ${PYTHON_PROJECT}/foobar/python
258+
COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/pybind11_timestamp
259+
MAIN_DEPENDENCY
260+
python/setup.py.in
261+
DEPENDS
262+
${PROJECT_NAMESPACE}::pyFoo
263+
${PROJECT_NAMESPACE}::pyBar
264+
${PROJECT_NAMESPACE}::pyFooBar
265+
WORKING_DIRECTORY python
266+
COMMAND_EXPAND_LISTS)
267+
268+
269+
# Look for required python modules
165270
search_python_module(
166271
NAME setuptools
167272
PACKAGE setuptools)
@@ -172,31 +277,16 @@ search_python_module(
172277
add_custom_command(
173278
OUTPUT python/dist_timestamp
174279
COMMAND ${CMAKE_COMMAND} -E remove_directory dist
175-
COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_PROJECT}/.libs
176-
# Don't need to copy static lib on Windows.
177-
COMMAND ${CMAKE_COMMAND} -E $<IF:$<STREQUAL:$<TARGET_PROPERTY:Foo,TYPE>,SHARED_LIBRARY>,copy,true>
178-
$<$<STREQUAL:$<TARGET_PROPERTY:Foo,TYPE>,SHARED_LIBRARY>:$<TARGET_SONAME_FILE:Foo>>
179-
${PYTHON_PROJECT}/.libs
180-
COMMAND ${CMAKE_COMMAND} -E $<IF:$<STREQUAL:$<TARGET_PROPERTY:Bar,TYPE>,SHARED_LIBRARY>,copy,true>
181-
$<$<STREQUAL:$<TARGET_PROPERTY:Bar,TYPE>,SHARED_LIBRARY>:$<TARGET_SONAME_FILE:Bar>>
182-
${PYTHON_PROJECT}/.libs
183-
COMMAND ${CMAKE_COMMAND} -E $<IF:$<STREQUAL:$<TARGET_PROPERTY:FooBar,TYPE>,SHARED_LIBRARY>,copy,true>
184-
$<$<STREQUAL:$<TARGET_PROPERTY:FooBar,TYPE>,SHARED_LIBRARY>:$<TARGET_SONAME_FILE:FooBar>>
185-
${PYTHON_PROJECT}/.libs
186-
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyFoo> ${PYTHON_PROJECT}/foo
187-
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyBar> ${PYTHON_PROJECT}/bar
188-
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pyFooBar> ${PYTHON_PROJECT}/foobar
189280
#COMMAND ${Python3_EXECUTABLE} setup.py bdist_egg bdist_wheel
190281
COMMAND ${Python3_EXECUTABLE} setup.py bdist_wheel
191282
COMMAND ${CMAKE_COMMAND} -E touch ${PROJECT_BINARY_DIR}/python/dist_timestamp
192283
MAIN_DEPENDENCY
193284
python/setup.py.in
194285
DEPENDS
195286
python/setup.py
196-
${PROJECT_NAMESPACE}::Foo
197-
${PROJECT_NAMESPACE}::pyFoo
198-
${PROJECT_NAMESPACE}::pyBar
199-
${PROJECT_NAMESPACE}::pyFooBar
287+
python/libs_timestamp
288+
python/pybind11_timestamp
289+
$<$<BOOL:${GENERATE_PYTHON_STUB}>:python/stub_timestamp>
200290
BYPRODUCTS
201291
python/${PYTHON_PROJECT}
202292
python/${PYTHON_PROJECT}.egg-info
@@ -226,7 +316,8 @@ if(BUILD_TESTING)
226316
COMMAND ${VENV_Python3_EXECUTABLE} -m pip install
227317
--find-links=${CMAKE_CURRENT_BINARY_DIR}/python/dist ${PYTHON_PROJECT}==${PROJECT_VERSION}
228318
# install modules only required to run examples
229-
COMMAND ${VENV_Python3_EXECUTABLE} -m pip install pytest
319+
COMMAND ${VENV_Python3_EXECUTABLE} -m pip install
320+
pytest
230321
BYPRODUCTS ${VENV_DIR}
231322
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
232323
COMMENT "Create venv and install ${PYTHON_PROJECT}"

python/__init__.py.in

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
r'''
2+
# Bazel PyBind11
3+
4+
This is the reference documentation for Pybind11 Abseil.
5+
'''
6+
7+
__docformat__ = "markdown" # explicitly disable rST processing above.
8+
__version__ = "@PROJECT_VERSION@"
9+
10+
import os
11+
import logging
12+
13+
def _load_dynamic_libs():
14+
"""Load shared libraries on Windows"""
15+
if os.name == "nt":
16+
logger = logging.getLogger("bp11")
17+
try:
18+
from ctypes import WinDLL
19+
basedir = os.path.dirname(__file__)
20+
except:
21+
logger.error(f"ImportError: Cannot import WinDLL")
22+
else:
23+
for dll in ["Foo.dll", "Bar.dll", "FooBar.dll"]:
24+
dll_path = os.path.join(basedir, ".libs", dll)
25+
if os.path.exists(dll_path):
26+
logger.debug(f"Loading {dll_path}...")
27+
WinDLL(dll_path)
28+
else:
29+
logger.debug(f"Cannot find {dll_path}")
30+
31+
32+
_load_dynamic_libs()

python/setup.py.in

Lines changed: 76 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,85 @@
1-
from setuptools import find_packages, setup
2-
from setuptools.dist import Distribution
1+
from sys import executable
2+
from os.path import join as pjoin
3+
from os.path import dirname
4+
5+
setuptools_import_error_message = """setuptools is not installed for """ + executable + """
6+
Please follow this link for installing instructions :
7+
https://pypi.python.org/pypi/setuptools
8+
make sure you use \"""" + executable + """\" during the installation"""
9+
10+
try:
11+
from setuptools import find_packages, setup
12+
from setuptools.dist import Distribution
13+
from setuptools.command.install import install
14+
except ImportError:
15+
raise ImportError(setuptools_import_error_message)
16+
317

418
class BinaryDistribution(Distribution):
5-
def is_pure(self):
6-
return False
7-
def has_ext_modules(self):
8-
return True
19+
def is_pure(self):
20+
return False
21+
22+
def has_ext_modules(self):
23+
return True
24+
925

10-
from setuptools.command.install import install
1126
class InstallPlatlib(install):
1227
def finalize_options(self):
1328
install.finalize_options(self)
14-
self.install_lib=self.install_platlib
29+
self.install_lib = self.install_platlib
30+
1531

1632
setup(
17-
name='@PYTHON_PROJECT@',
18-
version='@PROJECT_VERSION@',
19-
author='Mizux',
20-
author_email='\"Mizux Seiha\" <[email protected]>',
21-
url='https://github.com/Mizux/cmake-swig',
22-
distclass=BinaryDistribution,
23-
cmdclass={'install': InstallPlatlib},
24-
packages=find_packages(),
25-
package_data={
26-
'@PYTHON_PROJECT@':[$<$<NOT:$<PLATFORM_ID:Windows>>:'.libs/*'>],
27-
'@[email protected]':['$<TARGET_FILE_NAME:pyFoo>'],
28-
'@[email protected]':['$<TARGET_FILE_NAME:pyBar>'],
29-
'@[email protected]':['$<TARGET_FILE_NAME:pyFooBar>'],
30-
},
31-
include_package_data=True,
32-
classifiers=[
33-
'Development Status :: 5 - Production/Stable',
34-
'Intended Audience :: Developers',
35-
'License :: OSI Approved :: Apache Software License',
36-
'Operating System :: POSIX :: Linux',
37-
'Operating System :: MacOS :: MacOS X',
38-
'Operating System :: Microsoft :: Windows',
39-
'Programming Language :: Python',
40-
'Programming Language :: C++',
41-
'Topic :: Scientific/Engineering',
42-
'Topic :: Software Development :: Libraries :: Python Modules'
43-
],
33+
name='@PYTHON_PROJECT@',
34+
version='@PROJECT_VERSION@',
35+
packages=find_packages(),
36+
python_requires='>= 3.9',
37+
package_data={
38+
'@PYTHON_PROJECT@':['.libs/*'],
39+
'@[email protected]':['*.pyi'],
40+
'@[email protected]':['$<TARGET_FILE_NAME:pyFoo>', '*.pyi'],
41+
'@[email protected]':['*.pyi'],
42+
'@[email protected]':['$<TARGET_FILE_NAME:pyBar>', '*.pyi'],
43+
'@[email protected]':['*.pyi'],
44+
'@[email protected]':['$<TARGET_FILE_NAME:pyFooBar>', '*.pyi'],
45+
},
46+
include_package_data=True,
47+
license='Apache 2.0',
48+
author='Mizux',
49+
author_email='"Mizux Seiha" <[email protected]>',
50+
url='https://github.com/Mizux/cmake-pybind11',
51+
classifiers=[
52+
'Development Status :: 5 - Production/Stable',
53+
'Environment :: Console',
54+
'Intended Audience :: Developers',
55+
'Intended Audience :: Education',
56+
'Intended Audience :: Information Technology',
57+
'Intended Audience :: Science/Research',
58+
'License :: OSI Approved :: Apache Software License',
59+
'Operating System :: Unix',
60+
'Operating System :: POSIX :: Linux',
61+
'Operating System :: POSIX :: BSD :: FreeBSD',
62+
'Operating System :: POSIX :: BSD :: NetBSD',
63+
'Operating System :: POSIX :: BSD :: OpenBSD',
64+
'Operating System :: MacOS',
65+
'Operating System :: MacOS :: MacOS X',
66+
'Operating System :: Microsoft :: Windows',
67+
'Programming Language :: Python',
68+
'Programming Language :: Python :: 3',
69+
'Programming Language :: Python :: 3 :: Only',
70+
'Programming Language :: Python :: 3.9',
71+
'Programming Language :: Python :: 3.10',
72+
'Programming Language :: Python :: 3.11',
73+
'Programming Language :: Python :: 3.12',
74+
'Programming Language :: Python :: 3.13',
75+
'Programming Language :: C++',
76+
'Programming Language :: Python :: Implementation :: CPython',
77+
'Topic :: Office/Business',
78+
'Topic :: Scientific/Engineering',
79+
'Topic :: Scientific/Engineering :: Mathematics',
80+
'Topic :: Software Development',
81+
'Topic :: Software Development :: Libraries :: Python Modules',
82+
],
83+
distclass=BinaryDistribution,
84+
cmdclass={'install': InstallPlatlib},
4485
)

tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ endif()
44

55
file(GLOB PYTHON_SRCS "*.py")
66
foreach(FILE_NAME IN LISTS PYTHON_SRCS)
7-
add_python_test(${FILE_NAME})
7+
add_python_test(FILE_NAME ${FILE_NAME})
88
endforeach()

0 commit comments

Comments
 (0)