diff --git a/CMakeLists.txt b/CMakeLists.txt index e93edab..ea36122 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,10 @@ set(XTENSOR_PYTHON_HEADERS ) add_library(xtensor-python INTERFACE) +target_compile_definitions(xtensor-python + INTERFACE + "$<$,$>:HAVE_SNPRINTF>" + ) target_include_directories(xtensor-python INTERFACE "$" $) diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..2768f49 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,119 @@ +trigger: + - master + +jobs: + + # Configure, build, install, and test job + - job: 'build' + pool: + vmImage: 'vs2015-win2012r2' + timeoutInMinutes: 360 + steps: + + # Install Chocolatey (https://chocolatey.org/install#install-with-powershellexe) + - powershell: | + Set-ExecutionPolicy Bypass -Scope Process -Force + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + Write-Host "##vso[task.setvariable variable=PATH]$env:PATH" + choco --version + displayName: "Install Chocolatey" + + # Install Miniconda + - script: | + echo 'PATH before %PATH%' + echo 'PYTHONPATH before %PYTHONPATH%' + set PATH=%PATH:C:\Python27;=% + choco install miniconda3 --yes + echo 'PATH after %PATH%' + echo 'PYTHONPATH after %PYTHONPATH%' + set PATH=C:\tools\miniconda3\Scripts;C:\tools\miniconda3;C:\tools\miniconda3\Library\bin;%PATH% + echo '##vso[task.setvariable variable=PATH]%PATH%' + echo 'LIB before %LIB%' + set LIB=C:\tools\miniconda3\Library\lib;%LIB% + echo 'LIB after %LIB%' + echo '##vso[task.setvariable variable=LIB]%LIB%' + conda --version + displayName: "Install Miniconda" + + # Configure Miniconda + - script: | + conda config --set always_yes yes + conda config --append channels conda-forge + conda info + echo 'PATH again %PATH%' + echo 'PYTHONPATH again %PYTHONPATH%' + echo 'LIB again %LIB%' + displayName: "Configure Miniconda" + + # Create conda enviroment + # Note: conda activate doesn't work here, because it creates a new shell! + - script: | + conda install cmake ^ + gtest ^ + ninja ^ + pybind11 ^ + pytest ^ + numpy ^ + xtensor ^ + python=3.6 + conda list + displayName: "Install conda packages" + + # Install LLVM + # Note: LLVM distributed by conda is too old + - script: | + choco install llvm --yes + set PATH=C:\Program Files\LLVM\bin;%PATH% + echo '##vso[task.setvariable variable=PATH]%PATH%' + clang-cl --version + displayName: "Install LLVM" + + # Configure + - script: | + setlocal EnableDelayedExpansion + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 + mkdir build & cd build + cmake -G Ninja ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_C_COMPILER=clang-cl ^ + -DCMAKE_CXX_COMPILER=clang-cl ^ + -DBUILD_TESTS=ON ^ + -DCMAKE_INSTALL_PREFIX=C:\tools\miniconda3 ^ + $(Build.SourcesDirectory) + displayName: "Configure xtensor-python" + workingDirectory: $(Build.BinariesDirectory) + + # Build + - script: | + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 + cmake --build . ^ + --config Release ^ + --target test_xtensor_python ^ + -- -v + displayName: "Build xtensor-python" + workingDirectory: $(Build.BinariesDirectory)/build + + # Install + - script: | + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 + cmake --build . ^ + --config Release ^ + --target install ^ + -- -v + displayName: "Install xtensor-python" + workingDirectory: $(Build.BinariesDirectory)/build + + # Test (Google test) + - script: | + echo 'PATH and again %PATH%' + echo 'PYTHONPATH and again %PYTHONPATH%' + echo 'LIB and again %LIB%' + .\test_xtensor_python + displayName: "Test xtensor-python (Google test)" + workingDirectory: $(Build.BinariesDirectory)/build/test + + # Test (pytest) + - script: | + py.test -s + displayName: "Test xtensor-python (pytest)" + workingDirectory: $(Build.BinariesDirectory) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index dfb5311..9286453 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -9,44 +9,53 @@ message(STATUS "Forcing tests build type to Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) -include(CheckCXXCompilerFlag) - string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion") - CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG) +include(${CMAKE_CURRENT_LIST_DIR}/../test/set_compiler_flag.cmake) +if(CPP17) + # User requested C++17, but compiler might not oblige. + set_compiler_flag( + _cxx_std_flag CXX + "-std=c++17" # this should work with GNU, Intel, PGI + "/std:c++17" # this should work with MSVC + ) + if(_cxx_std_flag) + message(STATUS "Building with C++17") + endif() +else() + set_compiler_flag( + _cxx_std_flag CXX REQUIRED + "-std=c++14" # this should work with GNU, Intel, PGI + "/std:c++14" # this should work with MSVC + ) + message(STATUS "Building with C++14") +endif() - if (HAS_CPP14_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") - else() - message(FATAL_ERROR "Unsupported compiler -- xtensor requires C++14 support!") - endif() +if(NOT _cxx_std_flag) + message(FATAL_ERROR "xtensor-python needs a C++14-compliant compiler.") +endif() + +# Check for Link Time Optimization support +set_compiler_flag( + _lto_flag CXX + "-flto" # this works with GNU and Clang + "-ipo" # this works with Intel +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT WIN32)) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion") # Enable link time optimization and set the default symbol # visibility to hidden (very important to obtain small binaries) - if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) + if(NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) # Default symbol visibility set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - # Check for Link Time Optimization support - # (GCC/Clang) - CHECK_CXX_COMPILER_FLAG("-flto" HAS_LTO_FLAG) - if (HAS_LTO_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") - endif() - - # Intel equivalent to LTO is called IPO - if (CMAKE_CXX_COMPILER_ID MATCHES "Intel") - CHECK_CXX_COMPILER_FLAG("-ipo" HAS_IPO_FLAG) - if (HAS_IPO_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ipo") - endif() + if(_lto_flag) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_lto_flag}") endif() endif() -endif() - -if(MSVC) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /MP /bigobj") set(CMAKE_EXE_LINKER_FLAGS /MANIFEST:NO) foreach(flag_var @@ -54,6 +63,28 @@ if(MSVC) CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}") endforeach() +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion") + if(NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) + # Default symbol visibility + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + # Check for Link Time Optimization support + if(_lto_flag) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_lto_flag}") + endif() + endif() + else() # We are using clang-cl + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} /EHsc /MP /bigobj") + set(CMAKE_EXE_LINKER_FLAGS /MANIFEST:NO) + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}") + endforeach() + endif() +else() + message(FATAL_ERROR "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") endif() set(XTENSOR_PYTHON_BENCHMARK diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 30857d0..67585dd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,36 +12,56 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-python-test) find_package(pybind11 REQUIRED) - set(PYBIND11_INCLUDE_DIR ${pybind11_INCLUDE_DIRS}) find_package(xtensor REQUIRED CONFIG) - set(XTENSOR_INCLUDE_DIR ${xtensor_INCLUDE_DIRS}) find_package(xtensor-python REQUIRED CONFIG) - set(XTENSOR_PYTHON_INCLUDE_DIR ${xtensor-python_INCLUDE_DIRS}) endif () message(STATUS "Forcing tests build type to Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) -include(CheckCXXCompilerFlag) - string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion -fvisibility=hidden") - CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG) +include(set_compiler_flag.cmake) + +if(CPP17) + # User requested C++17, but compiler might not oblige. + set_compiler_flag( + _cxx_std_flag CXX + "-std=c++17" # this should work with GNU, Intel, PGI + "/std:c++17" # this should work with MSVC + ) + if(_cxx_std_flag) + message(STATUS "Building with C++17") + endif() +else() + set_compiler_flag( + _cxx_std_flag CXX REQUIRED + "-std=c++14" # this should work with GNU, Intel, PGI + "/std:c++14" # this should work with MSVC + ) + message(STATUS "Building with C++14") +endif() - if (HAS_CPP14_FLAG) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") - else() - message(FATAL_ERROR "Unsupported compiler -- xtensor requires C++14 support!") - endif() +if(NOT _cxx_std_flag) + message(FATAL_ERROR "xtensor-blas needs a C++14-compliant compiler.") endif() -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /MP /bigobj") +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT WIN32)) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion -fvisibility=hidden") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} /EHsc /MP /bigobj") + set(CMAKE_EXE_LINKER_FLAGS /MANIFEST:NO) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion -fvisibility-hidden") + else() # We are using clang-cl + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_cxx_std_flag} /EHsc /MP /bigobj") set(CMAKE_EXE_LINKER_FLAGS /MANIFEST:NO) + endif() +else() + message(FATAL_ERROR "Unsupported compiler: ${CMAKE_CXX_COMPILER_ID}") endif() if (DOWNLOAD_GTEST OR GTEST_SRC_DIR) diff --git a/test/set_compiler_flag.cmake b/test/set_compiler_flag.cmake new file mode 100644 index 0000000..1ce8c86 --- /dev/null +++ b/test/set_compiler_flag.cmake @@ -0,0 +1,66 @@ +# Copied from +# https://github.com/dev-cafe/cmake-cookbook/blob/master/chapter-07/recipe-03/c-cxx-example/set_compiler_flag.cmake +# Adapted from +# https://github.com/robertodr/ddPCM/blob/expose-C-api/cmake/custom/compilers/SetCompilerFlag.cmake +# which was adapted by Roberto Di Remigio from +# https://github.com/SethMMorton/cmake_fortran_template/blob/master/cmake/Modules/SetCompileFlag.cmake + +# Given a list of flags, this stateless function will try each, one at a time, +# and set result to the first flag that works. +# If none of the flags works, result is "". +# If the REQUIRED key is given and no flag is found, a FATAL_ERROR is raised. +# +# Call is: +# set_compile_flag(result (Fortran|C|CXX) flag1 flag2 ...) +# +# Example: +# set_compiler_flag(working_compile_flag C REQUIRED "-Wall" "-warn all") + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckFortranCompilerFlag) + +function(set_compiler_flag _result _lang) + # build a list of flags from the arguments + set(_list_of_flags) + # also figure out whether the function + # is required to find a flag + set(_flag_is_required FALSE) + foreach(_arg IN ITEMS ${ARGN}) + string(TOUPPER "${_arg}" _arg_uppercase) + if(_arg_uppercase STREQUAL "REQUIRED") + set(_flag_is_required TRUE) + else() + list(APPEND _list_of_flags "${_arg}") + endif() + endforeach() + + set(_flag_found FALSE) + # loop over all flags, try to find the first which works + foreach(flag IN ITEMS ${_list_of_flags}) + + unset(_${flag}_works CACHE) + if(_lang STREQUAL "C") + check_c_compiler_flag("${flag}" _${flag}_works) + elseif(_lang STREQUAL "CXX") + check_cxx_compiler_flag("${flag}" _${flag}_works) + elseif(_lang STREQUAL "Fortran") + check_Fortran_compiler_flag("${flag}" _${flag}_works) + else() + message(FATAL_ERROR "Unknown language in set_compiler_flag: ${_lang}") + endif() + + # if the flag works, use it, and exit + # otherwise try next flag + if(_${flag}_works) + set(${_result} "${flag}" PARENT_SCOPE) + set(_flag_found TRUE) + break() + endif() + endforeach() + + # raise an error if no flag was found + if(_flag_is_required AND NOT _flag_found) + message(FATAL_ERROR "None of the required flags were supported") + endif() +endfunction()