# 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( <target_name> <pyx_target_name> <output_files> <src1> <src2> ... <srcN> )
#
# Where <module_name> is the desired name of the target for the resulting Python module,
# <pyx_target_name> is the desired name of the target that runs the Cython compiler
# to generate the needed C or C++ files, <output_files> is a variable to hold the
# files generated by Cython, and <src1> <src2> ... are source files
# to be compiled into the module, e.g. *.pyx, *.c, *.cxx, etc.
# only one .pyx file may be present for each target
# (this is an inherent limitation of Cython).
#
# 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
#  CYTHON_IS_PUBLIC
#  CYTHON_IS_API
#
# 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)

find_package( PythonLibsNew REQUIRED )

# (using another C++ extension breaks coverage)
set( CYTHON_CXX_EXTENSION "cpp" )
set( CYTHON_C_EXTENSION "c" )

# Create a *.c or *.cpp file from a *.pyx file.
# Input the generated file basename.  The generate files will put into the variable
# placed in the "generated_files" argument. Finally all the *.py and *.pyx files.
function( compile_pyx _name pyx_target_name generated_files pyx_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}..." )

  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_source_file_property( pyx_location ${pyx_file} LOCATION )

  set ( output_file "${_name}.${extension}" )

  # Set additional flags.
  if( CYTHON_ANNOTATE )
    set( annotate_arg "--annotate" )
  endif()

  if( CYTHON_NO_DOCSTRINGS )
    set( no_docstrings_arg "--no-docstrings" )
  endif()

  if(NOT WIN32)
      if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR
            "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" )
          set( cython_debug_arg "--gdb" )
      endif()
  endif()

  # Determining generated file names.
  get_source_file_property( property_is_public ${pyx_file} CYTHON_PUBLIC )
  get_source_file_property( property_is_api ${pyx_file} CYTHON_API )
  if( ${property_is_api} )
      set( _generated_files "${output_file}" "${_name}.h" "${name}_api.h")
  elseif( ${property_is_public} )
      set( _generated_files "${output_file}" "${_name}.h")
  else()
      set( _generated_files "${output_file}")
  endif()
  set_source_files_properties( ${_generated_files} PROPERTIES GENERATED TRUE )

  if (NOT WIN32)
    # Cython creates a lot of compiler warning detritus on clang
    set_source_files_properties(${_generated_files} PROPERTIES
      COMPILE_FLAGS -Wno-unused-function)
  endif()

  set( ${generated_files} ${_generated_files} PARENT_SCOPE )

  # Add the command to run the compiler.
  add_custom_target(${pyx_target_name}
    COMMAND ${PYTHON_EXECUTABLE} -m cython ${cxx_arg}
    ${annotate_arg} ${no_docstrings_arg} ${cython_debug_arg}
    ${CYTHON_FLAGS}
    # Necessary for Cython code coverage
    --working ${CMAKE_CURRENT_SOURCE_DIR}
    --output-file "${CMAKE_CURRENT_BINARY_DIR}/${output_file}"
    "${CMAKE_CURRENT_SOURCE_DIR}/${pyx_file}"
    DEPENDS ${pyx_location}
    # Do not specify byproducts for now since they don't work with the older
    # version of cmake available in the apt repositories.
    #BYPRODUCTS ${_generated_files}
    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 pyx_target_name generated_files)
  set( pyx_module_source "" )
  set( other_module_sources "" )
  foreach( _file ${ARGN} )
    if( ${_file} MATCHES ".*\\.py[x]?$" )
      list( APPEND pyx_module_source ${_file} )
    else()
      list( APPEND other_module_sources ${_file} )
    endif()
  endforeach()
  compile_pyx( ${_name} ${pyx_target_name} _generated_files ${pyx_module_source} )
  set( ${generated_files} ${_generated_files} PARENT_SCOPE )
  include_directories( ${PYTHON_INCLUDE_DIRS} )
  python_add_module( ${_name} ${_generated_files} ${other_module_sources} )
  add_dependencies( ${_name} ${pyx_target_name})
endfunction()

include( CMakeParseArguments )
