308 lines · plain
1# In general, a flag is a string provided for supported functions under the2# multi-valued option `FLAGS`. It should be one of the following forms:3# FLAG_NAME4# FLAG_NAME__NO5# FLAG_NAME__ONLY6# A target will inherit all the flags of its upstream dependency.7#8# When we create a target `TARGET_NAME` with a flag using (add_header_library,9# add_object_library, ...), its behavior will depend on the flag form as follow:10# - FLAG_NAME: The following 2 targets will be generated:11# `TARGET_NAME` that has `FLAG_NAME` in its `FLAGS` property.12# `TARGET_NAME.__NO_FLAG_NAME` that depends on `DEP.__NO_FLAG_NAME` if13# `TARGET_NAME` depends on `DEP` and `DEP` has `FLAG_NAME` in its `FLAGS`14# property.15# - FLAG_NAME__ONLY: Only generate 1 target `TARGET_NAME` that has `FLAG_NAME`16# in its `FLAGS` property.17# - FLAG_NAME__NO: Only generate 1 target `TARGET_NAME` that depends on18# `DEP.__NO_FLAG_NAME` if `DEP` is in its DEPENDS list and `DEP` has `FLAG_NAME`19# in its `FLAGS` property.20#21# To show all the targets generated, pass SHOW_INTERMEDIATE_OBJECTS=ON to cmake.22# To show all the targets' dependency and flags, pass23# SHOW_INTERMEDIATE_OBJECTS=DEPS to cmake.24#25# To completely disable a flag FLAG_NAME expansion, set the variable26# SKIP_FLAG_EXPANSION_FLAG_NAME=TRUE in this file.27 28 29function(extract_flag_modifier input_flag output_flag modifier)30 if(${input_flag} MATCHES "__NO$")31 string(REGEX REPLACE "__NO$" "" flag "${input_flag}")32 set(${output_flag} ${flag} PARENT_SCOPE)33 set(${modifier} "NO" PARENT_SCOPE)34 elseif(${input_flag} MATCHES "__ONLY$")35 string(REGEX REPLACE "__ONLY$" "" flag "${input_flag}")36 set(${output_flag} ${flag} PARENT_SCOPE)37 set(${modifier} "ONLY" PARENT_SCOPE)38 else()39 set(${output_flag} ${input_flag} PARENT_SCOPE)40 set(${modifier} "" PARENT_SCOPE)41 endif()42endfunction(extract_flag_modifier)43 44function(remove_duplicated_flags input_flags output_flags)45 set(out_flags "")46 foreach(input_flag IN LISTS input_flags)47 if(NOT input_flag)48 continue()49 endif()50 51 extract_flag_modifier(${input_flag} flag modifier)52 53 # Check if the flag is skipped.54 if(${SKIP_FLAG_EXPANSION_${flag}})55 if("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")56 message(STATUS " Flag ${flag} is ignored.")57 endif()58 continue()59 endif()60 61 set(found FALSE)62 foreach(out_flag IN LISTS out_flags)63 extract_flag_modifier(${out_flag} o_flag o_modifier)64 if("${flag}" STREQUAL "${o_flag}")65 set(found TRUE)66 break()67 endif()68 endforeach()69 if(NOT found)70 list(APPEND out_flags ${input_flag})71 endif()72 endforeach()73 74 set(${output_flags} "${out_flags}" PARENT_SCOPE)75endfunction(remove_duplicated_flags)76 77# Collect flags from dependency list. To see which flags come with each78# dependence, pass `SHOW_INTERMEDIATE_OBJECTS=DEPS` to cmake.79function(get_flags_from_dep_list output_list)80 set(flag_list "")81 foreach(dep IN LISTS ARGN)82 if(NOT dep)83 continue()84 endif()85 86 get_fq_dep_name(fq_dep_name ${dep})87 88 if(NOT TARGET ${fq_dep_name})89 continue()90 endif()91 92 get_target_property(flags ${fq_dep_name} "FLAGS")93 94 if(flags AND "${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS")95 message(STATUS " FLAGS from dependency ${fq_dep_name} are ${flags}")96 endif()97 98 foreach(flag IN LISTS flags)99 if(flag)100 list(APPEND flag_list ${flag})101 endif()102 endforeach()103 endforeach(dep)104 105 list(REMOVE_DUPLICATES flag_list)106 107 set(${output_list} ${flag_list} PARENT_SCOPE)108endfunction(get_flags_from_dep_list)109 110# Given a `flag` without modifier, scan through the list of dependency, append111# `.__NO_flag` to any target that has `flag` in its FLAGS property.112function(get_fq_dep_list_without_flag output_list flag)113 set(fq_dep_no_flag_list "")114 foreach(dep IN LISTS ARGN)115 get_fq_dep_name(fq_dep_name ${dep})116 if(TARGET ${fq_dep_name})117 get_target_property(dep_flags ${fq_dep_name} "FLAGS")118 # Only target with `flag` has `.__NO_flag` target, `flag__NO` and119 # `flag__ONLY` do not.120 if(${flag} IN_LIST dep_flags)121 list(APPEND fq_dep_no_flag_list "${fq_dep_name}.__NO_${flag}")122 else()123 list(APPEND fq_dep_no_flag_list ${fq_dep_name})124 endif()125 else()126 list(APPEND fq_dep_no_flag_list ${fq_dep_name})127 endif()128 endforeach(dep)129 set(${output_list} ${fq_dep_no_flag_list} PARENT_SCOPE)130endfunction(get_fq_dep_list_without_flag)131 132# Check if a `flag` is set133function(check_flag result flag_name)134 list(FIND ARGN ${flag_name} has_flag)135 if(${has_flag} LESS 0)136 list(FIND ARGN "${flag_name}__ONLY" has_flag)137 endif()138 if(${has_flag} GREATER -1)139 set(${result} TRUE PARENT_SCOPE)140 else()141 set(${result} FALSE PARENT_SCOPE)142 endif()143endfunction(check_flag)144 145# Generate all flags' combinations and call the corresponding function provided146# by `CREATE_TARGET` to create a target for each combination.147function(expand_flags_for_target target_name flags)148 cmake_parse_arguments(149 "EXPAND_FLAGS"150 "" # Optional arguments151 "CREATE_TARGET" # Single-value arguments152 "DEPENDS;FLAGS" # Multi-value arguments153 ${ARGN}154 )155 156 list(LENGTH flags nflags)157 if(NOT ${nflags})158 cmake_language(CALL ${EXPAND_FLAGS_CREATE_TARGET}159 ${target_name}160 ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}161 DEPENDS ${EXPAND_FLAGS_DEPENDS}162 FLAGS ${EXPAND_FLAGS_FLAGS}163 )164 return()165 endif()166 167 list(GET flags 0 flag)168 list(REMOVE_AT flags 0)169 extract_flag_modifier(${flag} real_flag modifier)170 171 if(NOT "${modifier}" STREQUAL "NO")172 expand_flags_for_target(173 ${target_name}174 "${flags}"175 DEPENDS ${EXPAND_FLAGS_DEPENDS}176 FLAGS ${EXPAND_FLAGS_FLAGS}177 CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}178 ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}179 )180 endif()181 182 if("${real_flag}" STREQUAL "" OR "${modifier}" STREQUAL "ONLY")183 return()184 endif()185 186 set(NEW_FLAGS ${EXPAND_FLAGS_FLAGS})187 list(REMOVE_ITEM NEW_FLAGS ${flag})188 get_fq_dep_list_without_flag(NEW_DEPS ${real_flag} ${EXPAND_FLAGS_DEPENDS})189 190 # Only target with `flag` has `.__NO_flag` target, `flag__NO` and191 # `flag__ONLY` do not.192 if("${modifier}" STREQUAL "")193 set(TARGET_NAME "${target_name}.__NO_${flag}")194 else()195 set(TARGET_NAME "${target_name}")196 endif()197 198 expand_flags_for_target(199 ${TARGET_NAME}200 "${flags}"201 DEPENDS ${NEW_DEPS}202 FLAGS ${NEW_FLAGS}203 CREATE_TARGET ${EXPAND_FLAGS_CREATE_TARGET}204 ${EXPAND_FLAGS_UNPARSED_ARGUMENTS}205 )206endfunction(expand_flags_for_target)207 208# Collect all flags from a target's dependency, and then forward to209# `expand_flags_for_target to generate all flags' combinations and call210# the corresponding function provided by `CREATE_TARGET` to create a target for211# each combination.212function(add_target_with_flags target_name)213 cmake_parse_arguments(214 "ADD_TO_EXPAND"215 "" # Optional arguments216 "CREATE_TARGET;" # Single value arguments217 "DEPENDS;FLAGS;ADD_FLAGS" # Multi-value arguments218 ${ARGN}219 )220 221 if(NOT target_name)222 message(FATAL_ERROR "Bad target name")223 endif()224 225 if(NOT ADD_TO_EXPAND_CREATE_TARGET)226 message(FATAL_ERROR "Missing function to create targets. Please specify "227 "`CREATE_TARGET <function>`")228 endif()229 230 get_fq_target_name(${target_name} fq_target_name)231 232 if(ADD_TO_EXPAND_DEPENDS AND ("${SHOW_INTERMEDIATE_OBJECTS}" STREQUAL "DEPS"))233 message(STATUS "Gathering FLAGS from dependencies for ${fq_target_name}")234 endif()235 236 get_fq_deps_list(fq_deps_list ${ADD_TO_EXPAND_DEPENDS})237 get_flags_from_dep_list(deps_flag_list ${fq_deps_list})238 239 # Appending ADD_FLAGS before flags from dependency.240 if(ADD_TO_EXPAND_ADD_FLAGS)241 list(APPEND ADD_TO_EXPAND_FLAGS ${ADD_TO_EXPAND_ADD_FLAGS})242 endif()243 list(APPEND ADD_TO_EXPAND_FLAGS ${deps_flag_list})244 remove_duplicated_flags("${ADD_TO_EXPAND_FLAGS}" flags)245 list(SORT flags)246 247 if(SHOW_INTERMEDIATE_OBJECTS AND flags)248 message(STATUS "Target ${fq_target_name} has FLAGS: ${flags}")249 endif()250 251 expand_flags_for_target(252 ${fq_target_name}253 "${flags}"254 DEPENDS "${fq_deps_list}"255 FLAGS "${flags}"256 CREATE_TARGET ${ADD_TO_EXPAND_CREATE_TARGET}257 ${ADD_TO_EXPAND_UNPARSED_ARGUMENTS}258 )259endfunction(add_target_with_flags)260 261# Special flags262set(FMA_OPT_FLAG "FMA_OPT")263set(ROUND_OPT_FLAG "ROUND_OPT")264# This flag controls whether we use explicit SIMD instructions or not.265set(EXPLICIT_SIMD_OPT_FLAG "EXPLICIT_SIMD_OPT")266# This flag controls whether we use compiler builtin functions to implement267# various basic math operations or not.268set(MISC_MATH_BASIC_OPS_OPT_FLAG "MISC_MATH_BASIC_OPS_OPT")269 270# Skip FMA_OPT flag for targets that don't support fma.271if(NOT DEFINED SKIP_FLAG_EXPANSION_FMA_OPT)272 if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR273 LIBC_TARGET_ARCHITECTURE_IS_ANY_RISCV))274 set(SKIP_FLAG_EXPANSION_FMA_OPT TRUE)275 endif()276endif()277 278# Skip EXPLICIT_SIMD_OPT flag for targets that don't support SSE2.279# Note: one may want to revisit it if they want to control other explicit SIMD280if(NOT DEFINED SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT)281 if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))282 set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)283 endif()284endif()285 286# Skip ROUND_OPT flag for targets that don't support rounding instructions. On287# x86, these are SSE4.1 instructions, but we already had code to check for288# SSE4.2 support.289if(NOT DEFINED SKIP_FLAG_EXPANSION_ROUND_OPT)290 if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) OR291 LIBC_TARGET_ARCHITECTURE_IS_AARCH64 OR LIBC_TARGET_OS_IS_GPU))292 set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)293 endif()294endif()295 296# Choose whether time_t is 32- or 64-bit, based on target architecture297# and config options. This will be used to set a #define during the298# library build, and also to select the right version of time_t.h for299# the output headers.300if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT))301 # Set time_t to 32 bit for compatibility with glibc, unless302 # configuration says otherwise303 set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE)304else()305 # Other platforms default to 64-bit time_t306 set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)307endif()308