From 775aec03dd5ffed482a2c660a534c6ffa571a9eb Mon Sep 17 00:00:00 2001 From: Mizux Seiha Date: Tue, 19 May 2020 16:58:10 +0200 Subject: [PATCH] cmake(python): Refactor * Use new Python module * Use setup.py.in input file (should fix windows error) --- cmake/python.cmake | 126 ++++-------------- ortools/algorithms/python/CMakeLists.txt | 2 +- .../constraint_solver/python/CMakeLists.txt | 2 +- ortools/data/python/CMakeLists.txt | 2 +- ortools/graph/python/CMakeLists.txt | 2 +- ortools/linear_solver/python/CMakeLists.txt | 2 +- ortools/python/setup.py.in | 64 +++++++++ ortools/sat/python/CMakeLists.txt | 2 +- ortools/util/python/CMakeLists.txt | 2 +- 9 files changed, 97 insertions(+), 107 deletions(-) create mode 100644 ortools/python/setup.py.in diff --git a/cmake/python.cmake b/cmake/python.cmake index ddf0985960..ed03ac2c4b 100644 --- a/cmake/python.cmake +++ b/cmake/python.cmake @@ -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':[$<$>:'.libs/*', '../$'>]," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.constraint_solver':['$', '*.pyi']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.linear_solver':['$', '*.pyi']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.sat':['$', '*.pyi']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.graph':['$']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.algorithms':['$']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.data':['$', '*.pyi']," >> setup.py - COMMAND ${CMAKE_COMMAND} -E echo " 'ortools.util':['$', '*.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. $ +configure_file( + ortools/python/setup.py.in + ${CMAKE_CURRENT_BINARY_DIR}/python/setup.py.in + @ONLY) +file(GENERATE + OUTPUT python/$/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 $/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 $ ${PROJECT_NAME}/.libs + COMMAND ${CMAKE_COMMAND} -E copy $<$>:$> ${PROJECT_NAME}/.libs COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/algorithms COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/graph COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/constraint_solver COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/linear_solver COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/sat COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/data - #COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_NAME}/.libs - COMMAND ${CMAKE_COMMAND} -E copy $<$>:$> ${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) diff --git a/ortools/algorithms/python/CMakeLists.txt b/ortools/algorithms/python/CMakeLists.txt index 3b1e9f3e17..0a71c3c01e 100644 --- a/ortools/algorithms/python/CMakeLists.txt +++ b/ortools/algorithms/python/CMakeLists.txt @@ -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() diff --git a/ortools/constraint_solver/python/CMakeLists.txt b/ortools/constraint_solver/python/CMakeLists.txt index 8264c7ca42..04f472e023 100644 --- a/ortools/constraint_solver/python/CMakeLists.txt +++ b/ortools/constraint_solver/python/CMakeLists.txt @@ -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() diff --git a/ortools/data/python/CMakeLists.txt b/ortools/data/python/CMakeLists.txt index 83b3f97daa..3d96f25317 100644 --- a/ortools/data/python/CMakeLists.txt +++ b/ortools/data/python/CMakeLists.txt @@ -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() diff --git a/ortools/graph/python/CMakeLists.txt b/ortools/graph/python/CMakeLists.txt index 1ac6081e67..19ab13f660 100644 --- a/ortools/graph/python/CMakeLists.txt +++ b/ortools/graph/python/CMakeLists.txt @@ -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() diff --git a/ortools/linear_solver/python/CMakeLists.txt b/ortools/linear_solver/python/CMakeLists.txt index 02d320b8d2..2eb092a1a8 100644 --- a/ortools/linear_solver/python/CMakeLists.txt +++ b/ortools/linear_solver/python/CMakeLists.txt @@ -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() diff --git a/ortools/python/setup.py.in b/ortools/python/setup.py.in new file mode 100644 index 0000000000..2a0237d921 --- /dev/null +++ b/ortools/python/setup.py.in @@ -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\" ', + 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@':[$<$>:'.libs/*', '../$'>], + '@PROJECT_NAME@.constraint_solver':['$', '*.pyi'], + '@PROJECT_NAME@.linear_solver':['$', '*.pyi'], + '@PROJECT_NAME@.sat':['$', '*.pyi'], + '@PROJECT_NAME@.graph':['$'], + '@PROJECT_NAME@.algorithms':['$'], + '@PROJECT_NAME@.data':['$', '*.pyi'], + '@PROJECT_NAME@.util':['$', '*.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' + ], +) diff --git a/ortools/sat/python/CMakeLists.txt b/ortools/sat/python/CMakeLists.txt index ffa1ac8757..1a9792f965 100644 --- a/ortools/sat/python/CMakeLists.txt +++ b/ortools/sat/python/CMakeLists.txt @@ -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() diff --git a/ortools/util/python/CMakeLists.txt b/ortools/util/python/CMakeLists.txt index 8a483788e3..acb7284ffb 100644 --- a/ortools/util/python/CMakeLists.txt +++ b/ortools/util/python/CMakeLists.txt @@ -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()