cmake(python): Refactor

* Use new Python module
* Use setup.py.in input file (should fix windows error)
This commit is contained in:
Mizux Seiha
2020-05-19 16:58:10 +02:00
parent f384add856
commit 775aec03dd
9 changed files with 97 additions and 107 deletions

View File

@@ -22,24 +22,13 @@ if(UNIX AND NOT APPLE)
list(APPEND CMAKE_SWIG_FLAGS "-DSWIGWORDSIZE64")
endif()
# Setup Python
# prefer Python 3.8 over 3.7 over ...
# user can overwrite it e.g.:
# cmake -S. -Bbuild -DBUILD_PYTHON=ON -DPython_ADDITIONAL_VERSIONS="3.9"
set(Python_ADDITIONAL_VERSIONS "3.8;3.7;3.6;3.5" CACHE STRING "Python to use for binding")
find_package(PythonInterp REQUIRED)
message(STATUS "Found Python: ${PYTHON_EXECUTABLE} (found version \"${PYTHON_VERSION_STRING}\")")
# Find Python
find_package(Python REQUIRED COMPONENTS Interpreter Development)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
list(APPEND CMAKE_SWIG_FLAGS "-py3;-DPY3")
if(Python_VERSION VERSION_GREATER_EQUAL 3)
list(APPEND CMAKE_SWIG_FLAGS "-py3")
endif()
# Find Python Library
# Force PythonLibs to find the same version than the python interpreter.
set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_STRING}")
find_package(PythonLibs REQUIRED)
message(STATUS "Found Python Include: ${PYTHON_INCLUDE_DIRS} (found version \"${PYTHONLIBS_VERSION_STRING}\")")
# Generate Protobuf py sources
set(PROTO_PYS)
file(GLOB_RECURSE proto_py_files RELATIVE ${PROJECT_SOURCE_DIR}
@@ -104,84 +93,21 @@ file(COPY ortools/sat/python/cp_model.py
file(COPY ortools/sat/python/visualization.py
DESTINATION python/ortools/sat/python)
# To use a cmake generator expression (aka $<>), it must be processed at build time
# i.e. inside a add_custom_command()
# This command will depend on TARGET(s) in cmake generator expression
add_custom_command(OUTPUT python/setup.py
COMMAND ${CMAKE_COMMAND} -E echo "from setuptools import dist, find_packages, setup" > setup.py
COMMAND ${CMAKE_COMMAND} -E echo "" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "class BinaryDistribution(dist.Distribution):" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " def is_pure(self):" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " return False" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " def has_ext_modules(self):" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " return True" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "from setuptools.command.install import install" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "class InstallPlatlib(install):" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " def finalize_options(self):" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " install.finalize_options(self)" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " self.install_lib=self.install_platlib" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo "setup(" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " name='ortools'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " license='Apache 2.0'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " version='${PROJECT_VERSION}'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " author='Google Inc'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " author_email='lperron@google.com'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " description='Google OR-Tools python libraries and modules'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " long_description='read(README.txt)'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " keywords=('operations research' +" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ', constraint programming' +" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ', linear programming' +" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ', flow algoritms' +" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ', python')," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " url='https://developers.google.com/optimization/'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " download_url='https://github.com/google/or-tools/releases'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " distclass=BinaryDistribution," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " cmdclass={'install': InstallPlatlib}," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " packages=find_packages()," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " package_data={" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools':[$<$<NOT:$<PLATFORM_ID:Windows>>:'.libs/*', '../$<TARGET_SONAME_FILE_NAME:ortools>'>]," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.constraint_solver':['$<TARGET_FILE_NAME:pywrapcp>', '*.pyi']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.linear_solver':['$<TARGET_FILE_NAME:pywraplp>', '*.pyi']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.sat':['$<TARGET_FILE_NAME:pywrapsat>', '*.pyi']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.graph':['$<TARGET_FILE_NAME:pywrapgraph>']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.algorithms':['$<TARGET_FILE_NAME:pywrapknapsack_solver>']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.data':['$<TARGET_FILE_NAME:pywraprcpsp>', '*.pyi']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.util':['$<TARGET_FILE_NAME:sorted_interval_list>', '*.pyi']," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " }," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " include_package_data=True," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " install_requires=[" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'protobuf >= 3.11.3'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'six >= 1.10'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ]," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " classifiers=[" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Development Status :: 5 - Production/Stable'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Intended Audience :: Developers'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'License :: OSI Approved :: Apache Software License'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Operating System :: POSIX :: Linux'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Operating System :: MacOS :: MacOS X'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Operating System :: Microsoft :: Windows'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Programming Language :: Python :: 3'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Programming Language :: Python :: 3.5'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Programming Language :: Python :: 3.6'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Programming Language :: Python :: 3.7'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Programming Language :: Python :: 3.8'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Topic :: Office/Business :: Scheduling'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Topic :: Scientific/Engineering'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Topic :: Scientific/Engineering :: Mathematics'," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " 'Topic :: Software Development :: Libraries :: Python Modules'" >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo " ]," >> setup.py
COMMAND ${CMAKE_COMMAND} -E echo ")" >> setup.py
COMMENT "Generate setup.py at build time (to use generator expression)"
WORKING_DIRECTORY python
VERBATIM)
# setup.py.in contains cmake variable e.g. @PROJECT_NAME@ and
# generator expression e.g. $<TARGET_FILE_NAME:pyFoo>
configure_file(
ortools/python/setup.py.in
${CMAKE_CURRENT_BINARY_DIR}/python/setup.py.in
@ONLY)
file(GENERATE
OUTPUT python/$<CONFIG>/setup.py
INPUT ${CMAKE_CURRENT_BINARY_DIR}/python/setup.py.in)
# Find if python module MODULE_NAME is available,
# if not install it to the Python user install directory.
function(search_python_module MODULE_NAME)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c "import ${MODULE_NAME}; print(${MODULE_NAME}.__version__)"
COMMAND ${Python_EXECUTABLE} -c "import ${MODULE_NAME}; print(${MODULE_NAME}.__version__)"
RESULT_VARIABLE _RESULT
OUTPUT_VARIABLE MODULE_VERSION
ERROR_QUIET
@@ -192,7 +118,7 @@ function(search_python_module MODULE_NAME)
else()
message(WARNING "Can't find python module \"${MODULE_NAME}\", install it using pip...")
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -m pip install --user ${MODULE_NAME}
COMMAND ${Python_EXECUTABLE} -m pip install --user ${MODULE_NAME}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
@@ -204,20 +130,17 @@ search_python_module(wheel)
# Main Target
add_custom_target(python_package ALL
DEPENDS
ortools::ortools
Py${PROJECT_NAME}_proto
python/setup.py
COMMAND ${CMAKE_COMMAND} -E copy $<CONFIG>/setup.py setup.py
COMMAND ${CMAKE_COMMAND} -E remove_directory dist
COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_NAME}/.libs
#COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:ortools> ${PROJECT_NAME}/.libs
COMMAND ${CMAKE_COMMAND} -E copy $<$<NOT:$<PLATFORM_ID:Windows>>:$<TARGET_SONAME_FILE:ortools>> ${PROJECT_NAME}/.libs
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywrapknapsack_solver> ${PROJECT_NAME}/algorithms
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywrapgraph> ${PROJECT_NAME}/graph
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywrapcp> ${PROJECT_NAME}/constraint_solver
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywraplp> ${PROJECT_NAME}/linear_solver
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywrapsat> ${PROJECT_NAME}/sat
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:pywraprcpsp> ${PROJECT_NAME}/data
#COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:ortools> ${PROJECT_NAME}/.libs
COMMAND ${CMAKE_COMMAND} -E copy $<$<NOT:$<PLATFORM_ID:Windows>>:$<TARGET_SONAME_FILE:ortools>> ${PROJECT_NAME}/.libs
#COMMAND ${PYTHON_EXECUTABLE} setup.py bdist_egg bdist_wheel
COMMAND ${PYTHON_EXECUTABLE} setup.py bdist_wheel
BYPRODUCTS
@@ -227,29 +150,32 @@ add_custom_target(python_package ALL
python/${PROJECT_NAME}.egg-info
WORKING_DIRECTORY python
)
add_dependencies(python_package ortools::ortools Py${PROJECT_NAME}_proto)
# Test
if(BUILD_TESTING)
# Look for python module virtualenv
search_python_module(virtualenv)
# Testing using a vitual environment
set(VENV_EXECUTABLE ${Python_EXECUTABLE} -m virtualenv)
set(VENV_DIR ${CMAKE_BINARY_DIR}/venv)
if(WIN32)
set(VENV_PYTHON_EXECUTABLE "${VENV_DIR}\\Scripts\\python.exe")
set(VENV_Python_EXECUTABLE "${VENV_DIR}\\Scripts\\python.exe")
else()
set(VENV_PYTHON_EXECUTABLE ${VENV_DIR}/bin/python)
set(VENV_Python_EXECUTABLE ${VENV_DIR}/bin/python)
endif()
# make a virtualenv to install our python package in it
add_custom_command(TARGET python_package POST_BUILD
COMMAND ${PYTHON_EXECUTABLE} -m virtualenv -p ${PYTHON_EXECUTABLE} ${VENV_DIR}
COMMAND ${VENV_EXECUTABLE} -p ${Python_EXECUTABLE} ${VENV_DIR}
# Must not call it in a folder containing the setup.py otherwise pip call it
# (i.e. "python setup.py bdist") while we want to consume the wheel package
COMMAND ${VENV_PYTHON_EXECUTABLE} -m pip install --find-links=${CMAKE_CURRENT_BINARY_DIR}/python/dist ${PROJECT_NAME}
COMMAND ${VENV_Python_EXECUTABLE} -m pip install --find-links=${CMAKE_CURRENT_BINARY_DIR}/python/dist ${PROJECT_NAME}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/test.py.in ${VENV_DIR}/test.py
BYPRODUCTS ${VENV_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} VERBATIM)
# run the tests within the virtualenv
add_test(pytest_venv ${VENV_PYTHON_EXECUTABLE} ${VENV_DIR}/test.py)
add_test(NAME pytest_venv
COMMAND ${VENV_Python_EXECUTABLE} ${VENV_DIR}/test.py)
#add_subdirectory(examples/python)
#add_subdirectory(examples/notebook)

View File

@@ -10,7 +10,7 @@ swig_add_library(pywrapknapsack_solver
target_include_directories(pywrapknapsack_solver PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywrapknapsack_solver PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywrapknapsack_solver PUBLIC "PY3")
endif()

View File

@@ -11,7 +11,7 @@ swig_add_library(pywrapcp
target_include_directories(pywrapcp PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywrapcp PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywrapcp PUBLIC "PY3")
endif()

View File

@@ -10,7 +10,7 @@ swig_add_library(pywraprcpsp
target_include_directories(pywraprcpsp PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywraprcpsp PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywraprcpsp PUBLIC "PY3")
endif()

View File

@@ -10,7 +10,7 @@ swig_add_library(pywrapgraph
target_include_directories(pywrapgraph PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywrapgraph PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywrapgraph PUBLIC "PY3")
endif()

View File

@@ -10,7 +10,7 @@ swig_add_library(pywraplp
target_include_directories(pywraplp PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywraplp PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywraplp PUBLIC "PY3")
endif()

View File

@@ -0,0 +1,64 @@
from setuptools import find_packages, setup
from setuptools.dist import Distribution
class BinaryDistribution(Distribution):
def is_pure(self):
return False
def has_ext_modules(self):
return True
from setuptools.command.install import install
class InstallPlatlib(install):
def finalize_options(self):
install.finalize_options(self)
self.install_lib=self.install_platlib
setup(
name='@PROJECT_NAME@',
version='@PROJECT_VERSION@',
author='Google LLC',
author_email='\"Laurent Perron\" <lperron@google.com>',
description='Google OR-Tools python libraries and modules',
long_description='read(README.txt)',
keywords=('operations research' +
', constraint programming' +
', linear programming' +
', flow algoritms' +
', python'),
url='https://developers.google.com/optimization/',
download_url='https://github.com/google/or-tools/releases',
distclass=BinaryDistribution,
cmdclass={'install': InstallPlatlib},
packages=find_packages(),
package_data={
'@PROJECT_NAME@':[$<$<NOT:$<PLATFORM_ID:Windows>>:'.libs/*', '../$<TARGET_SONAME_FILE_NAME:ortools>'>],
'@PROJECT_NAME@.constraint_solver':['$<TARGET_FILE_NAME:pywrapcp>', '*.pyi'],
'@PROJECT_NAME@.linear_solver':['$<TARGET_FILE_NAME:pywraplp>', '*.pyi'],
'@PROJECT_NAME@.sat':['$<TARGET_FILE_NAME:pywrapsat>', '*.pyi'],
'@PROJECT_NAME@.graph':['$<TARGET_FILE_NAME:pywrapgraph>'],
'@PROJECT_NAME@.algorithms':['$<TARGET_FILE_NAME:pywrapknapsack_solver>'],
'@PROJECT_NAME@.data':['$<TARGET_FILE_NAME:pywraprcpsp>', '*.pyi'],
'@PROJECT_NAME@.util':['$<TARGET_FILE_NAME:sorted_interval_list>', '*.pyi'],
},
include_package_data=True,
install_requires=['protobuf >= 3.11.3', 'six >= 1.10'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Programming Language :: C++',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Office/Business :: Scheduling',
'Topic :: Scientific/Engineering',
'Topic :: Scientific/Engineering :: Mathematics',
'Topic :: Software Development :: Libraries :: Python Modules'
],
)

View File

@@ -10,7 +10,7 @@ swig_add_library(pywrapsat
target_include_directories(pywrapsat PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET pywrapsat PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(pywrapsat PUBLIC "PY3")
endif()

View File

@@ -10,7 +10,7 @@ swig_add_library(sorted_interval_list
target_include_directories(sorted_interval_list PRIVATE ${PYTHON_INCLUDE_DIRS})
set_property(TARGET sorted_interval_list PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON)
if(${PYTHON_VERSION_STRING} VERSION_GREATER_EQUAL 3)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
target_compile_definitions(sorted_interval_list PUBLIC "PY3")
endif()