| # 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 rounding instructions. On |
| # x86, these are SSE4.1 instructions, but we already had code to check for |
| # SSE4.2 support. |
| if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86 AND (LIBC_CPU_FEATURES MATCHES "SSE4_2")) OR |
| LIBC_TARGET_ARCHITECTURE_IS_AARCH64 OR LIBC_TARGET_OS_IS_GPU)) |
| set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE) |
| endif() |
| |
| # Choose whether time_t is 32- or 64-bit, based on target architecture |
| # and config options. This will be used to set a #define during the |
| # library build, and also to select the right version of time_t.h for |
| # the output headers. |
| if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT)) |
| # Set time_t to 32 bit for compatibility with glibc, unless |
| # configuration says otherwise |
| set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE) |
| else() |
| # Other platforms default to 64-bit time_t |
| set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE) |
| endif() |