# Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause # Sets '${var}' to a genex that extracts the target's property. # Sets 'have_${var}' to a genex that checks that the property has a # non-empty value. macro(qt_internal_genex_get_property var target property) set(${var} "$") set(have_${var} "$") endmacro() # Sets '${var}' to a genex that will join the given property values # using '${glue}' and will surround the entire output with '${prefix}' # and '${suffix}'. macro(qt_internal_genex_get_joined_property var target property prefix suffix glue) qt_internal_genex_get_property("${var}" "${target}" "${property}") set(${var} "$<${have_${var}}:${prefix}$${suffix}>") endmacro() # This function generates LD version script for the target and uses it in the target linker line. # Function has two modes dependending on the specified arguments. # Arguments: # PRIVATE_CONTENT_FILE specifies the pre-cooked content of Qt__PRIVATE_API section. # Requires the content file available at build time. function(qt_internal_add_linker_version_script target) if(WASM) return() endif() cmake_parse_arguments(PARSE_ARGV 1 arg "" "PRIVATE_CONTENT_FILE" "PRIVATE_HEADERS" ) _qt_internal_validate_all_args_are_parsed(arg) if(arg_PRIVATE_CONTENT_FILE AND arg_PRIVATE_HEADERS) message(FATAL_ERROR "Both PRIVATE_CONTENT_FILE and PRIVATE_HEADERS are specified.") endif() if(TEST_ld_version_script) # Create a list of mangled symbol matches for all "std::" symbols. This # list will catch most symbols, but will miss global-namespace symbols # that only have std parameters. # See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name for reference set(contents "NonQt {\nlocal:") # For types: vtable, VTT, typeinfo, typeinfo name foreach(ptrqualifier "" "P" "PK") # T, T *, const T * (volatile ignored) string(APPEND contents "\n _ZT[VTIS]${ptrqualifier}S*;" "_ZT[VTIS]${ptrqualifier}NS*;") endforeach() # For functions and variables foreach(special "" "G[VR]" # guard variables, extended-lifetime references "GTt") # transaction entry points foreach(cvqualifier "" "[VK]" "VK") # plain, const|volatile, const volatile string(APPEND contents "\n ") foreach(refqualifier "" "[RO]") # plain, & or && # For names in the std:: namespace, compression applies # (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression) string(APPEND contents " _Z${special}${cvqualifier}${refqualifier}S*;" # plain " _Z${special}N${cvqualifier}${refqualifier}S*;" # nested name ) endforeach() endforeach() endforeach() string(APPEND contents "\n};\nQt_${PROJECT_VERSION_MAJOR}") if(QT_FEATURE_elf_private_full_version) string(APPEND contents ".${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") endif() string(APPEND contents "_PRIVATE_API { qt_private_api_tag*;\n") if(arg_PRIVATE_HEADERS) foreach(ph ${arg_PRIVATE_HEADERS}) string(APPEND contents " @FILE:${ph}@\n") endforeach() else() string(APPEND contents "@PRIVATE_CONTENT@") endif() string(APPEND contents "};\n") set(current "Qt_${PROJECT_VERSION_MAJOR}") string(APPEND contents "${current} {\n *;") get_target_property(target_type ${target} TYPE) if(NOT target_type STREQUAL "INTERFACE_LIBRARY") # Export all specializations of the QExplicitlySharedDataPointer # and QSharedDataPointer destructors; due to use of the # QT_DECLARE_Q{,E}SDP_SPECIALIZATION_DTOR_WITH_EXPORT macros string(APPEND contents "\n _ZN*18QSharedDataPointerI*D?Ev;") string(APPEND contents "\n _ZN*28QExplicitlySharedDataPointerI*D?Ev;") set(genex_prefix "\n ") set(genex_glue "$\n ") set(genex_suffix "$") qt_internal_genex_get_joined_property( linker_exports "${target}" _qt_extra_linker_script_exports "${genex_prefix}" "${genex_suffix}" "${genex_glue}" ) string(APPEND contents "${linker_exports}") endif() string(APPEND contents "\n};\n") if(NOT target_type STREQUAL "INTERFACE_LIBRARY") set(property_genex "$") set(check_genex "$") string(APPEND contents "$<${check_genex}:${property_genex}>") endif() set(infile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version.in") set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version") file(GENERATE OUTPUT "${infile}" CONTENT "${contents}") if(NOT arg_PRIVATE_CONTENT_FILE) set(arg_PRIVATE_CONTENT_FILE "") endif() set(generator_command ${CMAKE_COMMAND} "-DIN_FILE=${infile}" "-DPRIVATE_CONTENT_FILE=${arg_PRIVATE_CONTENT_FILE}" "-DOUT_FILE=${outfile}" -P "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" ) set(generator_dependencies "${arg_PRIVATE_CONTENT_FILE}" "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" ) add_custom_command( OUTPUT "${outfile}" COMMAND ${generator_command} DEPENDS ${generator_dependencies} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Generating version linker script for target ${target}" VERBATIM ) add_custom_target(${target}_version_script DEPENDS ${outfile}) add_dependencies(${target} ${target}_version_script) target_link_options(${target} PRIVATE "-Wl,--version-script,${outfile}") endif() endfunction() function(qt_internal_add_link_flags_no_undefined target) if (NOT QT_BUILD_SHARED_LIBS OR WASM) return() endif() if (VXWORKS) # VxWorks requires thread_local-related symbols to be found at # runtime, resulting in linker error when no-undefined flag is # set and thread_local is used return() endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # ld64 defaults to -undefined,error, and in Xcode 15 # passing this option is deprecated, causing a warning. return() endif() if ((GCC OR CLANG) AND NOT MSVC) if(CLANG AND QT_FEATURE_sanitizer) return() endif() set(previous_CMAKE_REQUIRED_LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}) set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,-undefined,error") check_cxx_source_compiles("int main() {}" HAVE_DASH_UNDEFINED_SYMBOLS) if(HAVE_DASH_UNDEFINED_SYMBOLS) set(no_undefined_flag "-Wl,-undefined,error") endif() set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--no-undefined") check_cxx_source_compiles("int main() {}" HAVE_DASH_DASH_NO_UNDEFINED) if(HAVE_DASH_DASH_NO_UNDEFINED) set(no_undefined_flag "-Wl,--no-undefined") endif() set(CMAKE_REQUIRED_LINK_OPTIONS ${previous_CMAKE_REQUIRED_LINK_OPTIONS}) if (NOT HAVE_DASH_UNDEFINED_SYMBOLS AND NOT HAVE_DASH_DASH_NO_UNDEFINED) message(FATAL_ERROR "Platform linker doesn't support erroring upon encountering undefined symbols. Target:\"${target}\".") endif() target_link_options("${target}" PRIVATE "${no_undefined_flag}") endif() endfunction() function(qt_internal_apply_gc_binaries_conditional target visibility) # Should only be applied when the feature is enabled, aka for static builds. if(NOT QT_FEATURE_gc_binaries) return() endif() qt_internal_apply_gc_binaries("${target}" "${visibility}") endfunction() function(qt_internal_apply_gc_binaries target visibility) set(possible_visibilities PRIVATE INTERFACE PUBLIC) if(NOT visibility IN_LIST possible_visibilities) message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.") endif() string(JOIN "" clang_or_gcc_begin "$<$," "$," "$," "$" ">:" ) set(clang_or_gcc_end ">") if ((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC) if(APPLE) set(gc_sections_flag "-Wl,-dead_strip") elseif(SOLARIS) set(gc_sections_flag "-Wl,-z,ignore") elseif(LINUX OR BSD OR WIN32 OR ANDROID) set(gc_sections_flag "-Wl,--gc-sections") endif() # Save the flag value with and without genex wrapping, so we can remove the wrapping # when generating .pc pkgconfig files. set_property(GLOBAL PROPERTY _qt_internal_gc_sections_without_genex "${gc_sections_flag}") set(gc_sections_flag "${clang_or_gcc_begin}${gc_sections_flag}${clang_or_gcc_end}") set_property(GLOBAL PROPERTY _qt_internal_gc_sections_with_genex "${gc_sections_flag}") endif() if(gc_sections_flag) target_link_options("${target}" ${visibility} "${gc_sections_flag}") endif() if((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC) set(split_sections_flags "${clang_or_gcc_begin}-ffunction-sections;-fdata-sections${clang_or_gcc_end}") endif() if(split_sections_flags) target_compile_options("${target}" ${visibility} "${split_sections_flags}") endif() endfunction() function(qt_internal_apply_intel_cet target visibility) if(NOT QT_FEATURE_intelcet) return() endif() set(possible_visibilities PRIVATE INTERFACE PUBLIC) if(NOT visibility IN_LIST possible_visibilities) message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.") endif() if(GCC) string(JOIN "" flags "$<$," "$," "$" ">:-mshstk>") endif() if(flags) set(opt_out_condition "$>>") set(flags "$<${opt_out_condition}:${flags}>") target_compile_options("${target}" ${visibility} "${flags}") endif() endfunction() # Meant to be applied to PlatformCommonInternal. function(qt_internal_apply_intel_cet_harderning target) if(NOT QT_FEATURE_intelcet) return() endif() set(opt_out_condition "$>>") if(MSVC) set(intel_cet_flag "-CETCOMPAT") set(condition "$<${opt_out_condition}:${intel_cet_flag}>") qt_internal_platform_link_options("${target}" INTERFACE "${condition}") else() set(intel_cet_flag "-fcf-protection=full") set(condition "$<${opt_out_condition}:${intel_cet_flag}>") target_compile_options("${target}" INTERFACE "${condition}") endif() endfunction() # Allow opting out of the Intel CET hardening on a per-target basis. function(qt_internal_skip_intel_cet_hardening target) set_target_properties("${target}" PROPERTIES _qt_no_intel_cet_harderning TRUE) endfunction() # Sets the exceptions flags for the given target according to value. # If the value is not defined, set it to the exceptions feature value. function(qt_internal_set_exceptions_flags target value) #INTERFACE libraries compile nothing get_target_property(target_type ${target} TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") return() endif() set_target_properties(${target} PROPERTIES _qt_internal_use_exceptions ${value}) endfunction() # Deprecated. Replaced by qt_internal_set_skip_warnings_are_errors. function(qt_skip_warnings_are_errors target) qt_internal_set_skip_warnings_are_errors(${target} TRUE) endfunction() # Controls the QT_SKIP_WARNINGS_ARE_ERRORS property for the given target. function(qt_internal_set_skip_warnings_are_errors target value) get_target_property(target_type "${target}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") return() endif() set_target_properties("${target}" PROPERTIES QT_SKIP_WARNINGS_ARE_ERRORS ${value}) endfunction() # Sets the default warnings behavior according to the WARNINGS_ARE_ERRORS and # QT_REPO_NOT_WARNINGS_CLEAN flags. function(qt_internal_default_warnings_are_errors target) if(WARNINGS_ARE_ERRORS AND NOT QT_REPO_NOT_WARNINGS_CLEAN # Xcode enables additional warnings on top of the ones we # enable, and we are not warning clean in that context. AND NOT "${CMAKE_GENERATOR}" STREQUAL "Xcode") qt_internal_set_skip_warnings_are_errors("${target}" FALSE) else() qt_internal_set_skip_warnings_are_errors("${target}" TRUE) endif() endfunction() function(qt_disable_warnings target) get_target_property(target_type "${target}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") return() endif() set_target_properties("${target}" PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON) endfunction() function(qt_set_symbol_visibility_preset target value) get_target_property(target_type "${target}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") return() endif() set_target_properties("${target}" PROPERTIES C_VISIBILITY_PRESET "${value}") set_target_properties("${target}" PROPERTIES CXX_VISIBILITY_PRESET "${value}") set_target_properties("${target}" PROPERTIES OBJC_VISIBILITY_PRESET "${value}") set_target_properties("${target}" PROPERTIES OBJCXX_VISIBILITY_PRESET "${value}") endfunction() function(qt_set_symbol_visibility_hidden target) qt_set_symbol_visibility_preset("${target}" "hidden") endfunction() function(qt_set_language_standards) ## Use the latest standard the compiler supports (same as qt_common.prf) if (QT_FEATURE_cxx2b) set(CMAKE_CXX_STANDARD 23 PARENT_SCOPE) elseif (QT_FEATURE_cxx20) set(CMAKE_CXX_STANDARD 20 PARENT_SCOPE) else() set(CMAKE_CXX_STANDARD 17 PARENT_SCOPE) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON PARENT_SCOPE) set(CMAKE_C_STANDARD 11 PARENT_SCOPE) set(CMAKE_C_STANDARD_REQUIRED ON PARENT_SCOPE) endfunction() function(qt_set_language_standards_interface_compile_features target) # Regardless of which C++ standard is used to build Qt itself, require C++17 when building # Qt applications using CMake (because the Qt header files use C++17 features). set(cpp_feature "cxx_std_17") target_compile_features("${target}" INTERFACE ${cpp_feature}) endfunction() function(qt_set_msvc_cplusplus_options target visibility) # For MSVC we need to explicitly pass -Zc:__cplusplus to get correct __cplusplus. # Check qt_config_compile_test for more info. if(MSVC AND MSVC_VERSION GREATER_EQUAL 1913) set(flags "-Zc:__cplusplus" "-permissive-") target_compile_options("${target}" ${visibility} "$<$,$>:${flags}>") endif() endfunction() function(qt_enable_utf8_sources target) set(utf8_flags "") if(MSVC) list(APPEND utf8_flags "$<$:-utf-8>") endif() if(utf8_flags) # Allow opting out by specifying the QT_NO_UTF8_SOURCE target property. set(opt_out_condition "$>>") # Only set the compiler option for C and C++. set(language_condition "$") # Compose the full condition. set(genex_condition "$") set(utf8_flags "$<${genex_condition}:${utf8_flags}>") target_compile_options("${target}" INTERFACE "${utf8_flags}") endif() endfunction() function(qt_internal_enable_unicode_defines) if(WIN32) set(no_unicode_condition "$>>") target_compile_definitions(Platform INTERFACE "$<${no_unicode_condition}:UNICODE$_UNICODE>") endif() endfunction() # Saves the list of known optimization flags for the current compiler in out_var. # # Mostly used for removing them before adding new ones. function(qt_internal_get_all_possible_optimization_flag_values out_var) set(flag_values "") set(vars QT_CFLAGS_OPTIMIZE QT_CFLAGS_OPTIMIZE_FULL QT_CFLAGS_OPTIMIZE_DEBUG QT_CFLAGS_OPTIMIZE_SIZE) foreach(optimize_var ${vars}) set(value "${${optimize_var}}") if(value) list(APPEND flag_values "${value}") endif() endforeach() # Additional flag values which might not be used in qmake mkspecs, but might be set by CMake, # aka flags that are recognized by the compile which we might want to remove. if(QT_CFLAGS_OPTIMIZE_VALID_VALUES) list(APPEND flag_values ${QT_CFLAGS_OPTIMIZE_VALID_VALUES}) endif() set("${out_var}" "${flag_values}" PARENT_SCOPE) endfunction() # Return's the current compiler's optimize_full flags if available. # Otherwise returns the regular optimization level flag. function(qt_internal_get_optimize_full_flags out_var) set(optimize_full_flags "${QT_CFLAGS_OPTIMIZE_FULL}") if(NOT optimize_full_flags) set(optimize_full_flags "${QT_CFLAGS_OPTIMIZE}") endif() set(${out_var} "${optimize_full_flags}" PARENT_SCOPE) endfunction() # Prints the compiler and linker flags for each configuration, language and target type. # # Usually it would print the cache variables, but one may also override the variables # in a specific directory scope, so this is useful for debugging. # # Basically dumps either scoped or cached # CMAKE__FLAGS_CONFIG> and CMAKE__LINKER_FLAGS_ variables. function(qt_internal_print_optimization_flags_values) qt_internal_get_enabled_languages_for_flag_manipulation(languages) qt_internal_get_configs_for_flag_manipulation(configs) qt_internal_get_target_link_types_for_flag_manipulation(target_link_types) qt_internal_print_optimization_flags_values_helper( "${languages}" "${configs}" "${target_link_types}") endfunction() # Helper function for printing the optimization flags. function(qt_internal_print_optimization_flags_values_helper languages configs target_link_types) foreach(lang ${languages}) set(flag_var_name "CMAKE_${lang}_FLAGS") message(STATUS "${flag_var_name}: '${${flag_var_name}}'") foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") message(STATUS "${flag_var_name}: '${${flag_var_name}}'") endforeach() endforeach() foreach(t ${target_link_types}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS") message(STATUS "${flag_var_name}: '${${flag_var_name}}'") foreach(config ${configs}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}") message(STATUS "${flag_var_name}: '${${flag_var_name}}'") endforeach() endforeach() endfunction() # Saves the list of configs for which flag manipulation will occur. function(qt_internal_get_configs_for_flag_manipulation out_var) set(configs RELEASE RELWITHDEBINFO MINSIZEREL DEBUG) # Opt into additional non-standard configs for flag removal only. if(QT_ADDITIONAL_OPTIMIZATION_FLAG_CONFIGS) list(APPEND configs ${QT_ADDITIONAL_OPTIMIZATION_FLAG_CONFIGS}) endif() set(${out_var} "${configs}" PARENT_SCOPE) endfunction() # Saves the list of target link types for which flag manipulation will occur. function(qt_internal_get_target_link_types_for_flag_manipulation out_var) set(target_link_types EXE SHARED MODULE STATIC) set(${out_var} "${target_link_types}" PARENT_SCOPE) endfunction() # Saves list of enabled languages for which it is safe to manipulate compilation flags. function(qt_internal_get_enabled_languages_for_flag_manipulation out_var) # Limit flag modification to c-like code. We don't want to accidentally add incompatible # flags to MSVC's RC or Swift. set(languages_to_process ASM C CXX OBJC OBJCXX) get_property(globally_enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) set(enabled_languages "") foreach(lang ${languages_to_process}) if(lang IN_LIST globally_enabled_languages) list(APPEND enabled_languages "${lang}") endif() endforeach() set(${out_var} "${enabled_languages}" PARENT_SCOPE) endfunction() # Helper function used to update compiler and linker flags further below function(qt_internal_replace_flags_impl flag_var_name match_string replace_string IN_CACHE) # This must come before cache variable modification because setting the # cache variable with FORCE will overwrite the non-cache variable, but # we need to use the original value on entry to this function. # Handle an empty input string and an empty match string as a set(). if(match_string STREQUAL "" AND "${${flag_var_name}}" STREQUAL "") set(${flag_var_name} "${replace_string}" PARENT_SCOPE) else() string(REPLACE "${match_string}" "${replace_string}" ${flag_var_name} "${${flag_var_name}}") string(STRIP "${${flag_var_name}}" ${flag_var_name}) set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endif() if(IN_CACHE) # We must not use the non-cache variable's value because toolchain files # might be appending things to the cache variable's value and storing it # in a non-cache variable (e.g. Android NDK toolchain file does this). # Work exclusively on cache variable value only. get_property(help_text CACHE "${flag_var_name}" PROPERTY HELPSTRING) # Handle an empty input string and an empty match string as a set(). if(match_string STREQUAL "" AND "$CACHE{${flag_var_name}}" STREQUAL "") set(${flag_var_name} "${replace_string}" CACHE STRING "${help_text}" FORCE) else() set(mod_flags "$CACHE{${flag_var_name}}") string(REPLACE "${match_string}" "${replace_string}" mod_flags "${mod_flags}") string(STRIP "${mod_flags}" mod_flags) set(${flag_var_name} "${mod_flags}" CACHE STRING "${help_text}" FORCE) endif() endif() endfunction() # Helper function used to update compiler and linker flags further below function(qt_internal_remove_flags_impl flag_var_name flag_values IN_CACHE) cmake_parse_arguments(arg "REGEX" "" "" ${ARGN}) set(replace_type REPLACE) if(arg_REGEX) list(PREPEND replace_type REGEX) endif() # This must come before cache variable modification because setting the # cache variable with FORCE will overwrite the non-cache variable in this # function scope, but we need to use the original value before that change. foreach(flag_value IN LISTS flag_values) string(${replace_type} "${flag_value}" " " ${flag_var_name} "${${flag_var_name}}") endforeach() string(STRIP "${${flag_var_name}}" ${flag_var_name}) set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) if(IN_CACHE) # We must not use the non-cache variable's value because toolchain files # might be appending things to the cache variable's value and storing it # in a non-cache variable (e.g. Android NDK toolchain file does this). # Work exclusively on cache variable value only. set(mod_flags $CACHE{${flag_var_name}}) foreach(flag_value IN LISTS flag_values) string(${replace_type} "${flag_value}" " " mod_flags "${mod_flags}") endforeach() string(STRIP "${mod_flags}" mod_flags) get_property(help_text CACHE ${flag_var_name} PROPERTY HELPSTRING) set(${flag_var_name} "${mod_flags}" CACHE STRING "${help_text}" FORCE) endif() endfunction() # Helper function used to update compiler and linker flags further below function(qt_internal_add_flags_impl flag_var_name flags IN_CACHE) # This must come before cache variable modification because setting the # cache variable with FORCE will overwrite the non-cache variable, but # we need to use the original value on entry to this function. set(${flag_var_name} "${${flag_var_name}} ${flags}") string(STRIP "${${flag_var_name}}" ${flag_var_name}) set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) if(IN_CACHE) # We must not use the non-cache variable's value because toolchain files # might be appending things to the cache variable's value and storing it # in a non-cache variable (e.g. Android NDK toolchain file does this). # Work exclusively on cache variable value only. set(mod_flags "$CACHE{${flag_var_name}} ${flags}") string(STRIP "${mod_flags}" mod_flags) get_property(help_text CACHE ${flag_var_name} PROPERTY HELPSTRING) set(${flag_var_name} "${mod_flags}" CACHE STRING "${help_text}" FORCE) endif() endfunction() # Removes all known compiler optimization flags for the given CONFIGS, for all enabled 'safe' # languages. The flag variables are always updated in the calling scope, even if they did not # exist beforehand. # # IN_CACHE - remove them from the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # CONFIGS - should be a list of upper case configs like DEBUG, RELEASE, RELWITHDEBINFO. # LANGUAGES - optional list of languages like 'C', 'CXX', for which to remove the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_remove_known_optimization_flags) cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "" "CONFIGS;LANGUAGES") _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR "You must specify at least one configuration for which to remove the flags.") endif() if(arg_LANGUAGES) set(enabled_languages "${arg_LANGUAGES}") else() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) endif() qt_internal_get_all_possible_optimization_flag_values(flag_values) set(configs ${arg_CONFIGS}) foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") qt_internal_remove_flags_impl(${flag_var_name} "${flag_values}" "${arg_IN_CACHE}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Removes specified flags from CMAKE__FLAGS[_CONFIGS] variables # # Option Arguments: # IN_CACHE # Enables flags removal from CACHE # REGEX # Enables the flag processing as a regular expression. # # Multi-value Arguments: # CONFIGS # List of configurations that need to clear flags. Clears all configs by default if not # specified. # # LANGUAGES # List of LANGUAGES that need clear flags. Clears all languages by default if not # specified. function(qt_internal_remove_compiler_flags flags) cmake_parse_arguments(PARSE_ARGV 1 arg "IN_CACHE;REGEX" "" "CONFIGS;LANGUAGES" ) _qt_internal_validate_all_args_are_parsed(arg) if("${flags}" STREQUAL "") message(WARNING "qt_internal_remove_compiler_flags was called without any flags specified.") return() endif() if(arg_LANGUAGES) set(languages "${arg_LANGUAGES}") else() qt_internal_get_enabled_languages_for_flag_manipulation(languages) endif() if(arg_CONFIGS) set(configs "${arg_CONFIGS}") else() qt_internal_get_configs_for_flag_manipulation(configs) endif() if(arg_REGEX) list(APPEND extra_options "REGEX") endif() foreach(lang ${languages}) set(flag_var_name "CMAKE_${lang}_FLAGS") qt_internal_remove_flags_impl(${flag_var_name} "${flags}" "${arg_IN_CACHE}" ${extra_options} ) set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") qt_internal_remove_flags_impl(${flag_var_name} "${flags}" "${arg_IN_CACHE}" ${extra_options} ) set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Add a series of compile options as generator expressions # # Each condition and compiler requirement are glued by $ genex. # # Synopsis # # qt_internal_add_compiler_dependent_flags( # COMPILERS ... # [ CONDITIONS ... ] # OPTIONS ... # [ CONDITIONS ... # OPTIONS ... ] # [ COMPILERS ... ] # # [LANGUAGES ...] # [COMMON_CONDITIONS ...] # ) # # Example # qt_internal_add_compiler_dependent_flags(tgt PUBLIC # COMPILERS ALL # OPTIONS -Werror # CONDITIONS $ OR $ # OPTIONS -bar -baz # COMPILERS GNU # CONDITIONS VERSION_GREATER_EQUAL 10 # OPTIONS -baz # ) # # Is equivalent to: # # target_compile_options(tgt PUBLIC # $<$>:-Werror> # $<$,$,$>>:-bar;-baz> # $<$,$,$,10>>:-baz> # ) # # Arguments # # `` # Equivalent to `target_compile_options()`. # # `` # Equivalent to `target_compile_options()`. # # Unlike `target_compile_options`, only one set of target scope is implemented. # # `COMPILERS` # Starts a new set of compiler specific set of options gated by `$<${lang}_COMPILER_ID>`. # See `LANGUAGES` for how `${lang}` is determined. # # Can be any value defined in `CMAKE__COMPILER_ID`, or one of the following shortcuts # - `ALL`: drops the compiler specific condition # - `CLANG`: `Clang` and `IntelLLVM` (does not include `AppleClang`) # # At least one `COMPILERS` set *must* be defined. # # `CONDITIONS` # Starts a new set of `` gated options. # # `` can be one of: # - `VERSION_* `: Equivalent to `$,>` # See `LANGUAGES` for how `${lang}` is determined. # - Any genex matching regex: `\$<.*>` # # You can use `AND` and `OR` as well as parenthesis `()` to join multiple ``. # # If no `` is added, the current set only checks for compiler condition and the # `COMMON_CONDITIONS`. # # `OPTIONS` # Starts the list of compiler options for the current set of `COMPILERS` and `CONDITIONS` # # Equivalent to `target_compile_options()`. # # At least one `COMPILERS` set *must* be defined. # # `LANGUAGES` # Language conditions applied to all options. # # - If no `LANGUAGES` is passed `$` is used to compute compiler-dependent # variables such as `$` # - If exactly 1 `LANGUAGES` is passed, this language is used in `$<${lang}_COMPILER_>` like # variables # # `COMMON_CONDITIONS` # Additional genex conditions to include for all compiler flags. function(qt_internal_add_compiler_dependent_flags target target_scope) # We cannot use `cmake_parse_arguments` to parse all the other arguments. We use a special # parsing for that with the remaining `arg_UNPARSED_ARGUMENTS` set(option_args "") set(single_args "") set(multi_args LANGUAGES COMMON_CONDITIONS ) cmake_parse_arguments(PARSE_ARGV 2 arg "${option_args}" "${single_args}" "${multi_args}" ) # For debugging purposes we save the original list of arg_UNPARSED_ARGUMENTS set(arg_UNPARSED_ARGUMENTS_original ${arg_UNPARSED_ARGUMENTS}) # Set the language for the compiler checks set(lang CXX) list(LENGTH arg_LANGUAGES arg_LANGUAGES_length) if(arg_LANGUAGES_length EQUAL 1) set(lang ${arg_LANGUAGES}) endif() # Always add a language genex set(lang_ex "$") if(arg_LANGUAGES_length GREATER 1) list(JOIN arg_LANGUAGES "," arg_LANGUAGES_comma_list) set(lang_ex "$") endif() # Helper debugging function function(_qt_internal_add_compiler_dependent_flags_error msg) # If you are hitting such a function, something must have gone wrong with # `qt_internal_add_compiler_flags` implementation. message(FATAL_ERROR "${msg}\n" " curr_arg = ${curr_arg}\n" " arg_UNPARSED_ARGUMENTS = ${arg_UNPARSED_ARGUMENTS}\n" " arg_UNPARSED_ARGUMENTS_original = ${arg_UNPARSED_ARGUMENTS_original}" ) endfunction() # Helper function finalizing each keyword set # COMPILERS function(_qt_internal_add_compiler_dependent_flags_do_COMPILERS) set(compiler_ex "") if(NOT curr_COMPILERS) _qt_internal_add_compiler_dependent_flags_error( "COMPILERS set cannot be empty" ) endif() # If ALL compilers is passed, we can ignore the compiler check if(NOT "ALL" IN_LIST curr_COMPILERS) # Check for other aliases if("CLANG" IN_LIST curr_COMPILERS) list(REMOVE_ITEM curr_COMPILERS CLANG) list(APPEND curr_COMPILERS Clang IntelLLVM) endif() # Create compiler genex list(REMOVE_DUPLICATES curr_COMPILERS) list(JOIN curr_COMPILERS "," compilers) set(compiler_ex "$<${lang}_COMPILER_ID:${compilers}>" PARENT_SCOPE) endif() endfunction() # CONDITIONS function(_qt_internal_add_compiler_dependent_flags_do_CONDITIONS) function(_qt_internal_add_compiler_dependent_flags_do_CONDITIONS_end_stack stack_in stack_out ) # Resolve the current stack into a genex list set(stack_conditions_ex) set(prev_glue_word) set(glue_word) list(POP_FRONT ${stack_in} stack_conditions_ex) while(${stack_in}) list(POP_FRONT ${stack_in} glue_word) if(NOT (glue_word STREQUAL "AND" OR glue_word STREQUAL "OR")) _qt_internal_add_compiler_dependent_flags_error( "Expected AND/OR glue word, instead got: ${glue_word}" ) endif() list(POP_FRONT ${stack_in} next_condition) if(NOT next_condition) _qt_internal_add_compiler_dependent_flags_error( "No other condition provided after ${glue_word}" ) endif() if(prev_glue_word STREQUAL glue_word) # If the glue words are the same, we just add to current genex $<${glue_word}:> # First trim the last `>` character string(LENGTH "${stack_conditions_ex}" stack_conditions_ex_length) math(EXPR stack_conditions_ex_length "${stack_conditions_ex_length} - 1") string(SUBSTRING "${stack_conditions_ex}" 0 ${stack_conditions_ex_length} stack_conditions_ex ) set(stack_conditions_ex "${stack_conditions_ex},${next_condition}>") else() # Otherwise create a new $<${glue_word}:> set(stack_conditions_ex "$<${glue_word}:${stack_conditions_ex},${next_condition}>" ) endif() set(prev_glue_word "${glue_word}") endwhile() if(NOT stack_conditions_ex) _qt_internal_add_compiler_dependent_flags_error( "Empty parenthesis stack" ) endif() # Add the current list(APPEND ${stack_out} "${stack_conditions_ex}") set(${stack_out} "${${stack_out}}" PARENT_SCOPE) endfunction() set(conditions_ex "") set(stack_level 0) set(stack_0) while(curr_CONDITIONS) list(POP_FRONT curr_CONDITIONS condition_kw) # Parenthesis evaluation if(condition_kw MATCHES "^\\((.*)") # Start a new stack math(EXPR stack_level "${stack_level} + 1") set(stack_${stack_level}) # Check if the parenthesis was glued to another keyword # Resolve the remaining keyword in the next loop if(CMAKE_MATCH_1) list(PREPEND curr_CONDITIONS "${CMAKE_MATCH_1}") endif() continue() elseif(condition_kw MATCHES "(.*)\\)$") # Check if the parenthesis was glued to another keyword # Separate them and evaluate each one individually if(CMAKE_MATCH_1) set(condition_kw "${CMAKE_MATCH_1}") list(PREPEND curr_CONDITIONS "${CMAKE_MATCH_1}" ")") endif() # Finalize the current stack making it a genex for the next loop set(curr_stack stack_${stack_level}) math(EXPR stack_level "${stack_level} - 1") set(prev_stack stack_${stack_level}) _qt_internal_add_compiler_dependent_flags_do_CONDITIONS_end_stack( ${curr_stack} ${prev_stack} ) if(stack_level LESS 0) _qt_internal_add_compiler_dependent_flags_error( "Unbalanced parenthesis." ) endif() continue() endif() # Glue word evaluation if(condition_kw STREQUAL "AND" OR condition_kw STREQUAL "OR") # Insert the operator list(APPEND stack_${stack_level} "${condition_kw}") continue() endif() # Main condition keyword evaluation if(condition_kw MATCHES "^VERSION_.*") # Shortcut for VERSION_* keyword list(POP_FRONT curr_CONDITIONS version) list(APPEND stack_${stack_level} "$<${condition_kw}:$<${lang}_COMPILER_VERSION>,${version}>" ) continue() elseif(condition_kw MATCHES "^\\$<.*>$") # genex expression are added as-is list(APPEND stack_${stack_level} "${condition_kw}") continue() else() # All other unrecognized forms we do not know how to deal with _qt_internal_add_compiler_dependent_flags_error( "Unrecognized condition form: ${condition_kw}" ) endif() endwhile() # Finalize the top-level stack and put it in `conditions_ex` if(NOT stack_level EQUAL 0) _qt_internal_add_compiler_dependent_flags_error( "Unbalanced parenthesis." ) endif() _qt_internal_add_compiler_dependent_flags_do_CONDITIONS_end_stack(stack_0 conditions_ex) set(conditions_ex "${conditions_ex}" PARENT_SCOPE) endfunction() # OPTIONS function(_qt_internal_add_compiler_dependent_flags_do_OPTIONS) # Check for required keywords foreach(required_keyword IN ITEMS OPTIONS COMPILERS) if(curr_${required_keyword} STREQUAL "MISSING") _qt_internal_add_compiler_dependent_flags_error( "${required_keyword} keyword was not passed" ) endif() endforeach() # Only handle the current set if the OPTIONS did not evaluate to empty, otherwise # it is considered a no-op if(curr_OPTIONS) # No need to check the length of `all_conditions_ex` because `lang_ex` is # always defined. list(JOIN curr_OPTIONS ";" options) # Combine all conditions in an `AND` statement set(all_conditions_ex "") list(APPEND all_conditions_ex ${arg_COMMON_CONDITIONS} ${lang_ex} ${compiler_ex} ${conditions_ex} ) list(JOIN all_conditions_ex "," all_conditions_ex) list(APPEND flags "$<$:${options}>" ) endif() # Reset all loop variables # curr_COMPILERS is inherited from the last loop set(curr_CONDITIONS "" PARENT_SCOPE) set(curr_OPTIONS "MISSING" PARENT_SCOPE) set(conditions_ex "" PARENT_SCOPE) set(flags "${flags}" PARENT_SCOPE) endfunction() # Set initial loop variables set(compiler_ex "") set(conditions_ex "") set(flags "") # We set (REQUIRED) curr_* loop variables to a special keyword MISSING to identify when # the keyword was not passed set(curr_COMPILERS "MISSING") set(curr_CONDITIONS "") set(curr_OPTIONS "MISSING") set(curr_keyword "") # Parse the remaining arguments while(arg_UNPARSED_ARGUMENTS) list(POP_FRONT arg_UNPARSED_ARGUMENTS curr_arg) # Check for separator keywords if(curr_arg MATCHES "^(COMPILERS|CONDITIONS|OPTIONS)$") # Resolve the previous keyword set # Implicitly we skip the initial loop where curr_keyword == "" if(curr_keyword STREQUAL "COMPILERS") _qt_internal_add_compiler_dependent_flags_do_COMPILERS() elseif(curr_keyword STREQUAL "CONDITIONS") _qt_internal_add_compiler_dependent_flags_do_CONDITIONS() elseif(curr_keyword STREQUAL "OPTIONS") _qt_internal_add_compiler_dependent_flags_do_OPTIONS() endif() # Set the new keyword to accumulate the current `curr_*` variable set(curr_keyword "${curr_arg}") set(curr_${curr_keyword} "") continue() endif() # If no separator keyword is passed, add the current values to `curr_*` loop variable # and move on to the next loop if(NOT curr_keyword) _qt_internal_add_compiler_dependent_flags_error( "No keyword was passed: COMPILERS/CONDITIONS/OPTIONS" ) endif() list(APPEND curr_${curr_keyword} "${curr_arg}") endwhile() # finalize the last set _qt_internal_add_compiler_dependent_flags_do_OPTIONS() # Finally add all of the flags to `target_compile_options` target_compile_options("${target}" ${target_scope} ${flags}) endfunction() # Adds compiler flags for the given CONFIGS in the calling scope. Can also update the cache # if asked to do so. The flag variables are always updated in the calling scope, even if they # did not exist beforehand. # # FLAGS - should be a single string of flags separated by spaces. # IN_CACHE - add them to the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # CONFIGS - should be a list of upper case configs like DEBUG, RELEASE, RELWITHDEBINFO. # LANGUAGES - optional list of languages like 'C', 'CXX', for which to add the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_add_compiler_flags) cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" "CONFIGS;LANGUAGES") _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR "You must specify at least one configuration for which to add the flags.") endif() if(NOT arg_FLAGS) message(FATAL_ERROR "You must specify at least one flag to add.") endif() if(arg_LANGUAGES) set(enabled_languages "${arg_LANGUAGES}") else() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) endif() set(configs ${arg_CONFIGS}) foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") qt_internal_add_flags_impl(${flag_var_name} "${arg_FLAGS}" "${arg_IN_CACHE}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Convenience function that adds compiler flags for all release configurations. # The flag variables are always updated in the calling scope, even if they did not # exist beforehand. # # FLAGS - should be a single string of flags separated by spaces. # IN_CACHE - add them to the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # LANGUAGES - optional list of languages like 'C', 'CXX', for which to add the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_add_compiler_flags_for_release_configs) cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" "LANGUAGES") _qt_internal_validate_all_args_are_parsed(arg) set(args "") if(arg_LANGUAGES) set(enabled_languages "${arg_LANGUAGES}") else() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) endif() set(configs RELEASE RELWITHDEBINFO MINSIZEREL) list(APPEND args CONFIGS ${configs}) if(arg_FLAGS) list(APPEND args FLAGS "${arg_FLAGS}") endif() if(arg_IN_CACHE) list(APPEND args IN_CACHE) endif() list(APPEND args LANGUAGES ${enabled_languages}) qt_internal_add_compiler_flags(${args}) foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") set("${flag_var_name}" "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Convenience function that replaces all optimization flags with the equivalent of '-O3' # (optimize_full) flag for all release configs. # # This is the equivalent of qmake's CONFIG += optimize_full. # It is meant to be called in a subdirectory scope to enable full optimizations for a particular # Qt module, like Core or Gui. function(qt_internal_add_optimize_full_flags) cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "" "") _qt_internal_validate_all_args_are_parsed(arg) # QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS disables forced full optimization. if(QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS) return() endif() # Assume that FEATURE_optimize_full has higher priority. But if FEATURE_optimize_full is OFF, # flags are set by FEATURE_optimize_size should remain unchanged. if(QT_FEATURE_optimize_size AND NOT QT_FEATURE_optimize_full) return() endif() set(args "") if(arg_IN_CACHE) list(APPEND args IN_CACHE) endif() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) set(configs RELEASE RELWITHDEBINFO) if(QT_FEATURE_optimize_full) # Assume that FEATURE_optimize_full has higher priority. list(APPEND configs MINSIZEREL) endif() qt_internal_remove_known_optimization_flags(${args} CONFIGS ${configs}) # If the respective compiler doesn't have optimize_full flags, use regular optimization flags. # Mainly MSVC. qt_internal_get_optimize_full_flags(optimize_full_flags) list(APPEND args FLAGS "${optimize_full_flags}") qt_internal_add_compiler_flags_for_release_configs(${args}) foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") set("${flag_var_name}" "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Convenience function to replace a compiler flag with another one, for the given configurations # for all enabled 'safe' languages. # Essentially a glorified string(REPLACE). # Can be used to remove compiler flags. # The flag variables are always updated in the calling scope, even if they did not # exist beforehand. # # match_string - string to match # replace_string - replacement string # IN_CACHE - replace them in the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # CONFIGS - should be a list of upper case configs like DEBUG, RELEASE, RELWITHDEBINFO. # LANGUAGES - optional list of languages like 'C', 'CXX', for which to replace the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_replace_compiler_flags match_string replace_string) cmake_parse_arguments(PARSE_ARGV 2 arg "IN_CACHE" "" "CONFIGS;LANGUAGES") _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR "You must specify at least one configuration for which to replace the flags.") endif() if(arg_LANGUAGES) set(enabled_languages "${arg_LANGUAGES}") else() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) endif() set(configs ${arg_CONFIGS}) foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") qt_internal_replace_flags_impl(${flag_var_name} "${match_string}" "${replace_string}" "${arg_IN_CACHE}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Convenience function to add linker flags, for the given configurations and target link types. # The flag variables are always updated in the calling scope, even if they did not exist beforehand. # # FLAGS - should be a single string of flags separated by spaces. # IN_CACHE - add them to the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # CONFIGS - should be a list of upper case configs like DEBUG, RELEASE, RELWITHDEBINFO. # TYPES - should be a list of target link types as expected by CMake's # CMAKE__LINKER_FLAGS_ cache variable. # e.g EXE, MODULE, SHARED, STATIC. function(qt_internal_add_linker_flags) cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" "CONFIGS;TYPES") _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_TYPES) message(FATAL_ERROR "You must specify at least one linker target type for which to add the flags.") endif() if(NOT arg_CONFIGS) message(FATAL_ERROR "You must specify at least one configuration for which to add the flags.") endif() if(NOT arg_FLAGS) message(FATAL_ERROR "You must specify at least one flag to add.") endif() set(configs ${arg_CONFIGS}) set(target_link_types ${arg_TYPES}) foreach(config ${configs}) foreach(t ${target_link_types}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}") qt_internal_add_flags_impl(${flag_var_name} "${arg_FLAGS}" "${arg_IN_CACHE}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # Convenience function to replace a linker flag with another one, for the given configurations # and target link types. # Essentially a glorified string(REPLACE). # Can be used to remove linker flags. # The flag variables are always updated in the calling scope, even if they did not exist beforehand. # # match_string - string to match # replace_string - replacement string # IN_CACHE - replace them in the corresponding cache variable too. Note that the cache # variable may have a different value to the non-cache variable. # CONFIGS - should be a list of upper case configs like DEBUG, RELEASE, RELWITHDEBINFO. # TYPES - should be a list of target link types as expected by CMake's # CMAKE__LINKER_FLAGS_ cache variable. # e.g EXE, MODULE, SHARED, STATIC. function(qt_internal_replace_linker_flags match_string replace_string) cmake_parse_arguments(PARSE_ARGV 2 arg "IN_CACHE" "" "CONFIGS;TYPES") _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_TYPES) message(FATAL_ERROR "You must specify at least one linker target type for which to replace the flags.") endif() if(NOT arg_CONFIGS) message(FATAL_ERROR "You must specify at least one configuration for which to replace the flags.") endif() set(configs ${arg_CONFIGS}) set(target_link_types ${arg_TYPES}) foreach(config ${configs}) foreach(t ${target_link_types}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}") qt_internal_replace_flags_impl(${flag_var_name} "${match_string}" "${replace_string}" "${arg_IN_CACHE}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() endfunction() # This function finds the optimization flags set by the default CMake platform modules or toolchain # files and replaces them with flags that Qt qmake builds expect, for all the usual # CMAKE_BUILD_TYPE configurations. # This normalizes things like using -O2 for both Release and RelWithDebInfo, among other compilation # flags. Also some linker flags specific to MSVC. # See QTBUG-85992 for details. # # Note that both the calling scope and the CMake cache are updated. function(qt_internal_set_up_config_optimizations_like_in_qmake) # Allow opt out. if(QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS) return() endif() qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) qt_internal_get_configs_for_flag_manipulation(configs) qt_internal_get_target_link_types_for_flag_manipulation(target_link_types) # You can set QT_DEBUG_OPTIMIZATION_FLAGS to see the before and after results. if(QT_DEBUG_OPTIMIZATION_FLAGS) message(STATUS "") message(STATUS "DEBUG: Original CMake optimization flags.\n") qt_internal_print_optimization_flags_values_helper("${enabled_languages}" "${configs}" "${target_link_types}") endif() # Remove known optimization flags. qt_internal_remove_known_optimization_flags(IN_CACHE CONFIGS ${configs}) # Re-add optimization flags as per qmake mkspecs. foreach(lang ${enabled_languages}) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") set(value_to_append "") # Release and RelWithDebInfo should get the same base optimization flags. if(config STREQUAL "RELEASE" AND QT_CFLAGS_OPTIMIZE) set(value_to_append "${QT_CFLAGS_OPTIMIZE}") elseif(config STREQUAL "RELWITHDEBINFO" AND QT_CFLAGS_OPTIMIZE) set(value_to_append "${QT_CFLAGS_OPTIMIZE}") # MinSizeRel should get the optimize size flag if available, otherwise the regular # release flag. elseif(config STREQUAL "MINSIZEREL") if(QT_CFLAGS_OPTIMIZE_SIZE) set(value_to_append "${QT_CFLAGS_OPTIMIZE_SIZE}") else() set(value_to_append "${QT_CFLAGS_OPTIMIZE}") endif() endif() # Debug should get the OPTIMIZE_DEBUG flag if the respective feature is ON. if(config STREQUAL "DEBUG" AND QT_FEATURE_optimize_debug) set(value_to_append "${QT_CFLAGS_OPTIMIZE_DEBUG}") endif() set(configs_for_optimize_size RELEASE RELWITHDEBINFO) if(QT_FEATURE_optimize_size AND config IN_LIST configs_for_optimize_size) set(value_to_append "${QT_CFLAGS_OPTIMIZE_SIZE}") endif() # Check if the fake 'optimize_full' feature is enabled. # Use the max optimization level flag for all release configs, effectively # overriding any previous setting. set(configs_for_optimize RELEASE RELWITHDEBINFO MINSIZEREL) if(QT_FEATURE_optimize_full AND config IN_LIST configs_for_optimize) qt_internal_get_optimize_full_flags(optimize_full_flags) set(value_to_append "${optimize_full_flags}") endif() # Assign value to the cache entry. if(value_to_append) qt_internal_add_flags_impl(${flag_var_name} "${value_to_append}" TRUE) # Delay updating the calling scope's variables to the end of this function endif() endforeach() endforeach() if(MSVC) # Handle MSVC /INCREMENTAL flag which should not be enabled for Release configurations. # First remove them from all configs, and re-add INCREMENTAL for Debug only. set(flag_values "/INCREMENTAL:YES" "/INCREMENTAL:NO" "/INCREMENTAL") foreach(flag_value ${flag_values}) qt_internal_replace_linker_flags( "${flag_value}" "" CONFIGS ${configs} TYPES ${target_link_types} IN_CACHE) endforeach() set(flag_value "/INCREMENTAL:NO") qt_internal_add_linker_flags( FLAGS "${flag_value}" CONFIGS RELEASE RELWITHDEBINFO MINSIZEREL TYPES EXE SHARED MODULE # when linking static libraries, link.exe can't recognize this parameter, clang-cl will error out. IN_CACHE) qt_internal_remove_compiler_flags("(^| )/EH[scra-]*( |$)" LANGUAGES CXX CONFIGS ${configs} IN_CACHE REGEX) endif() # Allow opting into generating debug info in object files with a fake feature. # This would allow us to enable caching with sccache. # See QTQAINFRA-3934 for details. if(MSVC AND QT_FEATURE_msvc_obj_debug_info) qt_internal_replace_compiler_flags( "/Zi" "/Z7" CONFIGS RELWITHDEBINFO DEBUG TYPES ${target_link_types} IN_CACHE) endif() # Legacy Android toolchain file adds the `-g` flag to CMAKE__FLAGS, as a # result, our release build ends up containing debug symbols. To avoid that, we # remove the flag from CMAKE__FLAGS and add # it to CMAKE__FLAGS_DEBUG. # # Note: # The new `android.toolchain.cmake` file does not have this problem, but # it has other issues, eg., https://github.com/android/ndk/issues/1693, so we # cannot force it. While we do load the new toolchain, it automatically falls # back to the legacy toolchain, ie., `android-legacy.toolchain.cmake` which # has the problem described above. # # Todo: # When the new toolchain is fixed, and it doesn't fall back to the legacy # anymore by default, then we should be able to remove this workaround. if(ANDROID AND ANDROID_COMPILER_FLAGS MATCHES "(^| )-g") qt_internal_remove_compiler_flags("-g") qt_internal_add_compiler_flags(FLAGS "-g" CONFIGS DEBUG RELWITHDEBINFO) endif() # Update all relevant flags in the calling scope foreach(lang ${enabled_languages}) set(flag_var_name "CMAKE_${lang}_FLAGS") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() foreach(t ${target_link_types}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) foreach(config ${configs}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() endforeach() if(QT_DEBUG_OPTIMIZATION_FLAGS) message(STATUS "") message(STATUS "DEBUG: Modified optimization flags to mirror qmake mkspecs.\n") qt_internal_print_optimization_flags_values_helper("${enabled_languages}" "${configs}" "${target_link_types}") endif() endfunction()