cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
308 lines
10 KiB
CMake
308 lines
10 KiB
CMake
# 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 DEFINED SKIP_FLAG_EXPANSION_FMA_OPT)
|
|
if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "FMA")) OR
|
|
LIBC_TARGET_ARCHITECTURE_IS_RISCV64))
|
|
set(SKIP_FLAG_EXPANSION_FMA_OPT TRUE)
|
|
endif()
|
|
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 DEFINED SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT)
|
|
if(NOT(LIBC_TARGET_ARCHITECTURE_IS_X86_64 AND (LIBC_CPU_FEATURES MATCHES "SSE2")))
|
|
set(SKIP_FLAG_EXPANSION_EXPLICIT_SIMD_OPT TRUE)
|
|
endif()
|
|
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 DEFINED SKIP_FLAG_EXPANSION_ROUND_OPT)
|
|
if(NOT((LIBC_TARGET_ARCHITECTURE_IS_X86_64 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()
|
|
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()
|