Browse Source

Use cmake for building. Started rewriting the audio core in C++.

looper
Ben Niemann 5 years ago
parent
commit
51d6d2f70a
  1. 66
      CMakeLists.txt
  2. 4
      NOTES.org
  3. 4
      bin/noisicaä
  4. 44
      bin/runtests.py
  5. 78
      cmake/FindCython.cmake
  6. 63
      cmake/FindNumPy.cmake
  7. 539
      cmake/FindPythonExtensions.cmake
  8. 395
      cmake/UseCython.cmake
  9. 580
      cmake/targetLinkLibrariesWithDynamicLookup.cmake
  10. 22
      noisicaa/CMakeLists.txt
  11. 3
      noisicaa/audio_playground.py
  12. 32
      noisicaa/audioproc/CMakeLists.txt
  13. 28
      noisicaa/audioproc/nodes/CMakeLists.txt
  14. 82
      noisicaa/audioproc/nodes/csound_perftest.pyx
  15. 14
      noisicaa/audioproc/nodes/lv2.pyxbld
  16. 13
      noisicaa/audioproc/resample.pyxbld
  17. 9
      noisicaa/audioproc/resample_test.py
  18. 13
      noisicaa/audioproc/vm/CMakeLists.txt
  19. 30
      noisicaa/audioproc/vm/engine_perftest.py
  20. 31
      noisicaa/bindings/#CMakeLists.txt#
  21. 1
      noisicaa/bindings/.#CMakeLists.txt
  22. 31
      noisicaa/bindings/CMakeLists.txt
  23. 21
      noisicaa/bindings/csound.pxd
  24. 187
      noisicaa/bindings/csound.pyx
  25. 14
      noisicaa/bindings/csound.pyxbld
  26. 4
      noisicaa/bindings/csound_test.py
  27. 12
      noisicaa/bindings/fluidsynth.pyxbld
  28. 11
      noisicaa/bindings/ladspa.pyxbld
  29. 14
      noisicaa/bindings/lilv.pyxbld
  30. 14
      noisicaa/bindings/lilv_test.pyxbld
  31. 10
      noisicaa/bindings/lv2/CMakeLists.txt
  32. 1
      noisicaa/bindings/lv2/atom.pxd
  33. 2
      noisicaa/bindings/lv2/atom.pyx
  34. 12
      noisicaa/bindings/lv2/lv2.pyxbld
  35. 12
      noisicaa/bindings/sndfile.pyxbld
  36. 14
      noisicaa/bindings/sratom.pyxbld
  37. 25
      noisicaa/core/CMakeLists.txt
  38. 8
      noisicaa/core/stats/CMakeLists.txt
  39. 9
      noisicaa/devices/CMakeLists.txt
  40. 3
      noisicaa/devices/libalsa.pyx
  41. 10
      noisicaa/devices/libalsa.pyxbld
  42. 4
      noisicaa/devices/libalsa_test.py
  43. 4
      noisicaa/devices/midi_hub.py
  44. 4
      noisicaa/devices/midi_hub_test.py
  45. 4
      noisicaa/editor_main.py
  46. 8
      noisicaa/importers/CMakeLists.txt
  47. 4
      noisicaa/importers/abc_filetest.py
  48. 4
      noisicaa/importers/abc_parse.py
  49. 4
      noisicaa/importers/abc_test.py
  50. 6
      noisicaa/instr/CMakeLists.txt
  51. 9
      noisicaa/instrument_db/CMakeLists.txt
  52. 7
      noisicaa/instrument_db/private/CMakeLists.txt
  53. 43
      noisicaa/music/CMakeLists.txt
  54. 4
      noisicaa/music/project_test.py
  55. 11
      noisicaa/node_db/CMakeLists.txt
  56. 14
      noisicaa/node_db/private/CMakeLists.txt
  57. 44
      noisicaa/ui/CMakeLists.txt
  58. 4
      noisicaa/ui/render_sheet_dialog_test.py
  59. 9
      noisicaa/ui/sheet_view/CMakeLists.txt
  60. 26
      noisicore/CMakeLists.txt
  61. 0
      noisicore/__init__.py
  62. 205
      noisicore/buffers.cpp
  63. 88
      noisicore/buffers.h
  64. 34
      noisicore/buffers.pxd
  65. 299
      noisicore/buffers_test.pyx
  66. 22
      noisicore/misc.cpp
  67. 12
      noisicore/misc.h
  68. 8
      noisicore/noisicore.h
  69. 146
      noisicore/opcodes.cpp
  70. 74
      noisicore/opcodes.h
  71. 32
      noisicore/opcodes.pxd
  72. 44
      noisicore/spec.cpp
  73. 35
      noisicore/spec.h
  74. 15
      noisicore/spec.pxd
  75. 56
      noisicore/spec_test.pyx
  76. 46
      noisicore/status.h
  77. 7
      noisicore/status.pxd
  78. 77
      noisicore/vm.cpp
  79. 27
      noisicore/vm.h
  80. 9
      noisicore/vm.pxd
  81. 44
      noisicore/vm_test.pyx
  82. 4
      noisidev/CMakeLists.txt
  83. 18
      noisidev/profutil.py
  84. 3
      testlogs/engine_perftest.csv

66
CMakeLists.txt

@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.5)
project (noisicaä)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
find_package(Cython REQUIRED)
if("$ENV{VIRTUAL_ENV}" STREQUAL "")
message(FATAL_ERROR "Not running in a virtual env.")
endif("$ENV{VIRTUAL_ENV}" STREQUAL "")
find_package(PythonLibs REQUIRED)
include_directories(${PYTHON_INCLUDE_DIRS})
set(ENV{PKG_CONFIG_PATH} $ENV{VIRTUAL_ENV}/lib/pkgconfig)
find_package(PkgConfig)
pkg_check_modules(LIBSRATOM REQUIRED sratom-0)
pkg_check_modules(LIBLILV REQUIRED lilv-0)
pkg_check_modules(LIBSNDFILE REQUIRED sndfile)
pkg_check_modules(LIBFLUIDSYNTH REQUIRED fluidsynth)
set(LIBCSOUND_INCLUDE_DIRS)
set(LIBCSOUND_LIBRARIES csound64)
include_directories(${CMAKE_CURRENT_LIST_DIR})
include_directories($ENV{VIRTUAL_ENV}/include)
link_directories($ENV{VIRTUAL_ENV}/lib)
macro(add_python_package)
foreach(src __init__.py ${ARGN})
add_custom_command(
OUTPUT ${src}
COMMAND ln -sf ${CMAKE_CURRENT_LIST_DIR}/${src} ${src}
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/${src}
)
endforeach(src)
file(RELATIVE_PATH pkg_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR})
string(REGEX REPLACE "/" "." pkg_target ${pkg_path})
add_custom_target("pkg-${pkg_target}" ALL DEPENDS __init__.py ${ARGN})
endmacro(add_python_package)
macro(add_cython_module mod lang)
file(RELATIVE_PATH pkg_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR})
string(REGEX REPLACE "/" "." pkg_target ${pkg_path})
add_cython_target(${mod} ${mod}.pyx ${lang} PY3)
add_library(${pkg_target}.${mod} MODULE ${${mod}})
set_target_properties(${pkg_target}.${mod} PROPERTIES PREFIX "" OUTPUT_NAME ${mod})
set(${mod}.so ${pkg_target}.${mod})
endmacro(add_cython_module)
macro(add_capnp src)
add_custom_command(
OUTPUT ${src}
COMMAND ln -sf ${CMAKE_CURRENT_LIST_DIR}/${src} ${src}
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/${src}
)
file(RELATIVE_PATH pkg_path ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR})
string(REGEX REPLACE "/" "." pkg_target ${pkg_path})
add_custom_target(${pkg_target}.${src} ALL DEPENDS ${src})
endmacro(add_capnp)
add_subdirectory(noisicore)
add_subdirectory(noisicaa)
add_subdirectory(noisidev)
file(COPY data DESTINATION .)

4
NOTES.org

@ -1,6 +1,10 @@
# -*- org-tags-column: -98 -*-
* VM-based pipeline engine :FR:
- rebuild all cython modules before starting app
- consistent use of either malloc or PyObject_Malloc
- is PyObject_Malloc nogil?
- engine_perftest should focus on other opcodes than CALL
- add more nogils to methods in bindings
- can pyximport handle distutils pragmas?
- StaticMapper should have common URIDs as attributes, so client code doesn't have to call map

4
bin/noisicaä

@ -1,6 +1,8 @@
#!/bin/bash
LIBDIR=$(readlink -f "$(dirname "$0")/..")
LIBDIR=$(readlink -f "$(dirname "$0")/../build")
(cd $LIBDIR && make)
export PYTHONPATH="$LIBDIR:$PYTHONPATH"
export LD_LIBRARY_PATH=${VIRTUAL_ENV}/lib
cd $HOME
exec python3 -m noisicaa.editor_main "$@"

44
bin/runtests.py

@ -1,22 +1,21 @@
#!/usr/bin/env python3
import argparse
import itertools
import fnmatch
import logging
import os
import os.path
import shutil
import subprocess
import sys
import unittest
import coverage
import pyximport
LIBDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
LIBDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'build'))
sys.path.insert(0, LIBDIR)
from noisicaa import constants
os.environ['LD_LIBRARY_PATH'] = os.path.join(os.getenv('VIRTUAL_ENV'), 'lib')
def main(argv):
@ -32,12 +31,9 @@ def main(argv):
parser.add_argument('--write-perf-stats', action='store_true', default=False)
parser.add_argument('--profile', action='store_true', default=False)
parser.add_argument('--gdb', action='store_true', default=False)
parser.add_argument('--rebuild', action='store_true', default=False)
parser.add_argument('--norebuild', action='store_true', default=False)
args = parser.parse_args(argv[1:])
constants.TEST_OPTS.WRITE_PERF_STATS = args.write_perf_stats
constants.TEST_OPTS.ENABLE_PROFILER = args.profile
logging.basicConfig()
logging.getLogger().setLevel({
'debug': logging.DEBUG,
@ -58,25 +54,12 @@ def main(argv):
directives['gdb_debug'] = True
modifiers.add('dbg')
build_dir = os.path.join(
os.getenv('VIRTUAL_ENV'),
'pyxbuild',
'-'.join(sorted(modifiers)) or 'vanilla')
if args.rebuild:
shutil.rmtree(build_dir)
pyximport.install(
build_dir=build_dir,
setup_args={
'script_args': ['--verbose'],
'options': {
'build_ext': {
'cython_directives': directives,
}
}
}
)
if not args.norebuild:
subprocess.run(['make', '-j4'], cwd=LIBDIR, check=True)
from noisicaa import constants
constants.TEST_OPTS.WRITE_PERF_STATS = args.write_perf_stats
constants.TEST_OPTS.ENABLE_PROFILER = args.profile
loader = unittest.defaultTestLoader
suite = unittest.TestSuite()
@ -89,13 +72,14 @@ def main(argv):
cov.set_option("run:branch", True)
cov.start()
for dirpath, dirnames, filenames in os.walk(
os.path.join(LIBDIR, 'noisicaa')):
for dirpath, dirnames, filenames in itertools.chain(
os.walk(os.path.join(LIBDIR, 'noisicaa')),
os.walk(os.path.join(LIBDIR, 'noisicore'))):
if '__pycache__' in dirnames:
dirnames.remove('__pycache__')
for filename in filenames:
if not (fnmatch.fnmatch(filename, '*.py') or fnmatch.fnmatch(filename, '*.pyx')):
if not (fnmatch.fnmatch(filename, '*.py') or fnmatch.fnmatch(filename, '*.so')):
continue
filename = os.path.splitext(filename)[0]

78
cmake/FindCython.cmake

@ -0,0 +1,78 @@
#.rst:
#
# Find ``cython`` executable.
#
# This module will set the following variables in your project:
#
# ``CYTHON_EXECUTABLE``
# path to the ``cython`` program
#
# ``CYTHON_VERSION``
# version of ``cython``
#
# ``CYTHON_FOUND``
# true if the program was found
#
# For more information on the Cython project, see http://cython.org/.
#
# *Cython is a language that makes writing C extensions for the Python language
# as easy as Python itself.*
#
#=============================================================================
# Copyright 2011 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
# Use the Cython executable that lives next to the Python executable
# if it is a local installation.
find_package(PythonInterp)
if(PYTHONINTERP_FOUND)
get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH)
find_program(CYTHON_EXECUTABLE
NAMES cython cython.bat cython3
HINTS ${_python_path}
DOC "path to the cython executable")
else()
find_program(CYTHON_EXECUTABLE
NAMES cython cython.bat cython3
DOC "path to the cython executable")
endif()
if(CYTHON_EXECUTABLE)
set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version)
execute_process(COMMAND ${CYTHON_version_command}
OUTPUT_VARIABLE CYTHON_version_output
ERROR_VARIABLE CYTHON_version_error
RESULT_VARIABLE CYTHON_version_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT ${CYTHON_version_result} EQUAL 0)
set(_error_msg "Command \"${CYTHON_version_command}\" failed with")
set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}")
message(SEND_ERROR "${_error_msg}")
else()
if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)")
set(CYTHON_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
endif()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE)
mark_as_advanced(CYTHON_EXECUTABLE)
include(UseCython)

63
cmake/FindNumPy.cmake

@ -0,0 +1,63 @@
#.rst:
#
# Find the include directory for numpy/arrayobject.h
#
# This module sets the following variables:
#
# ``NumPy_FOUND``
# True if NumPy was found.
# ``NumPy_INCLUDE_DIR``
# The include directories needed to use NumpPy.
# ``NumPy_VERSION``
# The version of NumPy found.
#
#
# The module will also explicitly define one cache variable:
#
# ``NumPy_INCLUDE_DIR``
#
if(NOT NumPy_FOUND)
set(_find_extra_args)
if(NumPy_FIND_REQUIRED)
list(APPEND _find_extra_args REQUIRED)
endif()
if(NumPy_FIND_QUIET)
list(APPEND _find_extra_args QUIET)
endif()
find_package(PythonInterp ${_find_extra_args})
find_package(PythonLibs ${_find_extra_args})
if(PYTHON_EXECUTABLE)
execute_process(COMMAND "${PYTHON_EXECUTABLE}"
-c "import numpy; print(numpy.get_include())"
OUTPUT_VARIABLE _numpy_include_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
execute_process(COMMAND "${PYTHON_EXECUTABLE}"
-c "import numpy; print(numpy.__version__)"
OUTPUT_VARIABLE NumPy_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
endif()
endif()
find_path(NumPy_INCLUDE_DIR
numpy/arrayobject.h
PATHS "${_numpy_include_dir}" "${PYTHON_INCLUDE_DIR}"
PATH_SUFFIXES numpy/core/include
)
set(NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIR})
# handle the QUIETLY and REQUIRED arguments and set NumPy_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(NumPy
REQUIRED_VARS NumPy_INCLUDE_DIR
VERSION_VAR NumPy_VERSION
)
mark_as_advanced(NumPy_INCLUDE_DIR)

539
cmake/FindPythonExtensions.cmake

@ -0,0 +1,539 @@
#.rst:
#
# This module defines CMake functions to build Python extension modules and
# stand-alone executables.
#
# The following variables are defined:
# ::
#
# PYTHON_PREFIX - absolute path to the current Python
# distribution's prefix
# PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python
# distribution's site-packages directory
# PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python
# distribution's site-packages directory
# relative to its prefix
# PYTHON_SEPARATOR - separator string for file path
# components. Equivalent to ``os.sep`` in
# Python.
# PYTHON_PATH_SEPARATOR - separator string for PATH-style
# environment variables. Equivalent to
# ``os.pathsep`` in Python.
#
#
# The following functions are defined:
#
# .. cmake:command:: python_extension_module
#
# For libraries meant to be used as Python extension modules, either dynamically
# loaded or directly linked. Amend the configuration of the library target
# (created using ``add_library``) with additional options needed to build and
# use the referenced library as a Python extension module.
#
# python_extension_module(<Target>
# [LINKED_MODULES_VAR <LinkedModVar>]
# [FORWARD_DECL_MODULES_VAR <ForwardDeclModVar>]
# [MODULE_SUFFIX <ModuleSuffix>])
#
# Only extension modules that are configured to be built as MODULE libraries can
# be runtime-loaded through the standard Python import mechanism. All other
# modules can only be included in standalone applications that are written to
# expect their presence. In addition to being linked against the libraries for
# these modules, such applications must forward declare their entry points and
# initialize them prior to use. To generate these forward declarations and
# initializations, see ``python_modules_header``.
#
# If ``<Target>`` does not refer to a target, then it is assumed to refer to an
# extension module that is not linked at all, but compiled along with other
# source files directly into an executable. Adding these modules does not cause
# any library configuration modifications, and they are not added to the list of
# linked modules. They still must be forward declared and initialized, however,
# and so are added to the forward declared modules list.
#
# Options:
#
# ``LINKED_MODULES_VAR <LinkedModVar>``
# Name of the variable referencing a list of extension modules whose libraries
# must be linked into the executables of any stand-alone applications that use
# them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used.
#
# ``FORWARD_DECL_MODULES_VAR <ForwardDeclModVar>``
# Name of the variable referencing a list of extension modules whose entry
# points must be forward declared and called by any stand-alone applications
# that use them. By default, the global property
# ``PY_FORWARD_DECL_MODULES_LIST`` is used.
#
# ``MODULE_SUFFIX <ModuleSuffix>``
# Suffix appended to the python extension module file.
# The default suffix is retrieved using ``sysconfig.get_config_var("SO")"``,
# if not available, the default is then ``.so`` on unix and ``.pyd`` on
# windows.
# Setting the variable ``PYTHON_EXTENSION_MODULE_SUFFIX`` in the caller
# scope defines the value used for all extensions not having a suffix
# explicitly specified using ``MODULE_SUFFIX`` parameter.
#
#
# .. cmake:command:: python_standalone_executable
#
# python_standalone_executable(<Target>)
#
# For standalone executables that initialize their own Python runtime
# (such as when building source files that include one generated by Cython with
# the --embed option). Amend the configuration of the executable target
# (created using ``add_executable``) with additional options needed to properly
# build the referenced executable.
#
#
# .. cmake:command:: python_modules_header
#
# Generate a header file that contains the forward declarations and
# initialization routines for the given list of Python extension modules.
# ``<Name>`` is the logical name for the header file (no file extensions).
# ``<HeaderFilename>`` is the actual destination filename for the header file
# (e.g.: decl_modules.h).
#
# python_modules_header(<Name> [HeaderFilename]
# [FORWARD_DECL_MODULES_LIST <ForwardDeclModList>]
# [HEADER_OUTPUT_VAR <HeaderOutputVar>]
# [INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>])
#
# If only ``<Name>`` is provided, and it ends in the ".h" extension, then it
# is assumed to be the ``<HeaderFilename>``. The filename of the header file
# without the extension is used as the logical name. If only ``<Name>`` is
# provided, and it does not end in the ".h" extension, then the
# ``<HeaderFilename>`` is assumed to ``<Name>.h``.
#
# The exact contents of the generated header file depend on the logical
# ``<Name>``. It should be set to a value that corresponds to the target
# application, or for the case of multiple applications, some identifier that
# conveyes its purpose. It is featured in the generated multiple inclusion
# guard as well as the names of the generated initialization routines.
#
# The generated header file includes forward declarations for all listed
# modules, as well as implementations for the following class of routines:
#
# ``int <Name>_<Module>(void)``
# Initializes the python extension module, ``<Module>``. Returns an integer
# handle to the module.
#
# ``void <Name>_LoadAllPythonModules(void)``
# Initializes all listed python extension modules.
#
# ``void CMakeLoadAllPythonModules(void);``
# Alias for ``<Name>_LoadAllPythonModules`` whose name does not depend on
# ``<Name>``. This function is excluded during preprocessing if the
# preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined.
#
# ``void Py_Initialize_Wrapper();``
# Wrapper arpund ``Py_Initialize()`` that initializes all listed python
# extension modules. This function is excluded during preprocessing if the
# preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this
# function is generated, then ``Py_Initialize()`` is redefined to a macro
# that calls this function.
#
# Options:
#
# ``FORWARD_DECL_MODULES_LIST <ForwardDeclModList>``
# List of extension modules for which to generate forward declarations of
# their entry points and their initializations. By default, the global
# property ``PY_FORWARD_DECL_MODULES_LIST`` is used.
#
# ``HEADER_OUTPUT_VAR <HeaderOutputVar>``
# Name of the variable to set to the path to the generated header file. By
# default, ``<Name>`` is used.
#
# ``INCLUDE_DIR_OUTPUT_VAR <IncludeDirOutputVar>``
# Name of the variable to set to the path to the directory containing the
# generated header file. By default, ``<Name>_INCLUDE_DIRS`` is used.
#
# Defined variables:
#
# ``<HeaderOutputVar>``
# The path to the generated header file
#
# ``<IncludeDirOutputVar>``
# Directory containing the generated header file
#
#
# Example usage
# ^^^^^^^^^^^^^
#
# .. code-block:: cmake
#
# find_package(PythonInterp)
# find_package(PythonLibs)
# find_package(PythonExtensions)
# find_package(Cython)
# find_package(Boost COMPONENTS python)
#
# # Simple Cython Module -- no executables
# add_cython_target(_module.pyx)
# add_library(_module MODULE ${_module})
# python_extension_module(_module)
#
# # Mix of Cython-generated code and C++ code using Boost Python
# # Stand-alone executable -- no modules
# include_directories(${Boost_INCLUDE_DIRS})
# add_cython_target(main.pyx CXX EMBED_MAIN)
# add_executable(main boost_python_module.cxx ${main})
# target_link_libraries(main ${Boost_LIBRARIES})
# python_standalone_executable(main)
#
# # stand-alone executable with three extension modules:
# # one statically linked, one dynamically linked, and one loaded at runtime
# #
# # Freely mixes Cython-generated code, code using Boost-Python, and
# # hand-written code using the CPython API.
#
# # module1 -- statically linked
# add_cython_target(module1.pyx)
# add_library(module1 STATIC ${module1})
# python_extension_module(module1
# LINKED_MODULES_VAR linked_module_list
# FORWARD_DECL_MODULES_VAR fdecl_module_list)
#
# # module2 -- dynamically linked
# include_directories({Boost_INCLUDE_DIRS})
# add_library(module2 SHARED boost_module2.cxx)
# target_link_libraries(module2 ${Boost_LIBRARIES})
# python_extension_module(module2
# LINKED_MODULES_VAR linked_module_list
# FORWARD_DECL_MODULES_VAR fdecl_module_list)
#
# # module3 -- loaded at runtime
# add_cython_target(module3a.pyx)
# add_library(module1 MODULE ${module3a} module3b.cxx)
# target_link_libraries(module3 ${Boost_LIBRARIES})
# python_extension_module(module3
# LINKED_MODULES_VAR linked_module_list
# FORWARD_DECL_MODULES_VAR fdecl_module_list)
#
# # application executable -- generated header file + other source files
# python_modules_header(modules
# FORWARD_DECL_MODULES_LIST ${fdecl_module_list})
# include_directories(${modules_INCLUDE_DIRS})
#
# add_cython_target(mainA)
# add_cython_target(mainC)
# add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c)
#
# target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES})
# python_standalone_executable(main)
#
#=============================================================================
# Copyright 2011 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
find_package(PythonInterp REQUIRED)
find_package(PythonLibs)
include(targetLinkLibrariesWithDynamicLookup)
set(_command "
import distutils.sysconfig
import itertools
import os
import os.path
import site
import sys
import sysconfig
result = None
rel_result = None
candidate_lists = []
try:
candidate_lists.append((distutils.sysconfig.get_python_lib(),))
except AttributeError: pass
try:
candidate_lists.append(site.getsitepackages())
except AttributeError: pass
try:
candidate_lists.append((site.getusersitepackages(),))
except AttributeError: pass
candidates = itertools.chain.from_iterable(candidate_lists)
for candidate in candidates:
rel_candidate = os.path.relpath(
candidate, sys.prefix)
if not rel_candidate.startswith(\"..\"):
result = candidate
rel_result = rel_candidate
break
sys.stdout.write(\";\".join((
os.sep,
os.pathsep,
sys.prefix,
result,
rel_result,
sysconfig.get_config_var('SO')
)))
")
execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}"
OUTPUT_VARIABLE _list
RESULT_VARIABLE _result)
list(GET _list 0 _item)
set(PYTHON_SEPARATOR "${_item}")
mark_as_advanced(PYTHON_SEPARATOR)
list(GET _list 1 _item)
set(PYTHON_PATH_SEPARATOR "${_item}")
mark_as_advanced(PYTHON_PATH_SEPARATOR)
list(GET _list 2 _item)
set(PYTHON_PREFIX "${_item}")
mark_as_advanced(PYTHON_PREFIX)
list(GET _list 3 _item)
set(PYTHON_SITE_PACKAGES_DIR "${_item}")
mark_as_advanced(PYTHON_SITE_PACKAGES_DIR)
list(GET _list 4 _item)
set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}")
mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR)
if(NOT DEFINED PYTHON_EXTENSION_MODULE_SUFFIX)
list(GET _list 5 _item)
set(PYTHON_EXTENSION_MODULE_SUFFIX "${_item}")
endif()
function(python_extension_module _target)
set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR MODULE_SUFFIX)
cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN})
set(_lib_type "NA")
if(TARGET ${_target})
get_property(_lib_type TARGET ${_target} PROPERTY TYPE)
endif()
set(_is_non_lib TRUE)
set(_is_static_lib FALSE)
if(_lib_type STREQUAL "STATIC_LIBRARY")
set(_is_static_lib TRUE)
set(_is_non_lib FALSE)
endif()
set(_is_shared_lib FALSE)
if(_lib_type STREQUAL "SHARED_LIBRARY")
set(_is_shared_lib TRUE)
set(_is_non_lib FALSE)
endif()
set(_is_module_lib FALSE)
if(_lib_type STREQUAL "MODULE_LIBRARY")
set(_is_module_lib TRUE)
set(_is_non_lib FALSE)
endif()
if(_is_static_lib OR _is_shared_lib OR _is_non_lib)
if(_is_static_lib OR _is_shared_lib)
if(_args_LINKED_MODULES_VAR)
set(${_args_LINKED_MODULES_VAR}
${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE)
else()
set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target})
endif()
endif()
if(_args_FORWARD_DECL_MODULES_VAR)
set(${_args_FORWARD_DECL_MODULES_VAR}
${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE)
else()
set_property(GLOBAL APPEND PROPERTY
PY_FORWARD_DECL_MODULES_LIST ${_target})
endif()
endif()
if(NOT _is_non_lib)
include_directories("${PYTHON_INCLUDE_DIRS}")
endif()
if(_is_module_lib)
set_target_properties(${_target} PROPERTIES
PREFIX "${PYTHON_MODULE_PREFIX}")
endif()
if(_is_module_lib OR _is_shared_lib)
if(_is_module_lib)
if(NOT _args_MODULE_SUFFIX)
set(_args_MODULE_SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}")
endif()
if(_args_MODULE_SUFFIX STREQUAL "" AND WIN32 AND NOT CYGWIN)
set(_args_MODULE_SUFFIX ".pyd")
endif()
if(NOT _args_MODULE_SUFFIX STREQUAL "")
set_target_properties(${_target}
PROPERTIES SUFFIX ${_args_MODULE_SUFFIX})
endif()
endif()
target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES})
endif()
endfunction()
function(python_standalone_executable _target)
include_directories(${PYTHON_INCLUDE_DIRS})
target_link_libraries(${_target} ${PYTHON_LIBRARIES})
endfunction()
function(python_modules_header _name)
set(one_ops FORWARD_DECL_MODULES_LIST
HEADER_OUTPUT_VAR
INCLUDE_DIR_OUTPUT_VAR)
cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN})
list(GET _args_UNPARSED_ARGUMENTS 0 _arg0)
# if present, use arg0 as the input file path
if(_arg0)
set(_source_file ${_arg0})
# otherwise, must determine source file from name, or vice versa
else()
get_filename_component(_name_ext "${_name}" EXT)
# if extension provided, _name is the source file
if(_name_ext)
set(_source_file ${_name})
get_filename_component(_name "${_source_file}" NAME_WE)
# otherwise, assume the source file is ${_name}.h
else()
set(_source_file ${_name}.h)
endif()
endif()
if(_args_FORWARD_DECL_MODULES_LIST)
set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST})
else()
get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST)
endif()
string(REPLACE "." "_" _header_name "${_name}")
string(TOUPPER ${_header_name} _header_name_upper)
set(_header_name_upper "_${_header_name_upper}_H")
set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file})
set(generated_file_tmp "${generated_file}.in")
file(WRITE ${generated_file_tmp}
"/* Created by CMake. DO NOT EDIT; changes will be lost. */\n")
set(_chunk "")
set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n")
set(_chunk "${_chunk}#define ${_header_name_upper}\n")
set(_chunk "${_chunk}\n")
set(_chunk "${_chunk}#include <Python.h>\n")
set(_chunk "${_chunk}\n")
set(_chunk "${_chunk}#ifdef __cplusplus\n")
set(_chunk "${_chunk}extern \"C\" {\n")
set(_chunk "${_chunk}#endif /* __cplusplus */\n")
set(_chunk "${_chunk}\n")
set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n")
file(APPEND ${generated_file_tmp} "${_chunk}")
foreach(_module ${static_mod_list})
file(APPEND ${generated_file_tmp}
"PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n")
endforeach()
file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n")
foreach(_module ${static_mod_list})
file(APPEND ${generated_file_tmp}
"PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n")
endforeach()
set(_chunk "")
set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n")
set(_chunk "${_chunk}#ifdef __cplusplus\n")
set(_chunk "${_chunk}}\n")
set(_chunk "${_chunk}#endif /* __cplusplus */\n")
set(_chunk "${_chunk}\n")
file(APPEND ${generated_file_tmp} "${_chunk}")
foreach(_module ${static_mod_list})
set(_import_function "${_header_name}_${_module}")
set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}")
set(_chunk "")
set(_chunk "${_chunk}int ${_import_function}(void)\n")
set(_chunk "${_chunk}{\n")
set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n")
set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n")
set(_chunk "${_chunk} return PyImport_AppendInittab(")
set(_chunk "${_chunk}name, init${_prefixed_module});\n")
set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n")
set(_chunk "${_chunk} return PyImport_AppendInittab(")
set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n")
set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n")
set(_chunk "${_chunk}}\n\n")
file(APPEND ${generated_file_tmp} "${_chunk}")
endforeach()
file(APPEND ${generated_file_tmp}
"void ${_header_name}_LoadAllPythonModules(void)\n{\n")
foreach(_module ${static_mod_list})
file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n")
endforeach()
file(APPEND ${generated_file_tmp} "}\n\n")
set(_chunk "")
set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n")
set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n")
set(_chunk "${_chunk}{\n")
set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n")
set(_chunk "${_chunk}}\n")
set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n")
set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n")
set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n")
set(_chunk "${_chunk}{\n")
set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n")
set(_chunk "${_chunk} Py_Initialize();\n")
set(_chunk "${_chunk}}\n")
set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n")
set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n")
set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n")
file(APPEND ${generated_file_tmp} "${_chunk}")
# with configure_file() cmake complains that you may not use a file created
# using file(WRITE) as input file for configure_file()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${generated_file_tmp}" "${generated_file}"
OUTPUT_QUIET ERROR_QUIET)
set(_header_output_var ${_name})
if(_args_HEADER_OUTPUT_VAR)
set(_header_output_var ${_args_HEADER_OUTPUT_VAR})
endif()
set(${_header_output_var} ${generated_file} PARENT_SCOPE)
set(_include_dir_var ${_name}_INCLUDE_DIRS)
if(_args_INCLUDE_DIR_OUTPUT_VAR)
set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR})
endif()
set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
endfunction()

395
cmake/UseCython.cmake

@ -0,0 +1,395 @@
#.rst:
#
# The following functions are defined:
#
# .. cmake:command:: add_cython_target
#
# Create a custom rule to generate the source code for a Python extension module
# using cython.
#
# add_cython_target(<Name> [<CythonInput>]
# [EMBED_MAIN]
# [C | CXX]
# [PY2 | PY3]
# [OUTPUT_VAR <OutputVar>])
#
# ``<Name>`` is the name of the new target, and ``<CythonInput>``
# is the path to a cython source file. Note that, despite the name, no new
# targets are created by this function. Instead, see ``OUTPUT_VAR`` for
# retrieving the path to the generated source for subsequent targets.
#
# If only ``<Name>`` is provided, and it ends in the ".pyx" extension, then it
# is assumed to be the ``<CythonInput>``. The name of the input without the
# extension is used as the target name. If only ``<Name>`` is provided, and it
# does not end in the ".pyx" extension, then the ``<CythonInput>`` is assumed to
# be ``<Name>.pyx``.
#
# The Cython include search path is amended with any entries found in the
# ``INCLUDE_DIRECTORIES`` property of the directory containing the
# ``<CythonInput>`` file. Use ``iunclude_directories`` to add to the Cython
# include search path.
#
# Options:
#
# ``EMBED_MAIN``
# Embed a main() function in the generated output (for stand-alone
# applications that initialize their own Python runtime).
#
# ``C | CXX``
# Force the generation of either a C or C++ file. By default, a C file is
# generated, unless the C language is not enabled for the project; in this
# case, a C++ file is generated by default.
#
# ``PY2 | PY3``
# Force compilation using either Python-2 or Python-3 syntax and code
# semantics. By default, Python-2 syntax and semantics are used if the major
# version of Python found is 2. Otherwise, Python-3 syntax and sematics are
# used.
#
# ``OUTPUT_VAR <OutputVar>``
# Set the variable ``<OutputVar>`` in the parent scope to the path to the
# generated source file. By default, ``<Name>`` is used as the output
# variable name.
#
# Defined variables:
#
# ``<OutputVar>``
# The path of the generated source file.
#
# Cache variables that effect the behavior include:
#
# ``CYTHON_ANNOTATE``
# whether to create an annotated .html file when compiling
#
# ``CYTHON_FLAGS``
# additional flags to pass to the Cython compiler
#
# Example usage
# ^^^^^^^^^^^^^
#
# .. code-block:: cmake
#
# find_package(Cython)
#
# # Note: In this case, either one of these arguments may be omitted; their
# # value would have been inferred from that of the other.
# add_cython_target(cy_code cy_code.pyx)
#
# add_library(cy_code MODULE ${cy_code})
# target_link_libraries(cy_code ...)
#
#=============================================================================
# Copyright 2011 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
# Configuration options.
set(CYTHON_ANNOTATE OFF
CACHE BOOL "Create an annotated .html file when compiling *.pyx.")
set(CYTHON_FLAGS "" CACHE STRING
"Extra flags to the c