├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake ├── FindCython.cmake ├── FindF2PY.cmake ├── FindNumPy.cmake ├── FindPythonExtensions.cmake ├── UseCython.cmake ├── UseF2PY.cmake ├── UsePythonExtensions.cmake └── targetLinkLibrariesWithDynamicLookup.cmake ├── fortran_cython_examples └── __init__.py ├── notes.ipynb ├── notes ├── general.md └── getters_setters.md ├── pyproject.toml ├── setup.py ├── src ├── CMakeLists.txt ├── _fortran_cython_examples.pyx ├── characters │ ├── CMakeLists.txt │ ├── characters.f90 │ ├── characters.h │ ├── characters.pyx │ └── characters_wrapper.f90 ├── derived_type │ ├── CMakeLists.txt │ ├── derived_type.f90 │ ├── derived_type.h │ ├── derived_type.pyx │ └── derived_type_wrapper.f90 ├── dtype1 │ ├── CMakeLists.txt │ ├── dtype1.f90 │ ├── dtype1.pyx │ ├── dtype1_pxd.pxd │ ├── dtype1_wrapper.f90 │ └── dtype1_wrapper.h ├── module_variable │ ├── CMakeLists.txt │ ├── module_variable.f90 │ ├── module_variable.pyx │ ├── module_variable_pxd.pxd │ ├── module_variable_wrapper.f90 │ └── module_variable_wrapper.h ├── simple_function │ ├── CMakeLists.txt │ ├── simple_function.f90 │ ├── simple_function.h │ ├── simple_function.pyx │ └── simple_function_wrapper.f90 └── simple_subroutine │ ├── CMakeLists.txt │ ├── simple_subroutine.f90 │ ├── simple_subroutine.h │ ├── simple_subroutine.pyx │ └── simple_subroutine_wrapper.f90 └── tests ├── test_characters.py ├── test_derived_type.py ├── test_module_variable.py ├── test_simple_function.py └── test_simple_subroutine.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .ipynb_checkpoints 3 | __pycache__ 4 | *.so 5 | *.o 6 | Photo.run 7 | *.mod 8 | build 9 | *.a 10 | *.dylib 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.0) 2 | project(hello_cython LANGUAGES C Fortran) 3 | 4 | option(SKBUILD "Should be ON of being build by skbuild, 5 | and OFF of being build by regular cmake" OFF) 6 | 7 | if (NOT SKBUILD) 8 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") 9 | endif() 10 | 11 | find_package(PythonExtensions REQUIRED) 12 | find_package(NumPy REQUIRED) 13 | find_package(Cython REQUIRED) 14 | 15 | add_subdirectory(src) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fortran-cython-examples 2 | 3 | Examples for how to wrap fortran with `Cython`. 4 | 5 | # Check list 6 | 7 | ## Module variables 8 | 9 | - [ ] Scalars 10 | - [ ] Parameters 11 | - [ ] Characters 12 | - [ ] Explicit size arrays 13 | - [ ] Complex numbers (Scalar and parameters) 14 | - [ ] Getting a pointer 15 | - [ ] Getting the value of a pointer 16 | - [ ] Allocatable arrays 17 | - [ ] Derived types 18 | - [ ] Nested derived types 19 | - [ ] Arrays of derived types 20 | - [ ] Functions inside derived types 21 | - [ ] Arrays with dimension (:) (pointer, allocatable) inside derived types 22 | - [ ] Classes 23 | - [ ] Abstract interfaces 24 | - [ ] Common blocks 25 | - [ ] Equivalences 26 | - [ ] Namelists 27 | - [ ] Quad precision variables 28 | 29 | ## Functions/subroutines 30 | 31 | - [ ] Basic calling (no arguments) 32 | - [ ] Argument passing (scalars) 33 | - [ ] Argument passing (strings) 34 | - [ ] Argument passing (explicit arrays) 35 | - [ ] Argument passing (assumed size arrays) 36 | - [ ] Argument passing (assumed shape arrays) 37 | - [ ] Argument passing (allocatable arrays) 38 | - [ ] Argument passing (derived types) 39 | - [ ] Argument intents (in, out, inout and none) 40 | - [ ] Passing characters 41 | - [ ] Pointer Arguments 42 | - [ ] Optional arguments 43 | - [ ] Keyword arguments 44 | - [ ] Generic/Elemental functions 45 | - [ ] Functions as an argument 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /cmake/FindCython.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # Find ``cython`` executable. 4 | # 5 | # This module will set the following variables in your project: 6 | # 7 | # ``CYTHON_EXECUTABLE`` 8 | # path to the ``cython`` program 9 | # 10 | # ``CYTHON_VERSION`` 11 | # version of ``cython`` 12 | # 13 | # ``CYTHON_FOUND`` 14 | # true if the program was found 15 | # 16 | # For more information on the Cython project, see http://cython.org/. 17 | # 18 | # *Cython is a language that makes writing C extensions for the Python language 19 | # as easy as Python itself.* 20 | # 21 | #============================================================================= 22 | # Copyright 2011 Kitware, Inc. 23 | # 24 | # Licensed under the Apache License, Version 2.0 (the "License"); 25 | # you may not use this file except in compliance with the License. 26 | # You may obtain a copy of the License at 27 | # 28 | # http://www.apache.org/licenses/LICENSE-2.0 29 | # 30 | # Unless required by applicable law or agreed to in writing, software 31 | # distributed under the License is distributed on an "AS IS" BASIS, 32 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | # See the License for the specific language governing permissions and 34 | # limitations under the License. 35 | #============================================================================= 36 | 37 | # Use the Cython executable that lives next to the Python executable 38 | # if it is a local installation. 39 | find_package(PythonInterp) 40 | if(PYTHONINTERP_FOUND) 41 | get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH) 42 | find_program(CYTHON_EXECUTABLE 43 | NAMES cython cython.bat cython3 44 | HINTS ${_python_path} 45 | DOC "path to the cython executable") 46 | else() 47 | find_program(CYTHON_EXECUTABLE 48 | NAMES cython cython.bat cython3 49 | DOC "path to the cython executable") 50 | endif() 51 | 52 | if(CYTHON_EXECUTABLE) 53 | set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version) 54 | 55 | execute_process(COMMAND ${CYTHON_version_command} 56 | OUTPUT_VARIABLE CYTHON_version_output 57 | ERROR_VARIABLE CYTHON_version_error 58 | RESULT_VARIABLE CYTHON_version_result 59 | OUTPUT_STRIP_TRAILING_WHITESPACE 60 | ERROR_STRIP_TRAILING_WHITESPACE) 61 | 62 | if(NOT ${CYTHON_version_result} EQUAL 0) 63 | set(_error_msg "Command \"${CYTHON_version_command}\" failed with") 64 | set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}") 65 | message(SEND_ERROR "${_error_msg}") 66 | else() 67 | if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)") 68 | set(CYTHON_VERSION "${CMAKE_MATCH_1}") 69 | else() 70 | if("${CYTHON_version_error}" MATCHES "^[Cc]ython version ([^,]+)") 71 | set(CYTHON_VERSION "${CMAKE_MATCH_1}") 72 | endif() 73 | endif() 74 | endif() 75 | endif() 76 | 77 | include(FindPackageHandleStandardArgs) 78 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE) 79 | 80 | mark_as_advanced(CYTHON_EXECUTABLE) 81 | 82 | include(UseCython) 83 | -------------------------------------------------------------------------------- /cmake/FindF2PY.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # The purpose of the F2PY –Fortran to Python interface generator– project is to provide a 4 | # connection between Python and Fortran languages. 5 | # 6 | # F2PY is a Python package (with a command line tool f2py and a module f2py2e) that facilitates 7 | # creating/building Python C/API extension modules that make it possible to call Fortran 77/90/95 8 | # external subroutines and Fortran 90/95 module subroutines as well as C functions; to access Fortran 9 | # 77 COMMON blocks and Fortran 90/95 module data, including allocatable arrays from Python. 10 | # 11 | # For more information on the F2PY project, see http://www.f2py.com/. 12 | # 13 | # The following variables are defined: 14 | # 15 | # :: 16 | # 17 | # F2PY_EXECUTABLE - absolute path to the F2PY executable 18 | # 19 | # :: 20 | # 21 | # F2PY_VERSION_STRING - the version of F2PY found 22 | # F2PY_VERSION_MAJOR - the F2PY major version 23 | # F2PY_VERSION_MINOR - the F2PY minor version 24 | # F2PY_VERSION_PATCH - the F2PY patch version 25 | # 26 | # 27 | # .. note:: 28 | # 29 | # By default, the module finds the F2PY program associated with the installed NumPy package. 30 | # 31 | # Example usage 32 | # ^^^^^^^^^^^^^ 33 | # 34 | # Assuming that a package named ``method`` is declared in ``setup.py`` and that the corresponding directory 35 | # containing ``__init__.py`` also exists, the following CMake code can be added to ``method/CMakeLists.txt`` 36 | # to ensure the C sources associated with ``cylinder_methods.f90`` are generated and the corresponding module 37 | # is compiled: 38 | # 39 | # .. code-block:: cmake 40 | # 41 | # find_package(F2PY REQUIRED) 42 | # 43 | # set(f2py_module_name "_cylinder_methods") 44 | # set(fortran_src_file "${CMAKE_CURRENT_SOURCE_DIR}/cylinder_methods.f90") 45 | # 46 | # set(generated_module_file ${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_name}${PYTHON_EXTENSION_MODULE_SUFFIX}) 47 | # 48 | # add_custom_target(${f2py_module_name} ALL 49 | # DEPENDS ${generated_module_file} 50 | # ) 51 | # 52 | # add_custom_command( 53 | # OUTPUT ${generated_module_file} 54 | # COMMAND ${F2PY_EXECUTABLE} 55 | # -m ${f2py_module_name} 56 | # -c 57 | # ${fortran_src_file} 58 | # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 59 | # ) 60 | # 61 | # install(FILES ${generated_module_file} DESTINATION methods) 62 | # 63 | # .. warning:: 64 | # 65 | # Using ``f2py`` with ``-c`` argument means that f2py is also responsible to build the module. In that 66 | # case, CMake is not used to find the compiler and configure the associated build system. 67 | # 68 | 69 | find_program(F2PY_EXECUTABLE NAMES f2py${PYTHON_VERSION_MAJOR} f2py) 70 | 71 | # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. 72 | if(NOT F2PY_EXECUTABLE) 73 | find_package(NumPy) 74 | set(F2PY_EXECUTABLE "${PYTHON_EXECUTABLE}" "-m" "numpy.f2py.__main__") 75 | endif() 76 | 77 | if(NOT F2PY_INCLUDE_DIR) 78 | execute_process( 79 | COMMAND "${PYTHON_EXECUTABLE}" 80 | -c "import os; from numpy import f2py; print(os.path.dirname(f2py.__file__))" 81 | OUTPUT_VARIABLE _f2py_directory 82 | OUTPUT_STRIP_TRAILING_WHITESPACE 83 | ERROR_QUIET 84 | ) 85 | string(REPLACE "\\" "/" _f2py_directory ${_f2py_directory}) 86 | 87 | set(F2PY_INCLUDE_DIR "${_f2py_directory}/src" CACHE STRING "F2PY source directory location" FORCE) 88 | endif() 89 | 90 | # Set-up the F2PY libraries and include directories 91 | file(GLOB _f2py_sources "${F2PY_INCLUDE_DIR}/*.c") 92 | add_library(_f2py_runtime_library STATIC ${_f2py_sources}) 93 | target_include_directories( 94 | _f2py_runtime_library 95 | PRIVATE ${PYTHON_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS} 96 | ) 97 | 98 | set(F2PY_LIBRARIES _f2py_runtime_library) 99 | set(F2PY_INCLUDE_DIRS "${F2PY_INCLUDE_DIR}" "${NumPy_INCLUDE_DIRS}") 100 | 101 | if(F2PY_EXECUTABLE) 102 | # extract the version string 103 | execute_process(COMMAND "${F2PY_EXECUTABLE}" -v 104 | OUTPUT_VARIABLE F2PY_VERSION_STRING 105 | OUTPUT_STRIP_TRAILING_WHITESPACE) 106 | if("${F2PY_VERSION_STRING}" MATCHES "^([0-9]+)(.([0-9+]))?(.([0-9+]))?$") 107 | set(F2PY_VERSION_MAJOR ${CMAKE_MATCH_1}) 108 | set(F2PY_VERSION_MINOR "${CMAKE_MATCH_3}") 109 | set(F2PY_VERSION_PATCH "${CMAKE_MATCH_5}") 110 | endif() 111 | endif() 112 | 113 | # handle the QUIETLY and REQUIRED arguments and set F2PY_FOUND to TRUE if 114 | # all listed variables are TRUE 115 | include(FindPackageHandleStandardArgs) 116 | find_package_handle_standard_args(F2PY 117 | REQUIRED_VARS F2PY_EXECUTABLE 118 | VERSION_VAR F2PY_VERSION_STRING 119 | ) 120 | 121 | mark_as_advanced(F2PY_EXECUTABLE) 122 | 123 | include(UseF2PY) 124 | -------------------------------------------------------------------------------- /cmake/FindNumPy.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # Find the include directory for ``numpy/arrayobject.h`` as well as other NumPy tools like ``conv-template`` and 4 | # ``from-template``. 5 | # 6 | # This module sets the following variables: 7 | # 8 | # ``NumPy_FOUND`` 9 | # True if NumPy was found. 10 | # ``NumPy_INCLUDE_DIRS`` 11 | # The include directories needed to use NumpPy. 12 | # ``NumPy_VERSION`` 13 | # The version of NumPy found. 14 | # ``NumPy_CONV_TEMPLATE_EXECUTABLE`` 15 | # Path to conv-template executable. 16 | # ``NumPy_FROM_TEMPLATE_EXECUTABLE`` 17 | # Path to from-template executable. 18 | # 19 | # The module will also explicitly define one cache variable: 20 | # 21 | # ``NumPy_INCLUDE_DIR`` 22 | # 23 | # .. note:: 24 | # 25 | # To support NumPy < v0.15.0 where ``from-template`` and ``conv-template`` are not declared as entry points, 26 | # the module emulates the behavior of standalone executables by setting the corresponding variables with the 27 | # path the the python interpreter and the path to the associated script. For example: 28 | # :: 29 | # 30 | # set(NumPy_CONV_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/conv_template.py CACHE STRING "Command executing conv-template program" FORCE) 31 | # 32 | # set(NumPy_FROM_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/from_template.py CACHE STRING "Command executing from-template program" FORCE) 33 | # 34 | 35 | if(NOT NumPy_FOUND) 36 | set(_find_extra_args) 37 | if(NumPy_FIND_REQUIRED) 38 | list(APPEND _find_extra_args REQUIRED) 39 | endif() 40 | if(NumPy_FIND_QUIET) 41 | list(APPEND _find_extra_args QUIET) 42 | endif() 43 | find_package(PythonInterp ${_find_extra_args}) 44 | find_package(PythonLibs ${_find_extra_args}) 45 | 46 | find_program(NumPy_CONV_TEMPLATE_EXECUTABLE NAMES conv-template) 47 | find_program(NumPy_FROM_TEMPLATE_EXECUTABLE NAMES from-template) 48 | 49 | if(PYTHON_EXECUTABLE) 50 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" 51 | -c "import numpy; print(numpy.get_include())" 52 | OUTPUT_VARIABLE _numpy_include_dir 53 | OUTPUT_STRIP_TRAILING_WHITESPACE 54 | ERROR_QUIET 55 | ) 56 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" 57 | -c "import numpy; print(numpy.__version__)" 58 | OUTPUT_VARIABLE NumPy_VERSION 59 | OUTPUT_STRIP_TRAILING_WHITESPACE 60 | ERROR_QUIET 61 | ) 62 | 63 | # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. 64 | if(NOT NumPy_CONV_TEMPLATE_EXECUTABLE) 65 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" 66 | -c "from numpy.distutils import conv_template; print(conv_template.__file__)" 67 | OUTPUT_VARIABLE _numpy_conv_template_file 68 | OUTPUT_STRIP_TRAILING_WHITESPACE 69 | ERROR_QUIET 70 | ) 71 | set(NumPy_CONV_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_conv_template_file}" CACHE STRING "Command executing conv-template program" FORCE) 72 | endif() 73 | 74 | # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. 75 | if(NOT NumPy_FROM_TEMPLATE_EXECUTABLE) 76 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" 77 | -c "from numpy.distutils import from_template; print(from_template.__file__)" 78 | OUTPUT_VARIABLE _numpy_from_template_file 79 | OUTPUT_STRIP_TRAILING_WHITESPACE 80 | ERROR_QUIET 81 | ) 82 | set(NumPy_FROM_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_from_template_file}" CACHE STRING "Command executing from-template program" FORCE) 83 | endif() 84 | endif() 85 | endif() 86 | 87 | find_path(NumPy_INCLUDE_DIR 88 | numpy/arrayobject.h 89 | PATHS "${_numpy_include_dir}" "${PYTHON_INCLUDE_DIR}" 90 | PATH_SUFFIXES numpy/core/include 91 | ) 92 | 93 | set(NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIR}) 94 | 95 | # handle the QUIETLY and REQUIRED arguments and set NumPy_FOUND to TRUE if 96 | # all listed variables are TRUE 97 | include(FindPackageHandleStandardArgs) 98 | find_package_handle_standard_args(NumPy 99 | REQUIRED_VARS 100 | NumPy_INCLUDE_DIR 101 | NumPy_CONV_TEMPLATE_EXECUTABLE 102 | NumPy_FROM_TEMPLATE_EXECUTABLE 103 | VERSION_VAR NumPy_VERSION 104 | ) 105 | 106 | mark_as_advanced(NumPy_INCLUDE_DIR) 107 | -------------------------------------------------------------------------------- /cmake/FindPythonExtensions.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # This module defines CMake functions to build Python extension modules and 4 | # stand-alone executables. 5 | # 6 | # The following variables are defined: 7 | # :: 8 | # 9 | # PYTHON_PREFIX - absolute path to the current Python 10 | # distribution's prefix 11 | # PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python 12 | # distribution's site-packages directory 13 | # PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python 14 | # distribution's site-packages directory 15 | # relative to its prefix 16 | # PYTHON_SEPARATOR - separator string for file path 17 | # components. Equivalent to ``os.sep`` in 18 | # Python. 19 | # PYTHON_PATH_SEPARATOR - separator string for PATH-style 20 | # environment variables. Equivalent to 21 | # ``os.pathsep`` in Python. 22 | # PYTHON_EXTENSION_MODULE_SUFFIX - suffix of the compiled module. For example, on 23 | # Linux, based on environment, it could be ``.cpython-35m-x86_64-linux-gnu.so``. 24 | # 25 | # 26 | # 27 | # The following functions are defined: 28 | # 29 | # .. cmake:command:: python_extension_module 30 | # 31 | # For libraries meant to be used as Python extension modules, either dynamically 32 | # loaded or directly linked. Amend the configuration of the library target 33 | # (created using ``add_library``) with additional options needed to build and 34 | # use the referenced library as a Python extension module. 35 | # 36 | # python_extension_module( 37 | # [LINKED_MODULES_VAR ] 38 | # [FORWARD_DECL_MODULES_VAR ] 39 | # [MODULE_SUFFIX ]) 40 | # 41 | # Only extension modules that are configured to be built as MODULE libraries can 42 | # be runtime-loaded through the standard Python import mechanism. All other 43 | # modules can only be included in standalone applications that are written to 44 | # expect their presence. In addition to being linked against the libraries for 45 | # these modules, such applications must forward declare their entry points and 46 | # initialize them prior to use. To generate these forward declarations and 47 | # initializations, see ``python_modules_header``. 48 | # 49 | # If ```` does not refer to a target, then it is assumed to refer to an 50 | # extension module that is not linked at all, but compiled along with other 51 | # source files directly into an executable. Adding these modules does not cause 52 | # any library configuration modifications, and they are not added to the list of 53 | # linked modules. They still must be forward declared and initialized, however, 54 | # and so are added to the forward declared modules list. 55 | # 56 | # If the associated target is of type ``MODULE_LIBRARY``, the LINK_FLAGS target 57 | # property is used to set symbol visibility and export only the module init function. 58 | # This applies to GNU and MSVC compilers. 59 | # 60 | # Options: 61 | # 62 | # ``LINKED_MODULES_VAR `` 63 | # Name of the variable referencing a list of extension modules whose libraries 64 | # must be linked into the executables of any stand-alone applications that use 65 | # them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used. 66 | # 67 | # ``FORWARD_DECL_MODULES_VAR `` 68 | # Name of the variable referencing a list of extension modules whose entry 69 | # points must be forward declared and called by any stand-alone applications 70 | # that use them. By default, the global property 71 | # ``PY_FORWARD_DECL_MODULES_LIST`` is used. 72 | # 73 | # ``MODULE_SUFFIX `` 74 | # Suffix appended to the python extension module file. 75 | # The default suffix is retrieved using ``sysconfig.get_config_var("SO")"``, 76 | # if not available, the default is then ``.so`` on unix and ``.pyd`` on 77 | # windows. 78 | # Setting the variable ``PYTHON_EXTENSION_MODULE_SUFFIX`` in the caller 79 | # scope defines the value used for all extensions not having a suffix 80 | # explicitly specified using ``MODULE_SUFFIX`` parameter. 81 | # 82 | # 83 | # .. cmake:command:: python_standalone_executable 84 | # 85 | # python_standalone_executable() 86 | # 87 | # For standalone executables that initialize their own Python runtime 88 | # (such as when building source files that include one generated by Cython with 89 | # the --embed option). Amend the configuration of the executable target 90 | # (created using ``add_executable``) with additional options needed to properly 91 | # build the referenced executable. 92 | # 93 | # 94 | # .. cmake:command:: python_modules_header 95 | # 96 | # Generate a header file that contains the forward declarations and 97 | # initialization routines for the given list of Python extension modules. 98 | # ```` is the logical name for the header file (no file extensions). 99 | # ```` is the actual destination filename for the header file 100 | # (e.g.: decl_modules.h). 101 | # 102 | # python_modules_header( [HeaderFilename] 103 | # [FORWARD_DECL_MODULES_LIST ] 104 | # [HEADER_OUTPUT_VAR ] 105 | # [INCLUDE_DIR_OUTPUT_VAR ]) 106 | # 107 | # without the extension is used as the logical name. If only ```` is 108 | # 109 | # If only ```` is provided, and it ends in the ".h" extension, then it 110 | # is assumed to be the ````. The filename of the header file 111 | # provided, and it does not end in the ".h" extension, then the 112 | # ```` is assumed to ``.h``. 113 | # 114 | # The exact contents of the generated header file depend on the logical 115 | # ````. It should be set to a value that corresponds to the target 116 | # application, or for the case of multiple applications, some identifier that 117 | # conveyes its purpose. It is featured in the generated multiple inclusion 118 | # guard as well as the names of the generated initialization routines. 119 | # 120 | # The generated header file includes forward declarations for all listed 121 | # modules, as well as implementations for the following class of routines: 122 | # 123 | # ``int _(void)`` 124 | # Initializes the python extension module, ````. Returns an integer 125 | # handle to the module. 126 | # 127 | # ``void _LoadAllPythonModules(void)`` 128 | # Initializes all listed python extension modules. 129 | # 130 | # ``void CMakeLoadAllPythonModules(void);`` 131 | # Alias for ``_LoadAllPythonModules`` whose name does not depend on 132 | # ````. This function is excluded during preprocessing if the 133 | # preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined. 134 | # 135 | # ``void Py_Initialize_Wrapper();`` 136 | # Wrapper arpund ``Py_Initialize()`` that initializes all listed python 137 | # extension modules. This function is excluded during preprocessing if the 138 | # preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this 139 | # function is generated, then ``Py_Initialize()`` is redefined to a macro 140 | # that calls this function. 141 | # 142 | # Options: 143 | # 144 | # ``FORWARD_DECL_MODULES_LIST `` 145 | # List of extension modules for which to generate forward declarations of 146 | # their entry points and their initializations. By default, the global 147 | # property ``PY_FORWARD_DECL_MODULES_LIST`` is used. 148 | # 149 | # ``HEADER_OUTPUT_VAR `` 150 | # Name of the variable to set to the path to the generated header file. By 151 | # default, ```` is used. 152 | # 153 | # ``INCLUDE_DIR_OUTPUT_VAR `` 154 | # Name of the variable to set to the path to the directory containing the 155 | # generated header file. By default, ``_INCLUDE_DIRS`` is used. 156 | # 157 | # Defined variables: 158 | # 159 | # ```` 160 | # The path to the generated header file 161 | # 162 | # ```` 163 | # Directory containing the generated header file 164 | # 165 | # 166 | # Example usage 167 | # ^^^^^^^^^^^^^ 168 | # 169 | # .. code-block:: cmake 170 | # 171 | # find_package(PythonInterp) 172 | # find_package(PythonLibs) 173 | # find_package(PythonExtensions) 174 | # find_package(Cython) 175 | # find_package(Boost COMPONENTS python) 176 | # 177 | # # Simple Cython Module -- no executables 178 | # add_cython_target(_module.pyx) 179 | # add_library(_module MODULE ${_module}) 180 | # python_extension_module(_module) 181 | # 182 | # # Mix of Cython-generated code and C++ code using Boost Python 183 | # # Stand-alone executable -- no modules 184 | # include_directories(${Boost_INCLUDE_DIRS}) 185 | # add_cython_target(main.pyx CXX EMBED_MAIN) 186 | # add_executable(main boost_python_module.cxx ${main}) 187 | # target_link_libraries(main ${Boost_LIBRARIES}) 188 | # python_standalone_executable(main) 189 | # 190 | # # stand-alone executable with three extension modules: 191 | # # one statically linked, one dynamically linked, and one loaded at runtime 192 | # # 193 | # # Freely mixes Cython-generated code, code using Boost-Python, and 194 | # # hand-written code using the CPython API. 195 | # 196 | # # module1 -- statically linked 197 | # add_cython_target(module1.pyx) 198 | # add_library(module1 STATIC ${module1}) 199 | # python_extension_module(module1 200 | # LINKED_MODULES_VAR linked_module_list 201 | # FORWARD_DECL_MODULES_VAR fdecl_module_list) 202 | # 203 | # # module2 -- dynamically linked 204 | # include_directories({Boost_INCLUDE_DIRS}) 205 | # add_library(module2 SHARED boost_module2.cxx) 206 | # target_link_libraries(module2 ${Boost_LIBRARIES}) 207 | # python_extension_module(module2 208 | # LINKED_MODULES_VAR linked_module_list 209 | # FORWARD_DECL_MODULES_VAR fdecl_module_list) 210 | # 211 | # # module3 -- loaded at runtime 212 | # add_cython_target(module3a.pyx) 213 | # add_library(module3 MODULE ${module3a} module3b.cxx) 214 | # target_link_libraries(module3 ${Boost_LIBRARIES}) 215 | # python_extension_module(module3 216 | # LINKED_MODULES_VAR linked_module_list 217 | # FORWARD_DECL_MODULES_VAR fdecl_module_list) 218 | # 219 | # # application executable -- generated header file + other source files 220 | # python_modules_header(modules 221 | # FORWARD_DECL_MODULES_LIST ${fdecl_module_list}) 222 | # include_directories(${modules_INCLUDE_DIRS}) 223 | # 224 | # add_cython_target(mainA) 225 | # add_cython_target(mainC) 226 | # add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c) 227 | # 228 | # target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES}) 229 | # python_standalone_executable(main) 230 | # 231 | #============================================================================= 232 | # Copyright 2011 Kitware, Inc. 233 | # 234 | # Licensed under the Apache License, Version 2.0 (the "License"); 235 | # you may not use this file except in compliance with the License. 236 | # You may obtain a copy of the License at 237 | # 238 | # http://www.apache.org/licenses/LICENSE-2.0 239 | # 240 | # Unless required by applicable law or agreed to in writing, software 241 | # distributed under the License is distributed on an "AS IS" BASIS, 242 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 243 | # See the License for the specific language governing permissions and 244 | # limitations under the License. 245 | #============================================================================= 246 | 247 | find_package(PythonInterp REQUIRED) 248 | find_package(PythonLibs) 249 | include(targetLinkLibrariesWithDynamicLookup) 250 | 251 | set(_command " 252 | import distutils.sysconfig 253 | import itertools 254 | import os 255 | import os.path 256 | import site 257 | import sys 258 | 259 | result = None 260 | rel_result = None 261 | candidate_lists = [] 262 | 263 | try: 264 | candidate_lists.append((distutils.sysconfig.get_python_lib(),)) 265 | except AttributeError: pass 266 | 267 | try: 268 | candidate_lists.append(site.getsitepackages()) 269 | except AttributeError: pass 270 | 271 | try: 272 | candidate_lists.append((site.getusersitepackages(),)) 273 | except AttributeError: pass 274 | 275 | candidates = itertools.chain.from_iterable(candidate_lists) 276 | 277 | for candidate in candidates: 278 | rel_candidate = os.path.relpath( 279 | candidate, sys.prefix) 280 | if not rel_candidate.startswith(\"..\"): 281 | result = candidate 282 | rel_result = rel_candidate 283 | break 284 | 285 | ext_suffix_var = 'SO' 286 | if sys.version_info[:2] >= (3, 5): 287 | ext_suffix_var = 'EXT_SUFFIX' 288 | 289 | sys.stdout.write(\";\".join(( 290 | os.sep, 291 | os.pathsep, 292 | sys.prefix, 293 | result, 294 | rel_result, 295 | distutils.sysconfig.get_config_var(ext_suffix_var) 296 | ))) 297 | ") 298 | 299 | execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}" 300 | OUTPUT_VARIABLE _list 301 | RESULT_VARIABLE _result) 302 | 303 | list(GET _list 0 _item) 304 | set(PYTHON_SEPARATOR "${_item}") 305 | mark_as_advanced(PYTHON_SEPARATOR) 306 | 307 | list(GET _list 1 _item) 308 | set(PYTHON_PATH_SEPARATOR "${_item}") 309 | mark_as_advanced(PYTHON_PATH_SEPARATOR) 310 | 311 | list(GET _list 2 _item) 312 | set(PYTHON_PREFIX "${_item}") 313 | mark_as_advanced(PYTHON_PREFIX) 314 | 315 | list(GET _list 3 _item) 316 | set(PYTHON_SITE_PACKAGES_DIR "${_item}") 317 | mark_as_advanced(PYTHON_SITE_PACKAGES_DIR) 318 | 319 | list(GET _list 4 _item) 320 | set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}") 321 | mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR) 322 | 323 | if(NOT DEFINED PYTHON_EXTENSION_MODULE_SUFFIX) 324 | list(GET _list 5 _item) 325 | set(PYTHON_EXTENSION_MODULE_SUFFIX "${_item}") 326 | endif() 327 | 328 | function(_set_python_extension_symbol_visibility _target) 329 | if(PYTHON_VERSION_MAJOR VERSION_GREATER 2) 330 | set(_modinit_prefix "PyInit_") 331 | else() 332 | set(_modinit_prefix "init") 333 | endif() 334 | message("_modinit_prefix:${_modinit_prefix}") 335 | if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") 336 | set_target_properties(${_target} PROPERTIES LINK_FLAGS 337 | "/EXPORT:${_modinit_prefix}${_target}" 338 | ) 339 | elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 340 | set(_script_path 341 | ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}-version-script.map 342 | ) 343 | file(WRITE ${_script_path} 344 | "{global: ${_modinit_prefix}${_target}; local: *; };" 345 | ) 346 | set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS 347 | " -Wl,--version-script=\"${_script_path}\"" 348 | ) 349 | endif() 350 | endfunction() 351 | 352 | function(python_extension_module _target) 353 | set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR MODULE_SUFFIX) 354 | cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) 355 | 356 | set(_lib_type "NA") 357 | if(TARGET ${_target}) 358 | get_property(_lib_type TARGET ${_target} PROPERTY TYPE) 359 | endif() 360 | 361 | set(_is_non_lib TRUE) 362 | 363 | set(_is_static_lib FALSE) 364 | if(_lib_type STREQUAL "STATIC_LIBRARY") 365 | set(_is_static_lib TRUE) 366 | set(_is_non_lib FALSE) 367 | endif() 368 | 369 | set(_is_shared_lib FALSE) 370 | if(_lib_type STREQUAL "SHARED_LIBRARY") 371 | set(_is_shared_lib TRUE) 372 | set(_is_non_lib FALSE) 373 | endif() 374 | 375 | set(_is_module_lib FALSE) 376 | if(_lib_type STREQUAL "MODULE_LIBRARY") 377 | set(_is_module_lib TRUE) 378 | set(_is_non_lib FALSE) 379 | endif() 380 | 381 | if(_is_static_lib OR _is_shared_lib OR _is_non_lib) 382 | 383 | if(_is_static_lib OR _is_shared_lib) 384 | if(_args_LINKED_MODULES_VAR) 385 | set(${_args_LINKED_MODULES_VAR} 386 | ${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE) 387 | else() 388 | set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target}) 389 | endif() 390 | endif() 391 | 392 | if(_args_FORWARD_DECL_MODULES_VAR) 393 | set(${_args_FORWARD_DECL_MODULES_VAR} 394 | ${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE) 395 | else() 396 | set_property(GLOBAL APPEND PROPERTY 397 | PY_FORWARD_DECL_MODULES_LIST ${_target}) 398 | endif() 399 | endif() 400 | 401 | if(NOT _is_non_lib) 402 | include_directories("${PYTHON_INCLUDE_DIRS}") 403 | endif() 404 | 405 | if(_is_module_lib) 406 | set_target_properties(${_target} PROPERTIES 407 | PREFIX "${PYTHON_MODULE_PREFIX}") 408 | endif() 409 | 410 | if(_is_module_lib OR _is_shared_lib) 411 | if(_is_module_lib) 412 | 413 | if(NOT _args_MODULE_SUFFIX) 414 | set(_args_MODULE_SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") 415 | endif() 416 | 417 | if(_args_MODULE_SUFFIX STREQUAL "" AND WIN32 AND NOT CYGWIN) 418 | set(_args_MODULE_SUFFIX ".pyd") 419 | endif() 420 | 421 | if(NOT _args_MODULE_SUFFIX STREQUAL "") 422 | set_target_properties(${_target} 423 | PROPERTIES SUFFIX ${_args_MODULE_SUFFIX}) 424 | endif() 425 | endif() 426 | 427 | target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES}) 428 | 429 | if(_is_module_lib) 430 | _set_python_extension_symbol_visibility(${_target}) 431 | endif() 432 | endif() 433 | endfunction() 434 | 435 | function(python_standalone_executable _target) 436 | include_directories(${PYTHON_INCLUDE_DIRS}) 437 | target_link_libraries(${_target} ${PYTHON_LIBRARIES}) 438 | endfunction() 439 | 440 | function(python_modules_header _name) 441 | set(one_ops FORWARD_DECL_MODULES_LIST 442 | HEADER_OUTPUT_VAR 443 | INCLUDE_DIR_OUTPUT_VAR) 444 | cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) 445 | 446 | list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) 447 | # if present, use arg0 as the input file path 448 | if(_arg0) 449 | set(_source_file ${_arg0}) 450 | 451 | # otherwise, must determine source file from name, or vice versa 452 | else() 453 | get_filename_component(_name_ext "${_name}" EXT) 454 | 455 | # if extension provided, _name is the source file 456 | if(_name_ext) 457 | set(_source_file ${_name}) 458 | get_filename_component(_name "${_source_file}" NAME_WE) 459 | 460 | # otherwise, assume the source file is ${_name}.h 461 | else() 462 | set(_source_file ${_name}.h) 463 | endif() 464 | endif() 465 | 466 | if(_args_FORWARD_DECL_MODULES_LIST) 467 | set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST}) 468 | else() 469 | get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST) 470 | endif() 471 | 472 | string(REPLACE "." "_" _header_name "${_name}") 473 | string(TOUPPER ${_header_name} _header_name_upper) 474 | set(_header_name_upper "_${_header_name_upper}_H") 475 | set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file}) 476 | 477 | set(generated_file_tmp "${generated_file}.in") 478 | file(WRITE ${generated_file_tmp} 479 | "/* Created by CMake. DO NOT EDIT; changes will be lost. */\n") 480 | 481 | set(_chunk "") 482 | set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n") 483 | set(_chunk "${_chunk}#define ${_header_name_upper}\n") 484 | set(_chunk "${_chunk}\n") 485 | set(_chunk "${_chunk}#include \n") 486 | set(_chunk "${_chunk}\n") 487 | set(_chunk "${_chunk}#ifdef __cplusplus\n") 488 | set(_chunk "${_chunk}extern \"C\" {\n") 489 | set(_chunk "${_chunk}#endif /* __cplusplus */\n") 490 | set(_chunk "${_chunk}\n") 491 | set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n") 492 | file(APPEND ${generated_file_tmp} "${_chunk}") 493 | 494 | foreach(_module ${static_mod_list}) 495 | file(APPEND ${generated_file_tmp} 496 | "PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n") 497 | endforeach() 498 | 499 | file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n") 500 | 501 | foreach(_module ${static_mod_list}) 502 | file(APPEND ${generated_file_tmp} 503 | "PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n") 504 | endforeach() 505 | 506 | set(_chunk "") 507 | set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n") 508 | set(_chunk "${_chunk}#ifdef __cplusplus\n") 509 | set(_chunk "${_chunk}}\n") 510 | set(_chunk "${_chunk}#endif /* __cplusplus */\n") 511 | set(_chunk "${_chunk}\n") 512 | file(APPEND ${generated_file_tmp} "${_chunk}") 513 | 514 | foreach(_module ${static_mod_list}) 515 | set(_import_function "${_header_name}_${_module}") 516 | set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}") 517 | 518 | set(_chunk "") 519 | set(_chunk "${_chunk}int ${_import_function}(void)\n") 520 | set(_chunk "${_chunk}{\n") 521 | set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n") 522 | set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n") 523 | set(_chunk "${_chunk} return PyImport_AppendInittab(") 524 | set(_chunk "${_chunk}name, init${_prefixed_module});\n") 525 | set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n") 526 | set(_chunk "${_chunk} return PyImport_AppendInittab(") 527 | set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n") 528 | set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n") 529 | set(_chunk "${_chunk}}\n\n") 530 | file(APPEND ${generated_file_tmp} "${_chunk}") 531 | endforeach() 532 | 533 | file(APPEND ${generated_file_tmp} 534 | "void ${_header_name}_LoadAllPythonModules(void)\n{\n") 535 | foreach(_module ${static_mod_list}) 536 | file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n") 537 | endforeach() 538 | file(APPEND ${generated_file_tmp} "}\n\n") 539 | 540 | set(_chunk "") 541 | set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n") 542 | set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n") 543 | set(_chunk "${_chunk}{\n") 544 | set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") 545 | set(_chunk "${_chunk}}\n") 546 | set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n") 547 | 548 | set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n") 549 | set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n") 550 | set(_chunk "${_chunk}{\n") 551 | set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") 552 | set(_chunk "${_chunk} Py_Initialize();\n") 553 | set(_chunk "${_chunk}}\n") 554 | set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n") 555 | set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n") 556 | 557 | set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n") 558 | file(APPEND ${generated_file_tmp} "${_chunk}") 559 | 560 | # with configure_file() cmake complains that you may not use a file created 561 | # using file(WRITE) as input file for configure_file() 562 | execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different 563 | "${generated_file_tmp}" "${generated_file}" 564 | OUTPUT_QUIET ERROR_QUIET) 565 | 566 | set(_header_output_var ${_name}) 567 | if(_args_HEADER_OUTPUT_VAR) 568 | set(_header_output_var ${_args_HEADER_OUTPUT_VAR}) 569 | endif() 570 | set(${_header_output_var} ${generated_file} PARENT_SCOPE) 571 | 572 | set(_include_dir_var ${_name}_INCLUDE_DIRS) 573 | if(_args_INCLUDE_DIR_OUTPUT_VAR) 574 | set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR}) 575 | endif() 576 | set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) 577 | endfunction() 578 | -------------------------------------------------------------------------------- /cmake/UseCython.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # The following functions are defined: 4 | # 5 | # .. cmake:command:: add_cython_target 6 | # 7 | # Create a custom rule to generate the source code for a Python extension module 8 | # using cython. 9 | # 10 | # add_cython_target( [] 11 | # [EMBED_MAIN] 12 | # [C | CXX] 13 | # [PY2 | PY3] 14 | # [OUTPUT_VAR ]) 15 | # 16 | # ```` is the name of the new target, and ```` 17 | # is the path to a cython source file. Note that, despite the name, no new 18 | # targets are created by this function. Instead, see ``OUTPUT_VAR`` for 19 | # retrieving the path to the generated source for subsequent targets. 20 | # 21 | # If only ```` is provided, and it ends in the ".pyx" extension, then it 22 | # is assumed to be the ````. The name of the input without the 23 | # extension is used as the target name. If only ```` is provided, and it 24 | # does not end in the ".pyx" extension, then the ```` is assumed to 25 | # be ``.pyx``. 26 | # 27 | # The Cython include search path is amended with any entries found in the 28 | # ``INCLUDE_DIRECTORIES`` property of the directory containing the 29 | # ```` file. Use ``include_directories`` to add to the Cython 30 | # include search path. 31 | # 32 | # Options: 33 | # 34 | # ``EMBED_MAIN`` 35 | # Embed a main() function in the generated output (for stand-alone 36 | # applications that initialize their own Python runtime). 37 | # 38 | # ``C | CXX`` 39 | # Force the generation of either a C or C++ file. By default, a C file is 40 | # generated, unless the C language is not enabled for the project; in this 41 | # case, a C++ file is generated by default. 42 | # 43 | # ``PY2 | PY3`` 44 | # Force compilation using either Python-2 or Python-3 syntax and code 45 | # semantics. By default, Python-2 syntax and semantics are used if the major 46 | # version of Python found is 2. Otherwise, Python-3 syntax and sematics are 47 | # used. 48 | # 49 | # ``OUTPUT_VAR `` 50 | # Set the variable ```` in the parent scope to the path to the 51 | # generated source file. By default, ```` is used as the output 52 | # variable name. 53 | # 54 | # Defined variables: 55 | # 56 | # ```` 57 | # The path of the generated source file. 58 | # 59 | # Cache variables that effect the behavior include: 60 | # 61 | # ``CYTHON_ANNOTATE`` 62 | # whether to create an annotated .html file when compiling 63 | # 64 | # ``CYTHON_FLAGS`` 65 | # additional flags to pass to the Cython compiler 66 | # 67 | # Example usage 68 | # ^^^^^^^^^^^^^ 69 | # 70 | # .. code-block:: cmake 71 | # 72 | # find_package(Cython) 73 | # 74 | # # Note: In this case, either one of these arguments may be omitted; their 75 | # # value would have been inferred from that of the other. 76 | # add_cython_target(cy_code cy_code.pyx) 77 | # 78 | # add_library(cy_code MODULE ${cy_code}) 79 | # target_link_libraries(cy_code ...) 80 | # 81 | #============================================================================= 82 | # Copyright 2011 Kitware, Inc. 83 | # 84 | # Licensed under the Apache License, Version 2.0 (the "License"); 85 | # you may not use this file except in compliance with the License. 86 | # You may obtain a copy of the License at 87 | # 88 | # http://www.apache.org/licenses/LICENSE-2.0 89 | # 90 | # Unless required by applicable law or agreed to in writing, software 91 | # distributed under the License is distributed on an "AS IS" BASIS, 92 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 93 | # See the License for the specific language governing permissions and 94 | # limitations under the License. 95 | #============================================================================= 96 | 97 | # Configuration options. 98 | set(CYTHON_ANNOTATE OFF 99 | CACHE BOOL "Create an annotated .html file when compiling *.pyx.") 100 | 101 | set(CYTHON_FLAGS "" CACHE STRING 102 | "Extra flags to the cython compiler.") 103 | mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS) 104 | 105 | find_package(PythonLibs REQUIRED) 106 | 107 | set(CYTHON_CXX_EXTENSION "cxx") 108 | set(CYTHON_C_EXTENSION "c") 109 | 110 | get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 111 | 112 | function(add_cython_target _name) 113 | set(options EMBED_MAIN C CXX PY2 PY3) 114 | set(options1 OUTPUT_VAR) 115 | cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN}) 116 | 117 | list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) 118 | 119 | # if provided, use _arg0 as the input file path 120 | if(_arg0) 121 | set(_source_file ${_arg0}) 122 | 123 | # otherwise, must determine source file from name, or vice versa 124 | else() 125 | get_filename_component(_name_ext "${_name}" EXT) 126 | 127 | # if extension provided, _name is the source file 128 | if(_name_ext) 129 | set(_source_file ${_name}) 130 | get_filename_component(_name "${_source_file}" NAME_WE) 131 | 132 | # otherwise, assume the source file is ${_name}.pyx 133 | else() 134 | set(_source_file ${_name}.pyx) 135 | endif() 136 | endif() 137 | 138 | set(_embed_main FALSE) 139 | 140 | if("C" IN_LIST languages) 141 | set(_output_syntax "C") 142 | elseif("CXX" IN_LIST languages) 143 | set(_output_syntax "CXX") 144 | else() 145 | message(FATAL_ERROR "Either C or CXX must be enabled to use Cython") 146 | endif() 147 | 148 | if("${PYTHONLIBS_VERSION_STRING}" MATCHES "^2.") 149 | set(_input_syntax "PY2") 150 | else() 151 | set(_input_syntax "PY3") 152 | endif() 153 | 154 | if(_args_EMBED_MAIN) 155 | set(_embed_main TRUE) 156 | endif() 157 | 158 | if(_args_C) 159 | set(_output_syntax "C") 160 | endif() 161 | 162 | if(_args_CXX) 163 | set(_output_syntax "CXX") 164 | endif() 165 | 166 | if(_args_PY2) 167 | set(_input_syntax "PY2") 168 | endif() 169 | 170 | if(_args_PY3) 171 | set(_input_syntax "PY3") 172 | endif() 173 | 174 | set(embed_arg "") 175 | if(_embed_main) 176 | set(embed_arg "--embed") 177 | endif() 178 | 179 | set(cxx_arg "") 180 | set(extension "c") 181 | if(_output_syntax STREQUAL "CXX") 182 | set(cxx_arg "--cplus") 183 | set(extension "cxx") 184 | endif() 185 | 186 | set(py_version_arg "") 187 | if(_input_syntax STREQUAL "PY2") 188 | set(py_version_arg "-2") 189 | elseif(_input_syntax STREQUAL "PY3") 190 | set(py_version_arg "-3") 191 | endif() 192 | 193 | set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}") 194 | set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) 195 | 196 | set(_output_var ${_name}) 197 | if(_args_OUTPUT_VAR) 198 | set(_output_var ${_args_OUTPUT_VAR}) 199 | endif() 200 | set(${_output_var} ${generated_file} PARENT_SCOPE) 201 | 202 | file(RELATIVE_PATH generated_file_relative 203 | ${CMAKE_BINARY_DIR} ${generated_file}) 204 | 205 | set(comment "Generating ${_output_syntax} source ${generated_file_relative}") 206 | set(cython_include_directories "") 207 | set(pxd_dependencies "") 208 | set(c_header_dependencies "") 209 | 210 | # Get the include directories. 211 | get_source_file_property(pyx_location ${_source_file} LOCATION) 212 | get_filename_component(pyx_path ${pyx_location} PATH) 213 | get_directory_property(cmake_include_directories 214 | DIRECTORY ${pyx_path} 215 | INCLUDE_DIRECTORIES) 216 | list(APPEND cython_include_directories ${cmake_include_directories}) 217 | 218 | # Determine dependencies. 219 | # Add the pxd file with the same basename as the given pyx file. 220 | get_filename_component(pyx_file_basename ${_source_file} NAME_WE) 221 | unset(corresponding_pxd_file CACHE) 222 | find_file(corresponding_pxd_file ${pyx_file_basename}.pxd 223 | PATHS "${pyx_path}" ${cmake_include_directories} 224 | NO_DEFAULT_PATH) 225 | if(corresponding_pxd_file) 226 | list(APPEND pxd_dependencies "${corresponding_pxd_file}") 227 | endif() 228 | 229 | # pxd files to check for additional dependencies 230 | set(pxds_to_check "${_source_file}" "${pxd_dependencies}") 231 | set(pxds_checked "") 232 | set(number_pxds_to_check 1) 233 | while(number_pxds_to_check GREATER 0) 234 | foreach(pxd ${pxds_to_check}) 235 | list(APPEND pxds_checked "${pxd}") 236 | list(REMOVE_ITEM pxds_to_check "${pxd}") 237 | 238 | # look for C headers 239 | file(STRINGS "${pxd}" extern_from_statements 240 | REGEX "cdef[ ]+extern[ ]+from.*$") 241 | foreach(statement ${extern_from_statements}) 242 | # Had trouble getting the quote in the regex 243 | string(REGEX REPLACE 244 | "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1" 245 | header "${statement}") 246 | unset(header_location CACHE) 247 | find_file(header_location ${header} PATHS ${cmake_include_directories}) 248 | if(header_location) 249 | list(FIND c_header_dependencies "${header_location}" header_idx) 250 | if(${header_idx} LESS 0) 251 | list(APPEND c_header_dependencies "${header_location}") 252 | endif() 253 | endif() 254 | endforeach() 255 | 256 | # check for pxd dependencies 257 | # Look for cimport statements. 258 | set(module_dependencies "") 259 | file(STRINGS "${pxd}" cimport_statements REGEX cimport) 260 | foreach(statement ${cimport_statements}) 261 | if(${statement} MATCHES from) 262 | string(REGEX REPLACE 263 | "from[ ]+([^ ]+).*" "\\1" 264 | module "${statement}") 265 | else() 266 | string(REGEX REPLACE 267 | "cimport[ ]+([^ ]+).*" "\\1" 268 | module "${statement}") 269 | endif() 270 | list(APPEND module_dependencies ${module}) 271 | endforeach() 272 | 273 | # check for pxi dependencies 274 | # Look for include statements. 275 | set(include_dependencies "") 276 | file(STRINGS "${pxd}" include_statements REGEX include) 277 | foreach(statement ${include_statements}) 278 | string(REGEX REPLACE 279 | "include[ ]+[\"]([^\"]+)[\"].*" "\\1" 280 | module "${statement}") 281 | list(APPEND include_dependencies ${module}) 282 | endforeach() 283 | 284 | list(REMOVE_DUPLICATES module_dependencies) 285 | list(REMOVE_DUPLICATES include_dependencies) 286 | 287 | # Add modules to the files to check, if appropriate. 288 | foreach(module ${module_dependencies}) 289 | unset(pxd_location CACHE) 290 | find_file(pxd_location ${module}.pxd 291 | PATHS "${pyx_path}" ${cmake_include_directories} 292 | NO_DEFAULT_PATH) 293 | if(pxd_location) 294 | list(FIND pxds_checked ${pxd_location} pxd_idx) 295 | if(${pxd_idx} LESS 0) 296 | list(FIND pxds_to_check ${pxd_location} pxd_idx) 297 | if(${pxd_idx} LESS 0) 298 | list(APPEND pxds_to_check ${pxd_location}) 299 | list(APPEND pxd_dependencies ${pxd_location}) 300 | endif() # if it is not already going to be checked 301 | endif() # if it has not already been checked 302 | endif() # if pxd file can be found 303 | endforeach() # for each module dependency discovered 304 | 305 | # Add includes to the files to check, if appropriate. 306 | foreach(_include ${include_dependencies}) 307 | unset(pxi_location CACHE) 308 | find_file(pxi_location ${_include} 309 | PATHS "${pyx_path}" ${cmake_include_directories} 310 | NO_DEFAULT_PATH) 311 | if(pxi_location) 312 | list(FIND pxds_checked ${pxi_location} pxd_idx) 313 | if(${pxd_idx} LESS 0) 314 | list(FIND pxds_to_check ${pxi_location} pxd_idx) 315 | if(${pxd_idx} LESS 0) 316 | list(APPEND pxds_to_check ${pxi_location}) 317 | list(APPEND pxd_dependencies ${pxi_location}) 318 | endif() # if it is not already going to be checked 319 | endif() # if it has not already been checked 320 | endif() # if include file can be found 321 | endforeach() # for each include dependency discovered 322 | endforeach() # for each include file to check 323 | 324 | list(LENGTH pxds_to_check number_pxds_to_check) 325 | endwhile() 326 | 327 | # Set additional flags. 328 | set(annotate_arg "") 329 | if(CYTHON_ANNOTATE) 330 | set(annotate_arg "--annotate") 331 | endif() 332 | 333 | set(no_docstrings_arg "") 334 | if(CMAKE_BUILD_TYPE STREQUAL "Release" OR 335 | CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") 336 | set(no_docstrings_arg "--no-docstrings") 337 | endif() 338 | 339 | set(cython_debug_arg "") 340 | set(embed_pos_arg "") 341 | set(line_directives_arg "") 342 | if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR 343 | CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") 344 | set(cython_debug_arg "--gdb") 345 | set(embed_pos_arg "--embed-positions") 346 | set(line_directives_arg "--line-directives") 347 | endif() 348 | 349 | # Include directory arguments. 350 | list(REMOVE_DUPLICATES cython_include_directories) 351 | set(include_directory_arg "") 352 | foreach(_include_dir ${cython_include_directories}) 353 | set(include_directory_arg 354 | ${include_directory_arg} "--include-dir" "${_include_dir}") 355 | endforeach() 356 | 357 | list(REMOVE_DUPLICATES pxd_dependencies) 358 | list(REMOVE_DUPLICATES c_header_dependencies) 359 | 360 | string(REGEX REPLACE " " ";" CYTHON_FLAGS_LIST "${CYTHON_FLAGS}") 361 | 362 | # Add the command to run the compiler. 363 | add_custom_command(OUTPUT ${generated_file} 364 | COMMAND ${CYTHON_EXECUTABLE} 365 | ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg} 366 | ${embed_arg} ${annotate_arg} ${no_docstrings_arg} 367 | ${cython_debug_arg} ${embed_pos_arg} 368 | ${line_directives_arg} ${CYTHON_FLAGS_LIST} ${pyx_location} 369 | --output-file ${generated_file} 370 | DEPENDS ${_source_file} 371 | ${pxd_dependencies} 372 | IMPLICIT_DEPENDS ${_output_syntax} 373 | ${c_header_dependencies} 374 | COMMENT ${comment}) 375 | 376 | # NOTE(opadron): I thought about making a proper target, but after trying it 377 | # out, I decided that it would be far too convenient to use the same name as 378 | # the target for the extension module (e.g.: for single-file modules): 379 | # 380 | # ... 381 | # add_cython_target(_module.pyx) 382 | # add_library(_module ${_module}) 383 | # ... 384 | # 385 | # The above example would not be possible since the "_module" target name 386 | # would already be taken by the cython target. Since I can't think of a 387 | # reason why someone would need the custom target instead of just using the 388 | # generated file directly, I decided to leave this commented out. 389 | # 390 | # add_custom_target(${_name} DEPENDS ${generated_file}) 391 | 392 | # Remove their visibility to the user. 393 | set(corresponding_pxd_file "" CACHE INTERNAL "") 394 | set(header_location "" CACHE INTERNAL "") 395 | set(pxd_location "" CACHE INTERNAL "") 396 | endfunction() 397 | -------------------------------------------------------------------------------- /cmake/UseF2PY.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # The following functions are defined: 4 | # 5 | # .. cmake:command:: add_f2py_target 6 | # 7 | # Create a custom rule to generate the source code for a Python extension module 8 | # using f2py. 9 | # 10 | # add_f2py_target( [] 11 | # [OUTPUT_VAR ]) 12 | # 13 | # ```` is the name of the new target, and ```` 14 | # is the path to a pyf source file. Note that, despite the name, no new 15 | # targets are created by this function. Instead, see ``OUTPUT_VAR`` for 16 | # retrieving the path to the generated source for subsequent targets. 17 | # 18 | # If only ```` is provided, and it ends in the ".pyf" extension, then it 19 | # is assumed to be the ````. The name of the input without the 20 | # extension is used as the target name. If only ```` is provided, and it 21 | # does not end in the ".pyf" extension, then the ```` is assumed to 22 | # be ``.pyf``. 23 | # 24 | # 25 | # Options: 26 | # 27 | # ``OUTPUT_VAR `` 28 | # Set the variable ```` in the parent scope to the path to the 29 | # generated source file. By default, ```` is used as the output 30 | # variable name. 31 | # 32 | # ``DEPENDS [source [source2...]]`` 33 | # Sources that must be generated before the F2PY command is run. 34 | # 35 | # Defined variables: 36 | # 37 | # ```` 38 | # The path of the generated source file. 39 | # 40 | # Example usage 41 | # ^^^^^^^^^^^^^ 42 | # 43 | # .. code-block:: cmake 44 | # 45 | # find_package(F2PY) 46 | # 47 | # # Note: In this case, either one of these arguments may be omitted; their 48 | # # value would have been inferred from that of the other. 49 | # add_f2py_target(f2py_code f2py_code.pyf) 50 | # 51 | # add_library(f2py_code MODULE ${f2py_code}) 52 | # target_link_libraries(f2py_code ...) 53 | # 54 | #============================================================================= 55 | # Copyright 2011 Kitware, Inc. 56 | # 57 | # Licensed under the Apache License, Version 2.0 (the "License"); 58 | # you may not use this file except in compliance with the License. 59 | # You may obtain a copy of the License at 60 | # 61 | # http://www.apache.org/licenses/LICENSE-2.0 62 | # 63 | # Unless required by applicable law or agreed to in writing, software 64 | # distributed under the License is distributed on an "AS IS" BASIS, 65 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 66 | # See the License for the specific language governing permissions and 67 | # limitations under the License. 68 | #============================================================================= 69 | 70 | get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) 71 | 72 | function(add_f2py_target _name) 73 | set(options ) 74 | set(oneValueArgs OUTPUT_VAR) 75 | set(multiValueArgs DEPENDS) 76 | cmake_parse_arguments(_args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 77 | 78 | list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) 79 | 80 | # if provided, use _arg0 as the input file path 81 | if(_arg0) 82 | set(_source_file ${_arg0}) 83 | 84 | # otherwise, must determine source file from name, or vice versa 85 | else() 86 | get_filename_component(_name_ext "${_name}" EXT) 87 | 88 | # if extension provided, _name is the source file 89 | if(_name_ext) 90 | set(_source_file ${_name}) 91 | string(REGEX REPLACE "\\.[^.]*$" "" _name ${_source}) 92 | 93 | # otherwise, assume the source file is ${_name}.pyf 94 | else() 95 | set(_source_file ${_name}.pyf) 96 | endif() 97 | endif() 98 | 99 | set(_embed_main FALSE) 100 | 101 | if("C" IN_LIST languages) 102 | set(_output_syntax "C") 103 | else() 104 | message(FATAL_ERROR "C must be enabled to use F2PY") 105 | endif() 106 | 107 | set(extension "c") 108 | 109 | set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}module.${extension}") 110 | set(generated_wrapper "${CMAKE_CURRENT_BINARY_DIR}/${_name}-f2pywrappers.f") 111 | 112 | get_filename_component(generated_file_dir ${generated_file} DIRECTORY) 113 | 114 | set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) 115 | set_source_files_properties(${generated_wrapper} PROPERTIES GENERATED TRUE) 116 | 117 | set(_output_var ${_name}) 118 | if(_args_OUTPUT_VAR) 119 | set(_output_var ${_args_OUTPUT_VAR}) 120 | endif() 121 | set(${_output_var} ${generated_file} ${generated_wrapper} PARENT_SCOPE) 122 | 123 | file(RELATIVE_PATH generated_file_relative 124 | ${CMAKE_BINARY_DIR} ${generated_file}) 125 | 126 | set(comment "Generating ${_output_syntax} source ${generated_file_relative}") 127 | 128 | # Get the include directories. 129 | get_source_file_property(pyf_location ${_source_file} LOCATION) 130 | get_filename_component(pyf_path ${pyf_location} PATH) 131 | 132 | # Create the directory so that the command can cd to it 133 | file(MAKE_DIRECTORY ${generated_file_dir}) 134 | 135 | # Add the command to run the compiler. 136 | add_custom_command(OUTPUT ${generated_file} ${generated_wrapper} 137 | COMMAND ${F2PY_EXECUTABLE} ${pyf_location} 138 | COMMAND ${CMAKE_COMMAND} -E touch ${generated_wrapper} 139 | DEPENDS ${_source_file} 140 | ${_args_DEPENDS} 141 | WORKING_DIRECTORY ${generated_file_dir} 142 | COMMENT ${source_comment}) 143 | 144 | endfunction() 145 | -------------------------------------------------------------------------------- /cmake/UsePythonExtensions.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # The following functions are defined: 4 | # 5 | # .. cmake:command:: add_python_library 6 | # 7 | # Add a library that contains a mix of C, C++, Fortran, Cython, F2PY, Template, 8 | # and Tempita sources. The required targets are automatically generated to 9 | # "lower" source files from their high-level representation to a file that the 10 | # compiler can accept. 11 | # 12 | # 13 | # add_python_library( 14 | # SOURCES [source1 [source2 ...]] 15 | # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] 16 | # [LINK_LIBRARIES [lib1 [lib2 ...]] 17 | # [DEPENDS [source1 [source2 ...]]]) 18 | # 19 | # 20 | # Example usage 21 | # ^^^^^^^^^^^^^ 22 | # 23 | # .. code-block:: cmake 24 | # 25 | # find_package(PythonExtensions) 26 | # 27 | # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) 28 | # 29 | # add_python_library(arpack_scipy 30 | # SOURCES ${arpack_sources} 31 | # ${g77_wrapper_sources} 32 | # INCLUDE_DIRECTORIES ARPACK/SRC 33 | # ) 34 | # 35 | # .. cmake:command:: add_python_extension 36 | # 37 | # Add a extension that contains a mix of C, C++, Fortran, Cython, F2PY, Template, 38 | # and Tempita sources. The required targets are automatically generated to 39 | # "lower" source files from their high-level representation to a file that the 40 | # compiler can accept. 41 | # 42 | # 43 | # add_python_extension( 44 | # SOURCES [source1 [source2 ...]] 45 | # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] 46 | # [LINK_LIBRARIES [lib1 [lib2 ...]] 47 | # [DEPENDS [source1 [source2 ...]]]) 48 | # 49 | # 50 | # Example usage 51 | # ^^^^^^^^^^^^^ 52 | # 53 | # .. code-block:: cmake 54 | # 55 | # find_package(PythonExtensions) 56 | # 57 | # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) 58 | # 59 | # add_python_extension(arpack_scipy 60 | # SOURCES ${arpack_sources} 61 | # ${g77_wrapper_sources} 62 | # INCLUDE_DIRECTORIES ARPACK/SRC 63 | # ) 64 | # 65 | # 66 | #============================================================================= 67 | # Copyright 2011 Kitware, Inc. 68 | # 69 | # Licensed under the Apache License, Version 2.0 (the "License"); 70 | # you may not use this file except in compliance with the License. 71 | # You may obtain a copy of the License at 72 | # 73 | # http://www.apache.org/licenses/LICENSE-2.0 74 | # 75 | # Unless required by applicable law or agreed to in writing, software 76 | # distributed under the License is distributed on an "AS IS" BASIS, 77 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 | # See the License for the specific language governing permissions and 79 | # limitations under the License. 80 | #============================================================================= 81 | 82 | macro(_remove_whitespace _output) 83 | string(REGEX REPLACE "[ \r\n\t]+" " " ${_output} "${${_output}}") 84 | string(STRIP "${${_output}}" ${_output}) 85 | endmacro() 86 | 87 | function(add_python_library _name) 88 | set(options STATIC SHARED MODULE) 89 | set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) 90 | cmake_parse_arguments(_args "${options}" "" "${multiValueArgs}" ${ARGN} ) 91 | 92 | # Validate arguments to allow simpler debugging 93 | if(NOT _args_SOURCES) 94 | message( 95 | FATAL_ERROR 96 | "You have called add_python_library for library ${_name} without " 97 | "any source files. This typically indicates a problem with " 98 | "your CMakeLists.txt file" 99 | ) 100 | endif() 101 | 102 | # Initialize the list of sources 103 | set(_sources ${_args_SOURCES}) 104 | 105 | # Generate targets for all *.src files 106 | set(_processed ) 107 | foreach(_source IN LISTS _sources) 108 | if(${_source} MATCHES .pyf.src$ OR ${_source} MATCHES \\.f\\.src$) 109 | if(NOT NumPy_FOUND) 110 | message( 111 | FATAL_ERROR 112 | "NumPy is required to process *.src Template files" 113 | ) 114 | endif() 115 | string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) 116 | add_custom_command( 117 | OUTPUT ${_source_we} 118 | COMMAND ${NumPy_FROM_TEMPLATE_EXECUTABLE} 119 | ${CMAKE_CURRENT_SOURCE_DIR}/${_source} 120 | ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} 121 | DEPENDS ${_source} ${_args_DEPENDS} 122 | COMMENT "Generating ${_source_we} from template ${_source}" 123 | ) 124 | list(APPEND _processed ${_source_we}) 125 | elseif(${_source} MATCHES \\.c\\.src$) 126 | if(NOT NumPy_FOUND) 127 | message( 128 | FATAL_ERROR 129 | "NumPy is required to process *.src Template files" 130 | ) 131 | endif() 132 | string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) 133 | add_custom_command( 134 | OUTPUT ${_source_we} 135 | COMMAND ${NumPy_CONV_TEMPLATE_EXECUTABLE} 136 | ${CMAKE_CURRENT_SOURCE_DIR}/${_source} 137 | ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} 138 | DEPENDS ${_source} ${_args_DEPENDS} 139 | COMMENT "Generating ${_source_we} from template ${_source}" 140 | ) 141 | list(APPEND _processed ${_source_we}) 142 | elseif(${_source} MATCHES \\.pyx\\.in$) 143 | if(NOT Cython_FOUND) 144 | message( 145 | FATAL_ERROR 146 | "Cython is required to process *.in Tempita files" 147 | ) 148 | endif() 149 | string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) 150 | configure_file( 151 | ${CMAKE_CURRENT_SOURCE_DIR}/${_source} 152 | ${CMAKE_CURRENT_BINARY_DIR}/${_source} 153 | COPYONLY 154 | ) 155 | set(_tempita_command 156 | " 157 | import os; 158 | import sys; 159 | from Cython.Tempita import Template; 160 | cwd = os.getcwd(); 161 | open(os.path.join(cwd, '${_source_we}'), 'w+') 162 | .write( 163 | Template.from_filename(os.path.join(cwd, '${_source}'), 164 | encoding=sys.getdefaultencoding()).substitute() 165 | ) 166 | " 167 | ) 168 | _remove_whitespace(_tempita_command) 169 | add_custom_command( 170 | OUTPUT ${_source_we} 171 | COMMAND ${PYTHON_EXECUTABLE} -c "${_tempita_command}" 172 | DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_source}" 173 | ${_args_DEPENDS} 174 | ) 175 | list(APPEND _processed ${_source_we}) 176 | else() 177 | list(APPEND _processed ${_source}) 178 | endif() 179 | endforeach() 180 | set(_sources ${_processed}) 181 | 182 | # If we're building a Python extension and we're given only Fortran sources, 183 | # We can conclude that we need to generate a Fortran interface file 184 | list(FILTER _processed EXCLUDE REGEX "(\\.f|\\.f90)$") 185 | if(NOT _processed AND _args_MODULE) 186 | if(NOT NumPy_FOUND) 187 | message( 188 | FATAL_ERROR 189 | "NumPy is required to process *.pyf F2PY files" 190 | ) 191 | endif() 192 | set(_sources_abs ) 193 | foreach(_source IN LISTS _sources) 194 | list(APPEND _sources_abs ${CMAKE_CURRENT_SOURCE_DIR}/${_source}) 195 | endforeach() 196 | add_custom_command( 197 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.pyf 198 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 199 | COMMAND ${F2PY_EXECUTABLE} 200 | ARGS -h ${_name}.pyf -m ${_name} --overwrite-signature 201 | ${_sources_abs} 202 | DEPENDS ${_sources} ${_args_DEPENDS} 203 | COMMENT "Generating ${_name} Fortan interface file" 204 | ) 205 | list(APPEND _sources ${_name}.pyf) 206 | endif() 207 | 208 | # Are there F2PY targets? 209 | set(_has_f2py_targets OFF) 210 | set(_has_cython_targets OFF) 211 | 212 | # Generate targets for all *.pyx and *.pyf files 213 | set(_processed ) 214 | foreach(_source IN LISTS _sources) 215 | if(${_source} MATCHES \\.pyx$) 216 | if(NOT Cython_FOUND) 217 | message( 218 | FATAL_ERROR 219 | "Cython is required to process *.pyx Cython files" 220 | ) 221 | endif() 222 | string(REGEX REPLACE "\\.[^.]*$" "" _pyx_target_name ${_source}) 223 | set(_has_cython_targets ON) 224 | add_cython_target(${_pyx_target_name} 225 | ${_source} 226 | OUTPUT_VAR _pyx_target_output 227 | DEPENDS ${_args_DEPENDS} 228 | ) 229 | list(APPEND _processed ${_pyx_target_output}) 230 | elseif(${_source} MATCHES \\.pyf$) 231 | if(NOT NumPy_FOUND) 232 | message( 233 | FATAL_ERROR 234 | "NumPy is required to process *.pyf F2PY files" 235 | ) 236 | endif() 237 | string(REGEX REPLACE "\\.[^.]*$" "" _pyf_target_name ${_source}) 238 | set(_has_f2py_targets ON) 239 | add_f2py_target(${_pyf_target_name} 240 | ${_source} 241 | OUTPUT_VAR _pyf_target_output 242 | DEPENDS ${_args_DEPENDS} 243 | ) 244 | list(APPEND _processed ${_pyf_target_output}) 245 | else() 246 | list(APPEND _processed ${_source}) 247 | endif() 248 | endforeach() 249 | set(_sources ${_processed}) 250 | 251 | if(_args_SHARED) 252 | add_library(${_name} SHARED ${_sources}) 253 | elseif(_args_MODULE) 254 | add_library(${_name} MODULE ${_sources}) 255 | else() 256 | # Assume static 257 | add_library(${_name} STATIC ${_sources}) 258 | endif() 259 | 260 | target_include_directories(${_name} PRIVATE ${_args_INCLUDE_DIRECTORIES}) 261 | target_link_libraries(${_name} ${_args_LINK_LIBRARIES}) 262 | 263 | if(_has_f2py_targets) 264 | target_include_directories(${_name} PRIVATE ${F2PY_INCLUDE_DIRS}) 265 | target_link_libraries(${_name} ${F2PY_LIBRARIES}) 266 | endif() 267 | 268 | if(_args_COMPILE_DEFINITIONS) 269 | target_compile_definitions(${_name} PRIVATE ${_args_COMPILE_DEFINITIONS}) 270 | endif() 271 | 272 | if(_args_DEPENDS) 273 | add_custom_target( 274 | "${_name}_depends" 275 | DEPENDS ${_args_DEPENDS} 276 | ) 277 | add_dependencies(${_name} "${_name}_depends") 278 | endif() 279 | endfunction() 280 | 281 | function(add_python_extension _name) 282 | # FIXME: make sure that extensions with the same name can happen 283 | # in multiple directories 284 | 285 | set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) 286 | cmake_parse_arguments(_args "" "" "${multiValueArgs}" ${ARGN} ) 287 | 288 | # Validate arguments to allow simpler debugging 289 | if(NOT _args_SOURCES) 290 | message( 291 | FATAL_ERROR 292 | "You have called add_python_extension for library ${_name} without " 293 | "any source files. This typically indicates a problem with " 294 | "your CMakeLists.txt file" 295 | ) 296 | endif() 297 | 298 | add_python_library(${_name} MODULE 299 | SOURCES ${_args_SOURCES} 300 | INCLUDE_DIRECTORIES ${_args_INCLUDE_DIRECTORIES} 301 | LINK_LIBRARIES ${_args_LINK_LIBRARIES} 302 | COMPILE_DEFINITIONS ${_args_COMPILE_DEFINITIONS} 303 | DEPENDS ${_args_DEPENDS} 304 | ) 305 | python_extension_module(${_name}) 306 | 307 | file(RELATIVE_PATH _relative "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") 308 | install( 309 | TARGETS ${_name} 310 | LIBRARY DESTINATION "${_relative}" 311 | RUNTIME DESTINATION "${_relative}" 312 | ) 313 | endfunction() 314 | -------------------------------------------------------------------------------- /cmake/targetLinkLibrariesWithDynamicLookup.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # 3 | # Public Functions 4 | # ^^^^^^^^^^^^^^^^ 5 | # 6 | # The following functions are defined: 7 | # 8 | # .. cmake:command:: target_link_libraries_with_dynamic_lookup 9 | # 10 | # :: 11 | # 12 | # target_link_libraries_with_dynamic_lookup( []) 13 | # 14 | # 15 | # Useful to "weakly" link a loadable module. For example, it should be used 16 | # when compiling a loadable module when the symbols should be resolve from 17 | # the run-time environment where the module is loaded, and not a specific 18 | # system library. 19 | # 20 | # Like proper linking, except that the given ```` are not necessarily 21 | # linked. Instead, the ```` is produced in a manner that allows for 22 | # symbols unresolved within it to be resolved at runtime, presumably by the 23 | # given ````. If such a target can be produced, the provided 24 | # ```` are not actually linked. 25 | # 26 | # It links a library to a target such that the symbols are resolved at 27 | # run-time not link-time. 28 | # 29 | # The linker is checked to see if it supports undefined 30 | # symbols when linking a shared library. If it does then the library 31 | # is not linked when specified with this function. 32 | # 33 | # On platforms that do not support weak-linking, this function works just 34 | # like ``target_link_libraries``. 35 | # 36 | # .. note:: 37 | # 38 | # For OSX it uses ``undefined dynamic_lookup``. This is similar to using 39 | # ``-shared`` on Linux where undefined symbols are ignored. 40 | # 41 | # For more details, see `blog `_ 42 | # from Tim D. Smith. 43 | # 44 | # 45 | # .. cmake:command:: check_dynamic_lookup 46 | # 47 | # Check if the linker requires a command line flag to allow leaving symbols 48 | # unresolved when producing a target of type ```` that is 49 | # weakly-linked against a dependency of type ````. 50 | # 51 | # ```` 52 | # can be one of "STATIC", "SHARED", "MODULE", or "EXE". 53 | # 54 | # ```` 55 | # can be one of "STATIC", "SHARED", or "MODULE". 56 | # 57 | # Long signature: 58 | # 59 | # :: 60 | # 61 | # check_dynamic_lookup( 62 | # 63 | # 64 | # []) 65 | # 66 | # 67 | # Short signature: 68 | # 69 | # :: 70 | # 71 | # check_dynamic_lookup() # set to "MODULE" 72 | # # set to "SHARED" 73 | # 74 | # 75 | # The result is cached between invocations and recomputed only when the value 76 | # of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if 77 | # ```` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise. 78 | # 79 | # 80 | # Defined variables: 81 | # 82 | # ```` 83 | # Whether the current C toolchain supports weak-linking for target binaries of 84 | # type ```` that are weakly-linked against a dependency target of 85 | # type ````. 86 | # 87 | # ```` 88 | # List of flags to add to the linker command to produce a working target 89 | # binary of type ```` that is weakly-linked against a dependency 90 | # target of type ````. 91 | # 92 | # ``HAS_DYNAMIC_LOOKUP__`` 93 | # Cached, global alias for ```` 94 | # 95 | # ``DYNAMIC_LOOKUP_FLAGS__`` 96 | # Cached, global alias for ```` 97 | # 98 | # 99 | # Private Functions 100 | # ^^^^^^^^^^^^^^^^^ 101 | # 102 | # The following private functions are defined: 103 | # 104 | # .. warning:: These functions are not part of the scikit-build API. They 105 | # exist purely as an implementation detail and may change from version 106 | # to version without notice, or even be removed. 107 | # 108 | # We mean it. 109 | # 110 | # 111 | # .. cmake:command:: _get_target_type 112 | # 113 | # :: 114 | # 115 | # _get_target_type( ) 116 | # 117 | # 118 | # Shorthand for querying an abbreviated version of the target type 119 | # of the given ````. 120 | # 121 | # ```` is set to: 122 | # 123 | # - "STATIC" for a STATIC_LIBRARY, 124 | # - "SHARED" for a SHARED_LIBRARY, 125 | # - "MODULE" for a MODULE_LIBRARY, 126 | # - and "EXE" for an EXECUTABLE. 127 | # 128 | # Defined variables: 129 | # 130 | # ```` 131 | # The abbreviated version of the ````'s type. 132 | # 133 | # 134 | # .. cmake:command:: _test_weak_link_project 135 | # 136 | # :: 137 | # 138 | # _test_weak_link_project( 139 | # 140 | # 141 | # ) 142 | # 143 | # 144 | # Attempt to compile and run a test project where a target of type 145 | # ```` is weakly-linked against a dependency of type ````: 146 | # 147 | # - ```` can be one of "STATIC", "SHARED", "MODULE", or "EXE". 148 | # - ```` can be one of "STATIC", "SHARED", or "MODULE". 149 | # 150 | # Defined variables: 151 | # 152 | # ```` 153 | # Whether the current C toolchain can produce a working target binary of type 154 | # ```` that is weakly-linked against a dependency target of type 155 | # ````. 156 | # 157 | # ```` 158 | # List of flags to add to the linker command to produce a working target 159 | # binary of type ```` that is weakly-linked against a dependency 160 | # target of type ````. 161 | # 162 | 163 | function(_get_target_type result_var target) 164 | set(target_type "SHARED_LIBRARY") 165 | if(TARGET ${target}) 166 | get_property(target_type TARGET ${target} PROPERTY TYPE) 167 | endif() 168 | 169 | set(result "STATIC") 170 | 171 | if(target_type STREQUAL "STATIC_LIBRARY") 172 | set(result "STATIC") 173 | endif() 174 | 175 | if(target_type STREQUAL "SHARED_LIBRARY") 176 | set(result "SHARED") 177 | endif() 178 | 179 | if(target_type STREQUAL "MODULE_LIBRARY") 180 | set(result "MODULE") 181 | endif() 182 | 183 | if(target_type STREQUAL "EXECUTABLE") 184 | set(result "EXE") 185 | endif() 186 | 187 | set(${result_var} ${result} PARENT_SCOPE) 188 | endfunction() 189 | 190 | 191 | function(_test_weak_link_project 192 | target_type 193 | lib_type 194 | can_weak_link_var 195 | project_name) 196 | 197 | set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all") 198 | set(osx_dynamic_lookup "-undefined dynamic_lookup") 199 | set(no_flag "") 200 | 201 | foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag) 202 | set(link_flag "${${link_flag_spec}}") 203 | 204 | set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp") 205 | set(test_project_dir "${test_project_dir}/${project_name}") 206 | set(test_project_dir "${test_project_dir}/${link_flag_spec}") 207 | set(test_project_dir "${test_project_dir}/${target_type}") 208 | set(test_project_dir "${test_project_dir}/${lib_type}") 209 | 210 | set(test_project_src_dir "${test_project_dir}/src") 211 | set(test_project_bin_dir "${test_project_dir}/build") 212 | 213 | file(MAKE_DIRECTORY ${test_project_src_dir}) 214 | file(MAKE_DIRECTORY ${test_project_bin_dir}) 215 | 216 | set(mod_type "STATIC") 217 | set(link_mod_lib TRUE) 218 | set(link_exe_lib TRUE) 219 | set(link_exe_mod FALSE) 220 | 221 | if("${target_type}" STREQUAL "EXE") 222 | set(link_exe_lib FALSE) 223 | set(link_exe_mod TRUE) 224 | else() 225 | set(mod_type "${target_type}") 226 | endif() 227 | 228 | if("${mod_type}" STREQUAL "MODULE") 229 | set(link_mod_lib FALSE) 230 | endif() 231 | 232 | 233 | file(WRITE "${test_project_src_dir}/CMakeLists.txt" " 234 | cmake_minimum_required(VERSION ${CMAKE_VERSION}) 235 | project(${project_name} C) 236 | 237 | include_directories(${test_project_src_dir}) 238 | 239 | add_library(number ${lib_type} number.c) 240 | add_library(counter ${mod_type} counter.c) 241 | ") 242 | 243 | if("${mod_type}" STREQUAL "MODULE") 244 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 245 | set_target_properties(counter PROPERTIES PREFIX \"\") 246 | ") 247 | endif() 248 | 249 | if(link_mod_lib) 250 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 251 | target_link_libraries(counter number) 252 | ") 253 | elseif(NOT link_flag STREQUAL "") 254 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 255 | set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\") 256 | ") 257 | endif() 258 | 259 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 260 | add_executable(main main.c) 261 | ") 262 | 263 | if(link_exe_lib) 264 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 265 | target_link_libraries(main number) 266 | ") 267 | elseif(NOT link_flag STREQUAL "") 268 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 269 | target_link_libraries(main \"${link_flag}\") 270 | ") 271 | endif() 272 | 273 | if(link_exe_mod) 274 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 275 | target_link_libraries(main counter) 276 | ") 277 | else() 278 | file(APPEND "${test_project_src_dir}/CMakeLists.txt" " 279 | target_link_libraries(main \"${CMAKE_DL_LIBS}\") 280 | ") 281 | endif() 282 | 283 | file(WRITE "${test_project_src_dir}/number.c" " 284 | #include 285 | 286 | static int _number; 287 | void set_number(int number) { _number = number; } 288 | int get_number() { return _number; } 289 | ") 290 | 291 | file(WRITE "${test_project_src_dir}/number.h" " 292 | #ifndef _NUMBER_H 293 | #define _NUMBER_H 294 | extern void set_number(int); 295 | extern int get_number(void); 296 | #endif 297 | ") 298 | 299 | file(WRITE "${test_project_src_dir}/counter.c" " 300 | #include 301 | int count() { 302 | int result = get_number(); 303 | set_number(result + 1); 304 | return result; 305 | } 306 | ") 307 | 308 | file(WRITE "${test_project_src_dir}/counter.h" " 309 | #ifndef _COUNTER_H 310 | #define _COUNTER_H 311 | extern int count(void); 312 | #endif 313 | ") 314 | 315 | file(WRITE "${test_project_src_dir}/main.c" " 316 | #include 317 | #include 318 | #include 319 | ") 320 | 321 | if(NOT link_exe_mod) 322 | file(APPEND "${test_project_src_dir}/main.c" " 323 | #include 324 | ") 325 | endif() 326 | 327 | file(APPEND "${test_project_src_dir}/main.c" " 328 | int my_count() { 329 | int result = get_number(); 330 | set_number(result + 1); 331 | return result; 332 | } 333 | 334 | int main(int argc, char **argv) { 335 | int result; 336 | ") 337 | 338 | if(NOT link_exe_mod) 339 | file(APPEND "${test_project_src_dir}/main.c" " 340 | void *counter_module; 341 | int (*count)(void); 342 | 343 | counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL); 344 | if(!counter_module) goto error; 345 | 346 | count = dlsym(counter_module, \"count\"); 347 | if(!count) goto error; 348 | ") 349 | endif() 350 | 351 | file(APPEND "${test_project_src_dir}/main.c" " 352 | result = count() != 0 ? EXIT_FAILURE : 353 | my_count() != 1 ? EXIT_FAILURE : 354 | my_count() != 2 ? EXIT_FAILURE : 355 | count() != 3 ? EXIT_FAILURE : 356 | count() != 4 ? EXIT_FAILURE : 357 | count() != 5 ? EXIT_FAILURE : 358 | my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS; 359 | ") 360 | 361 | if(NOT link_exe_mod) 362 | file(APPEND "${test_project_src_dir}/main.c" " 363 | goto done; 364 | error: 365 | fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror()); 366 | result = 1; 367 | 368 | done: 369 | if(counter_module) dlclose(counter_module); 370 | ") 371 | endif() 372 | 373 | file(APPEND "${test_project_src_dir}/main.c" " 374 | return result; 375 | } 376 | ") 377 | 378 | set(_rpath_arg) 379 | if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11) 380 | set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'") 381 | endif() 382 | 383 | try_compile(project_compiles 384 | "${test_project_bin_dir}" 385 | "${test_project_src_dir}" 386 | "${project_name}" 387 | CMAKE_FLAGS 388 | "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'" 389 | "-DCMAKE_ENABLE_EXPORTS=ON" 390 | ${_rpath_arg} 391 | OUTPUT_VARIABLE compile_output) 392 | 393 | set(project_works 1) 394 | set(run_output) 395 | 396 | if(project_compiles) 397 | execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} 398 | "${test_project_bin_dir}/main" 399 | WORKING_DIRECTORY "${test_project_bin_dir}" 400 | RESULT_VARIABLE project_works 401 | OUTPUT_VARIABLE run_output 402 | ERROR_VARIABLE run_output) 403 | endif() 404 | 405 | set(test_description 406 | "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})") 407 | 408 | if(project_works EQUAL 0) 409 | set(project_works TRUE) 410 | message(STATUS "Performing Test ${test_description} - Success") 411 | else() 412 | set(project_works FALSE) 413 | message(STATUS "Performing Test ${test_description} - Failed") 414 | file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log 415 | "Performing Test ${test_description} failed with the " 416 | "following output:\n" 417 | "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n") 418 | endif() 419 | 420 | set(${can_weak_link_var} ${project_works} PARENT_SCOPE) 421 | if(project_works) 422 | set(${project_name} ${link_flag} PARENT_SCOPE) 423 | break() 424 | endif() 425 | endforeach() 426 | endfunction() 427 | 428 | function(check_dynamic_lookup) 429 | # Two signatures are supported: 430 | 431 | if(ARGC EQUAL "1") 432 | # 433 | # check_dynamic_lookup() 434 | # 435 | set(target_type "MODULE") 436 | set(lib_type "SHARED") 437 | set(has_dynamic_lookup_var "${ARGV0}") 438 | set(link_flags_var "unused") 439 | 440 | elseif(ARGC GREATER "2") 441 | # 442 | # check_dynamic_lookup( 443 | # 444 | # 445 | # []) 446 | # 447 | set(target_type "${ARGV0}") 448 | set(lib_type "${ARGV1}") 449 | set(has_dynamic_lookup_var "${ARGV2}") 450 | if(ARGC EQUAL "3") 451 | set(link_flags_var "unused") 452 | else() 453 | set(link_flags_var "${ARGV3}") 454 | endif() 455 | else() 456 | message(FATAL_ERROR "missing arguments") 457 | endif() 458 | 459 | _check_dynamic_lookup( 460 | ${target_type} 461 | ${lib_type} 462 | ${has_dynamic_lookup_var} 463 | ${link_flags_var} 464 | ) 465 | set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE) 466 | if(NOT "x${link_flags_var}x" STREQUAL "xunusedx") 467 | set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE) 468 | endif() 469 | endfunction() 470 | 471 | function(_check_dynamic_lookup 472 | target_type 473 | lib_type 474 | has_dynamic_lookup_var 475 | link_flags_var 476 | ) 477 | 478 | # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun 479 | if("${target_type}" STREQUAL "STATIC") 480 | string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}") 481 | else() 482 | string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}") 483 | endif() 484 | 485 | set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}") 486 | set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash") 487 | set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}") 488 | 489 | if( NOT DEFINED ${cache_hash_var} 490 | OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}") 491 | unset(${cache_var} CACHE) 492 | endif() 493 | 494 | if(NOT DEFINED ${cache_var}) 495 | set(skip_test FALSE) 496 | 497 | if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) 498 | set(skip_test TRUE) 499 | endif() 500 | 501 | if(skip_test) 502 | set(has_dynamic_lookup FALSE) 503 | set(link_flags) 504 | else() 505 | _test_weak_link_project(${target_type} 506 | ${lib_type} 507 | has_dynamic_lookup 508 | link_flags) 509 | endif() 510 | 511 | set(caveat " (when linking ${target_type} against ${lib_type})") 512 | 513 | set(${cache_var} "${has_dynamic_lookup}" 514 | CACHE BOOL 515 | "linker supports dynamic lookup for undefined symbols${caveat}") 516 | mark_as_advanced(${cache_var}) 517 | 518 | set(${result_var} "${link_flags}" 519 | CACHE STRING 520 | "linker flags for dynamic lookup${caveat}") 521 | mark_as_advanced(${result_var}) 522 | 523 | set(${cache_hash_var} "${cmake_flags_hash}" 524 | CACHE INTERNAL "hashed flags for ${cache_var} check") 525 | endif() 526 | 527 | set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE) 528 | set(${link_flags_var} "${${result_var}}" PARENT_SCOPE) 529 | endfunction() 530 | 531 | function(target_link_libraries_with_dynamic_lookup target) 532 | _get_target_type(target_type ${target}) 533 | 534 | set(link_props) 535 | set(link_items) 536 | set(link_libs) 537 | 538 | foreach(lib ${ARGN}) 539 | _get_target_type(lib_type ${lib}) 540 | check_dynamic_lookup(${target_type} 541 | ${lib_type} 542 | has_dynamic_lookup 543 | dynamic_lookup_flags) 544 | 545 | if(has_dynamic_lookup) 546 | if(dynamic_lookup_flags) 547 | if("${target_type}" STREQUAL "EXE") 548 | list(APPEND link_items "${dynamic_lookup_flags}") 549 | else() 550 | list(APPEND link_props "${dynamic_lookup_flags}") 551 | endif() 552 | endif() 553 | elseif(${lib} MATCHES "(debug|optimized|general)") 554 | # See gh-255 555 | else() 556 | list(APPEND link_libs "${lib}") 557 | endif() 558 | endforeach() 559 | 560 | if(link_props) 561 | list(REMOVE_DUPLICATES link_props) 562 | endif() 563 | 564 | if(link_items) 565 | list(REMOVE_DUPLICATES link_items) 566 | endif() 567 | 568 | if(link_libs) 569 | list(REMOVE_DUPLICATES link_libs) 570 | endif() 571 | 572 | if(link_props) 573 | set_target_properties(${target} 574 | PROPERTIES LINK_FLAGS "${link_props}") 575 | endif() 576 | 577 | set(links "${link_items}" "${link_libs}") 578 | if(links) 579 | target_link_libraries(${target} "${links}") 580 | endif() 581 | endfunction() 582 | -------------------------------------------------------------------------------- /fortran_cython_examples/__init__.py: -------------------------------------------------------------------------------- 1 | from ._fortran_cython_examples import * -------------------------------------------------------------------------------- /notes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import fortran_cython_examples\n", 10 | "import numpy as np\n", 11 | "# import numba as nb" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "f = fortran_cython_examples.module_variable\n", 21 | "f = fortran_cython_examples.dtype1" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 3, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "var1 = np.empty((3),dtype=np.dtype(('S', 20)),order='F')\n", 31 | "var1[0] = \"{:20}\".format(\"hello\")\n", 32 | "var1[1] = \"{:20}\".format(\"nias\")\n", 33 | "var1[0] = \"hello\"\n", 34 | "var1[1] = \"nick\"\n" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 5, 40 | "metadata": { 41 | "scrolled": false 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "a = f.mytype()" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 11, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "a.arr3 = np.array([1,2],dtype=np.int32)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 12, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "data": { 64 | "text/plain": [ 65 | "array([1, 2], dtype=int32)" 66 | ] 67 | }, 68 | "execution_count": 12, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "a.arr3" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 34, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "text/plain": [ 85 | "array([b'hello', b'nick',\n", 86 | " b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00p\\x82\\x04\\x81\\x14'],\n", 87 | " dtype='|S20')" 88 | ] 89 | }, 90 | "execution_count": 34, 91 | "metadata": {}, 92 | "output_type": "execute_result" 93 | } 94 | ], 95 | "source": [ 96 | "var1" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 18, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "a = np.array([\"hi\",'hid '])" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 11, 118 | "metadata": {}, 119 | "outputs": [ 120 | { 121 | "data": { 122 | "text/plain": [ 123 | "dtype('S20')" 124 | ] 125 | }, 126 | "execution_count": 11, 127 | "metadata": {}, 128 | "output_type": "execute_result" 129 | } 130 | ], 131 | "source": [ 132 | "np.dtype(('S', 20))" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 1, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "import mytest" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 7, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [ 150 | "mytest.mytest.b = b'gd'" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 45, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "array(b'd', dtype='|S8')" 162 | ] 163 | }, 164 | "execution_count": 45, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "np.array(\"d\",dtype=np.dtype(('S', 8)))" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 8, 176 | "metadata": {}, 177 | "outputs": [ 178 | { 179 | "data": { 180 | "text/plain": [ 181 | "array(b'11', dtype='|S2')" 182 | ] 183 | }, 184 | "execution_count": 8, 185 | "metadata": {}, 186 | "output_type": "execute_result" 187 | } 188 | ], 189 | "source": [ 190 | "mytest.mytest.b" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": null, 196 | "metadata": {}, 197 | "outputs": [], 198 | "source": [] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | "cdef pystring2cstring(str pystring):\n", 214 | " # add a null c char, and convert to byes\n", 215 | " cdef bytes cstring = (pystring+'\\0').encode('utf-8')\n", 216 | " return cstring" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 69, 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "py_list_str = [\"hello\",'Helasdflkjhlkhlkjhlo']\n", 226 | "\n", 227 | "def pyliststr2cstring(py_list_str, d):\n", 228 | " fmt = \"{:\"+(\"%i\"%d)+\"}\"\n", 229 | " n = \"\"\n", 230 | " for a in py_list_str:\n", 231 | " if len(a) > d:\n", 232 | " n+=a[:d]\n", 233 | " else:\n", 234 | " n+=fmt.format(a)\n", 235 | " return (n+'\\0').encode('utf-8')\n" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 70, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "data": { 245 | "text/plain": [ 246 | "b'hello Helasdflkj\\x00'" 247 | ] 248 | }, 249 | "execution_count": 70, 250 | "metadata": {}, 251 | "output_type": "execute_result" 252 | } 253 | ], 254 | "source": [ 255 | "pyliststr2cstring(py_list_str,10)" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": 62, 261 | "metadata": {}, 262 | "outputs": [ 263 | { 264 | "data": { 265 | "text/plain": [ 266 | "'hasdfasdfasdfasdfasdfi'" 267 | ] 268 | }, 269 | "execution_count": 62, 270 | "metadata": {}, 271 | "output_type": "execute_result" 272 | } 273 | ], 274 | "source": [ 275 | "fmt = '{:10}'\n", 276 | "fmt.format(\"hasdfasdfasdfasdfasdfi\")" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 95, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "a = np.empty((1),dtype=np.dtype(('U', 10)))\n", 286 | "\n", 287 | "a[0] = \"hiasdfaasdfa\"\n" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 96, 293 | "metadata": {}, 294 | "outputs": [ 295 | { 296 | "data": { 297 | "text/plain": [ 298 | "array(['hiasdfaasd'], dtype='\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marr2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mint32\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 323 | "\u001b[0;31mAttributeError\u001b[0m: attribute 'arr2' of '_fortran_cython_examples.__module_variable' objects is not writable" 324 | ] 325 | } 326 | ], 327 | "source": [ 328 | "f.arr2 = np.zeros(1,np.int32)" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 7, 334 | "metadata": {}, 335 | "outputs": [ 336 | { 337 | "data": { 338 | "text/plain": [ 339 | "b'hello\\x00'" 340 | ] 341 | }, 342 | "execution_count": 7, 343 | "metadata": {}, 344 | "output_type": "execute_result" 345 | } 346 | ], 347 | "source": [ 348 | "v = \"hello\"\n", 349 | "(v+'\\0').encode('utf-8')" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": 8, 355 | "metadata": {}, 356 | "outputs": [ 357 | { 358 | "data": { 359 | "text/plain": [ 360 | "b'hello'" 361 | ] 362 | }, 363 | "execution_count": 8, 364 | "metadata": {}, 365 | "output_type": "execute_result" 366 | } 367 | ], 368 | "source": [ 369 | "(v).encode('utf-8')" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": null, 375 | "metadata": {}, 376 | "outputs": [], 377 | "source": [] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": null, 382 | "metadata": {}, 383 | "outputs": [], 384 | "source": [] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": null, 389 | "metadata": {}, 390 | "outputs": [], 391 | "source": [] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": 31, 396 | "metadata": {}, 397 | "outputs": [ 398 | { 399 | "ename": "ValueError", 400 | "evalue": "VARIABLE must have shape (10,10), but got (9,10)", 401 | "output_type": "error", 402 | "traceback": [ 403 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 404 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 405 | "\u001b[0;32m/var/folders/sf/43vm953d201c4jw4yg22hnhc0000gn/T/ipykernel_3449/2670681492.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mvar\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"VARIABLE must have shape (10,10), but got (9,10)\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 406 | "\u001b[0;31mValueError\u001b[0m: VARIABLE must have shape (10,10), but got (9,10)" 407 | ] 408 | } 409 | ], 410 | "source": [ 411 | "var = np.zeros((10))\n", 412 | "\n", 413 | "if (10,10) != var.shape:\n", 414 | " raise ValueError(\"VARIABLE must have shape (10,10), but got (9,10)\")" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 32, 420 | "metadata": {}, 421 | "outputs": [ 422 | { 423 | "data": { 424 | "text/plain": [ 425 | "'(10,)'" 426 | ] 427 | }, 428 | "execution_count": 32, 429 | "metadata": {}, 430 | "output_type": "execute_result" 431 | } 432 | ], 433 | "source": [ 434 | "str(var.shape)" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": 25, 440 | "metadata": {}, 441 | "outputs": [ 442 | { 443 | "ename": "ValueError", 444 | "evalue": "0-th dimension must be fixed to 8 but got 9\n", 445 | "output_type": "error", 446 | "traceback": [ 447 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 448 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 449 | "\u001b[0;32m/var/folders/sf/43vm953d201c4jw4yg22hnhc0000gn/T/ipykernel_3449/1923213731.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmytest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m9\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 450 | "\u001b[0;31mValueError\u001b[0m: 0-th dimension must be fixed to 8 but got 9\n" 451 | ] 452 | } 453 | ], 454 | "source": [ 455 | "mytest.a = np.zeros(9)get" 456 | ] 457 | }, 458 | { 459 | "cell_type": "code", 460 | "execution_count": 38, 461 | "metadata": {}, 462 | "outputs": [ 463 | { 464 | "data": { 465 | "text/plain": [ 466 | "(10,)" 467 | ] 468 | }, 469 | "execution_count": 38, 470 | "metadata": {}, 471 | "output_type": "execute_result" 472 | } 473 | ], 474 | "source": [ 475 | "var.shape\n" 476 | ] 477 | }, 478 | { 479 | "cell_type": "code", 480 | "execution_count": null, 481 | "metadata": {}, 482 | "outputs": [], 483 | "source": [] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": 1, 488 | "metadata": {}, 489 | "outputs": [ 490 | { 491 | "ename": "NameError", 492 | "evalue": "name 'fortran_cython_examples' is not defined", 493 | "output_type": "error", 494 | "traceback": [ 495 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 496 | "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", 497 | "\u001b[0;32m/var/folders/sf/43vm953d201c4jw4yg22hnhc0000gn/T/ipykernel_5912/132772784.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmytype\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfortran_cython_examples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mderived_type\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmytype\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 498 | "\u001b[0;31mNameError\u001b[0m: name 'fortran_cython_examples' is not defined" 499 | ] 500 | } 501 | ], 502 | "source": [ 503 | "mytype = fortran_cython_examples.derived_type.mytype" 504 | ] 505 | }, 506 | { 507 | "cell_type": "code", 508 | "execution_count": 3, 509 | "metadata": {}, 510 | "outputs": [], 511 | "source": [ 512 | "my = mytype()" 513 | ] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": 4, 518 | "metadata": {}, 519 | "outputs": [], 520 | "source": [ 521 | "my.arr = np.array([1])" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": 5, 527 | "metadata": {}, 528 | "outputs": [ 529 | { 530 | "name": "stdout", 531 | "output_type": "stream", 532 | "text": [ 533 | "57 ns ± 0.423 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n" 534 | ] 535 | } 536 | ], 537 | "source": [ 538 | "%timeit my.a" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": 8, 544 | "metadata": {}, 545 | "outputs": [ 546 | { 547 | "name": "stdout", 548 | "output_type": "stream", 549 | "text": [ 550 | "69.4 ns ± 0.381 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)\n" 551 | ] 552 | } 553 | ], 554 | "source": [ 555 | "%timeit my.add2a(10)" 556 | ] 557 | }, 558 | { 559 | "cell_type": "code", 560 | "execution_count": 9, 561 | "metadata": {}, 562 | "outputs": [ 563 | { 564 | "data": { 565 | "text/plain": [ 566 | "array([1.])" 567 | ] 568 | }, 569 | "execution_count": 9, 570 | "metadata": {}, 571 | "output_type": "execute_result" 572 | } 573 | ], 574 | "source": [ 575 | "my.arr" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": 6, 581 | "metadata": {}, 582 | "outputs": [], 583 | "source": [ 584 | "my.a = 10" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": 14, 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "@nb.cfunc(nb.types.void(nb.types.int32,nb.types.int32))\n", 594 | "def test(a,b):\n", 595 | " b = a + 1" 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 19, 601 | "metadata": {}, 602 | "outputs": [ 603 | { 604 | "data": { 605 | "text/plain": [ 606 | "" 607 | ] 608 | }, 609 | "execution_count": 19, 610 | "metadata": {}, 611 | "output_type": "execute_result" 612 | } 613 | ], 614 | "source": [ 615 | "test" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 21, 621 | "metadata": {}, 622 | "outputs": [ 623 | { 624 | "data": { 625 | "text/plain": [ 626 | "" 627 | ] 628 | }, 629 | "execution_count": 21, 630 | "metadata": {}, 631 | "output_type": "execute_result" 632 | } 633 | ], 634 | "source": [ 635 | "np.empty(100,dtype='c').data" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": null, 641 | "metadata": {}, 642 | "outputs": [], 643 | "source": [] 644 | }, 645 | { 646 | "cell_type": "code", 647 | "execution_count": 8, 648 | "metadata": {}, 649 | "outputs": [ 650 | { 651 | "name": "stdout", 652 | "output_type": "stream", 653 | "text": [ 654 | "1.62 µs ± 23.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n" 655 | ] 656 | } 657 | ], 658 | "source": [ 659 | "%timeit b = fortran_cython_examples.simple_subroutine.returns_arr1()" 660 | ] 661 | }, 662 | { 663 | "cell_type": "code", 664 | "execution_count": 7, 665 | "metadata": {}, 666 | "outputs": [ 667 | { 668 | "data": { 669 | "text/plain": [ 670 | "array([12, 12, 12, 12, 12, 12, 12, 12, 12, 12], dtype=int32)" 671 | ] 672 | }, 673 | "execution_count": 7, 674 | "metadata": {}, 675 | "output_type": "execute_result" 676 | } 677 | ], 678 | "source": [ 679 | "b" 680 | ] 681 | }, 682 | { 683 | "cell_type": "code", 684 | "execution_count": 15, 685 | "metadata": {}, 686 | "outputs": [], 687 | "source": [ 688 | "my.arr = np.array([1,2,3])" 689 | ] 690 | }, 691 | { 692 | "cell_type": "code", 693 | "execution_count": 4, 694 | "metadata": {}, 695 | "outputs": [], 696 | "source": [ 697 | "a = my.add2a(100)" 698 | ] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "execution_count": 6, 703 | "metadata": {}, 704 | "outputs": [ 705 | { 706 | "name": "stdout", 707 | "output_type": "stream", 708 | "text": [ 709 | "None\n" 710 | ] 711 | } 712 | ], 713 | "source": [ 714 | "print(a)" 715 | ] 716 | }, 717 | { 718 | "cell_type": "code", 719 | "execution_count": 24, 720 | "metadata": {}, 721 | "outputs": [], 722 | "source": [ 723 | "b = 10\n", 724 | "\n", 725 | "\n", 726 | "@property\n", 727 | "def a(self):\n", 728 | " pass\n", 729 | " \n", 730 | "@a.setter\n", 731 | "def a(self,aa):\n", 732 | " print(a)\n", 733 | " b = aa\n", 734 | " \n", 735 | "@a.getter\n", 736 | "def a(self):\n", 737 | " return b" 738 | ] 739 | }, 740 | { 741 | "cell_type": "code", 742 | "execution_count": 22, 743 | "metadata": {}, 744 | "outputs": [], 745 | "source": [ 746 | "a = 1" 747 | ] 748 | }, 749 | { 750 | "cell_type": "code", 751 | "execution_count": 23, 752 | "metadata": {}, 753 | "outputs": [ 754 | { 755 | "data": { 756 | "text/plain": [ 757 | "1" 758 | ] 759 | }, 760 | "execution_count": 23, 761 | "metadata": {}, 762 | "output_type": "execute_result" 763 | } 764 | ], 765 | "source": [ 766 | "a" 767 | ] 768 | }, 769 | { 770 | "cell_type": "code", 771 | "execution_count": null, 772 | "metadata": {}, 773 | "outputs": [], 774 | "source": [] 775 | } 776 | ], 777 | "metadata": { 778 | "kernelspec": { 779 | "display_name": "Python 3 (ipykernel)", 780 | "language": "python", 781 | "name": "python3" 782 | }, 783 | "language_info": { 784 | "codemirror_mode": { 785 | "name": "ipython", 786 | "version": 3 787 | }, 788 | "file_extension": ".py", 789 | "mimetype": "text/x-python", 790 | "name": "python", 791 | "nbconvert_exporter": "python", 792 | "pygments_lexer": "ipython3", 793 | "version": "3.9.7" 794 | } 795 | }, 796 | "nbformat": 4, 797 | "nbformat_minor": 4 798 | } 799 | -------------------------------------------------------------------------------- /notes/general.md: -------------------------------------------------------------------------------- 1 | # general notes 2 | 3 | Fortran features that seem not wrap-able: 4 | 5 | - Parameterized derived types. The type of attributes is not known at compile time. So C-interface is really hard. 6 | - Pointers. They are sort-of possible to wrap if they are never allocated. But allocating will lead to leaks. 7 | - `type(c_ptr)` -------------------------------------------------------------------------------- /notes/getters_setters.md: -------------------------------------------------------------------------------- 1 | # getters and setters 2 | 3 | From `module_variable` and `dtype1`, looks like we get get and set all intrinsic types in modules and derived types with the following templates. Exception is characters. Still need to work on that 4 | 5 | ## Intrinsic module variable. Not a character. Not an array. 6 | 7 | Fortran wrapper 8 | ``` 9 | subroutine _get_(var) bind(c) 10 | use , only: a => 11 | (), intent(out) :: var 12 | if (allocated(a)) then ! IF ALLOCATBLE 13 | var = a ! indented IF ALLOCATBLE 14 | endif ! IF ALLOCATBLE 15 | end subroutine 16 | 17 | ! IF NOT PARAMETER, we make setter 18 | subroutine _set_(var) bind(c) 19 | use , only: a => 20 | (), intent(in) :: var 21 | if (.not. allocated(a)) then ! ] 22 | allocate(a) ! ] ONLY if allocatable 23 | endif ! ] 24 | a = var 25 | end subroutine 26 | ``` 27 | 28 | ## -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel", "scikit-build", "cmake>=3.18", "ninja", "cython", "numpy"] 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from skbuild import setup 2 | 3 | setup( 4 | name="fortran_cython_examples", 5 | version="0.1", 6 | description="a description", 7 | author='Nicholas Wogan', 8 | license="MIT", 9 | packages=['fortran_cython_examples'], 10 | install_requires=['cython','numpy'], 11 | cmake_args=['-DSKBUILD=ON'] 12 | ) 13 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(FORTRAN_MODULES simple_subroutine simple_function module_variable derived_type characters dtype1) 2 | 3 | foreach (F_MODULE ${FORTRAN_MODULES}) 4 | add_subdirectory(${F_MODULE}) 5 | endforeach() 6 | 7 | 8 | 9 | set(module_name examples) 10 | 11 | add_cython_target(_fortran_cython_examples _fortran_cython_examples.pyx) 12 | add_library(_fortran_cython_examples MODULE ${_fortran_cython_examples}) 13 | python_extension_module(_fortran_cython_examples) 14 | 15 | foreach (F_MODULE ${FORTRAN_MODULES}) 16 | target_link_libraries(_fortran_cython_examples ${F_MODULE}_fortran) 17 | endforeach() 18 | 19 | target_include_directories(_fortran_cython_examples PUBLIC ${NumPy_INCLUDE_DIRS}) 20 | 21 | foreach (F_MODULE ${FORTRAN_MODULES}) 22 | target_include_directories(_fortran_cython_examples PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/${F_MODULE}) 23 | endforeach() 24 | 25 | if (SKBUILD) 26 | install(TARGETS _fortran_cython_examples LIBRARY DESTINATION fortran_cython_examples) 27 | else() 28 | install(TARGETS _fortran_cython_examples DESTINATION ${CMAKE_SOURCE_DIR}/fortran_cython_examples/) 29 | endif() -------------------------------------------------------------------------------- /src/_fortran_cython_examples.pyx: -------------------------------------------------------------------------------- 1 | include "module_variable/module_variable.pyx" 2 | include "simple_subroutine/simple_subroutine.pyx" 3 | include "simple_function/simple_function.pyx" 4 | include "derived_type/derived_type.pyx" 5 | include "characters/characters.pyx" 6 | include "dtype1/dtype1.pyx" -------------------------------------------------------------------------------- /src/characters/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name characters) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/characters/characters.f90: -------------------------------------------------------------------------------- 1 | module characters 2 | implicit none 3 | character(len=20) :: str1 4 | character(len=:), allocatable :: str2 5 | end module -------------------------------------------------------------------------------- /src/characters/characters.h: -------------------------------------------------------------------------------- 1 | void characters_get_str1(char *str1_copy); 2 | void characters_set_str1(char *str1_copy, int *str1_len); 3 | 4 | void characters_get_str2(char *str2_copy, int *str2_len); 5 | void characters_get_str2_len(int *str2_len); 6 | void characters_set_str2(char *new_str2, int *str2_len); -------------------------------------------------------------------------------- /src/characters/characters.pyx: -------------------------------------------------------------------------------- 1 | from numpy cimport ndarray 2 | from libc.stdlib cimport malloc 3 | from libc.stdlib cimport free 4 | 5 | cdef extern from "characters.h": 6 | cdef void characters_get_str1(char *str1_copy); 7 | cdef void characters_set_str1(char *str1_copy, int *str1_len); 8 | 9 | cdef void characters_get_str2(char *str2_copy, int *str2_len); 10 | cdef void characters_get_str2_len(int *str2_len); 11 | cdef void characters_set_str2(char *new_str2, int *str2_len) 12 | 13 | cdef class __characters: 14 | """module characters 15 | character(len=20) :: str1 16 | character(len=:), allocatable :: str2 17 | end module""" 18 | property str1: 19 | def __get__(self): 20 | cdef char str1_copy[20+1] 21 | characters_get_str1(str1_copy) 22 | return str1_copy 23 | def __set__(self, char* new_str1): 24 | cdef int str1_len = len(new_str1) 25 | characters_set_str1(new_str1, &str1_len) 26 | 27 | property str2: 28 | def __get__(self): 29 | cdef int str2_len 30 | characters_get_str2_len(&str2_len) 31 | cdef char* str2_copy = malloc((str2_len + 1) * sizeof(char)) 32 | characters_get_str2(str2_copy, &str2_len) 33 | cdef bytes py_string 34 | py_string = str2_copy 35 | if str2_copy: 36 | free(str2_copy) 37 | return py_string 38 | def __set__(self, char* new_str2): 39 | cdef int str2_len = len(new_str2) 40 | characters_set_str2(new_str2, &str2_len) 41 | 42 | 43 | 44 | characters = __characters() 45 | -------------------------------------------------------------------------------- /src/characters/characters_wrapper.f90: -------------------------------------------------------------------------------- 1 | module characters_wrapper 2 | use iso_c_binding 3 | implicit none 4 | contains 5 | 6 | subroutine characters_get_str1(str1_copy) bind(c) 7 | use characters, only: str1 8 | character(len=c_char), intent(out) :: str1_copy(20+1) 9 | call copy_string_ftoc(str1, str1_copy) 10 | end subroutine 11 | 12 | subroutine characters_set_str1(new_str1, str1_len) bind(c) 13 | use characters, only: str1 14 | character(len=c_char), intent(in) :: new_str1(str1_len+1) 15 | integer(c_int), intent(in) :: str1_len 16 | call copy_string_ctof(new_str1, str1) 17 | end subroutine 18 | 19 | subroutine characters_get_str2(str2_copy, str2_len) bind(c) 20 | use characters, only: str2 21 | character(len=c_char), intent(out) :: str2_copy(str2_len+1) 22 | integer(c_int), intent(in) :: str2_len 23 | call copy_string_ftoc(str2, str2_copy) 24 | end subroutine 25 | 26 | subroutine characters_get_str2_len(str2_len) bind(c) 27 | use characters, only: str2 28 | integer(c_int), intent(out) :: str2_len 29 | str2_len = len(str2) 30 | end subroutine 31 | 32 | subroutine characters_set_str2(new_str2, str2_len) bind(c) 33 | use characters, only: str2 34 | character(len=c_char), intent(in) :: new_str2(str2_len+1) 35 | integer(c_int), intent(in) :: str2_len 36 | if (allocated(str2)) deallocate(str2) 37 | allocate(character(str2_len) :: str2) 38 | call copy_string_ctof(new_str2, str2) 39 | end subroutine 40 | 41 | !!!!!!!!!!!!!!!!!! 42 | !!! Utilities !!! 43 | !!!!!!!!!!!!!!!!!! 44 | 45 | subroutine copy_string_ctof(stringc,stringf) 46 | ! utility function to convert c string to fortran string 47 | character(len=*), intent(out) :: stringf 48 | character(c_char), intent(in) :: stringc(:) 49 | integer j 50 | stringf = '' 51 | char_loop: do j=1,min(size(stringc),len(stringf)) 52 | if (stringc(j)==c_null_char) exit char_loop 53 | stringf(j:j) = stringc(j) 54 | end do char_loop 55 | end subroutine copy_string_ctof 56 | 57 | subroutine copy_string_ftoc(stringf,stringc) 58 | ! utility function to convert c string to fortran string 59 | character(len=*), intent(in) :: stringf 60 | character(c_char), intent(out) :: stringc(:) 61 | integer j,n 62 | n = len_trim(stringf) 63 | do j=1,n 64 | stringc(j) = stringf(j:j) 65 | end do 66 | stringc(n+1) = c_null_char 67 | end subroutine copy_string_ftoc 68 | 69 | end module -------------------------------------------------------------------------------- /src/derived_type/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name derived_type) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/derived_type/derived_type.f90: -------------------------------------------------------------------------------- 1 | module derived_type 2 | implicit none 3 | private 4 | 5 | public :: Mytype 6 | 7 | integer, parameter :: real_kind = kind(1.d0) 8 | 9 | type Mytype 10 | real(real_kind) :: a 11 | real(real_kind), allocatable :: arr(:) 12 | contains 13 | procedure :: add2a 14 | procedure :: sumarr 15 | procedure :: printarr 16 | end type 17 | 18 | contains 19 | 20 | subroutine add2a(self, b) 21 | class(Mytype), intent(inout) :: self 22 | real(real_kind), intent(in) :: b 23 | self%a = self%a + b 24 | end subroutine 25 | 26 | subroutine printarr(self) 27 | class(Mytype), intent(in) :: self 28 | print*,self%arr 29 | end subroutine 30 | 31 | subroutine sumarr(self, result) 32 | class(Mytype), intent(in) :: self 33 | real(real_kind), intent(out) :: result 34 | result = sum(self%arr) 35 | end subroutine 36 | 37 | end module -------------------------------------------------------------------------------- /src/derived_type/derived_type.h: -------------------------------------------------------------------------------- 1 | #include 2 | void allocate_mytype(void *ptr); 3 | void destroy_mytype(void *ptr); 4 | 5 | void mytype_set_a(void *ptr, double *a); 6 | void mytype_get_a(void *ptr, double *a); 7 | void mytype_set_arr(void *ptr, int *arr_size, double *arr); 8 | void mytype_get_arr_size(void *ptr, int *arr_size, bool *alloc); 9 | void mytype_get_arr(void *ptr, int *arrs_size, double *arr); 10 | 11 | void mytype_add2a_wrapper(void *ptr, double *b) ; 12 | void mytype_printarr_wrapper(void *ptr) ; 13 | void mytype_sumarr_wrapper(void *ptr, double *result); -------------------------------------------------------------------------------- /src/derived_type/derived_type.pyx: -------------------------------------------------------------------------------- 1 | import numpy as __np 2 | from numpy cimport ndarray 3 | 4 | cdef extern from "derived_type.h": 5 | cdef extern void allocate_mytype(void *ptr) 6 | cdef extern void destroy_mytype(void *ptr) 7 | 8 | cdef extern void mytype_set_a(void *ptr, double *a) 9 | cdef extern void mytype_get_a(void *ptr, double *a) 10 | cdef extern void mytype_set_arr(void *ptr, int *arr_size, double *arr) 11 | cdef extern void mytype_get_arr_size(void *ptr, int *arr_size, bint *alloc) 12 | cdef extern void mytype_get_arr(void *ptr, int *arrs_size, double *arr) 13 | 14 | cdef extern void mytype_add2a_wrapper(void *ptr, double *b) 15 | cdef extern void mytype_printarr_wrapper(void *ptr) 16 | cdef extern void mytype_sumarr_wrapper(void *ptr, double *result) 17 | 18 | cdef class __mytype: 19 | """type(mytype) 20 | real(c_double) :: a 21 | real(c_double), allocatable :: arr(:) 22 | contains 23 | procedure :: add2a 24 | procedure :: sumarr 25 | procedure :: printarr 26 | end type""" 27 | cdef void *_ptr 28 | cdef bint _destroy 29 | 30 | def __cinit__(self, bint alloc = True): 31 | if alloc: 32 | allocate_mytype(&self._ptr) 33 | self._destroy = True 34 | else: 35 | self._destroy = False 36 | 37 | def __dealloc__(self): 38 | if self._destroy: 39 | destroy_mytype(&self._ptr) 40 | self._ptr = NULL 41 | 42 | property a: 43 | def __get__(self): 44 | cdef double a 45 | mytype_get_a(&self._ptr, &a) 46 | return a 47 | def __set__(self,double a): 48 | mytype_set_a(&self._ptr, &a) 49 | 50 | property arr: 51 | def __get__(self): 52 | cdef int arr_size 53 | cdef bint alloc 54 | mytype_get_arr_size(&self._ptr, &arr_size, &alloc) 55 | cdef ndarray arr = __np.empty(arr_size, __np.double) 56 | if alloc: 57 | mytype_get_arr(&self._ptr, &arr_size, arr.data) 58 | return arr 59 | 60 | def __set__(self, ndarray arr): 61 | arr = arr.astype(__np.double) 62 | cdef int arr_size = len(arr) 63 | mytype_set_arr(&self._ptr, &arr_size, arr.data) 64 | 65 | def add2a(self, double b): 66 | """add2a(self, b) 67 | 68 | Parameters 69 | ---------- 70 | b : float 71 | 72 | Returns 73 | ------- 74 | None 75 | """ 76 | mytype_add2a_wrapper(&self._ptr, &b) 77 | 78 | def printarr(self): 79 | """printarr(self) 80 | 81 | Parameters 82 | ---------- 83 | None 84 | 85 | Returns 86 | ------- 87 | None 88 | """ 89 | mytype_printarr_wrapper(&self._ptr) 90 | 91 | def sumarr(self): 92 | """sumarr(self) 93 | 94 | Parameters 95 | ---------- 96 | None 97 | 98 | Returns 99 | ------- 100 | None 101 | """ 102 | cdef double result 103 | mytype_sumarr_wrapper(&self._ptr, &result) 104 | return result 105 | 106 | cdef class __derived_type: 107 | """module module_variable 108 | type mytype 109 | end module""" 110 | property mytype: 111 | def __get__(self): 112 | return __mytype 113 | 114 | derived_type = __derived_type() 115 | 116 | -------------------------------------------------------------------------------- /src/derived_type/derived_type_wrapper.f90: -------------------------------------------------------------------------------- 1 | module derived_type_wrapper 2 | use iso_c_binding 3 | implicit none 4 | 5 | contains 6 | 7 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 8 | !!! allocator and destroyer !!! 9 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 10 | 11 | subroutine allocate_Mytype(ptr) bind(c) 12 | use derived_type, only: Mytype 13 | type(c_ptr), intent(out) :: ptr 14 | type(Mytype), pointer :: my 15 | allocate(my) 16 | ptr = c_loc(my) 17 | end subroutine 18 | 19 | subroutine destroy_Mytype(ptr) bind(c) 20 | use derived_type, only: Mytype 21 | type(c_ptr), intent(in) :: ptr 22 | type(Mytype), pointer :: my 23 | call c_f_pointer(ptr, my) 24 | deallocate(my) 25 | end subroutine 26 | 27 | !!!!!!!!!!!!!!!!!!!!!!!!!!! 28 | !!! getters and setters !!! 29 | !!!!!!!!!!!!!!!!!!!!!!!!!!! 30 | 31 | subroutine mytype_set_a(ptr, var) bind(c) 32 | use derived_type, only: dtype => Mytype 33 | type(c_ptr), intent(in) :: ptr 34 | real(c_double), intent(in) :: var 35 | type(dtype), pointer :: t 36 | call c_f_pointer(ptr, t) 37 | t%a = var 38 | end subroutine 39 | 40 | subroutine mytype_get_a(ptr, a) bind(c) 41 | use derived_type, only: Mytype 42 | type(c_ptr), intent(in) :: ptr 43 | real(c_double), intent(out) :: a 44 | type(Mytype), pointer :: my 45 | call c_f_pointer(ptr, my) 46 | a = my%a 47 | end subroutine 48 | 49 | subroutine mytype_set_arr(ptr, arr_size, arr) bind(c) 50 | use derived_type, only: Mytype 51 | type(c_ptr), intent(in) :: ptr 52 | integer(c_int), intent(in) :: arr_size 53 | real(c_double), intent(in) :: arr(arr_size) 54 | 55 | type(Mytype), pointer :: my 56 | call c_f_pointer(ptr, my) 57 | 58 | if (allocated(my%arr)) then 59 | deallocate(my%arr) 60 | endif 61 | allocate(my%arr(arr_size)) 62 | my%arr = arr 63 | end subroutine 64 | 65 | subroutine mytype_get_arr_size(ptr, arr_size, alloc) bind(c) 66 | use derived_type, only: Mytype 67 | type(c_ptr), intent(in) :: ptr 68 | integer(c_int), intent(out) :: arr_size 69 | logical(c_bool), intent(out) :: alloc 70 | 71 | type(Mytype), pointer :: my 72 | call c_f_pointer(ptr, my) 73 | 74 | if (allocated(my%arr)) then 75 | arr_size = size(my%arr) 76 | alloc = .true. 77 | else 78 | arr_size = 0 79 | alloc = .false. 80 | endif 81 | 82 | end subroutine 83 | 84 | subroutine mytype_get_arr(ptr, arr_size, arr) bind(c) 85 | use derived_type, only: Mytype 86 | type(c_ptr), intent(in) :: ptr 87 | integer(c_int), intent(in) :: arr_size 88 | real(c_double), intent(out) :: arr(arr_size) 89 | 90 | type(Mytype), pointer :: my 91 | call c_f_pointer(ptr, my) 92 | 93 | arr = my%arr 94 | end subroutine 95 | 96 | !!!!!!!!!!!!!!!!!!!!!!!!!!!! 97 | !!! subroutine wrappers !!! 98 | !!!!!!!!!!!!!!!!!!!!!!!!!!!! 99 | 100 | subroutine mytype_add2a_wrapper(ptr, b) bind(c) 101 | use derived_type, only: Mytype 102 | type(c_ptr), intent(in) :: ptr 103 | real(c_double), intent(in) :: b 104 | 105 | type(mytype), pointer :: my 106 | call c_f_pointer(ptr, my) 107 | 108 | call my%add2a(b) 109 | end subroutine 110 | 111 | subroutine mytype_printarr_wrapper(ptr) bind(c) 112 | use derived_type, only: Mytype 113 | type(c_ptr), intent(in) :: ptr 114 | 115 | type(Mytype), pointer :: my 116 | call c_f_pointer(ptr, my) 117 | call my%printarr() 118 | end subroutine 119 | 120 | subroutine mytype_sumarr_wrapper(ptr, result) bind(c) 121 | use derived_type, only: Mytype 122 | type(c_ptr), intent(in) :: ptr 123 | real(c_double), intent(out) :: result 124 | 125 | type(Mytype), pointer :: my 126 | call c_f_pointer(ptr, my) 127 | 128 | call my%sumarr(result) 129 | end subroutine 130 | 131 | end module -------------------------------------------------------------------------------- /src/dtype1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name dtype1) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/dtype1/dtype1.f90: -------------------------------------------------------------------------------- 1 | module dtype1 2 | implicit none 3 | 4 | type :: myothertype 5 | integer :: a1 = 10 6 | end type 7 | 8 | type :: mytype 9 | integer :: a1 10 | integer, allocatable :: arr3(:) 11 | type(myothertype) :: my 12 | type(myothertype) :: my1(3) 13 | end type 14 | 15 | ! Purpose of this simple type is to consider how to implement 16 | ! getters and setters for cython wrappers. 17 | 18 | ! looks like they are almost identical to getters and setters for 19 | ! fortran modules, except a pointer is passed around. 20 | 21 | end module -------------------------------------------------------------------------------- /src/dtype1/dtype1.pyx: -------------------------------------------------------------------------------- 1 | from libc.stdlib cimport malloc, free 2 | from numpy cimport import_array, ndarray, int8_t, int16_t, int32_t, int64_t 3 | import numpy as np 4 | cimport dtype1_pxd 5 | 6 | cdef class __dtype1: 7 | property mytype: 8 | def __get__(self): 9 | return __dtype1_mytype 10 | property myothertype: 11 | def __get__(self): 12 | return __dtype1_myothertype 13 | 14 | cdef class __dtype1_mytype: 15 | cdef void *_ptr 16 | cdef bint _destroy 17 | 18 | def __cinit__(self, bint alloc = True): 19 | if alloc: 20 | dtype1_pxd.dtype1_alloc_mytype(&self._ptr) 21 | self._destroy = True 22 | else: 23 | self._destroy = False 24 | 25 | def __dealloc__(self): 26 | if self._destroy: 27 | dtype1_pxd.dtype1_dealloc_mytype(&self._ptr) 28 | 29 | property a1: 30 | def __get__(self): 31 | cdef int32_t var 32 | dtype1_pxd.dtype1_mytype_get_a1(&self._ptr, &var) 33 | return var 34 | def __set__(self,int32_t var): 35 | dtype1_pxd.dtype1_mytype_set_a1(&self._ptr, &var) 36 | 37 | property arr3: 38 | def __get__(self): 39 | cdef int32_t dim1 40 | dtype1_pxd.dtype1_mytype_get_arr3_size(&self._ptr, &dim1) 41 | cdef ndarray var = np.empty((dim1,), np.int32) 42 | dtype1_pxd.dtype1_mytype_get_arr3(&self._ptr, &dim1, var.data) 43 | return var 44 | def __set__(self, ndarray[int32_t, ndim=1] var): 45 | cdef int32_t dim1 = var.shape[0] 46 | dtype1_pxd.dtype1_mytype_set_arr3(&self._ptr, &dim1, var.data) 47 | 48 | property my: 49 | def __get__(self): 50 | cdef void *ptr1; 51 | dtype1_pxd.dtype1_mytype_my_get(&self._ptr, &ptr1) 52 | var = __dtype1_myothertype(alloc=False) 53 | var._ptr = ptr1; 54 | return var 55 | def __set__(self, __dtype1_myothertype t1): 56 | dtype1_pxd.dtype1_mytype_my_set(&self._ptr, &t1._ptr) 57 | 58 | property my1: 59 | def __get__(self): 60 | cdef void **ptr1 = malloc(3 * sizeof(void *)) 61 | if not ptr1: 62 | raise Exception("malloc failed!") 63 | dtype1_pxd.dtype1_mytype_my1_get(&self._ptr, ptr1) 64 | cdef ndarray var1 = np.empty((3,), dtype = object, order='F') 65 | for i in range(3): 66 | t1 = __dtype1_myothertype(alloc=False) 67 | t1._ptr = ptr1[i] 68 | var1[i] = t1 69 | free(ptr1) 70 | cdef ndarray var = np.reshape(var1, (3), order="F") 71 | return var 72 | def __set__(self, ndarray[object, ndim=1] var_in): 73 | cdef ndarray var = np.asfortranarray(var_in) 74 | cdef ndarray var1 = np.reshape(var,(3),order="F") 75 | cdef ndarray d = np.empty((var.ndim,), np.int64) 76 | for i in range(var.ndim): 77 | d[i] = var.shape[i] 78 | if (3,) != tuple(d): 79 | raise ValueError("my1 has wrong shape") 80 | for i in range(3): 81 | if type(var1[i]) != __dtype1_myothertype: 82 | raise ValueError("my1 has wrong type!") 83 | cdef void **ptr1 = malloc(3 * sizeof(void *)) 84 | if not ptr1: 85 | raise Exception("malloc failed!") 86 | cdef __dtype1_myothertype tmp 87 | for i in range(3): 88 | tmp = var1[i] 89 | ptr1[i] = tmp._ptr 90 | dtype1_pxd.dtype1_mytype_my1_set(&self._ptr, ptr1) 91 | free(ptr1) 92 | 93 | 94 | cdef class __dtype1_myothertype: 95 | cdef void *_ptr 96 | cdef bint _destroy 97 | 98 | def __cinit__(self, bint alloc = True): 99 | if alloc: 100 | dtype1_pxd.dtype1_alloc_myothertype(&self._ptr) 101 | self._destroy = True 102 | else: 103 | self._destroy = False 104 | 105 | def __dealloc__(self): 106 | if self._destroy: 107 | dtype1_pxd.dtype1_dealloc_myothertype(&self._ptr) 108 | 109 | property a1: 110 | def __get__(self): 111 | cdef int32_t var 112 | dtype1_pxd.dtype1_mytype_get_a1(&self._ptr, &var) 113 | return var 114 | def __set__(self,int32_t var): 115 | dtype1_pxd.dtype1_mytype_set_a1(&self._ptr, &var) 116 | 117 | 118 | dtype1 = __dtype1() 119 | 120 | -------------------------------------------------------------------------------- /src/dtype1/dtype1_pxd.pxd: -------------------------------------------------------------------------------- 1 | 2 | from libc.stdint cimport int8_t, int16_t, int32_t, int64_t 3 | cdef extern from "dtype1_wrapper.h": 4 | cdef void dtype1_alloc_mytype(void *ptr); 5 | cdef void dtype1_dealloc_mytype(void *ptr); 6 | cdef void dtype1_alloc_myothertype(void *ptr); 7 | cdef void dtype1_dealloc_myothertype(void *ptr); 8 | 9 | cdef void dtype1_mytype_get_a1(void *ptr, int32_t *var); 10 | cdef void dtype1_mytype_set_a1(void *ptr, int32_t *var); 11 | 12 | cdef void dtype1_mytype_get_arr3_size(void *ptr, int32_t *dim1); 13 | cdef void dtype1_mytype_get_arr3(void *ptr, int32_t *dim1, int32_t *var); 14 | cdef void dtype1_mytype_set_arr3(void *ptr, int32_t *dim1, int32_t *var); 15 | 16 | cdef void dtype1_mytype_my_get(void *ptr, void *ptr1); 17 | cdef void dtype1_mytype_my_set(void *ptr, void *ptr1); 18 | 19 | cdef void dtype1_mytype_my1_get(void *ptr, void **ptr1); 20 | cdef void dtype1_mytype_my1_set(void *ptr, void **ptr1); 21 | 22 | cdef void dtype1_myothertype_get_a1(void *ptr, int32_t *var); 23 | cdef void dtype1_myothertype_set_a1(void *ptr, int32_t *var); 24 | 25 | -------------------------------------------------------------------------------- /src/dtype1/dtype1_wrapper.f90: -------------------------------------------------------------------------------- 1 | module dtype1_wrapper 2 | use iso_c_binding 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine dtype1_alloc_mytype(ptr) bind(c) 8 | use dtype1, only: dtype => mytype 9 | type(c_ptr), intent(out) :: ptr 10 | type(dtype), pointer :: t 11 | allocate(t) 12 | ptr = c_loc(t) 13 | end subroutine 14 | 15 | subroutine dtype1_dealloc_mytype(ptr) bind(c) 16 | use dtype1, only: dtype => mytype 17 | type(c_ptr), intent(in) :: ptr 18 | type(dtype), pointer :: t 19 | call c_f_pointer(ptr, t) 20 | deallocate(t) 21 | end subroutine 22 | 23 | subroutine dtype1_mytype_get_a1(ptr, var) bind(c) 24 | use dtype1, only: dtype => mytype 25 | type(c_ptr), intent(in) :: ptr 26 | integer(c_int32_t), intent(out) :: var 27 | type(dtype), pointer :: t 28 | call c_f_pointer(ptr, t) 29 | var = t%a1 30 | end subroutine 31 | 32 | subroutine dtype1_mytype_set_a1(ptr, var) bind(c) 33 | use dtype1, only: dtype => mytype 34 | type(c_ptr), intent(in) :: ptr 35 | integer(c_int32_t), intent(in) :: var 36 | type(dtype), pointer :: t 37 | call c_f_pointer(ptr, t) 38 | t%a1 = var 39 | end subroutine 40 | 41 | subroutine dtype1_mytype_get_arr3_size(ptr, dim1) bind(c) 42 | use dtype1, only: dtype => mytype 43 | type(c_ptr), intent(in) :: ptr 44 | integer(c_int32_t), intent(out) :: dim1 45 | type(dtype), pointer :: t 46 | call c_f_pointer(ptr, t) 47 | if (allocated(t%arr3)) then 48 | dim1 = size(t%arr3,1) 49 | else 50 | dim1 = 0 51 | endif 52 | end subroutine 53 | 54 | subroutine dtype1_mytype_get_arr3(ptr, dim1, var) bind(c) 55 | use dtype1, only: dtype => mytype 56 | type(c_ptr), intent(in) :: ptr 57 | integer(c_int32_t), intent(in) :: dim1 58 | integer(c_int32_t), intent(out) :: var(dim1) 59 | type(dtype), pointer :: t 60 | call c_f_pointer(ptr, t) 61 | if (allocated(t%arr3)) then 62 | var = t%arr3 63 | endif 64 | end subroutine 65 | 66 | subroutine dtype1_mytype_set_arr3(ptr, dim1, var) bind(c) 67 | use dtype1, only: dtype => mytype 68 | type(c_ptr), intent(in) :: ptr 69 | integer(c_int32_t), intent(in) :: dim1 70 | integer(c_int32_t), intent(in) :: var(dim1) 71 | type(dtype), pointer :: t 72 | call c_f_pointer(ptr, t) 73 | if (allocated(t%arr3)) deallocate(t%arr3) 74 | allocate(t%arr3(dim1)) 75 | t%arr3 = var 76 | end subroutine 77 | 78 | subroutine dtype1_mytype_my_get(ptr, ptr1) bind(c) 79 | use dtype1, only: dtype => mytype 80 | type(c_ptr), intent(in) :: ptr 81 | type(c_ptr), intent(out) :: ptr1 82 | type(dtype), pointer :: t 83 | call c_f_pointer(ptr, t) 84 | ptr1 = c_loc(t%my) 85 | end subroutine 86 | 87 | subroutine dtype1_mytype_my_set(ptr, ptr1) bind(c) 88 | use dtype1, only: dtype => mytype 89 | use dtype1, only: dtype_1 => myothertype 90 | type(c_ptr), intent(in) :: ptr 91 | type(c_ptr), intent(in) :: ptr1 92 | type(dtype), pointer :: t 93 | type(dtype_1), pointer :: t1 94 | call c_f_pointer(ptr, t) 95 | call c_f_pointer(ptr1, t1) 96 | t%my = t1 97 | end subroutine 98 | 99 | subroutine dtype1_mytype_my1_get(ptr, ptr1) bind(c) 100 | use dtype1, only: dtype => mytype 101 | use dtype1, only: dtype_1 => myothertype 102 | type(c_ptr), intent(in) :: ptr 103 | type(c_ptr), intent(out), target :: ptr1(3) 104 | integer :: i 105 | type(dtype_1), pointer :: t1_p(:) 106 | type(dtype), pointer :: t 107 | call c_f_pointer(ptr, t) 108 | t1_p(1:3) => t%my1 109 | do i = 1,3 110 | ptr1(i) = c_loc(t1_p(i)) 111 | enddo 112 | end subroutine 113 | 114 | subroutine dtype1_mytype_my1_set(ptr, ptr1) bind(c) 115 | use dtype1, only: dtype => mytype 116 | use dtype1, only: dtype_1 => myothertype 117 | type(c_ptr), intent(in) :: ptr 118 | type(c_ptr), intent(in) :: ptr1(3) 119 | integer :: i 120 | type(dtype), pointer :: t 121 | type(dtype_1), pointer :: t1 122 | type(dtype_1), pointer :: t2(:) 123 | call c_f_pointer(ptr, t) 124 | t2(1:3) => t%my1 125 | do i = 1,3 126 | call c_f_pointer(ptr1(i), t1) 127 | t2(i) = t1 128 | enddo 129 | end subroutine 130 | 131 | subroutine dtype1_alloc_myothertype(ptr) bind(c) 132 | use dtype1, only: dtype => myothertype 133 | type(c_ptr), intent(out) :: ptr 134 | type(dtype), pointer :: t 135 | allocate(t) 136 | ptr = c_loc(t) 137 | end subroutine 138 | 139 | subroutine dtype1_dealloc_myothertype(ptr) bind(c) 140 | use dtype1, only: dtype => myothertype 141 | type(c_ptr), intent(in) :: ptr 142 | type(dtype), pointer :: t 143 | call c_f_pointer(ptr, t) 144 | deallocate(t) 145 | end subroutine 146 | 147 | subroutine dtype1_myothertype_get_a1(ptr, var) bind(c) 148 | use dtype1, only: dtype => mytype 149 | type(c_ptr), intent(in) :: ptr 150 | integer(c_int32_t), intent(out) :: var 151 | type(dtype), pointer :: t 152 | call c_f_pointer(ptr, t) 153 | var = t%a1 154 | end subroutine 155 | 156 | subroutine dtype1_myothertype_set_a1(ptr, var) bind(c) 157 | use dtype1, only: dtype => myothertype 158 | type(c_ptr), intent(in) :: ptr 159 | integer(c_int32_t), intent(in) :: var 160 | type(dtype), pointer :: t 161 | call c_f_pointer(ptr, t) 162 | t%a1 = var 163 | end subroutine 164 | 165 | 166 | 167 | end module -------------------------------------------------------------------------------- /src/dtype1/dtype1_wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void dtype1_alloc_mytype(void *ptr); 4 | void dtype1_dealloc_mytype(void *ptr); 5 | void dtype1_alloc_myothertype(void *ptr); 6 | void dtype1_dealloc_myothertype(void *ptr); 7 | 8 | void dtype1_mytype_get_a1(void *ptr, int32_t *var); 9 | void dtype1_mytype_set_a1(void *ptr, int32_t *var); 10 | 11 | void dtype1_mytype_get_arr3_size(void *ptr, int32_t *dim1); 12 | void dtype1_mytype_get_arr3(void *ptr, int32_t *dim1, int32_t *var); 13 | void dtype1_mytype_set_arr3(void *ptr, int32_t *dim1, int32_t *var); 14 | 15 | void dtype1_mytype_my_get(void *ptr, void *ptr1); 16 | void dtype1_mytype_my_set(void *ptr, void *ptr1); 17 | 18 | void dtype1_mytype_my1_get(void *ptr, void **ptr1); 19 | void dtype1_mytype_my1_set(void *ptr, void **ptr1); 20 | 21 | void dtype1_myothertype_get_a1(void *ptr, int32_t *var); 22 | void dtype1_myothertype_set_a1(void *ptr, int32_t *var); 23 | 24 | -------------------------------------------------------------------------------- /src/module_variable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name module_variable) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/module_variable/module_variable.f90: -------------------------------------------------------------------------------- 1 | module module_variable 2 | implicit none 3 | public 4 | 5 | ! ignore if private 6 | ! read-only if protected or parameter 7 | ! read-write otherwise 8 | 9 | integer :: a1 10 | integer, parameter :: a2 = 3 11 | integer, allocatable :: a3 12 | 13 | integer :: arr1(8) 14 | integer, parameter :: arr2(3) = [1,2,3] 15 | integer, allocatable :: arr3(:) 16 | 17 | ! characters. Work in progress 18 | 19 | character(len=20) :: str1 20 | character(len=:), allocatable :: str2 21 | 22 | character(len=20) :: str3(3) 23 | character(len=20), allocatable :: str4(:) 24 | 25 | 26 | ! to be investigated: 27 | character(len=20), parameter :: str1_2 = "Hello2" 28 | character(len=*), parameter :: str1_3 = "Hello3" 29 | character(len=*), parameter :: str5(3) = ["hi",'hi','hi'] 30 | 31 | end module -------------------------------------------------------------------------------- /src/module_variable/module_variable.pyx: -------------------------------------------------------------------------------- 1 | 2 | from numpy cimport import_array, ndarray, int8_t, int16_t, int32_t, int64_t 3 | import numpy as np 4 | cimport module_variable_pxd 5 | 6 | ################# 7 | ### utilities ### 8 | ################# 9 | cdef pystring2cstring(str pystring): 10 | # add a null c char, and convert to byes 11 | cdef bytes cstring = (pystring+'\0').encode('utf-8') 12 | return cstring 13 | 14 | 15 | cdef class __module_variable: 16 | 17 | property a1: 18 | def __get__(self): 19 | cdef int32_t var 20 | module_variable_pxd.module_variable_get_a1(&var) 21 | return var 22 | def __set__(self, int32_t var): 23 | module_variable_pxd.module_variable_set_a1(&var) 24 | 25 | property a2: 26 | def __get__(self): 27 | cdef int32_t var 28 | module_variable_pxd.module_variable_get_a2(&var) 29 | return var 30 | 31 | property a3: 32 | def __get__(self): 33 | cdef int32_t var 34 | module_variable_pxd.module_variable_get_a3(&var) 35 | return var 36 | def __set__(self, int32_t var): 37 | module_variable_pxd.module_variable_set_a3(&var) 38 | 39 | # template is 40 | # 41 | # property : 42 | # def __get__(self): 43 | # cdef var 44 | # .(&var) 45 | # return var 46 | # def __set__(self, var): 47 | # .(&var) 48 | 49 | property arr1: 50 | def __get__(self): 51 | cdef ndarray var = np.empty((8,), np.int32) 52 | module_variable_pxd.module_variable_get_arr1(var.data) 53 | return var 54 | def __set__(self, ndarray[int32_t, ndim=1] var): 55 | cdef ndarray d = np.empty((var.ndim,), np.int64) 56 | for i in range(var.ndim): 57 | d[i] = var.shape[i] 58 | if (8,) != tuple(d): 59 | raise ValueError("arr1 has wrong shape") 60 | module_variable_pxd.module_variable_set_arr1(var.data) 61 | 62 | property arr2: 63 | def __get__(self): 64 | cdef ndarray var = np.empty((3,), np.int32) 65 | module_variable_pxd.module_variable_get_arr2(var.data) 66 | return var 67 | 68 | property arr3: 69 | def __get__(self): 70 | cdef ndarray d = np.empty((1,), np.int64) 71 | module_variable_pxd.module_variable_get_arr3_size( d.data) 72 | cdef ndarray var = np.empty(tuple(d), np.int32) 73 | module_variable_pxd.module_variable_get_arr3( d.data, var.data) 74 | return var 75 | def __set__(self, ndarray[int32_t, ndim=1] var): 76 | cdef ndarray d = np.empty((var.ndim,), np.int64) 77 | for i in range(var.ndim): 78 | d[i] = var.shape[i] 79 | module_variable_pxd.module_variable_set_arr3( d.data, var.data) 80 | 81 | property str1: 82 | def __get__(self): 83 | cdef char var[20+1] 84 | module_variable_pxd.module_variable_get_str1(var) 85 | return var.decode('utf-8') 86 | def __set__(self, str var): 87 | cdef int32_t len1 = len(var) 88 | cdef bytes var_b = pystring2cstring(var) 89 | cdef char *var_c = var_b 90 | module_variable_pxd.module_variable_set_str1(&len1, var_c) 91 | 92 | property str2: 93 | def __get__(self): 94 | cdef int32_t len1 95 | module_variable_pxd.module_variable_get_str2_len(&len1) 96 | cdef ndarray var = np.zeros((),dtype=np.dtype(('S', len1)),order='F') 97 | module_variable_pxd.module_variable_get_str2(&len1, var.data) 98 | return var.item().decode() 99 | def __set__(self, str var): 100 | cdef int32_t len1 = len(var) 101 | cdef bytes var_b = pystring2cstring(var) 102 | cdef char *var_c = var_b 103 | module_variable_pxd.module_variable_set_str2(&len1, var_c) 104 | 105 | 106 | property str4: 107 | def __get__(self): 108 | cdef int32_t dim1 109 | cdef int32_t len1 = 20; 110 | module_variable_pxd.module_variable_get_str4_size(&dim1) 111 | cdef ndarray var = np.empty((dim1),dtype=np.dtype(('S', len1)),order='F') 112 | module_variable_pxd.module_variable_get_str4(&len1, &dim1, var.data) 113 | return var 114 | def __set__(self, ndarray var1): 115 | if var1.dtype != np.dtype(('S', 20)): 116 | raise ValueError("str4 must be type") 117 | 118 | # print(var) 119 | cdef int32_t dim1 = var1.shape[0] 120 | cdef int32_t len1 = 20; 121 | # cdef ndarray var1 = np.empty((dim1),dtype=np.dtype(('S', len1)),order='F') 122 | # var1[0] = "{:20}".format("hello") 123 | # var1[1] = "{:20}".format("nias") 124 | module_variable_pxd.module_variable_set_str4(&len1, &dim1, var1.data) 125 | 126 | 127 | module_variable = __module_variable() 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/module_variable/module_variable_pxd.pxd: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by the LFortran compiler. 2 | # Editing by hand is discouraged. 3 | 4 | from libc.stdint cimport int8_t, int16_t, int32_t, int64_t 5 | cdef extern from "module_variable_wrapper.h": 6 | cdef void module_variable_get_a1(int32_t *var) 7 | cdef void module_variable_set_a1(int32_t *var) 8 | 9 | cdef void module_variable_get_a2(int32_t *var) 10 | 11 | cdef void module_variable_get_a3(int32_t *var) 12 | cdef void module_variable_set_a3(int32_t *var) 13 | 14 | cdef void module_variable_get_arr1(int32_t *var) 15 | cdef void module_variable_set_arr1(int32_t *var) 16 | 17 | cdef void module_variable_get_arr2(int32_t *var) 18 | 19 | cdef void module_variable_get_arr3_size(int64_t *d) 20 | cdef void module_variable_get_arr3(int64_t *d, int32_t *var) 21 | cdef void module_variable_set_arr3(int64_t *d, int32_t *var) 22 | 23 | cdef void module_variable_get_str1(char *var) 24 | cdef void module_variable_set_str1(int32_t *len1, char *var) 25 | 26 | cdef void module_variable_get_str2_len(int32_t *len1) 27 | cdef void module_variable_get_str2(int32_t *len1, char *var) 28 | cdef void module_variable_set_str2(int32_t *len1, char *var) 29 | 30 | cdef void module_variable_get_str4_size(int32_t *dim1) 31 | cdef void module_variable_get_str4(int32_t *len1, int32_t *dim1, char *var) 32 | cdef void module_variable_set_str4(int32_t *len1, int32_t *dim1, char *var) -------------------------------------------------------------------------------- /src/module_variable/module_variable_wrapper.f90: -------------------------------------------------------------------------------- 1 | module module_variable_wrapper 2 | use iso_c_binding 3 | implicit none 4 | 5 | contains 6 | 7 | !!!!!!!!!!!!!!!!! 8 | !!! variables !!! 9 | !!!!!!!!!!!!!!!!! 10 | 11 | ! integer :: a1 12 | subroutine module_variable_get_a1(var) bind(c) 13 | use module_variable, only: a => a1 14 | integer(c_int32_t), intent(out) :: var 15 | var = a 16 | end subroutine 17 | 18 | subroutine module_variable_set_a1(var) bind(c) 19 | use module_variable, only: a => a1 20 | integer(c_int32_t), intent(in) :: var 21 | a = var 22 | end subroutine 23 | 24 | ! integer, parameter :: a2 = 3 25 | subroutine module_variable_get_a2(var) bind(c) 26 | use module_variable, only: a => a2 27 | integer(c_int32_t), intent(out) :: var 28 | var = a 29 | end subroutine 30 | 31 | ! integer, allocatable :: a3 32 | subroutine module_variable_get_a3(var) bind(c) 33 | use module_variable, only: a => a3 34 | integer(c_int32_t), intent(out) :: var 35 | if (allocated(a)) then 36 | var = a 37 | endif 38 | end subroutine 39 | 40 | subroutine module_variable_set_a3(var) bind(c) 41 | use module_variable, only: a => a3 42 | integer(c_int32_t), intent(in) :: var 43 | if (.not. allocated(a)) then 44 | allocate(a) 45 | endif 46 | a = var 47 | end subroutine 48 | 49 | ! We can wrap any intrinsic type with the 50 | ! following template 51 | 52 | ! subroutine _get_(var) bind(c) 53 | ! use , only: a => 54 | ! (), intent(out) :: var 55 | ! if (allocated(a)) then ! IF ALLOCATBLE 56 | ! var = a ! indented IF ALLOCATBLE 57 | ! endif ! IF ALLOCATBLE 58 | ! end subroutine 59 | ! 60 | ! IF NOT PARAMETER, we make setter 61 | ! subroutine _set_(var) bind(c) 62 | ! use , only: a => 63 | ! (), intent(in) :: var 64 | ! if (.not. allocated(a)) then ! ] 65 | ! allocate(a) ! ] ONLY if allocatable 66 | ! endif ! ] 67 | ! a = var 68 | ! end subroutine 69 | 70 | !!!!!!!!!!!!!! 71 | !!! arrays !!! 72 | !!!!!!!!!!!!!! 73 | 74 | ! integer :: arr1(8) 75 | subroutine module_variable_get_arr1(var) bind(c) 76 | use module_variable, only: a => arr1 77 | integer(c_int32_t), intent(out) :: var(8) 78 | var = a 79 | end subroutine 80 | 81 | subroutine module_variable_set_arr1(var) bind(c) 82 | use module_variable, only: a => arr1 83 | integer(c_int32_t), intent(in) :: var(8) 84 | a = var 85 | end subroutine 86 | 87 | ! integer, parameter :: arr2(3) = [1,2,3] 88 | subroutine module_variable_get_arr2(var) bind(c) 89 | use module_variable, only: a => arr2 90 | integer(c_int32_t), intent(out) :: var(3) 91 | var = a 92 | end subroutine 93 | 94 | ! fixed-sized arrays have the following template 95 | ! 96 | ! subroutine _get_(var) bind(c) 97 | ! use , only: a => 98 | ! (), intent(out) :: var() 99 | ! var = a 100 | ! end subroutine 101 | ! 102 | ! subroutine _set_(var) bind(c) 103 | ! use , only: a => 104 | ! (), intent(in) :: var() 105 | ! a = var 106 | ! end subroutine 107 | 108 | ! integer, allocatable :: arr3(:) 109 | subroutine module_variable_get_arr3_size(d) bind(c) 110 | use module_variable, only: a => arr3 111 | integer(c_int64_t), intent(out) :: d(1) 112 | if (allocated(a)) then 113 | d = shape(a) 114 | else 115 | d = 0 116 | endif 117 | end subroutine 118 | 119 | subroutine module_variable_get_arr3(d, var) bind(c) 120 | use module_variable, only: a => arr3 121 | integer(c_int64_t), intent(in) :: d(1) 122 | integer(c_int32_t), intent(out) :: var(d(1)) 123 | if (allocated(a)) then 124 | var = a 125 | endif 126 | end subroutine 127 | 128 | subroutine module_variable_set_arr3(d, var) bind(c) 129 | use module_variable, only: a => arr3 130 | integer(c_int64_t), intent(in) :: d(1) 131 | integer(c_int32_t), intent(in) :: var(d(1)) 132 | if (allocated(a)) deallocate(a) 133 | allocate(a(d(1))) 134 | a = var 135 | end subroutine 136 | 137 | ! allocatable arrays have the above unique template 138 | 139 | ! integer, real 140 | ! any kind 141 | ! allocatable, parameter, default 142 | ! 0-dimension or n-dimension 143 | ! 144 | ! Fit into the above 3 templates 145 | 146 | 147 | 148 | ! work in progress... 149 | 150 | !!!!!!!!!!!!!!!!!! 151 | !!! characters !!! 152 | !!!!!!!!!!!!!!!!!! 153 | 154 | subroutine module_variable_get_str1(var) bind(c) 155 | use module_variable, only: a => str1 156 | character(kind=c_char), intent(out) :: var(20+1) 157 | call copy_string_ftoc(a, var) 158 | end subroutine 159 | 160 | subroutine module_variable_set_str1(len1, var) bind(c) 161 | use module_variable, only: a => str1 162 | integer(c_int32_t), intent(in) :: len1 163 | character(kind=c_char), intent(in) :: var(len1+1) 164 | call copy_string_ctof(var, a) 165 | end subroutine 166 | 167 | subroutine module_variable_get_str2_len(len1) bind(c) 168 | use module_variable, only: a => str2 169 | integer(c_int32_t), intent(out) :: len1 170 | if (allocated(a)) then 171 | len1 = len(a) 172 | else 173 | len1 = 0 174 | endif 175 | end subroutine 176 | 177 | subroutine module_variable_get_str2(len1, var) bind(c) 178 | use module_variable, only: a => str2 179 | integer(c_int32_t), intent(in) :: len1 180 | character(kind=c_char), intent(out) :: var(len1+1) 181 | if (allocated(a)) then 182 | call copy_string_ftoc(a, var) 183 | endif 184 | end subroutine 185 | 186 | subroutine module_variable_set_str2(len1, var) bind(c) 187 | use module_variable, only: a => str2 188 | integer(c_int32_t), intent(in) :: len1 189 | character(kind=c_char), intent(in) :: var(len1+1) 190 | if (allocated(a)) deallocate(a) 191 | allocate(character(len1) :: a) 192 | call copy_string_ctof(var, a) 193 | end subroutine 194 | 195 | subroutine module_variable_get_str4_size(dim1) bind(c) 196 | use module_variable, only: str4 197 | integer(c_int32_t), intent(out) :: dim1 198 | if (allocated(str4)) then 199 | dim1 = size(str4,1) 200 | else 201 | dim1 = 0 202 | endif 203 | end subroutine 204 | 205 | subroutine module_variable_get_str4(len1, dim1, var) bind(c) 206 | use module_variable, only: str4 207 | integer(c_int32_t), intent(in) :: len1 208 | integer(c_int32_t), intent(in) :: dim1 209 | character(kind=c_char), intent(out) :: var(len1*dim1+1) 210 | integer :: k, i1, i2 211 | 212 | if (allocated(str4)) then 213 | do i2 = 1,dim1 214 | do i1 = 1,len1 215 | k = (i2-1)*len1+i1 216 | var(k) = str4(i2)(i1:i1) 217 | enddo 218 | enddo 219 | endif 220 | end subroutine 221 | 222 | subroutine module_variable_set_str4(len1, dim1, var) bind(c) 223 | use module_variable, only: str4 224 | integer(c_int32_t), intent(in) :: len1 225 | integer(c_int32_t), intent(in) :: dim1 226 | character(kind=c_char), intent(in) :: var(len1*dim1) 227 | integer :: k, i1, i 228 | 229 | if (allocated(str4)) deallocate(str4) 230 | allocate(str4(dim1)) 231 | str4 = "" 232 | do i1 = 1,dim1 233 | k = (i1-1)*len1+1 234 | call copy_string_ctof(var(k:k+len1), str4(i1)(1:len1)) 235 | enddo 236 | 237 | end subroutine 238 | 239 | 240 | !!!!!!!!!!!!!!!!!! 241 | !!! Utilities !!! 242 | !!!!!!!!!!!!!!!!!! 243 | 244 | 245 | subroutine copy_string_ctof(stringc,stringf) 246 | ! utility function to convert c string to fortran string 247 | character(len=*), intent(out) :: stringf 248 | character(c_char), intent(in) :: stringc(:) 249 | integer j 250 | stringf = '' 251 | char_loop: do j=1,min(size(stringc),len(stringf)) 252 | if (stringc(j)==c_null_char) exit char_loop 253 | stringf(j:j) = stringc(j) 254 | end do char_loop 255 | end subroutine copy_string_ctof 256 | 257 | subroutine copy_string_ftoc(stringf,stringc) 258 | ! utility function to convert c string to fortran string 259 | character(len=*), intent(in) :: stringf 260 | character(c_char), intent(out) :: stringc(:) 261 | integer j,n 262 | n = len_trim(stringf) 263 | do j=1,n 264 | stringc(j) = stringf(j:j) 265 | end do 266 | stringc(n+1) = c_null_char 267 | end subroutine copy_string_ftoc 268 | 269 | 270 | end module -------------------------------------------------------------------------------- /src/module_variable/module_variable_wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void module_variable_get_a1(int32_t *var); 4 | void module_variable_set_a1(int32_t *var); 5 | 6 | void module_variable_get_a2(int32_t *var); 7 | 8 | void module_variable_get_a3(int32_t *var); 9 | void module_variable_set_a3(int32_t *var); 10 | 11 | void module_variable_get_arr1(int32_t *var); 12 | void module_variable_set_arr1(int32_t *var); 13 | 14 | void module_variable_get_arr2(int32_t *var); 15 | 16 | void module_variable_get_arr3_size(int64_t *d); 17 | void module_variable_get_arr3(int64_t *d, int32_t *var); 18 | void module_variable_set_arr3(int64_t *d, int32_t *var); 19 | 20 | // characters 21 | 22 | void module_variable_get_str1(char *var); 23 | void module_variable_set_str1(int32_t *len1, char *var); 24 | 25 | void module_variable_get_str2_len(int32_t *len1); 26 | void module_variable_get_str2(int32_t *len1, char *var); 27 | void module_variable_set_str2(int32_t *len1, char *var); 28 | 29 | void module_variable_get_str4_size(int32_t *dim1); 30 | void module_variable_get_str4(int32_t *len1, int32_t *dim1, char *var); 31 | void module_variable_set_str4(int32_t *len1, int32_t *dim1, char *var); 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/simple_function/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name simple_function) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/simple_function/simple_function.f90: -------------------------------------------------------------------------------- 1 | module simple_function 2 | implicit none 3 | contains 4 | function myfunc(a) result(b) 5 | integer, intent(in) :: a 6 | integer :: b 7 | b = a + 1 8 | end function 9 | end module -------------------------------------------------------------------------------- /src/simple_function/simple_function.h: -------------------------------------------------------------------------------- 1 | int myfunc_wrapper(int *a); -------------------------------------------------------------------------------- /src/simple_function/simple_function.pyx: -------------------------------------------------------------------------------- 1 | cdef extern from "simple_function.h": 2 | cdef int myfunc_wrapper(int *a) 3 | 4 | cdef class __simple_function: 5 | """module simple_function 6 | contains 7 | myfunc() 8 | end module""" 9 | def myfunc(self, int a): 10 | """b = myfunc(a) 11 | 12 | Parameters 13 | ---------- 14 | a : int 15 | 16 | Returns 17 | ------- 18 | b : int 19 | """ 20 | return myfunc_wrapper(&a) 21 | 22 | simple_function = __simple_function() -------------------------------------------------------------------------------- /src/simple_function/simple_function_wrapper.f90: -------------------------------------------------------------------------------- 1 | module simple_functione_wrapper 2 | use iso_c_binding 3 | implicit none 4 | contains 5 | function myfunc_wrapper(a) result(b) bind(c) 6 | use simple_function, only: myfunc 7 | implicit none 8 | integer(c_int), intent(in) :: a 9 | integer(c_int) :: b 10 | b = myfunc(a) 11 | end function 12 | end module -------------------------------------------------------------------------------- /src/simple_subroutine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(module_name simple_subroutine) 2 | add_library(${module_name}_fortran ${module_name}.f90 ${module_name}_wrapper.f90) -------------------------------------------------------------------------------- /src/simple_subroutine/simple_subroutine.f90: -------------------------------------------------------------------------------- 1 | module simple_subroutine 2 | implicit none 3 | contains 4 | subroutine mysub(a, b) 5 | integer, intent(in) :: a 6 | integer, intent(out) :: b 7 | b = a + 1 8 | end subroutine 9 | 10 | subroutine returns_arr1(arr) 11 | integer, allocatable, intent(out) :: arr(:) 12 | allocate(arr(10)) 13 | arr = 12 14 | end subroutine 15 | 16 | subroutine returns_arr2(arr) 17 | integer, intent(out) :: arr(:) 18 | arr = 14 19 | end subroutine 20 | end module -------------------------------------------------------------------------------- /src/simple_subroutine/simple_subroutine.h: -------------------------------------------------------------------------------- 1 | void mysub_wrapper(int *a, int *b); 2 | void returns_arr1_len_wrapper(int *arr_size, void *ptr); 3 | void returns_arr1_wrapper(void *ptr, int *arr_len, int *arr); -------------------------------------------------------------------------------- /src/simple_subroutine/simple_subroutine.pyx: -------------------------------------------------------------------------------- 1 | import numpy as __np 2 | from numpy cimport ndarray 3 | 4 | cdef extern from "simple_subroutine.h": 5 | cdef void mysub_wrapper(int *a, int *b) 6 | cdef void returns_arr1_len_wrapper(int *arr_size, void *ptr) 7 | cdef void returns_arr1_wrapper(void *ptr, int *arr_len, int *arr) 8 | 9 | cdef class __simple_subroutine: 10 | """module simple_subroutine 11 | contains 12 | mysub() 13 | end module""" 14 | def mysub(self, int a): 15 | """b = mysub(a) 16 | 17 | Parameters 18 | ---------- 19 | a : int 20 | 21 | Returns 22 | ------- 23 | b : int 24 | """ 25 | cdef int b 26 | mysub_wrapper(&a, &b) 27 | return b 28 | 29 | def returns_arr1(self): 30 | cdef int arr_len 31 | cdef void *ptr 32 | returns_arr1_len_wrapper(&arr_len, &ptr) 33 | cdef ndarray arr = __np.empty(arr_len, __np.int32) 34 | returns_arr1_wrapper(&ptr, &arr_len, arr.data) 35 | return arr 36 | 37 | simple_subroutine = __simple_subroutine() 38 | -------------------------------------------------------------------------------- /src/simple_subroutine/simple_subroutine_wrapper.f90: -------------------------------------------------------------------------------- 1 | module simple_subroutine_wrapper 2 | use iso_c_binding 3 | implicit none 4 | contains 5 | subroutine mysub_wrapper(a1, a2) bind(c) 6 | use simple_subroutine, only: sub => mysub 7 | integer(c_int), intent(in) :: a1 8 | integer(c_int), intent(out) :: a2 9 | call sub(a1, a2) 10 | end subroutine 11 | 12 | subroutine returns_arr1_wrapper(ptr, arr_len, arr) bind(c) 13 | type(c_ptr), intent(in) :: ptr 14 | integer(c_int), intent(in) :: arr_len 15 | integer(c_int), intent(out) :: arr(arr_len) 16 | 17 | integer(c_int), pointer :: arr_ptr(:) 18 | 19 | call c_f_pointer(ptr, arr_ptr, [arr_len]) 20 | 21 | arr = arr_ptr 22 | deallocate(arr_ptr) 23 | end subroutine 24 | 25 | subroutine returns_arr1_len_wrapper(arr_len, ptr) bind(c) 26 | use simple_subroutine, only: sub => returns_arr1 27 | integer(c_int), intent(out) :: arr_len 28 | type(c_ptr), intent(out) :: ptr 29 | 30 | integer(c_int), allocatable, target :: arr(:) 31 | integer(c_int), pointer :: arr_ptr(:) 32 | 33 | call sub(arr) 34 | arr_len = size(arr) 35 | allocate(arr_ptr(arr_len)) 36 | arr_ptr = arr 37 | ptr = c_loc(arr_ptr) 38 | 39 | end subroutine 40 | 41 | end module -------------------------------------------------------------------------------- /tests/test_characters.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | from fortran_cython_examples import characters 5 | except: 6 | import sys 7 | import os 8 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | sys.path.append(os.path.dirname(SCRIPT_DIR)) 10 | from fortran_cython_examples import characters 11 | 12 | def test(): 13 | characters.str1 = b'hello1' 14 | assert characters.str1 == b'hello1' 15 | 16 | characters.str2 = b'hello2' 17 | assert characters.str2 == b'hello2' -------------------------------------------------------------------------------- /tests/test_derived_type.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | from fortran_cython_examples import derived_type 5 | except: 6 | import sys 7 | import os 8 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | sys.path.append(os.path.dirname(SCRIPT_DIR)) 10 | from fortran_cython_examples import derived_type 11 | 12 | mytype = derived_type.mytype 13 | 14 | def test(): 15 | my = mytype() 16 | 17 | my.a = 2.0 18 | assert my.a == 2.0 19 | 20 | my.add2a(100.0) 21 | assert my.a == 102.0 22 | 23 | my.arr = np.array([1,2,3]) 24 | assert np.all(my.arr == np.array([1,2,3])) 25 | assert my.sumarr() == 6.0 26 | 27 | 28 | my1 = mytype() 29 | 30 | my1.a = 1 31 | my.a = 2 32 | 33 | assert my1.a != my.a 34 | -------------------------------------------------------------------------------- /tests/test_module_variable.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | from fortran_cython_examples import module_variable 5 | except: 6 | import sys 7 | import os 8 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | sys.path.append(os.path.dirname(SCRIPT_DIR)) 10 | from fortran_cython_examples import module_variable 11 | 12 | def test(): 13 | module_variable.a1 = 1 14 | assert module_variable.a1 == 1 -------------------------------------------------------------------------------- /tests/test_simple_function.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | from fortran_cython_examples import simple_function 5 | except: 6 | import sys 7 | import os 8 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | sys.path.append(os.path.dirname(SCRIPT_DIR)) 10 | from fortran_cython_examples import simple_function 11 | 12 | def test(): 13 | assert simple_function.myfunc(1) == 2 14 | 15 | assert simple_function.myfunc(1.0) == 2 16 | 17 | assert simple_function.myfunc(np.array(1)) == 2 18 | 19 | try: 20 | simple_function.myfunc(np.array([1,2,3])) 21 | except TypeError as e: 22 | assert str(e) == "only size-1 arrays can be converted to Python scalars" -------------------------------------------------------------------------------- /tests/test_simple_subroutine.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | try: 4 | from fortran_cython_examples import simple_subroutine 5 | except: 6 | import sys 7 | import os 8 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | sys.path.append(os.path.dirname(SCRIPT_DIR)) 10 | from fortran_cython_examples import simple_subroutine 11 | 12 | def test(): 13 | assert simple_subroutine.mysub(1) == 2 14 | 15 | assert simple_subroutine.mysub(1.0) == 2 16 | 17 | assert simple_subroutine.mysub(np.array(1)) == 2 18 | 19 | try: 20 | simple_subroutine.mysub(np.array([1,2,3])) 21 | except TypeError as e: 22 | assert str(e) == "only size-1 arrays can be converted to Python scalars" 23 | 24 | assert np.all(simple_subroutine.returns_arr1() == np.ones(10)*12.0) --------------------------------------------------------------------------------