diff --git a/.github/workflows/build_cpp.yml b/.github/workflows/build_cpp.yml index a983a4c4ef..226d24d4bf 100644 --- a/.github/workflows/build_cpp.yml +++ b/.github/workflows/build_cpp.yml @@ -221,6 +221,26 @@ jobs: run: build/Release/dtest.exe --runall -q - name: Build ancillary tools run: cmake --build build --config Release --target imglab htmlify dtoc --parallel 4 + - name: Install an AVX-enabled MSVC package + working-directory: ${{ github.workspace }} + run: | + cmake . -B "${{ runner.temp }}/dlib-package-build" -DCMAKE_INSTALL_PREFIX="${{ runner.temp }}/dlib-package-install" -DDLIB_NO_GUI_SUPPORT=ON -DDLIB_USE_BLAS=OFF -DDLIB_USE_LAPACK=OFF -DDLIB_USE_CUDA=OFF -DUSE_AVX_INSTRUCTIONS=ON + cmake --build "${{ runner.temp }}/dlib-package-build" --config Release --target install --parallel 4 + - name: Test installed package with MSVC + working-directory: ${{ github.workspace }} + run: | + cmake dlib/cmake_utils/test_for_dlib_config -B "${{ runner.temp }}/dlib-package-consumer-msvc" -Ddlib_DIR="${{ runner.temp }}/dlib-package-install/lib/cmake/dlib" -DDLIB_CONFIG_TEST_EXPECT_AVX=ON + cmake --build "${{ runner.temp }}/dlib-package-consumer-msvc" --config Release --parallel 4 + - name: Test installed MSVC package with clang++ + working-directory: ${{ github.workspace }} + run: | + cmake dlib/cmake_utils/test_for_dlib_config -B "${{ runner.temp }}/dlib-package-consumer-clang" -G Ninja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -Ddlib_DIR="${{ runner.temp }}/dlib-package-install/lib/cmake/dlib" -DDLIB_CONFIG_TEST_EXPECT_AVX=ON + cmake --build "${{ runner.temp }}/dlib-package-consumer-clang" --parallel 4 + - name: Test installed MSVC package with clang-cl + working-directory: ${{ github.workspace }} + run: | + cmake dlib/cmake_utils/test_for_dlib_config -B "${{ runner.temp }}/dlib-package-consumer-clang-cl" -T ClangCL -Ddlib_DIR="${{ runner.temp }}/dlib-package-install/lib/cmake/dlib" -DDLIB_CONFIG_TEST_EXPECT_AVX=ON + cmake --build "${{ runner.temp }}/dlib-package-consumer-clang-cl" --config Release --parallel 4 # Disable this because macos targets aren't working on github actions right now. #macos-latest: diff --git a/dlib/CMakeLists.txt b/dlib/CMakeLists.txt index 04b5d92059..cb63942cdf 100644 --- a/dlib/CMakeLists.txt +++ b/dlib/CMakeLists.txt @@ -806,13 +806,12 @@ if (NOT TARGET dlib) endif() target_compile_features(dlib PUBLIC cxx_std_14) - if((MSVC AND CMAKE_VERSION VERSION_LESS 3.11)) - target_compile_options(dlib PUBLIC ${active_compile_opts}) - target_compile_options(dlib PRIVATE ${active_compile_opts_private}) - else() - target_compile_options(dlib PUBLIC $<$:${active_compile_opts}>) - target_compile_options(dlib PRIVATE $<$:${active_compile_opts_private}>) - endif() + # Build-tree clients share dlib's compiler, so forward the options detected + # above to them. Do not put those options in the installed target, where + # dlibConfig.cmake recreates them for the consuming compiler's frontend. + target_compile_options(dlib INTERFACE "$:${active_compile_opts}>>") + target_compile_options(dlib PRIVATE $<$:${active_compile_opts}>) + target_compile_options(dlib PRIVATE $<$:${active_compile_opts_private}>) # Install the library if (NOT DLIB_IN_PROJECT_BUILD) diff --git a/dlib/cmake_utils/dlibConfig.cmake.in b/dlib/cmake_utils/dlibConfig.cmake.in index 4f7ea1ee28..4389b8182c 100644 --- a/dlib/cmake_utils/dlibConfig.cmake.in +++ b/dlib/cmake_utils/dlibConfig.cmake.in @@ -15,7 +15,7 @@ # Our library dependencies (contains definitions for IMPORTED targets) -if(NOT TARGET dlib-shared AND NOT dlib_BINARY_DIR) +if(NOT TARGET dlib::dlib AND NOT TARGET dlib-shared AND NOT dlib_BINARY_DIR) # Compute paths get_filename_component(dlib_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) include("${dlib_CMAKE_DIR}/dlib.cmake") @@ -29,6 +29,73 @@ if(NOT TARGET dlib-shared AND NOT dlib_BINARY_DIR) endif() unset(dlib_deps_threads_idx) unset(dlib_deps_threads_check) + + # Compiler options on an imported target are evaluated in the consuming + # project, not in the project that built dlib. Select them here so an + # installed package never passes (for example) cl-style options to the + # clang++ driver. MSVC is intentionally used instead of the compiler ID: + # CMake also sets it for compilers such as clang-cl that accept cl syntax. + set(_dlib_compile_options) + set(_dlib_compile_definitions) + set(_dlib_simd_level "@DLIB_SIMD_LEVEL@") + + if(MSVC) + list(APPEND _dlib_compile_options /bigobj) + + if(_dlib_simd_level STREQUAL "AVX") + list(APPEND _dlib_compile_options /arch:AVX) + elseif(_dlib_simd_level STREQUAL "SSE4") + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + list(APPEND _dlib_compile_options /arch:SSE2) + endif() + list(APPEND _dlib_compile_definitions DLIB_HAVE_SSE2 DLIB_HAVE_SSE3 DLIB_HAVE_SSE41) + elseif(_dlib_simd_level STREQUAL "SSE2") + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + list(APPEND _dlib_compile_options /arch:SSE2) + endif() + list(APPEND _dlib_compile_definitions DLIB_HAVE_SSE2) + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.3) + list(APPEND _dlib_compile_options -Xclang -fcxx-exceptions) + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0) + list(APPEND _dlib_compile_options -ftemplate-depth=500) + endif() + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR + CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + if(_dlib_simd_level STREQUAL "AVX") + list(APPEND _dlib_compile_options -mavx) + elseif(_dlib_simd_level STREQUAL "SSE4") + list(APPEND _dlib_compile_options -msse4) + elseif(_dlib_simd_level STREQUAL "SSE2") + list(APPEND _dlib_compile_options -msse2) + elseif(_dlib_simd_level STREQUAL "NEON") + list(APPEND _dlib_compile_options -mfpu=neon) + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND _dlib_compile_options -Wreturn-type) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.0) + list(APPEND _dlib_compile_options -ftemplate-depth=500) + endif() + endif() + + if(_dlib_compile_options) + target_compile_options(dlib::dlib INTERFACE "$<$:${_dlib_compile_options}>") + endif() + if(_dlib_compile_definitions) + target_compile_definitions(dlib::dlib INTERFACE "$<$:${_dlib_compile_definitions}>") + endif() + + unset(_dlib_compile_definitions) + unset(_dlib_compile_options) + unset(_dlib_simd_level) endif() set(dlib_LIBRARIES dlib::dlib) @@ -52,6 +119,3 @@ endfunction() variable_watch(dlib_LIBRARIES __deprecated_var) variable_watch(dlib_LIBS __deprecated_var) variable_watch(dlib_INCLUDE_DIRS __deprecated_var) - - - diff --git a/dlib/cmake_utils/set_compiler_specific_options.cmake b/dlib/cmake_utils/set_compiler_specific_options.cmake index 6e2682da08..be05c88f45 100644 --- a/dlib/cmake_utils/set_compiler_specific_options.cmake +++ b/dlib/cmake_utils/set_compiler_specific_options.cmake @@ -24,25 +24,29 @@ endif() set(gcc_like_compilers GNU Clang Intel) set(intel_archs x86_64 i386 i686 AMD64 amd64 x86) +set(DLIB_SIMD_LEVEL NONE) -# Setup some options to allow a user to enable SSE and AVX instruction use. +# Setup some options to allow a user to enable SSE and AVX instruction use. if ((";${gcc_like_compilers};" MATCHES ";${CMAKE_CXX_COMPILER_ID};") AND (";${intel_archs};" MATCHES ";${CMAKE_SYSTEM_PROCESSOR};") AND NOT USE_AUTO_VECTOR) option(USE_SSE2_INSTRUCTIONS "Compile your program with SSE2 instructions" OFF) option(USE_SSE4_INSTRUCTIONS "Compile your program with SSE4 instructions" OFF) option(USE_AVX_INSTRUCTIONS "Compile your program with AVX instructions" OFF) if(USE_AVX_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL AVX) list(APPEND active_compile_opts -mavx) message(STATUS "Enabling AVX instructions") elseif (USE_SSE4_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL SSE4) list(APPEND active_compile_opts -msse4) message(STATUS "Enabling SSE4 instructions") elseif(USE_SSE2_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL SSE2) list(APPEND active_compile_opts -msse2) message(STATUS "Enabling SSE2 instructions") endif() -elseif (MSVC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # else if using Visual Studio +elseif (MSVC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # else if using Visual Studio # Use SSE2 by default when using Visual Studio. option(USE_SSE2_INSTRUCTIONS "Compile your program with SSE2 instructions" ON) option(USE_SSE4_INSTRUCTIONS "Compile your program with SSE4 instructions" OFF) @@ -51,9 +55,11 @@ elseif (MSVC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # else if using Visu include(CheckTypeSize) check_type_size( "void*" SIZE_OF_VOID_PTR) if(USE_AVX_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL AVX) list(APPEND active_compile_opts /arch:AVX) message(STATUS "Enabling AVX instructions") elseif (USE_SSE4_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL SSE4) # Visual studio doesn't have an /arch:SSE2 flag when building in 64 bit modes. # So only give it when we are doing a 32 bit build. if (SIZE_OF_VOID_PTR EQUAL 4) @@ -64,6 +70,7 @@ elseif (MSVC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # else if using Visu list(APPEND active_preprocessor_switches "-DDLIB_HAVE_SSE3") list(APPEND active_preprocessor_switches "-DDLIB_HAVE_SSE41") elseif(USE_SSE2_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL SSE2) # Visual studio doesn't have an /arch:SSE2 flag when building in 64 bit modes. # So only give it when we are doing a 32 bit build. if (SIZE_OF_VOID_PTR EQUAL 4) @@ -77,6 +84,7 @@ elseif((";${gcc_like_compilers};" MATCHES ";${CMAKE_CXX_COMPILER_ID};") AND ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "^arm")) option(USE_NEON_INSTRUCTIONS "Compile your program with ARM-NEON instructions" OFF) if(USE_NEON_INSTRUCTIONS) + set(DLIB_SIMD_LEVEL NEON) list(APPEND active_compile_opts -mfpu=neon) message(STATUS "Enabling ARM-NEON instructions") endif() @@ -110,11 +118,10 @@ if (MSVC) # RAM. list(APPEND active_compile_opts_private "/MP") - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.3) + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.3) # Clang can compile all Dlib's code at Windows platform. Tested with Clang 5 list(APPEND active_compile_opts -Xclang) list(APPEND active_compile_opts -fcxx-exceptions) endif() endif() - diff --git a/dlib/cmake_utils/test_for_dlib_config/CMakeLists.txt b/dlib/cmake_utils/test_for_dlib_config/CMakeLists.txt new file mode 100644 index 0000000000..e64217368a --- /dev/null +++ b/dlib/cmake_utils/test_for_dlib_config/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.17.0) + +project(dlib_config_test LANGUAGES CXX) + +find_package(dlib CONFIG REQUIRED) + +get_target_property(dlib_config_test_options dlib::dlib INTERFACE_COMPILE_OPTIONS) +find_package(dlib CONFIG REQUIRED) +get_target_property(dlib_config_test_options_after_second_find dlib::dlib INTERFACE_COMPILE_OPTIONS) +if(NOT "${dlib_config_test_options}" STREQUAL "${dlib_config_test_options_after_second_find}") + message(FATAL_ERROR "Finding the dlib package twice duplicated its compiler options") +endif() + +if(MSVC) + if(NOT ";${dlib_config_test_options};" MATCHES "/bigobj") + message(FATAL_ERROR "dlib::dlib did not provide /bigobj to an MSVC-style compiler frontend") + endif() +elseif(";${dlib_config_test_options};" MATCHES "/bigobj") + message(FATAL_ERROR "dlib::dlib provided /bigobj to a non-MSVC compiler frontend") +endif() + +if(DLIB_CONFIG_TEST_EXPECT_AVX) + if(MSVC) + if(NOT ";${dlib_config_test_options};" MATCHES "/arch:AVX") + message(FATAL_ERROR "dlib::dlib did not translate its AVX requirement to /arch:AVX") + endif() + elseif(NOT ";${dlib_config_test_options};" MATCHES "-mavx") + message(FATAL_ERROR "dlib::dlib did not translate its AVX requirement to -mavx") + endif() +endif() + +add_executable(dlib_config_test dlib_config_test.cpp) +target_link_libraries(dlib_config_test PRIVATE dlib::dlib) + +if(DLIB_CONFIG_TEST_EXPECT_AVX) + target_compile_definitions(dlib_config_test PRIVATE DLIB_CONFIG_TEST_EXPECT_AVX) +endif() diff --git a/dlib/cmake_utils/test_for_dlib_config/dlib_config_test.cpp b/dlib/cmake_utils/test_for_dlib_config/dlib_config_test.cpp new file mode 100644 index 0000000000..dc22ffc735 --- /dev/null +++ b/dlib/cmake_utils/test_for_dlib_config/dlib_config_test.cpp @@ -0,0 +1,10 @@ +#include + +#if defined(DLIB_CONFIG_TEST_EXPECT_AVX) && !defined(DLIB_HAVE_AVX) +#error "The installed dlib package did not enable AVX for its consumer" +#endif + +int main() +{ + return 0; +}