When processing multiple attribution files to collect all attribution ids, the values of the previous iteration extracted keys were not reset. This caused the values to leak to the next processed attribution id target if the specific id key was empty. Make sure to clean up the values before parsing the next attribution key. Pick-to: 6.8 6.9 Change-Id: I386fc9c09276c49207382b06605efeb0d939b6da Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
600 lines
25 KiB
CMake
600 lines
25 KiB
CMake
# Copyright (C) 2024 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# Handles attribution information for a target.
|
|
#
|
|
# If CREATE_SBOM_FOR_EACH_ATTRIBUTION is set, a separate sbom target is created for each parsed
|
|
# attribution entry, and the new targets are added as dependencies to the parent target.
|
|
#
|
|
# If CREATE_SBOM_FOR_EACH_ATTRIBUTION is not set, the information read from the first attribution
|
|
# entry is added directly to the parent target, aka the the values are propagated to the outer
|
|
# function scope to be read.. The rest of the attribution entries are created as separate targets
|
|
# and added as dependencies, as if the option was passed.
|
|
#
|
|
# Handles multiple attribution files and entries within a file.
|
|
# Attribution files can be specified either via directories and direct file paths.
|
|
# If ATTRIBUTION_ENTRY_INDEX is set, only that specific attribution entry will be processed
|
|
# from the given attribution file.
|
|
function(_qt_internal_sbom_handle_qt_attribution_files out_prefix_outer)
|
|
if(NOT QT_GENERATE_SBOM)
|
|
return()
|
|
endif()
|
|
|
|
if(CMAKE_VERSION LESS_EQUAL "3.19")
|
|
message(DEBUG "CMake version is too low, can't parse attribution.json file.")
|
|
return()
|
|
endif()
|
|
|
|
set(opt_args "")
|
|
set(single_args "")
|
|
set(multi_args "")
|
|
|
|
_qt_internal_get_sbom_specific_options(sbom_opt_args sbom_single_args sbom_multi_args)
|
|
list(APPEND opt_args ${sbom_opt_args})
|
|
list(APPEND single_args ${sbom_single_args})
|
|
list(APPEND multi_args ${sbom_multi_args})
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
set(attribution_files "")
|
|
set(attribution_file_count 0)
|
|
|
|
foreach(attribution_file_path IN LISTS arg_ATTRIBUTION_FILE_PATHS)
|
|
get_filename_component(real_path "${attribution_file_path}" REALPATH)
|
|
list(APPEND attribution_files "${real_path}")
|
|
math(EXPR attribution_file_count "${attribution_file_count} + 1")
|
|
endforeach()
|
|
|
|
foreach(attribution_file_dir_path IN LISTS arg_ATTRIBUTION_FILE_DIR_PATHS)
|
|
get_filename_component(real_path
|
|
"${attribution_file_dir_path}/qt_attribution.json" REALPATH)
|
|
list(APPEND attribution_files "${real_path}")
|
|
math(EXPR attribution_file_count "${attribution_file_count} + 1")
|
|
endforeach()
|
|
|
|
# If CREATE_SBOM_FOR_EACH_ATTRIBUTION is set, that means the parent target is likely not a
|
|
# 3rd party library, so each attribution entry should create a separate attribution target.
|
|
# In which case we don't want to proagate options like CPE to the child attribution targets,
|
|
# because the CPE is meant for the parent target.
|
|
set(propagate_sbom_options_to_new_attribution_targets TRUE)
|
|
if(arg___QT_INTERNAL_HANDLE_QT_ENTITY_ATTRIBUTION_FILES)
|
|
_qt_internal_sbom_is_qt_entity_type("${arg_TYPE}" is_qt_entity_type)
|
|
if(is_qt_entity_type)
|
|
set(arg_CREATE_SBOM_FOR_EACH_ATTRIBUTION TRUE)
|
|
endif()
|
|
endif()
|
|
|
|
if(arg_CREATE_SBOM_FOR_EACH_ATTRIBUTION)
|
|
set(propagate_sbom_options_to_new_attribution_targets FALSE)
|
|
if(NOT arg_ATTRIBUTION_PARENT_TARGET)
|
|
message(FATAL_ERROR "ATTRIBUTION_PARENT_TARGET must be set")
|
|
endif()
|
|
endif()
|
|
|
|
if(arg_ATTRIBUTION_ENTRY_INDEX AND attribution_file_count GREATER 1)
|
|
message(FATAL_ERROR
|
|
"ATTRIBUTION_ENTRY_INDEX should only be set if a single attribution "
|
|
"file is specified."
|
|
)
|
|
endif()
|
|
|
|
set(ids_to_add "")
|
|
set(ids_found "")
|
|
if(arg_ATTRIBUTION_IDS)
|
|
set(ids_to_add ${arg_ATTRIBUTION_IDS})
|
|
endif()
|
|
|
|
set(file_index 0)
|
|
set(first_attribution_processed FALSE)
|
|
foreach(attribution_file_path IN LISTS attribution_files)
|
|
# Collect all processed attribution files to later create a configure-time dependency on
|
|
# them so that the SBOM is regenerated (and CMake is re-ran) if they are modified.
|
|
set_property(GLOBAL APPEND PROPERTY _qt_internal_project_attribution_files
|
|
"${attribution_file_path}")
|
|
|
|
# Set a unique out_prefix that will not overlap when multiple entries are processed.
|
|
set(out_prefix_file "${out_prefix_outer}_${file_index}")
|
|
|
|
# Get the number of entries in the attribution file.
|
|
_qt_internal_sbom_read_qt_attribution(${out_prefix_file}
|
|
GET_ATTRIBUTION_ENTRY_COUNT
|
|
OUT_VAR_VALUE attribution_entry_count
|
|
FILE_PATH "${attribution_file_path}"
|
|
)
|
|
|
|
# If a specific entry was specified, we will only process it from the file.
|
|
if(NOT "${arg_ATTRIBUTION_ENTRY_INDEX}" STREQUAL "")
|
|
set(entry_index ${arg_ATTRIBUTION_ENTRY_INDEX})
|
|
else()
|
|
set(entry_index 0)
|
|
endif()
|
|
|
|
# Go through each entry in the attribution file.
|
|
while("${entry_index}" LESS "${${out_prefix_file}_attribution_entry_count}")
|
|
# If this is the first entry to be processed, or if CREATE_SBOM_FOR_EACH_ATTRIBUTION
|
|
# is not set, we read the attribution file entry directly, and propagate the values
|
|
# to the parent scope.
|
|
if(NOT first_attribution_processed AND NOT arg_CREATE_SBOM_FOR_EACH_ATTRIBUTION)
|
|
# Set a prefix without indices, so that the parent scope add_sbom call can
|
|
# refer to the values directly with the outer prefix, without any index infix.
|
|
set(out_prefix "${out_prefix_outer}")
|
|
|
|
_qt_internal_sbom_read_qt_attribution(${out_prefix}
|
|
GET_DEFAULT_KEYS
|
|
ENTRY_INDEX "${entry_index}"
|
|
OUT_VAR_ASSIGNED_VARIABLE_NAMES variable_names
|
|
FILE_PATH "${attribution_file_path}"
|
|
)
|
|
|
|
# Check if we need to filter for specific ids.
|
|
if(ids_to_add AND ${out_prefix}_attribution_id)
|
|
if("${${out_prefix}_attribution_id}" IN_LIST ids_to_add)
|
|
list(APPEND ids_found "${${out_prefix}_attribution_id}")
|
|
else()
|
|
# Skip to next entry.
|
|
math(EXPR entry_index "${entry_index} + 1")
|
|
continue()
|
|
endif()
|
|
endif()
|
|
|
|
# Propagate the values to the outer scope.
|
|
foreach(variable_name IN LISTS variable_names)
|
|
set(${out_prefix}_${variable_name} "${${out_prefix}_${variable_name}}"
|
|
PARENT_SCOPE)
|
|
endforeach()
|
|
|
|
get_filename_component(relative_attribution_file_path
|
|
"${attribution_file_path}" REALPATH)
|
|
|
|
set(${out_prefix}_chosen_attribution_file_path "${relative_attribution_file_path}"
|
|
PARENT_SCOPE)
|
|
set(${out_prefix}_chosen_attribution_entry_index "${entry_index}"
|
|
PARENT_SCOPE)
|
|
|
|
set(first_attribution_processed TRUE)
|
|
if(NOT "${arg_ATTRIBUTION_ENTRY_INDEX}" STREQUAL "")
|
|
# We had a specific index to process, so break right after processing it.
|
|
break()
|
|
endif()
|
|
else()
|
|
# We are processing the second or later entry, or CREATE_SBOM_FOR_EACH_ATTRIBUTION
|
|
# was set. Instead of directly reading all the keys from the attribution file,
|
|
# we get the Id, and create a new sbom target for the entry.
|
|
# That will recursively call this function with a specific attribution file path
|
|
# and index, to process the specific entry.
|
|
|
|
set(out_prefix "${out_prefix_outer}_${file_index}_${entry_index}")
|
|
|
|
# Get the attribution id.
|
|
_qt_internal_sbom_read_qt_attribution(${out_prefix}
|
|
GET_KEY
|
|
KEY Id
|
|
OUT_VAR_VALUE attribution_id
|
|
ENTRY_INDEX "${entry_index}"
|
|
FILE_PATH "${attribution_file_path}"
|
|
)
|
|
|
|
# Check if we need to filter for specific ids
|
|
if(ids_to_add AND ${out_prefix}_attribution_id)
|
|
if("${${out_prefix}_attribution_id}" IN_LIST ids_to_add)
|
|
list(APPEND ids_found "${${out_prefix}_attribution_id}")
|
|
else()
|
|
# Skip to next entry.
|
|
math(EXPR entry_index "${entry_index} + 1")
|
|
continue()
|
|
endif()
|
|
endif()
|
|
|
|
# If no Id was retrieved, just add a numeric one, to make the sbom target
|
|
# unique.
|
|
set(attribution_target "${arg_ATTRIBUTION_PARENT_TARGET}_Attribution_")
|
|
if(NOT ${out_prefix}_attribution_id)
|
|
string(APPEND attribution_target "${file_index}_${entry_index}")
|
|
else()
|
|
string(APPEND attribution_target "${${out_prefix}_attribution_id}")
|
|
endif()
|
|
|
|
# Sanitize the target name, to avoid issues with slashes and other unsupported chars
|
|
# in target names.
|
|
string(REGEX REPLACE "[^a-zA-Z0-9_-]" "_"
|
|
attribution_target "${attribution_target}")
|
|
|
|
set(sbom_args "")
|
|
|
|
# Always propagate the package supplier, because we assume the supplier for 3rd
|
|
# party libs is the same as the current project supplier.
|
|
# Also propagate the internal qt entity type values like CPE, supplier, PURL
|
|
# handling options, attribution file values, if set.
|
|
_qt_internal_forward_function_args(
|
|
FORWARD_APPEND
|
|
FORWARD_PREFIX arg
|
|
FORWARD_OUT_VAR sbom_args
|
|
FORWARD_OPTIONS
|
|
USE_ATTRIBUTION_FILES
|
|
__QT_INTERNAL_HANDLE_QT_ENTITY_TYPE_CPE
|
|
__QT_INTERNAL_HANDLE_QT_ENTITY_TYPE_SUPPLIER
|
|
__QT_INTERNAL_HANDLE_QT_ENTITY_TYPE_PURL
|
|
__QT_INTERNAL_HANDLE_QT_ENTITY_ATTRIBUTION_FILES
|
|
FORWARD_SINGLE
|
|
SUPPLIER
|
|
)
|
|
|
|
if(propagate_sbom_options_to_new_attribution_targets)
|
|
# Filter out the attributtion options, they will be passed mnaually
|
|
# depending on which file and index is currently being processed.
|
|
_qt_internal_get_sbom_specific_options(
|
|
sbom_opt_args sbom_single_args sbom_multi_args)
|
|
list(REMOVE_ITEM sbom_opt_args
|
|
NO_CURRENT_DIR_ATTRIBUTION
|
|
CREATE_SBOM_FOR_EACH_ATTRIBUTION
|
|
)
|
|
list(REMOVE_ITEM sbom_single_args ATTRIBUTION_ENTRY_INDEX)
|
|
list(REMOVE_ITEM sbom_multi_args
|
|
ATTRIBUTION_IDS
|
|
ATTRIBUTION_FILE_PATHS
|
|
ATTRIBUTION_FILE_DIR_PATHS
|
|
)
|
|
|
|
# Also filter out the FRIENDLY_PACKAGE_NAME option, otherwise we'd try to
|
|
# file(GENERATE) multiple times with the same file name, but different content.
|
|
list(REMOVE_ITEM sbom_single_args FRIENDLY_PACKAGE_NAME)
|
|
|
|
_qt_internal_forward_function_args(
|
|
FORWARD_APPEND
|
|
FORWARD_PREFIX arg
|
|
FORWARD_OUT_VAR sbom_args
|
|
FORWARD_OPTIONS
|
|
${sbom_opt_args}
|
|
FORWARD_SINGLE
|
|
${sbom_single_args}
|
|
FORWARD_MULTI
|
|
${sbom_multi_args}
|
|
)
|
|
endif()
|
|
|
|
# Create another sbom target with the id as a hint for the target name,
|
|
# the attribution file passed, and make the new target a dependency of the
|
|
# parent one.
|
|
if(arg___QT_INTERNAL_HANDLE_QT_ENTITY_ATTRIBUTION_FILES)
|
|
set(attribution_entity_type QT_THIRD_PARTY_SOURCES)
|
|
else()
|
|
set(attribution_entity_type THIRD_PARTY_SOURCES)
|
|
endif()
|
|
|
|
_qt_internal_add_sbom("${attribution_target}"
|
|
IMMEDIATE_FINALIZATION
|
|
TYPE "${attribution_entity_type}"
|
|
ATTRIBUTION_FILE_PATHS "${attribution_file_path}"
|
|
ATTRIBUTION_ENTRY_INDEX "${entry_index}"
|
|
NO_CURRENT_DIR_ATTRIBUTION
|
|
${sbom_args}
|
|
)
|
|
|
|
_qt_internal_extend_sbom_dependencies(${arg_ATTRIBUTION_PARENT_TARGET}
|
|
SBOM_DEPENDENCIES ${attribution_target}
|
|
)
|
|
endif()
|
|
|
|
math(EXPR entry_index "${entry_index} + 1")
|
|
endwhile()
|
|
|
|
math(EXPR file_index "${file_index} + 1")
|
|
endforeach()
|
|
|
|
# Show an error if an id is unaccounted for, it might be it has moved to a different file, that
|
|
# is not referenced.
|
|
if(ids_to_add)
|
|
set(attribution_ids_diff ${ids_to_add})
|
|
list(REMOVE_ITEM attribution_ids_diff ${ids_found})
|
|
if(attribution_ids_diff)
|
|
set(error_message
|
|
"The following required attribution ids were not found in the attribution files")
|
|
if(arg_ATTRIBUTION_PARENT_TARGET)
|
|
string(APPEND error_message " for target: ${arg_ATTRIBUTION_PARENT_TARGET}")
|
|
endif()
|
|
string(APPEND error_message " ids: ${attribution_ids_diff}")
|
|
message(FATAL_ERROR "${error_message}")
|
|
endif()
|
|
endif()
|
|
endfunction()
|
|
|
|
# Helper to parse a qt_attribution.json file and do various operations:
|
|
# - GET_DEFAULT_KEYS extracts the license id, copyrights, version, etc.
|
|
# - GET_KEY extracts a single given json key's value, as specified with KEY and saved into
|
|
# OUT_VAR_VALUE
|
|
# - GET_ATTRIBUTION_ENTRY_COUNT returns the number of entries in the json file, set in
|
|
# OUT_VAR_VALUE
|
|
#
|
|
# ENTRY_INDEX can be used to specify the array index to select a specific entry in the json file.
|
|
#
|
|
# Any retrieved value is set in the outer scope.
|
|
# The variables are prefixed with ${out_prefix}.
|
|
# OUT_VAR_ASSIGNED_VARIABLE_NAMES contains the list of variables set in the parent scope, the
|
|
# variables names in this list are not prefixed with ${out_prefix}.
|
|
#
|
|
# Requires cmake 3.19 for json parsing.
|
|
function(_qt_internal_sbom_read_qt_attribution out_prefix)
|
|
if(NOT QT_GENERATE_SBOM)
|
|
return()
|
|
endif()
|
|
|
|
if(CMAKE_VERSION LESS_EQUAL "3.19")
|
|
message(DEBUG "CMake version is too low, can't parse attribution.json file.")
|
|
return()
|
|
endif()
|
|
|
|
set(opt_args
|
|
GET_DEFAULT_KEYS
|
|
GET_KEY
|
|
GET_ATTRIBUTION_ENTRY_COUNT
|
|
)
|
|
set(single_args
|
|
FILE_PATH
|
|
KEY
|
|
ENTRY_INDEX
|
|
OUT_VAR_VALUE
|
|
OUT_VAR_ASSIGNED_VARIABLE_NAMES
|
|
)
|
|
set(multi_args "")
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
set(file_path "${arg_FILE_PATH}")
|
|
|
|
if(NOT file_path)
|
|
message(FATAL_ERROR "qt attribution file path not given")
|
|
endif()
|
|
|
|
file(READ "${file_path}" contents)
|
|
if(NOT contents)
|
|
message(FATAL_ERROR "qt attribution file is empty: ${file_path}")
|
|
endif()
|
|
|
|
if(NOT arg_GET_DEFAULT_KEYS AND NOT arg_GET_KEY AND NOT arg_GET_ATTRIBUTION_ENTRY_COUNT)
|
|
message(FATAL_ERROR
|
|
"No valid operation specified to _qt_internal_sbom_read_qt_attribution call.")
|
|
endif()
|
|
|
|
if(arg_GET_KEY)
|
|
if(NOT arg_KEY)
|
|
message(FATAL_ERROR "KEY must be set")
|
|
endif()
|
|
if(NOT arg_OUT_VAR_VALUE)
|
|
message(FATAL_ERROR "OUT_VAR_VALUE must be set")
|
|
endif()
|
|
endif()
|
|
|
|
get_filename_component(attribution_file_dir "${file_path}" DIRECTORY)
|
|
|
|
# Parse the json file.
|
|
# The first element might be an array, or an object. We need to detect which one.
|
|
# Do that by trying to query index 0 of the potential root array.
|
|
# If the index is found, that means the root is an array, and elem_error is set to NOTFOUND,
|
|
# because there was no error.
|
|
# Otherwise elem_error will be something like 'member '0' not found', and we can assume the
|
|
# root is an object.
|
|
string(JSON first_elem_type ERROR_VARIABLE elem_error TYPE "${contents}" 0)
|
|
if(elem_error STREQUAL "NOTFOUND")
|
|
# Root is an array. The attribution file might contain multiple entries.
|
|
# Pick the first one if no specific index was specified, otherwise use the given index.
|
|
if(NOT "${arg_ENTRY_INDEX}" STREQUAL "")
|
|
set(indices "${arg_ENTRY_INDEX}")
|
|
else()
|
|
set(indices "0")
|
|
endif()
|
|
set(is_array TRUE)
|
|
else()
|
|
# Root is an object, not an array, which means the file has a single entry.
|
|
set(indices "")
|
|
set(is_array FALSE)
|
|
endif()
|
|
|
|
set(variable_names "")
|
|
|
|
if(arg_GET_KEY)
|
|
_qt_internal_sbom_get_attribution_key(${arg_KEY} ${arg_OUT_VAR_VALUE} ${out_prefix})
|
|
endif()
|
|
|
|
if(arg_GET_ATTRIBUTION_ENTRY_COUNT)
|
|
if(NOT arg_OUT_VAR_VALUE)
|
|
message(FATAL_ERROR "OUT_VAR_VALUE must be set")
|
|
endif()
|
|
|
|
if(is_array)
|
|
string(JSON attribution_entry_count ERROR_VARIABLE elem_error LENGTH "${contents}")
|
|
# There was an error getting the length of the array, so we assume it's empty.
|
|
if(NOT elem_error STREQUAL "NOTFOUND")
|
|
set(attribution_entry_count 0)
|
|
endif()
|
|
else()
|
|
set(attribution_entry_count 1)
|
|
endif()
|
|
|
|
set(${out_prefix}_${arg_OUT_VAR_VALUE} "${attribution_entry_count}" PARENT_SCOPE)
|
|
endif()
|
|
|
|
if(arg_GET_DEFAULT_KEYS)
|
|
_qt_internal_sbom_get_attribution_key(Id attribution_id "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(LicenseId license_id "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(License license "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(LicenseFile license_file "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(Version version "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(Homepage homepage "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(Name attribution_name "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(Description description "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(QtUsage qt_usage "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(DownloadLocation download_location "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(Copyright copyrights "${out_prefix}" IS_MULTI_VALUE)
|
|
_qt_internal_sbom_get_attribution_key(CopyrightFile copyright_file "${out_prefix}")
|
|
_qt_internal_sbom_get_attribution_key(PURL purls "${out_prefix}" IS_MULTI_VALUE)
|
|
_qt_internal_sbom_get_attribution_key(CPE cpes "${out_prefix}" IS_MULTI_VALUE)
|
|
|
|
# Some attribution files contain a copyright file that contains the actual list of
|
|
# copyrights. Read it and use it.
|
|
set(copyright_file_path "${attribution_file_dir}/${copyright_file}")
|
|
get_filename_component(copyright_file_path "${copyright_file_path}" REALPATH)
|
|
if(NOT copyrights AND copyright_file AND EXISTS "${copyright_file_path}")
|
|
file(READ "${copyright_file_path}" copyright_contents)
|
|
if(copyright_contents)
|
|
set(copyright_contents "${copyright_contents}")
|
|
set(copyrights "${copyright_contents}")
|
|
set(${out_prefix}_copyrights "${copyright_contents}" PARENT_SCOPE)
|
|
list(APPEND variable_names "copyrights")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
|
|
if(arg_OUT_VAR_ASSIGNED_VARIABLE_NAMES)
|
|
set(${arg_OUT_VAR_ASSIGNED_VARIABLE_NAMES} "${variable_names}" PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Extracts a string or an array of strings from a json index path, depending on the extracted value
|
|
# type.
|
|
#
|
|
# Given the 'contents' of the whole json document and the EXTRACTED_VALUE of a json key specified
|
|
# by the INDICES path, it tries to determine whether the value is an array, in which case the array
|
|
# is converted to a cmake list and assigned to ${out_var} in the parent scope.
|
|
# Otherwise the function assumes the EXTRACTED_VALUE was not an array, and just assigns the value
|
|
# of EXTRACTED_VALUE to ${out_var}
|
|
function(_qt_internal_sbom_handle_attribution_json_array contents)
|
|
set(opt_args "")
|
|
set(single_args
|
|
EXTRACTED_VALUE
|
|
OUT_VAR
|
|
)
|
|
set(multi_args
|
|
INDICES
|
|
)
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
# Write the original value to the parent scope, in case it was not an array.
|
|
set(${arg_OUT_VAR} "${arg_EXTRACTED_VALUE}" PARENT_SCOPE)
|
|
|
|
if(NOT arg_EXTRACTED_VALUE)
|
|
return()
|
|
endif()
|
|
|
|
string(JSON element_type TYPE "${contents}" ${arg_INDICES})
|
|
|
|
if(NOT element_type STREQUAL "ARRAY")
|
|
return()
|
|
endif()
|
|
|
|
set(json_array "${arg_EXTRACTED_VALUE}")
|
|
string(JSON array_len LENGTH "${json_array}")
|
|
|
|
set(value_list "")
|
|
|
|
math(EXPR array_len "${array_len} - 1")
|
|
foreach(index RANGE 0 "${array_len}")
|
|
string(JSON value GET "${json_array}" ${index})
|
|
if(value)
|
|
list(APPEND value_list "${value}")
|
|
endif()
|
|
endforeach()
|
|
|
|
if(value_list)
|
|
set(${arg_OUT_VAR} "${value_list}" PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Escapes various characters in json content, so that the generate cmake code to append the content
|
|
# to the spdx document is syntactically valid.
|
|
function(_qt_internal_sbom_escape_json_content content out_var)
|
|
# Escape backslashes
|
|
string(REPLACE "\\" "\\\\" escaped_content "${content}")
|
|
|
|
# Escape quotes
|
|
string(REPLACE "\"" "\\\"" escaped_content "${escaped_content}")
|
|
|
|
set(${out_var} "${escaped_content}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# This macro reads a json key from a qt_attribution.json file, and assigns the escaped value to
|
|
# out_var.
|
|
# Also appends the name of the out_var to the parent scope 'variable_names' var.
|
|
#
|
|
# Expects 'contents' and 'indices' to already be set in the calling scope.
|
|
#
|
|
# If IS_MULTI_VALUE is set, handles the key as if it contained an array of
|
|
# values, by converting the array of json values to a cmake list.
|
|
macro(_qt_internal_sbom_get_attribution_key json_key out_var out_prefix)
|
|
cmake_parse_arguments(arg "IS_MULTI_VALUE" "" "" ${ARGN})
|
|
|
|
# Reset any leftover value that might have been set in a previous iteration.
|
|
set(${out_prefix}_${out_var} "" PARENT_SCOPE)
|
|
|
|
string(JSON "${out_var}" ERROR_VARIABLE get_error GET "${contents}" ${indices} "${json_key}")
|
|
if(NOT "${${out_var}}" STREQUAL "" AND NOT get_error)
|
|
set(extracted_value "${${out_var}}")
|
|
|
|
if(arg_IS_MULTI_VALUE)
|
|
_qt_internal_sbom_handle_attribution_json_array("${contents}"
|
|
EXTRACTED_VALUE "${extracted_value}"
|
|
INDICES ${indices} ${json_key}
|
|
OUT_VAR value_list
|
|
)
|
|
if(value_list)
|
|
set(extracted_value "${value_list}")
|
|
endif()
|
|
endif()
|
|
|
|
_qt_internal_sbom_escape_json_content("${extracted_value}" escaped_content)
|
|
|
|
set(${out_prefix}_${out_var} "${escaped_content}" PARENT_SCOPE)
|
|
list(APPEND variable_names "${out_var}")
|
|
|
|
unset(extracted_value)
|
|
unset(escaped_content)
|
|
unset(value_list)
|
|
endif()
|
|
endmacro()
|
|
|
|
# Replaces placeholders in CPE and PURL strings read from qt_attribution.json files.
|
|
#
|
|
# VALUES - list of CPE or PURL strings
|
|
# OUT_VAR - variable to store the replaced values
|
|
# VERSION - version to replace in the placeholders
|
|
|
|
# Known placeholders:
|
|
# $<VERSION> - Replaces occurrences of the placeholder with the value passed to the VERSION option.
|
|
# $<VERSION_DASHED> - Replaces occurrences of the placeholder with the value passed to the VERSION
|
|
# option, but with dots replaced by dashes.
|
|
function(_qt_internal_sbom_replace_qa_placeholders)
|
|
set(opt_args "")
|
|
set(single_args
|
|
OUT_VAR
|
|
VERSION
|
|
)
|
|
set(multi_args
|
|
VALUES
|
|
)
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_OUT_VAR)
|
|
message(FATAL_ERROR "OUT_VAR must be set")
|
|
endif()
|
|
|
|
set(result "")
|
|
|
|
if(arg_VERSION)
|
|
string(REPLACE "." "-" dashed_version "${arg_VERSION}")
|
|
endif()
|
|
|
|
foreach(value IN LISTS arg_VALUES)
|
|
if(arg_VERSION)
|
|
string(REPLACE "$<VERSION>" "${arg_VERSION}" value "${value}")
|
|
string(REPLACE "$<VERSION_DASHED>" "${dashed_version}" value "${value}")
|
|
endif()
|
|
|
|
list(APPEND result "${value}")
|
|
endforeach()
|
|
|
|
set(${arg_OUT_VAR} "${result}" PARENT_SCOPE)
|
|
endfunction()
|