| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| # Define a function to create Cython modules. |
| # |
| # For more information on the Cython project, see http://cython.org/. |
| # "Cython is a language that makes writing C extensions for the Python language |
| # as easy as Python itself." |
| # |
| # This file defines a CMake function to build a Cython Python module. |
| # To use it, first include this file. |
| # |
| # include( UseCython ) |
| # |
| # Then call cython_add_module to create a module. |
| # |
| # cython_add_module( <module_name> <src1> <src2> ... <srcN> ) |
| # |
| # To create a standalone executable, the function |
| # |
| # cython_add_standalone_executable( <executable_name> [MAIN_MODULE src1] <src1> <src2> ... <srcN> ) |
| # |
| # To avoid dependence on Python, set the PYTHON_LIBRARY cache variable to point |
| # to a static library. If a MAIN_MODULE source is specified, |
| # the "if __name__ == '__main__':" from that module is used as the C main() method |
| # for the executable. If MAIN_MODULE, the source with the same basename as |
| # <executable_name> is assumed to be the MAIN_MODULE. |
| # |
| # Where <module_name> is the name of the resulting Python module and |
| # <src1> <src2> ... are source files to be compiled into the module, e.g. *.pyx, |
| # *.py, *.c, *.cxx, etc. A CMake target is created with name <module_name>. This can |
| # be used for target_link_libraries(), etc. |
| # |
| # The sample paths set with the CMake include_directories() command will be used |
| # for include directories to search for *.pxd when running the Cython complire. |
| # |
| # Cache variables that effect the behavior include: |
| # |
| # CYTHON_ANNOTATE |
| # CYTHON_NO_DOCSTRINGS |
| # CYTHON_FLAGS |
| # |
| # Source file properties that effect the build process are |
| # |
| # CYTHON_IS_CXX |
| # |
| # If this is set of a *.pyx file with CMake set_source_files_properties() |
| # command, the file will be compiled as a C++ file. |
| # |
| # See also FindCython.cmake |
| |
| #============================================================================= |
| # Copyright 2011 Kitware, Inc. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| #============================================================================= |
| |
| # Configuration options. |
| set( CYTHON_ANNOTATE OFF |
| CACHE BOOL "Create an annotated .html file when compiling *.pyx." ) |
| set( CYTHON_NO_DOCSTRINGS OFF |
| CACHE BOOL "Strip docstrings from the compiled module." ) |
| set( CYTHON_FLAGS "" CACHE STRING |
| "Extra flags to the cython compiler." ) |
| mark_as_advanced( CYTHON_ANNOTATE CYTHON_NO_DOCSTRINGS CYTHON_FLAGS ) |
| |
| unset(PYTHONLIBS_FOUND) |
| unset(PYTHONINTERP_FOUND) |
| unset(CYTHON_FOUND) |
| |
| if(NOT python_libs_version) |
| message(STATUS "Looking for python dependencies, version: ${python_libs_version}") |
| endif() |
| |
| find_package(PythonInterp ${python_libs_version}) |
| if(PYTHONINTERP_FOUND) |
| message(STATUS "Python ${python_libs_version} executable: ${PYTHON_EXECUTABLE}") |
| find_package(PythonLibs ${python_libs_version}) |
| if(PYTHONLIBS_FOUND) |
| set(PYTHON_DEBUG_LIBRARY ${PYTHON_LIBRARY}) |
| set(PYTHON_DEBUG_LIBRARIES ${PYTHON_DEBUG_LIBRARIES}) |
| |
| message(STATUS "PYTHONLIBS_VERSION_STRING: ${PYTHONLIBS_VERSION_STRING}") |
| string(REPLACE "." ";" PYTHON_VERSION_LIST ${PYTHONLIBS_VERSION_STRING}) |
| list(GET PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) |
| list(GET PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) |
| list(GET PYTHON_VERSION_LIST 1 PYTHON_VERSION_PATCH) |
| |
| find_package(Cython ${python_libs_version}) |
| if(CYTHON_FOUND) |
| set(CYTHON${python_libs_version}_FOUND ${python_libs_version}) |
| message(STATUS " CYTHON${python_libs_version}_FOUND: ${CYTHON${python_libs_version}_FOUND}") |
| else() |
| message(WARNING " Could not find package: Cython") |
| endif() |
| else() |
| message(WARNING " Could not find package: PythonLibs") |
| endif() |
| else() |
| message(WARNING " Could not find package: PythonInterp") |
| endif() |
| |
| if(NOT CYTHON${python_libs_version}_FOUND) |
| message(WARNING " Could not find cython and/or dependencies for major version ${python_libs_version}") |
| return() |
| endif() |
| |
| if(NOT PYTHON_VERSION_MAJOR EQUAL ${python_libs_version}) |
| message(WARNING "Scripts found wrong python major version: ${PYTHON_VERSION_MAJOR} instead of ${python_libs_version}. This is most likely due to version ${python_libs_version} not being installed or not found") |
| unset(PYTHONLIBS_FOUND) |
| unset(PYTHONINTERP_FOUND) |
| unset(CYTHON_FOUND) |
| return() |
| endif() |
| |
| set( CYTHON_CXX_EXTENSION "cxx" ) |
| set( CYTHON_C_EXTENSION "c" ) |
| |
| # Create a *.c or *.cxx file from a *.pyx file. |
| # Input the generated file basename. The generate file will put into the variable |
| # placed in the "generated_file" argument. Finally all the *.py and *.pyx files. |
| function(compile_pyx _name c_cxx_output_subdir debug_output_dir generated_file) |
| # Default to assuming all files are C. |
| set( cxx_arg "" ) |
| set( extension ${CYTHON_C_EXTENSION} ) |
| set( pyx_lang "C" ) |
| set( comment "Compiling Cython C source for ${_name}..." ) |
| |
| set( cython_include_directories "" ) |
| set( pxd_dependencies "" ) |
| set( pxi_dependencies "" ) |
| set( c_header_dependencies "" ) |
| set( pyx_locations "" ) |
| |
| #message(STATUS " compile_pyx( ${_name} ${generated_file} ${ARGN} )") |
| |
| foreach( pyx_file ${ARGN} ) |
| get_filename_component( pyx_file_basename "${pyx_file}" NAME_WE ) |
| |
| # Determine if it is a C or C++ file. |
| get_source_file_property( property_is_cxx ${pyx_file} CYTHON_IS_CXX ) |
| if( ${property_is_cxx} ) |
| set( cxx_arg "--cplus" ) |
| set( extension ${CYTHON_CXX_EXTENSION} ) |
| set( pyx_lang "CXX" ) |
| set( comment "Compiling Cython CXX source for ${_name}..." ) |
| endif() |
| |
| # Get the include directories. |
| get_source_file_property( pyx_location ${pyx_file} LOCATION ) |
| |
| get_filename_component( pyx_path ${pyx_location} PATH ) |
| |
| #get_directory_property( cmake_include_directories DIRECTORY ${pyx_path} INCLUDE_DIRECTORIES ) |
| |
| list( APPEND cython_include_directories ${cmake_include_directories} ) |
| list( APPEND pyx_locations "${pyx_location}" ) |
| |
| # Determine dependencies. |
| # Add the pxd file will the same name as the given pyx file. |
| unset( corresponding_pxd_file CACHE ) |
| find_file( corresponding_pxd_file ${pyx_file_basename}.pxd |
| PATHS "${pyx_path}" ${cmake_include_directories} |
| NO_DEFAULT_PATH ) |
| if( corresponding_pxd_file ) |
| list( APPEND pxd_dependencies "${corresponding_pxd_file}" ) |
| endif() |
| |
| # Look for included pxi files |
| file(STRINGS "${pyx_file}" include_statements REGEX "include +['\"]([^'\"]+).*") |
| foreach(statement ${include_statements}) |
| string(REGEX REPLACE "include +['\"]([^'\"]+).*" "\\1" pxi_file "${statement}") |
| unset(pxi_location CACHE) |
| find_file(pxi_location ${pxi_file} |
| PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) |
| if (pxi_location) |
| list(APPEND pxi_dependencies ${pxi_location}) |
| get_filename_component( found_pyi_file_basename "${pxi_file}" NAME_WE ) |
| get_filename_component( found_pyi_path ${pxi_location} PATH ) |
| unset( found_pyi_pxd_file CACHE ) |
| find_file( found_pyi_pxd_file ${found_pyi_file_basename}.pxd |
| PATHS "${found_pyi_path}" ${cmake_include_directories} NO_DEFAULT_PATH ) |
| if (found_pyi_pxd_file) |
| list( APPEND pxd_dependencies "${found_pyi_pxd_file}" ) |
| endif() |
| endif() |
| endforeach() # for each include statement found |
| |
| # pxd files to check for additional dependencies. |
| set( pxds_to_check "${pyx_file}" "${pxd_dependencies}" ) |
| set( pxds_checked "" ) |
| set( number_pxds_to_check 1 ) |
| while( ${number_pxds_to_check} GREATER 0 ) |
| foreach( pxd ${pxds_to_check} ) |
| list( APPEND pxds_checked "${pxd}" ) |
| list( REMOVE_ITEM pxds_to_check "${pxd}" ) |
| |
| # check for C header dependencies |
| file( STRINGS "${pxd}" extern_from_statements |
| REGEX "cdef[ ]+extern[ ]+from.*$" ) |
| foreach( statement ${extern_from_statements} ) |
| # Had trouble getting the quote in the regex |
| string( REGEX REPLACE "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1" header "${statement}" ) |
| unset( header_location CACHE ) |
| find_file( header_location ${header} PATHS ${cmake_include_directories} ) |
| if( header_location ) |
| list( FIND c_header_dependencies "${header_location}" header_idx ) |
| if( ${header_idx} LESS 0 ) |
| list( APPEND c_header_dependencies "${header_location}" ) |
| endif() |
| endif() |
| endforeach() |
| |
| # check for pxd dependencies |
| |
| # Look for cimport statements. |
| set( module_dependencies "" ) |
| file( STRINGS "${pxd}" cimport_statements REGEX cimport ) |
| foreach( statement ${cimport_statements} ) |
| if( ${statement} MATCHES from ) |
| string( REGEX REPLACE "from[ ]+([^ ]+).*" "\\1" module "${statement}" ) |
| else() |
| string( REGEX REPLACE "cimport[ ]+([^ ]+).*" "\\1" module "${statement}" ) |
| endif() |
| list( APPEND module_dependencies ${module} ) |
| endforeach() |
| list( REMOVE_DUPLICATES module_dependencies ) |
| # Add the module to the files to check, if appropriate. |
| foreach( module ${module_dependencies} ) |
| unset( pxd_location CACHE ) |
| find_file( pxd_location ${module}.pxd |
| PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH ) |
| if( pxd_location ) |
| list( FIND pxds_checked ${pxd_location} pxd_idx ) |
| if( ${pxd_idx} LESS 0 ) |
| list( FIND pxds_to_check ${pxd_location} pxd_idx ) |
| if( ${pxd_idx} LESS 0 ) |
| list( APPEND pxds_to_check ${pxd_location} ) |
| list( APPEND pxd_dependencies ${pxd_location} ) |
| endif() # if it is not already going to be checked |
| endif() # if it has not already been checked |
| endif() # if pxd file can be found |
| endforeach() # for each module dependency discovered |
| endforeach() # for each pxd file to check |
| list( LENGTH pxds_to_check number_pxds_to_check ) |
| endwhile() |
| |
| |
| |
| endforeach() # pyx_file |
| |
| # Set additional flags. |
| if( CYTHON_ANNOTATE ) |
| set( annotate_arg "--annotate" ) |
| endif() |
| |
| if( CYTHON_NO_DOCSTRINGS ) |
| set( no_docstrings_arg "--no-docstrings" ) |
| endif() |
| |
| if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR |
| "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" ) |
| set( cython_debug_arg |
| #"--gdb" |
| --gdb-outdir ${debug_output_dir} |
| ) |
| endif() |
| |
| if( "${PYTHONLIBS_VERSION_STRING}" MATCHES "^2." ) |
| set( version_arg "-2" ) |
| elseif( "${PYTHONLIBS_VERSION_STRING}" MATCHES "^3." ) |
| set( version_arg "-3" ) |
| else() |
| set( version_arg ) |
| endif() |
| |
| # Include directory arguments. |
| list( REMOVE_DUPLICATES cython_include_directories ) |
| set( include_directory_arg "" ) |
| foreach( _include_dir ${cython_include_directories} ) |
| set( include_directory_arg ${include_directory_arg} "-I" "${_include_dir}" ) |
| endforeach() |
| |
| # Determining generated file name. |
| set( _generated_file "${CMAKE_CURRENT_BINARY_DIR}/${c_cxx_output_subdir}/${_name}.${extension}" ) |
| set_source_files_properties( ${_generated_file} PROPERTIES GENERATED TRUE ) |
| set( ${generated_file} ${_generated_file} PARENT_SCOPE ) |
| |
| list( REMOVE_DUPLICATES pxd_dependencies ) |
| list( REMOVE_DUPLICATES c_header_dependencies ) |
| |
| # Add the command to run the compiler. |
| add_custom_command( OUTPUT ${_generated_file} |
| COMMAND ${CYTHON_EXECUTABLE} |
| ARGS ${cxx_arg} ${include_directory_arg} ${version_arg} |
| ${annotate_arg} ${no_docstrings_arg} ${cython_debug_arg} ${CYTHON_FLAGS} |
| --output-file ${_generated_file} ${pyx_locations} |
| DEPENDS ${pyx_locations} ${pxd_dependencies} ${pxi_dependencies} |
| IMPLICIT_DEPENDS ${pyx_lang} ${c_header_dependencies} |
| WORKING_DIRECTORY ${c_cxx_output_subdir} |
| COMMENT ${comment} |
| ) |
| |
| # Remove their visibility to the user. |
| set( corresponding_pxd_file "" CACHE INTERNAL "" ) |
| set( header_location "" CACHE INTERNAL "" ) |
| set( pxd_location "" CACHE INTERNAL "" ) |
| endfunction() |
| |
| # cython_add_module( <name> src1 src2 ... srcN ) |
| # Build the Cython Python module. |
| function( cython_add_module _name c_cxx_output_subdir debug_output_dir) |
| set( pyx_module_sources "" ) |
| set( other_module_sources "" ) |
| foreach( _file ${ARGN} ) |
| if( ${_file} MATCHES ".*\\.py[x]?$" ) |
| list( APPEND pyx_module_sources ${_file} ) |
| else() |
| list( APPEND other_module_sources ${_file} ) |
| endif() |
| endforeach() |
| compile_pyx( ${_name} ${c_cxx_output_subdir} ${debug_output_dir} generated_file ${pyx_module_sources}) |
| include_directories( ${PYTHON_INCLUDE_DIRS} ) |
| python_add_module( ${_name} ${generated_file} ${other_module_sources} ) |
| if( APPLE ) |
| set_target_properties( ${_name} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" ) |
| else() |
| target_link_libraries( ${_name} ${PYTHON_LIBRARIES} ) |
| endif() |
| endfunction() |
| |
| include( CMakeParseArguments ) |
| |
| # cython_add_standalone_executable( _name [MAIN_MODULE src3.py] src1 src2 ... srcN ) |
| # Creates a standalone executable the given sources. |
| function(cython_add_standalone_executable _name debug_output_dir) |
| set( pyx_module_sources "" ) |
| set( other_module_sources "" ) |
| set( main_module "" ) |
| cmake_parse_arguments( cython_arguments "" "MAIN_MODULE" "" ${ARGN} ) |
| include_directories( ${PYTHON_INCLUDE_DIRS} ) |
| foreach( _file ${cython_arguments_UNPARSED_ARGUMENTS} ) |
| if( ${_file} MATCHES ".*\\.py[x]?$" ) |
| get_filename_component( _file_we ${_file} NAME_WE ) |
| if( "${_file_we}" STREQUAL "${_name}" ) |
| set( main_module "${_file}" ) |
| elseif( NOT "${_file}" STREQUAL "${cython_arguments_MAIN_MODULE}" ) |
| set( PYTHON_MODULE_${_file_we}_static_BUILD_SHARED OFF ) |
| compile_pyx( "${_file_we}_static" generated_file ${debug_output_dir} "${_file}" ) |
| list( APPEND pyx_module_sources "${generated_file}" ) |
| endif() |
| else() |
| list( APPEND other_module_sources ${_file} ) |
| endif() |
| endforeach() |
| |
| if( cython_arguments_MAIN_MODULE ) |
| set( main_module ${cython_arguments_MAIN_MODULE} ) |
| endif() |
| if( NOT main_module ) |
| message( FATAL_ERROR "main module not found." ) |
| endif() |
| get_filename_component( main_module_we "${main_module}" NAME_WE ) |
| set( CYTHON_FLAGS ${CYTHON_FLAGS} --embed ) |
| compile_pyx( "${main_module_we}_static" generated_file ${main_module} ) |
| add_executable( ${_name} ${generated_file} ${pyx_module_sources} ${other_module_sources} ) |
| target_link_libraries( ${_name} ${PYTHON_LIBRARIES} ${pyx_module_libs} ) |
| endfunction() |