Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clang-cl support #199

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ set(XTENSOR_PYTHON_HEADERS
)

add_library(xtensor-python INTERFACE)
target_compile_definitions(xtensor-python
INTERFACE
"$<$<AND:$<C_COMPILER_ID:Clang>,$<BOOL:${WIN32}>>:HAVE_SNPRINTF>"
)
target_include_directories(xtensor-python INTERFACE
"$<BUILD_INTERFACE:${XTENSOR_PYTHON_INCLUDE_DIR};${pybind11_INCLUDE_DIRS};${NUMPY_INCLUDE_DIRS}>"
$<INSTALL_INTERFACE:include>)
Expand Down
119 changes: 119 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -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)
85 changes: 58 additions & 27 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,82 @@
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
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()
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
Expand Down
50 changes: 35 additions & 15 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
66 changes: 66 additions & 0 deletions test/set_compiler_flag.cmake
Original file line number Diff line number Diff line change
@@ -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) <REQUIRED> 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()