Files
ortools-clone/cmake/python.cmake

259 lines
9.1 KiB
CMake
Raw Normal View History

if(NOT BUILD_PYTHON)
return()
endif()
# Use latest UseSWIG module
cmake_minimum_required(VERSION 3.14)
if(NOT TARGET ortools::ortools)
message(FATAL_ERROR "Python: missing ortools TARGET")
endif()
# Will need swig
set(CMAKE_SWIG_FLAGS)
find_package(SWIG REQUIRED)
include(UseSWIG)
if(${SWIG_VERSION} VERSION_GREATER_EQUAL 4)
list(APPEND CMAKE_SWIG_FLAGS "-doxygen")
endif()
if(UNIX AND NOT APPLE)
list(APPEND CMAKE_SWIG_FLAGS "-DSWIGWORDSIZE64")
endif()
# Find Python
find_package(Python REQUIRED COMPONENTS Interpreter Development)
if(Python_VERSION VERSION_GREATER_EQUAL 3)
2020-09-18 19:56:56 +02:00
list(APPEND CMAKE_SWIG_FLAGS "-py3" "-DPY3")
endif()
2020-11-30 21:11:12 +01:00
# 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__)"
RESULT_VARIABLE _RESULT
OUTPUT_VARIABLE MODULE_VERSION
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(${_RESULT} STREQUAL "0")
message(STATUS "Found python module: ${MODULE_NAME} (found version \"${MODULE_VERSION}\")")
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}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
endfunction()
# Generate Protobuf py sources with mypy support
search_python_module(mypy-protobuf)
set(PROTO_PYS)
file(GLOB_RECURSE proto_py_files RELATIVE ${PROJECT_SOURCE_DIR}
"ortools/constraint_solver/*.proto"
"ortools/data/*.proto"
"ortools/linear_solver/*.proto"
"ortools/sat/*.proto"
"ortools/util/*.proto"
)
list(REMOVE_ITEM proto_py_files "ortools/constraint_solver/demon_profiler.proto")
2020-10-08 16:15:49 +02:00
foreach(PROTO_FILE IN LISTS proto_py_files)
#message(STATUS "protoc proto(py): ${PROTO_FILE}")
get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY)
get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
set(PROTO_PY ${PROJECT_BINARY_DIR}/python/${PROTO_DIR}/${PROTO_NAME}_pb2.py)
#message(STATUS "protoc py: ${PROTO_PY}")
add_custom_command(
OUTPUT ${PROTO_PY}
COMMAND protobuf::protoc
"--proto_path=${PROJECT_SOURCE_DIR}"
2020-07-17 11:19:12 +02:00
${PROTO_DIRS}
"--python_out=${PROJECT_BINARY_DIR}/python"
2020-11-30 21:11:12 +01:00
"--mypy_out=${PROJECT_BINARY_DIR}/python"
${PROTO_FILE}
DEPENDS ${PROTO_FILE} protobuf::protoc
2020-03-04 17:46:34 +01:00
COMMENT "Generate Python protocol buffer for ${PROTO_FILE}"
VERBATIM)
list(APPEND PROTO_PYS ${PROTO_PY})
endforeach()
add_custom_target(Py${PROJECT_NAME}_proto DEPENDS ${PROTO_PYS} ortools::ortools)
# CMake will remove all '-D' prefix (i.e. -DUSE_FOO become USE_FOO)
#get_target_property(FLAGS ortools::ortools COMPILE_DEFINITIONS)
set(FLAGS -DUSE_BOP -DUSE_GLOP -DABSL_MUST_USE_RESULT)
if(USE_SCIP)
list(APPEND FLAGS "-DUSE_SCIP")
endif()
2020-02-12 11:30:15 +01:00
if(USE_COINOR)
list(APPEND FLAGS "-DUSE_CBC" "-DUSE_CLP")
2020-02-12 11:30:15 +01:00
endif()
list(APPEND CMAKE_SWIG_FLAGS ${FLAGS} "-I${PROJECT_SOURCE_DIR}")
2020-03-02 14:27:13 +01:00
foreach(SUBPROJECT IN ITEMS algorithms graph linear_solver constraint_solver sat data util)
add_subdirectory(ortools/${SUBPROJECT}/python)
endforeach()
#######################
## Python Packaging ##
#######################
#file(MAKE_DIRECTORY python/${PROJECT_NAME})
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME})
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/util)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/constraint_solver)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/linear_solver)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/sat)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/sat/python)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/graph)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/algorithms)
file(COPY ortools/__init__.py DESTINATION python/${PROJECT_NAME}/data)
file(COPY
ortools/linear_solver/linear_solver_natural_api.py
DESTINATION python/ortools/linear_solver)
file(COPY
ortools/sat/python/cp_model.py
ortools/sat/python/cp_model_helper.py
ortools/sat/python/visualization.py
DESTINATION python/ortools/sat/python)
# setup.py.in contains cmake variable e.g. @PROJECT_NAME@ and
# generator expression e.g. $<TARGET_FILE_NAME:pyFoo>
configure_file(
2020-05-27 17:13:48 +02:00
${PROJECT_SOURCE_DIR}/ortools/python/setup.py.in
${PROJECT_BINARY_DIR}/python/setup.py.in
@ONLY)
file(GENERATE
2020-05-27 17:13:48 +02:00
OUTPUT ${PROJECT_BINARY_DIR}/python/$<CONFIG>/setup.py
INPUT ${PROJECT_BINARY_DIR}/python/setup.py.in)
2020-11-30 21:11:12 +01:00
configure_file(
${PROJECT_SOURCE_DIR}/tools/README.pypi.txt
${PROJECT_BINARY_DIR}/python/README.txt
COPYONLY)
# Look for python module wheel
2020-02-12 11:30:15 +01:00
search_python_module(setuptools)
search_python_module(wheel)
# Main Target
2020-02-12 11:30:15 +01:00
add_custom_target(python_package ALL
2020-05-27 17:13:48 +02:00
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
2020-05-28 18:42:21 +02:00
# Don't need to copy static lib on windows.
2020-05-28 22:12:16 +02:00
COMMAND ${CMAKE_COMMAND} -E $<IF:$<BOOL:${UNIX}>,copy,true>
$<$<BOOL:${UNIX}>:$<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:sorted_interval_list> ${PROJECT_NAME}/util
2020-05-21 09:39:36 +02:00
#COMMAND ${Python_EXECUTABLE} setup.py bdist_egg bdist_wheel
COMMAND ${Python_EXECUTABLE} setup.py bdist_wheel
BYPRODUCTS
python/${PROJECT_NAME}
python/build
python/dist
python/${PROJECT_NAME}.egg-info
WORKING_DIRECTORY python
)
add_dependencies(python_package ortools::ortools Py${PROJECT_NAME}_proto)
2017-06-28 15:45:56 +05:30
# Install rules
configure_file(
${PROJECT_SOURCE_DIR}/cmake/python-install.cmake.in
${PROJECT_BINARY_DIR}/python/python-install.cmake
@ONLY)
install(SCRIPT ${PROJECT_BINARY_DIR}/python/python-install.cmake)
# Test
if(BUILD_TESTING)
# Look for python module venv
2020-11-18 11:41:16 +01:00
search_python_module(absl-py)
search_python_module(venv)
# Testing using a vitual environment
set(VENV_EXECUTABLE ${Python_EXECUTABLE} -m venv)
set(VENV_DIR ${PROJECT_BINARY_DIR}/python/venv)
if(WIN32)
set(VENV_Python_EXECUTABLE "${VENV_DIR}\\Scripts\\python.exe")
else()
set(VENV_Python_EXECUTABLE ${VENV_DIR}/bin/python)
endif()
# make a virtualenv to install our python package in it
2020-02-12 11:30:15 +01:00
add_custom_command(TARGET python_package POST_BUILD
COMMAND ${VENV_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 ${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
2020-05-27 17:13:48 +02:00
add_test(NAME pytest_venv
COMMAND ${VENV_Python_EXECUTABLE} ${VENV_DIR}/test.py)
2020-07-26 00:06:59 +02:00
endif()
2020-09-17 18:45:56 +02:00
# add_python_sample()
# CMake function to generate and build python sample.
# Parameters:
# the python filename
# e.g.:
# add_python_sample(foo.py)
function(add_python_sample FILE_NAME)
get_filename_component(SAMPLE_NAME ${FILE_NAME} NAME_WE)
get_filename_component(SAMPLE_DIR ${FILE_NAME} DIRECTORY)
get_filename_component(COMPONENT_DIR ${SAMPLE_DIR} DIRECTORY)
get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME)
if(BUILD_TESTING)
add_test(
NAME python_${COMPONENT_NAME}_${SAMPLE_NAME}
COMMAND ${VENV_Python_EXECUTABLE} ${FILE_NAME}
WORKING_DIRECTORY ${VENV_DIR})
endif()
endfunction()
# add_python_example()
2020-09-18 19:57:25 +02:00
# CMake function to generate and build python example.
2020-09-17 18:45:56 +02:00
# Parameters:
# the python filename
# e.g.:
# add_python_example(foo.py)
function(add_python_example FILE_NAME)
get_filename_component(EXAMPLE_NAME ${FILE_NAME} NAME_WE)
get_filename_component(COMPONENT_DIR ${FILE_NAME} DIRECTORY)
get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME)
if(BUILD_TESTING)
add_test(
NAME python_${COMPONENT_NAME}_${EXAMPLE_NAME}
COMMAND ${VENV_Python_EXECUTABLE} ${FILE_NAME}
WORKING_DIRECTORY ${VENV_DIR})
endif()
endfunction()
# add_python_test()
2020-09-18 19:57:25 +02:00
# CMake function to generate and build python test.
2020-09-17 18:45:56 +02:00
# Parameters:
# the python filename
# e.g.:
# add_python_test(foo.py)
function(add_python_test FILE_NAME)
get_filename_component(TEST_NAME ${FILE_NAME} NAME_WE)
get_filename_component(COMPONENT_DIR ${FILE_NAME} DIRECTORY)
get_filename_component(COMPONENT_NAME ${COMPONENT_DIR} NAME)
if(BUILD_TESTING)
add_test(
2020-09-22 14:51:37 +02:00
NAME python_${COMPONENT_NAME}_${TEST_NAME}
2020-09-17 18:45:56 +02:00
COMMAND ${VENV_Python_EXECUTABLE} ${FILE_NAME}
WORKING_DIRECTORY ${VENV_DIR})
endif()
endfunction()