blob: 18e36dfde5cc194898691363fb8e3b7d8b9ac4be [file] [log] [blame]
# 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")
# 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")))
set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)
endif()