From c137c0a87b58da4c5a8af7d18681ec60361c156c Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Thu, 9 Feb 2017 21:27:29 -0500 Subject: [PATCH] Store LTO flags in PYBIND11_LTO_{CXX,LINKER}_FLAGS cache variables This both lets us not bother rechecking LTO flags when cmake reinvokes itself, and also lets the cmake invoker override to specify custom or no LTO flags by setting the cache variable with -DPYBIND11_LTO_CXX_FLAGS= when invoking cmake. --- tools/pybind11Tools.cmake | 115 +++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index dd3fadf4f..e0985452b 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -36,61 +36,72 @@ function(select_cxx_standard) endif() endfunction() -# Internal: try compiling with the given c++ and linker flags; set success_out to 1 if it works -function(_pybind11_check_cxx_and_linker_flags cxxflags linkerflags success_out) - set(CMAKE_REQUIRED_QUIET 1) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${linkerflags}) - check_cxx_compiler_flag("${cxxflags}" has_lto) - set(${success_out} ${has_lto} PARENT_SCOPE) +# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and +# linkerflags are lists of flags to use. The result variable is a unique variable name for each set +# of flags: the compilation result will be cached base on the result variable. If the flags work, +# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if (${result}) + set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) + set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) + endif() endfunction() -# Wraps the above in a macro that sets the flags in the parent scope and returns if the check -# succeeded (since this is a macro, not a function, the PARENT_SCOPE set and return is from the -# caller's POV) -macro(_pybind11_return_if_cxx_and_linker_flags_work feature cxxflags linkerflags cxx_out linker_out) - _pybind11_check_cxx_and_linker_flags(${cxxflags} ${linkerflags} check_success) - if(check_success) - set(${cxx_out} ${cxxflags} PARENT_SCOPE) - set(${linker_out} ${linkerflags} PARENT_SCOPE) - message(STATUS "Found ${feature} flags: ${cxxflags}") - return() - endif() -endmacro() - # Internal: find the appropriate link time optimization flags for this compiler -function(_pybind11_find_lto_flags cxxflags_out linkerflags_out prefer_thin_lto) - if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - set(cxx_append "") - set(linker_append "") - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) - # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it - set(linker_append "$<$: -O3>") - elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(cxx_append " -fno-fat-lto-objects") +function(_pybind11_add_lto_flags target_name prefer_thin_lto) + if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) + set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") + set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN + "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO + "-flto${cxx_append}" "-flto${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO + "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG + "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) endif() - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND ${prefer_thin_lto}) - _pybind11_return_if_cxx_and_linker_flags_work("LTO" - "-flto=thin${cxx_append}" "-flto=thin${linker_append}" ${cxxflags_out} ${linkerflags_out}) + if (PYBIND11_LTO_CXX_FLAGS) + message(STATUS "LTO enabled") + else() + message(STATUS "LTO disabled (not supported by the compiler and/or linker)") endif() - - _pybind11_return_if_cxx_and_linker_flags_work("LTO" - "-flto${cxx_append}" "-flto${linker_append}" ${cxxflags_out} ${linkerflags_out}) - elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") - # Intel equivalent to LTO is called IPO - _pybind11_return_if_cxx_and_linker_flags_work("LTO" - "-ipo" "-ipo" ${cxxflags_out} ${linkerflags_out}) - elseif(MSVC) - # cmake only interprets libraries as linker flags when they start with a - (otherwise it - # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags - # with - instead of /, even if it is a bit non-standard: - _pybind11_return_if_cxx_and_linker_flags_work("LTO" - "/GL" "-LTCG" ${cxxflags_out} ${linkerflags_out}) endif() - set(${cxxflags_out} "" PARENT_SCOPE) - set(${linkerflags_out} "" PARENT_SCOPE) - message(STATUS "LTO disabled (not supported by the compiler and/or linker)") + # Enable LTO flags if found, except for Debug builds + if (PYBIND11_LTO_CXX_FLAGS) + target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") + endif() + if (PYBIND11_LTO_LINKER_FLAGS) + target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() endfunction() # Build a Python extension module: @@ -160,15 +171,7 @@ function(pybind11_add_module target_name) return() endif() - # Find LTO flags - _pybind11_find_lto_flags(lto_cxx_flags lto_linker_flags ARG_THIN_LTO) - if (lto_cxx_flags OR lto_linker_flags) - # Enable LTO flags if found, except for Debug builds - separate_arguments(lto_cxx_flags) - separate_arguments(lto_linker_flags) - target_compile_options(${target_name} PRIVATE "$<$>:${lto_cxx_flags}>") - target_link_libraries(${target_name} PRIVATE "$<$>:${lto_linker_flags}>") - endif() + _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) # Set the default symbol visibility to hidden (very important to obtain small binaries) if (NOT MSVC)