summaryrefslogtreecommitdiffstats
path: root/CMakeScripts/UseCython.cmake
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CMakeScripts/UseCython.cmake288
1 files changed, 288 insertions, 0 deletions
diff --git a/CMakeScripts/UseCython.cmake b/CMakeScripts/UseCython.cmake
new file mode 100644
index 0000000..38c3155
--- /dev/null
+++ b/CMakeScripts/UseCython.cmake
@@ -0,0 +1,288 @@
+# 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.
+
+#TODO - rewrite to use ALLCAPS?
+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 )
+
+find_package( Cython REQUIRED )
+find_package( PythonLibs REQUIRED )
+
+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 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( c_header_dependencies "" )
+ set( pyx_locations "" )
+
+ 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()
+
+ # 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()
+
+ set( module_dependencies "" )
+
+ # Look for cimport and include statements.
+ file( STRINGS "${pxd}" cimport_statements REGEX "(cimport|include)" )
+ foreach( statement ${cimport_statements} )
+ if( ${statement} MATCHES from )
+ string( REGEX REPLACE "from[ ]+([^ ]+).*" "\\1.pxd" module "${statement}" )
+ elseif( ${statement} MATCHES include )
+ string( REGEX REPLACE "include[ ]+[\"]([^\"]+)[\"].*" "\\1" module "${statement}" )
+ else()
+ string( REGEX REPLACE "cimport[ ]+([^ ]+).*" "\\1.pxd" 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}
+ 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
+ else()
+ message("${module} ignored")
+ 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" )
+ 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 "${_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}
+ ${annotate_arg} ${no_docstrings_arg} ${cython_debug_arg} ${CYTHON_FLAGS}
+ --output-file ${_generated_file} ${pyx_locations}
+ DEPENDS ${pyx_locations} ${pxd_dependencies}
+ IMPLICIT_DEPENDS ${pyx_lang} ${c_header_dependencies}
+ 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 )
+ 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} generated_file ${pyx_module_sources} )
+ include_directories( ${PYTHON_INCLUDE_DIRS} )
+ python_add_module( ${_name} ${generated_file} ${other_module_sources} )
+ target_link_libraries( ${_name} ${PYTHON_LIBRARIES} )
+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 )
+ 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 "${_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()