From 0b59058fa2e4404481d979d0ad87208a885adaa8 Mon Sep 17 00:00:00 2001 From: Philip Miller Date: Fri, 22 Feb 2013 23:18:50 -0500 Subject: [PATCH] Add cmake build system, fix examples for Windows compatibility. --- .gitignore | 5 + CMake-init.sh | 5 + CMakeLists.txt | 122 +++++++++++++ libs/numpy/cmake/FindNumPy.cmake | 90 ++++++++++ libs/numpy/cmake/FindPythonLibsNew.cmake | 211 +++++++++++++++++++++++ libs/numpy/cmake/README.txt | 9 + libs/numpy/doc/CMakeLists.txt | 56 ++++++ libs/numpy/doc/cmakeBuild.rst | 167 ++++++++++++++++++ libs/numpy/doc/index.rst | 1 + libs/numpy/example/CMakeLists.txt | 51 ++++++ libs/numpy/example/gaussian.cpp | 5 + libs/numpy/example/ndarray.cpp | 4 + libs/numpy/src/CMakeLists.txt | 25 +++ libs/numpy/test/CMakeLists.txt | 65 +++++++ libs/numpy/test/runCmakeTest.bat.in | 4 + libs/numpy/test/runCmakeTest.sh.in | 3 + 16 files changed, 823 insertions(+) create mode 100644 CMake-init.sh create mode 100644 CMakeLists.txt create mode 100644 libs/numpy/cmake/FindNumPy.cmake create mode 100644 libs/numpy/cmake/FindPythonLibsNew.cmake create mode 100644 libs/numpy/cmake/README.txt create mode 100644 libs/numpy/doc/CMakeLists.txt create mode 100644 libs/numpy/doc/cmakeBuild.rst create mode 100644 libs/numpy/example/CMakeLists.txt create mode 100644 libs/numpy/src/CMakeLists.txt create mode 100644 libs/numpy/test/CMakeLists.txt create mode 100644 libs/numpy/test/runCmakeTest.bat.in create mode 100755 libs/numpy/test/runCmakeTest.sh.in diff --git a/.gitignore b/.gitignore index 1366a50e..024e2344 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,8 @@ libs/numpy/example/ndarray libs/numpy/example/simple libs/numpy/example/ufunc libs/numpy/example/wrap +build +build-vc90 +build-vc100 +*~ +\#*\# \ No newline at end of file diff --git a/CMake-init.sh b/CMake-init.sh new file mode 100644 index 00000000..c8635365 --- /dev/null +++ b/CMake-init.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +mkdir build +cd build +cmake -D Boost_NO_BOOST_CMAKE=ON .. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..ee3b641f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,122 @@ +cmake_minimum_required(VERSION 2.8.3) + +project( boost.numpy ) + +# put our local cmake find scripts at the beginning of the cmake +# module search path +set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/libs/numpy/cmake ${CMAKE_MODULE_PATH}) + +# configure output folders so artifacts are built into a single set of +# top-level folders rather than the default cmake build structure that +# matches the source tree. +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib + CACHE PATH "Output directory for static libraries.") + +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib + CACHE PATH "Output directory for shared libraries.") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + CACHE PATH "Output directory for executables and DLL's.") + +# find required python packages +find_package(PythonInterp REQUIRED) +find_package(PythonLibsNew REQUIRED) +find_package(NumPy REQUIRED) + +# find boost +# +# set(Boost_USE_STATIC_LIBS ON) +# set(Boost_USE_MULTITHREADED ON) +# set(Boost_USE_STATIC_RUNTIME ON) +FIND_PACKAGE(Boost COMPONENTS python REQUIRED) + +message( STATUS "found boost:" + "\nINCLUDE: ${Boost_INCLUDE_DIRS}" + "\nLIB: ${Boost_LIBRARIES}" +) + +# add_definitions( -DBOOST_ALL_NO_LIB ) +# In some cases, you may need to explicitly specify that a dynamic Boost is used; if so use: +# add_definitions( -DBOOST_ALL_DYN_LINK ) + +# variable controlling whether the boost_numpy is a shared or static library +if (WIN32) + set(LIBRARY_TYPE STATIC CACHE STRING "type of library to make for boost_numpy") +else() + set(LIBRARY_TYPE SHARED CACHE STRING "type of library to make for boost_numpy") +endif() + +# variable controlling building of documentation +set(BUILD_DOCS OFF CACHE BOOL "Build Boost.NumPy Documentation") + +# logic for configuring documentation generation +if(BUILD_DOCS) + # find sphinx + EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "import sphinx; print sphinx.__version__" + RESULT_VARIABLE SPHINX_PROCESS + OUTPUT_VARIABLE SPHINX_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(HAVE_SPHINX 0) + if(SPHINX_PROCESS EQUAL 0) + FIND_PROGRAM(SPHINX_BUILD sphinx-build) + if(SPHINX_BUILD) + set(HAVE_SPHINX 1) + message(STATUS " Found Sphinx ${SPHINX_VERSION}: ${SPHINX_BUILD}") + else() + # sphinx is required to generate documention, so it is an error + # if we cannot find it + MESSAGE(SEND_ERROR "must be able to find sphinx-build when BUILD_DOCS is enabled") + endif() + endif() + + # find pdflatex, which is only required for doc-pdf + FIND_PACKAGE(LATEX) + if (PDFLATEX_COMPILER) + message( STATUS "Found PDFLATEX_COMPILER=${PDFLATEX_COMPILER}" ) + else() + message( STATUS "Found PDFLATEX_COMPILER NOT found" ) + endif() +endif() + +# compiler definitions for non-windows builds +if (NOT WIN32) + add_definitions(-fPIC) +endif() + +# enable ctest targets +ENABLE_TESTING() + +# turn on visual studio solution folders +SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) + +# global settings for include paths +include_directories( + ${PROJECT_SOURCE_DIR} + ${PYTHON_INCLUDE_DIRS} + ${NUMPY_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ) + +# link against boost and python libraries +LINK_LIBRARIES(${Boost_LIBRARIES}) # Deprecated but so convenient! +LINK_LIBRARIES(${PYTHON_LIBRARY}) + +# install headers +install(DIRECTORY boost + DESTINATION "include" + FILES_MATCHING + PATTERN "*.hpp" + ${INSTALL_PERMISSIONS_SRC} + ) + +# add submodules +ADD_SUBDIRECTORY(libs/numpy/src) +ADD_SUBDIRECTORY(libs/numpy/example) +ADD_SUBDIRECTORY(libs/numpy/test) + +if (BUILD_DOCS) + ADD_SUBDIRECTORY(libs/numpy/doc) +endif() + + diff --git a/libs/numpy/cmake/FindNumPy.cmake b/libs/numpy/cmake/FindNumPy.cmake new file mode 100644 index 00000000..ecae7e6b --- /dev/null +++ b/libs/numpy/cmake/FindNumPy.cmake @@ -0,0 +1,90 @@ +# - Find the NumPy libraries +# This module finds if NumPy is installed, and sets the following variables +# indicating where it is. +# +# TODO: Update to provide the libraries and paths for linking npymath lib. +# +# NUMPY_FOUND - was NumPy found +# NUMPY_VERSION - the version of NumPy found as a string +# NUMPY_VERSION_MAJOR - the major version number of NumPy +# NUMPY_VERSION_MINOR - the minor version number of NumPy +# NUMPY_VERSION_PATCH - the patch version number of NumPy +# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 +# NUMPY_INCLUDE_DIRS - path to the NumPy include files + +#============================================================================ +# Copyright 2012 Continuum Analytics, Inc. +# +# MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# +#============================================================================ + +# Finding NumPy involves calling the Python interpreter +if(NumPy_FIND_REQUIRED) + find_package(PythonInterp REQUIRED) +else() + find_package(PythonInterp) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(NUMPY_FOUND FALSE) +endif() + +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "import numpy as n; print(n.__version__); print(n.get_include());" + RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS + OUTPUT_VARIABLE _NUMPY_VALUES + ERROR_VARIABLE _NUMPY_ERROR_VALUE + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) + if(NumPy_FIND_REQUIRED) + message(FATAL_ERROR + "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") + endif() + set(NUMPY_FOUND FALSE) +endif() + +# Convert the process output into a list +string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES}) +string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) +list(GET _NUMPY_VALUES 0 NUMPY_VERSION) +list(GET _NUMPY_VALUES 1 NUMPY_INCLUDE_DIRS) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) + +# Get the major and minor version numbers +string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) +list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) +list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) +list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) +string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) +math(EXPR NUMPY_VERSION_DECIMAL + "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") + +find_package_message(NUMPY + "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" + "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") + +set(NUMPY_FOUND TRUE) + diff --git a/libs/numpy/cmake/FindPythonLibsNew.cmake b/libs/numpy/cmake/FindPythonLibsNew.cmake new file mode 100644 index 00000000..b2a5d603 --- /dev/null +++ b/libs/numpy/cmake/FindPythonLibsNew.cmake @@ -0,0 +1,211 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpeter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# PYTHON_INCLUDE_PATH - path to where Python.h is found (deprecated) +# +# A function PYTHON_ADD_MODULE( src1 src2 ... srcN) is defined to build modules for python. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +# Use the Python interpreter to find the libs. +if(PythonLibsNew_FIND_REQUIRED) + find_package(PythonInterp REQUIRED) +else() + find_package(PythonInterp) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(s.PREFIX); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE + OUTPUT_STRIP_TRAILING_WHITESPACE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR + "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) + +# Make sure the Python has the same pointer-size as the chosen compiler +if(NOT ${PYTHON_SIZEOF_VOID_P} MATCHES ${CMAKE_SIZEOF_VOID_P}) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR + "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX ${PYTHON_PREFIX}) +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR ${PYTHON_INCLUDE_DIR}) +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES ${PYTHON_SITE_PACKAGES}) + +# TODO: All the nuances of CPython debug builds have not been dealt with/tested. +if(PYTHON_IS_DEBUG) + set(PYTHON_MODULE_EXTENSION "_d${PYTHON_MODULE_EXTENSION}") +endif() + +if(CMAKE_HOST_WIN32) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}.lib") +elseif(APPLE) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/lib/libpython${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.dylib") +else() + if(${PYTHON_SIZEOF_VOID_P} MATCHES 8) + set(_PYTHON_LIBS_SEARCH "${PYTHON_PREFIX}/lib64" "${PYTHON_PREFIX}/lib") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_PREFIX}/lib") + endif() + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library(PYTHON_LIBRARY + NAMES "python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_SYSTEM_ENVIRONMENT_PATH) +endif() + +# For backward compatibility, set PYTHON_INCLUDE_PATH, but make it internal. +SET(PYTHON_INCLUDE_PATH "${PYTHON_INCLUDE_DIR}" CACHE INTERNAL + "Path to where Python.h is found (deprecated)") + +MARK_AS_ADVANCED( + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + + +# Don't know how to get to this directory, just doing something simple :P +#INCLUDE(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +#FIND_PACKAGE_HANDLE_STANDARD_ARGS(PythonLibs DEFAULT_MSG PYTHON_LIBRARIES PYTHON_INCLUDE_DIRS) +find_package_message(PYTHON + "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") + + +# PYTHON_ADD_MODULE( src1 src2 ... srcN) is used to build modules for python. +FUNCTION(PYTHON_ADD_MODULE _NAME ) + GET_PROPERTY(_TARGET_SUPPORTS_SHARED_LIBS + GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) + OPTION(PYTHON_ENABLE_MODULE_${_NAME} "Add module ${_NAME}" TRUE) + OPTION(PYTHON_MODULE_${_NAME}_BUILD_SHARED + "Add module ${_NAME} shared" ${_TARGET_SUPPORTS_SHARED_LIBS}) + + # Mark these options as advanced + MARK_AS_ADVANCED(PYTHON_ENABLE_MODULE_${_NAME} + PYTHON_MODULE_${_NAME}_BUILD_SHARED) + + IF(PYTHON_ENABLE_MODULE_${_NAME}) + IF(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + SET(PY_MODULE_TYPE MODULE) + ELSE(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + SET(PY_MODULE_TYPE STATIC) + SET_PROPERTY(GLOBAL APPEND PROPERTY PY_STATIC_MODULES_LIST ${_NAME}) + ENDIF(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + + SET_PROPERTY(GLOBAL APPEND PROPERTY PY_MODULES_LIST ${_NAME}) + ADD_LIBRARY(${_NAME} ${PY_MODULE_TYPE} ${ARGN}) + TARGET_LINK_LIBRARIES(${_NAME} ${PYTHON_LIBRARIES}) + + IF(PYTHON_MODULE_${_NAME}_BUILD_SHARED) + SET_TARGET_PROPERTIES(${_NAME} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + SET_TARGET_PROPERTIES(${_NAME} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + ELSE() + ENDIF() + + ENDIF(PYTHON_ENABLE_MODULE_${_NAME}) +ENDFUNCTION(PYTHON_ADD_MODULE) + diff --git a/libs/numpy/cmake/README.txt b/libs/numpy/cmake/README.txt new file mode 100644 index 00000000..806d9fa5 --- /dev/null +++ b/libs/numpy/cmake/README.txt @@ -0,0 +1,9 @@ +The cmake files, FindNumPy.cmake and FindPythonLibsNew.cmake, in this +folder came from the numexpr project at +http://code.google.com/p/numexpr. + +The numexpr project was also a valuable resource in making many of the +cmake constructs used in the cmake lists files written for +Boost.NumPy. The boost-python-examples project at +https://github.com/TNG/boost-python-examples was another helpful +resource for understanding how to use cmake with boost.python. diff --git a/libs/numpy/doc/CMakeLists.txt b/libs/numpy/doc/CMakeLists.txt new file mode 100644 index 00000000..159f3fff --- /dev/null +++ b/libs/numpy/doc/CMakeLists.txt @@ -0,0 +1,56 @@ +project(doc-html) +file(GLOB NUMPY_DOC_DEPS conf.py *.rst) +message( STATUS "NUMPY_DOC_DEPS=${NUMPY_DOC_DEPS}" ) + +# add_custom_target(doc-html make -C ${PROJECT_SOURCE_DIR} html BUILDDIR=${PROJECT_BINARY_DIR}/_build) +# +# this custom target is a cross-platform python/sphinx way to +# replicate what the above make command is doing. +# +add_custom_target(doc-html + ${SPHINX_BUILD} + -b html + -c ${CMAKE_CURRENT_SOURCE_DIR} + -d .doctrees + ${CMAKE_CURRENT_SOURCE_DIR} + html + + DEPENDS ${NUMPY_DOC_DEPS} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Generating HTML Documentation" +) + +SET_PROPERTY(TARGET doc-html PROPERTY FOLDER "doc") + +install(DIRECTORY ${PROJECT_BINARY_DIR}/html + DESTINATION share/doc/libboost_numpy + OPTIONAL + ) + +if (PDFLATEX_COMPILER) + project(doc-pdf) + + add_custom_target(doc-pdf + ${SPHINX_BUILD} + -b latex + -c ${CMAKE_CURRENT_SOURCE_DIR} + -d .doctrees + ${CMAKE_CURRENT_SOURCE_DIR} + latex + + COMMAND ${PDFLATEX_COMPILER} --include-directory=latex --output-directory=latex latex/BoostNumPy.tex + COMMAND ${PDFLATEX_COMPILER} --include-directory=latex --output-directory=latex latex/BoostNumPy.tex + + DEPENDS ${NUMPY_DOC_DEPS} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Generating Latex-pdf Documentation" + ) + + SET_PROPERTY(TARGET doc-pdf PROPERTY FOLDER "doc") + + install(FILES ${PROJECT_BINARY_DIR}/latex/BoostNumPy.pdf + DESTINATION share/doc/libboost_numpy + OPTIONAL + ) + +endif() diff --git a/libs/numpy/doc/cmakeBuild.rst b/libs/numpy/doc/cmakeBuild.rst new file mode 100644 index 00000000..c8d41983 --- /dev/null +++ b/libs/numpy/doc/cmakeBuild.rst @@ -0,0 +1,167 @@ +============= + CMake Build +============= + +.. contents:: + :local: + +Usage +===== + +.. code-block:: bash + + $ mkdir build + $ cd build + $ cmake .. + +On my CentOs 6.3 linux system with a custom installation of boost, I +needed to invoke cmake with a special option as shown here to get +cmake to properly use the boost installation as referenced by the +environment variable :envvar:`BOOST_ROOT` or :envvar:`BOOST_DIR`. + +.. code-block:: bash + + $ cmake -D Boost_NO_BOOST_CMAKE=ON .. + +On windows I invoked cmake using: + +.. code-block:: bash + + > cmake -G "Visual Studio 9 2008 Win64" ^ + -D CMAKE_INSTALL_PREFIX=c:/pkg/x64-vc90 ^ + -D CMAKE_PREFIX_PATH=c:/pkg/x64-vc90 ^ + -D CMAKE_CONFIGURATION_TYPES="Debug;Release" ^ + .. + +Once you have the cmake generated build files you may build +Boost.NumPy. On linux you may build it using: + +.. code-block:: bash + + $ make + $ make install + +On windows you may build it using: + +.. code-block:: bash + + $ cmake --build . --config release + $ cmake --build . --config release --target install + +Note: You need to make sure that the cmake generator you use is +compatible with your python installation. The cmake scripts try to be +helpful, but the verification logic is incomplete. On both Linux and +Windows, I am using the 64-bit python from Enthought. On windows it is +built using Visual Studio 2008. I have also successfully used Visual +Studio 2010 for Boost.NumPy extension modules, but the VS 2010 +generated executables that embed python do not run because of an +apparent conflict with the runtimes. + +The build artifacts get installed to ``${CMAKE_INSTALL_PREFIX}`` +:file:`include` :file:`lib` and :file:`boost.numpy` where the first +two are the conventional locations for header files and libraries (aka +archives, shared objects, DLLs). The last one :file:`boost.numpy` is +my guess at how to install the tests and examples in a place that is +useful. But it is likely that this will need to be tweaked once other +people start using it. Here is an outline of the installed files. + +:: + + boost.numpy/doc/BoostNumPy.pdf + | |- html/index.html + |- example/demo_gaussian.py + | |- dtype.exe + | |- fromdata.exe + | |- gaussian.pyd + | |- ndarray.exe + | |- simple.exe + | |- ufunc.exe + | |- wrap.exe + |- test/dtype.py + |- dtype_mod.pyd + |- indexing.py + |- indexing_mod.pyd + |- ndarray.py + |- ndarray_mod.pyd + |- shapes.py + |- shapes_mod.pyd + |- templates.py + |- templates_mod.pyd + |- ufunc.py + |- ufunc_mod.pyd + + +You may develope and test without performing an install. The build +binary directory is configured so the executables are in the +:file:`build/bin` folder and the shared objects are in the +:file:`build/lib` folder. If you want to test then you simply need to +set the :envvar:`PYTHONPATH` environment variable to the lib folder +containing the shared object files so that python can find the +imported extension modules. + +Details +======= + +I borrowed from the python ``numexpr`` project the two ``.cmake`` +files :file:`FindNumPy.cmake` and :file:`FindPythonLibsNew.cmake` in +:file:`libs/numpy/cmake`. + +I followed a conventional structuring of the cmake +:file:`CMakeLists.txt` input files where the one at the top level +contains all of the configuration logic for the submodules that are +built. + +If you want to also generate this documentation, invoke cmake with the +additional argument ``-DBUILD_DOCS=ON`` and make sure that the sphinx +package is in your path. You may build the documentation using ``make +doc-html``. If pdflatex is also in your path, then there is an +additonal target ``make doc-pdf`` that will generate the pdf manual. + +CMakeLists.txt Source Files +=========================== + +For reference the source code of each of the new +:file:`CMakeLists.txt` files are included below. + +Top-Level +--------- + +:file:`Boost.NumPy/CMakeLists.txt` where the parent subdirectory +:file:`Boost.NumPy` is ommited in directory references in the rest of +this section. + +.. literalinclude:: ../../../CMakeLists.txt + :language: cmake + :linenos: + +Library Source +-------------- + +The file :file:`libs/numpy/src/CMakeLists.txt` contains the build of the :file:`boost_numpy library`. + +.. literalinclude:: ../src/CMakeLists.txt + :language: cmake + :linenos: + +Tests +----- + +The file :file:`libs/numpy/test/CMakeLists.txt` contains the python tests. + +.. literalinclude:: ../test/CMakeLists.txt + :language: cmake + :linenos: + +Examples +-------- + +The file :file:`libs/numpy/example/CMakeLists.txt` contains simple +examples (both an extension module and executables embedding python). + +.. literalinclude:: ../example/CMakeLists.txt + :language: cmake + :linenos: + + + + diff --git a/libs/numpy/doc/index.rst b/libs/numpy/doc/index.rst index 01c8c08a..8456fc32 100644 --- a/libs/numpy/doc/index.rst +++ b/libs/numpy/doc/index.rst @@ -13,4 +13,5 @@ Contents: Tutorial Reference + cmakeBuild.rst diff --git a/libs/numpy/example/CMakeLists.txt b/libs/numpy/example/CMakeLists.txt new file mode 100644 index 00000000..260af359 --- /dev/null +++ b/libs/numpy/example/CMakeLists.txt @@ -0,0 +1,51 @@ +# custom macro with most of the redundant code for making a python example module +macro( addPythonExe _name _srccpp ) + ADD_EXECUTABLE(${_name} ${_srccpp}) + + # make the pyd library link against boost_numpy python and boost + TARGET_LINK_LIBRARIES(${_name} boost_numpy ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) + + # put the example target into a VS solution folder named example (should + # be a no-op for Linux) + SET_PROPERTY(TARGET ${_name} PROPERTY FOLDER "example") +endmacro() + +macro( addPythonMod _name _srccpp ) + PYTHON_ADD_MODULE(${_name} ${_srccpp}) + + # make the pyd library link against boost_numpy python and boost + TARGET_LINK_LIBRARIES(${_name} boost_numpy ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) + + # put the example target into a VS solution folder named example (should + # be a no-op for Linux) + SET_PROPERTY(TARGET ${_name} PROPERTY FOLDER "example") +endmacro() + +addPythonMod(gaussian gaussian.cpp) +addPythonExe(dtype dtype.cpp) +addPythonExe(fromdata fromdata.cpp) +addPythonExe(ndarray ndarray.cpp) +addPythonExe(simple simple.cpp) +addPythonExe(ufunc ufunc.cpp) +addPythonExe(wrap wrap.cpp) + +# # installation logic (skip until it is better thought out) +# set(DEST_EXAMPLE boost.numpy/example) +# +# # install executables demonstrating embedding python +# install(TARGETS dtype fromdata ndarray simple ufunc wrap RUNTIME +# DESTINATION ${DEST_EXAMPLE} +# ${INSTALL_PERMSSIONS_RUNTIME} +# ) +# +# # install extension module +# install(TARGETS gaussian LIBRARY +# DESTINATION ${DEST_EXAMPLE} +# ${INSTALL_PERMSSIONS_RUNTIME} +# ) +# +# # install source file using the extension module +# install(FILES demo_gaussian.py +# DESTINATION ${DEST_EXAMPLE} +# ${INSTALL_PERMSSIONS_SRC} +# ) diff --git a/libs/numpy/example/gaussian.cpp b/libs/numpy/example/gaussian.cpp index 41945989..85045382 100644 --- a/libs/numpy/example/gaussian.cpp +++ b/libs/numpy/example/gaussian.cpp @@ -8,6 +8,11 @@ #include #include +#ifndef M_PI +#include +const double M_PI = boost::math::constants::pi(); +#endif + namespace bp = boost::python; namespace bn = boost::numpy; diff --git a/libs/numpy/example/ndarray.cpp b/libs/numpy/example/ndarray.cpp index 84560c20..510fd94a 100644 --- a/libs/numpy/example/ndarray.cpp +++ b/libs/numpy/example/ndarray.cpp @@ -17,6 +17,10 @@ namespace p = boost::python; namespace np = boost::numpy; +#if _MSC_VER +using boost::uint8_t; +#endif + int main(int argc, char **argv) { // Initialize the Python runtime. diff --git a/libs/numpy/src/CMakeLists.txt b/libs/numpy/src/CMakeLists.txt new file mode 100644 index 00000000..af11cb30 --- /dev/null +++ b/libs/numpy/src/CMakeLists.txt @@ -0,0 +1,25 @@ +add_library(boost_numpy ${LIBRARY_TYPE} + # header files + ../../../boost/numpy/dtype.hpp + ../../../boost/numpy/internal.hpp + ../../../boost/numpy/invoke_matching.hpp + ../../../boost/numpy/matrix.hpp + ../../../boost/numpy/ndarray.hpp + ../../../boost/numpy/numpy_object_mgr_traits.hpp + ../../../boost/numpy/scalars.hpp + ../../../boost/numpy/ufunc.hpp + + # source files (in current directory) + dtype.cpp + scalars.cpp + ndarray.cpp + matrix.cpp + ufunc.cpp + numpy.cpp +) + +install(TARGETS boost_numpy + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE +) diff --git a/libs/numpy/test/CMakeLists.txt b/libs/numpy/test/CMakeLists.txt new file mode 100644 index 00000000..1c89e853 --- /dev/null +++ b/libs/numpy/test/CMakeLists.txt @@ -0,0 +1,65 @@ +project(BoostNumpyTests) + +if (WIN32) + set(runCmakeTest runCmakeTest.bat) + foreach(cfg ${CMAKE_CONFIGURATION_TYPES}) + message( STATUS "configuring runCmakeTest for cfg=${cfg}" ) + CONFIGURE_FILE( ${runCmakeTest}.in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${cfg}/${runCmakeTest} @ONLY ) + endforeach() +else() + set(runCmakeTest runCmakeTest.sh) + CONFIGURE_FILE( ${runCmakeTest}.in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${runCmakeTest} @ONLY ) +endif() + +set( TEST_SOURCE_DIR ${PROJECT_SOURCE_DIR} ) +set( TestCommand ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${runCmakeTest} ) + +# custom macro with most of the redundant code for making a python test +macro( addPythonTest _name _srcpy ) + # make the pyd library link against boost_numpy python and boost + TARGET_LINK_LIBRARIES(${_name} boost_numpy ${PYTHON_LIBRARIES} ${Boost_LIBRARIES}) + + # make a test of the module using the python source file in the test directory + ADD_TEST(${_name} ${TestCommand} ${TEST_SOURCE_DIR}/${_srcpy}) + + # set the regex to use to recognize a failure since `python testfoo.py` + # does not seem to return non-zero with a test failure + set_property(TEST ${_name} PROPERTY FAIL_REGULAR_EXPRESSION "ERROR\\:" "ImportError\\: DLL load failed\\: " ) + + # put the test target into a VS solution folder named test (should + # be a no-op for Linux) + SET_PROPERTY(TARGET ${_name} PROPERTY FOLDER "test") +endmacro() + +PYTHON_ADD_MODULE(dtype_mod dtype_mod.cpp) +addPythonTest( dtype_mod dtype.py ) + +PYTHON_ADD_MODULE(indexing_mod indexing_mod.cpp) +addPythonTest( indexing_mod indexing.py ) + +PYTHON_ADD_MODULE(ndarray_mod ndarray_mod.cpp) +addPythonTest( ndarray_mod ndarray.py ) + +PYTHON_ADD_MODULE(shapes_mod shapes_mod.cpp) +addPythonTest( shapes_mod shapes.py ) + +PYTHON_ADD_MODULE(templates_mod templates_mod.cpp) +addPythonTest( templates_mod templates.py ) + +PYTHON_ADD_MODULE(ufunc_mod ufunc_mod.cpp) +addPythonTest( ufunc_mod ufunc.py ) + +# installation logic (skip until it is better thought out) +# set(DEST_TEST boost.numpy/test) +# +# # copy the extension modules to DEST_TEST +# install(TARGETS dtype_mod indexing_mod ndarray_mod shapes_mod templates_mod ufunc_mod LIBRARY +# DESTINATION ${DEST_TEST} +# ${INSTALL_PERMSSIONS_RUNTIME} +# ) +# +# # copy the source test python modules to DEST_TEST too +# install(FILES dtype.py indexing.py ndarray.py shapes.py templates.py ufunc.py +# DESTINATION ${DEST_TEST} +# ${INSTALL_PERMSSIONS_SRC} +# ) diff --git a/libs/numpy/test/runCmakeTest.bat.in b/libs/numpy/test/runCmakeTest.bat.in new file mode 100644 index 00000000..fb1ba438 --- /dev/null +++ b/libs/numpy/test/runCmakeTest.bat.in @@ -0,0 +1,4 @@ +set local +set PYTHONPATH=@CMAKE_LIBRARY_OUTPUT_DIRECTORY@/@cfg@;%PYTHONPATH% +python %1 +endlocal diff --git a/libs/numpy/test/runCmakeTest.sh.in b/libs/numpy/test/runCmakeTest.sh.in new file mode 100755 index 00000000..efaa756d --- /dev/null +++ b/libs/numpy/test/runCmakeTest.sh.in @@ -0,0 +1,3 @@ +#!/bin/bash +export PYTHONPATH=@CMAKE_LIBRARY_OUTPUT_DIRECTORY@:${PYTHONPATH} +python $1 \ No newline at end of file