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