Initially the git vars were assigned to the parent scope of the _qt_internal_sbom_begin_project function, with the intent to set them in the global scope. But the function was later wrapped in other functions, so the variables stopped being accessible. Instead of playing with recursive PARENT_SCOPEs, save the variables in global properties like we do for other info, and use a new _qt_internal_sbom_get_git_version_vars() function to query the vars in the code that needs them. This fixes generated purls to contain the git version and hashes. Also add a new internal API wrapper macro called qt_internal_sbom_get_git_version_vars to allow calling it in other repos. Pick-to: 6.8 6.9 Task-number: QTBUG-122899 Change-Id: I061b34f418c1ecc1c66c8c01ef758d2f40611ede Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
447 lines
16 KiB
CMake
447 lines
16 KiB
CMake
# Copyright (C) 2024 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# Parse purl arguments for a specific purl variant, e.g. for parsing all values of arg_PURL_QT_ARGS.
|
|
# arguments_var_name is the variable name that contains the args.
|
|
macro(_qt_internal_sbom_parse_purl_variant_options prefix arguments_var_name)
|
|
_qt_internal_get_sbom_purl_parsing_options(purl_opt_args purl_single_args purl_multi_args)
|
|
|
|
cmake_parse_arguments(arg "${purl_opt_args}" "${purl_single_args}" "${purl_multi_args}"
|
|
${${arguments_var_name}})
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
endmacro()
|
|
|
|
# Returns a vcs url where for purls where qt entities of the current repo are hosted.
|
|
function(_qt_internal_sbom_get_qt_entity_vcs_url target)
|
|
set(opt_args "")
|
|
set(single_args
|
|
REPO_NAME
|
|
VERSION
|
|
OUT_VAR
|
|
)
|
|
set(multi_args "")
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_REPO_NAME)
|
|
message(FATAL_ERROR "REPO_NAME must be set")
|
|
endif()
|
|
|
|
if(NOT arg_OUT_VAR)
|
|
message(FATAL_ERROR "OUT_VAR must be set")
|
|
endif()
|
|
|
|
set(version_part "")
|
|
if(arg_VERSION)
|
|
set(version_part "@${arg_VERSION}")
|
|
endif()
|
|
|
|
set(vcs_url "https://code.qt.io/qt/${arg_REPO_NAME}.git${version_part}")
|
|
set(${arg_OUT_VAR} "${vcs_url}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Returns a relative path to the source where the target was created, to be embedded into a
|
|
# mirror purl as a subpath.
|
|
function(_qt_internal_sbom_get_qt_entity_repo_source_dir target)
|
|
set(opt_args "")
|
|
set(single_args
|
|
OUT_VAR
|
|
)
|
|
set(multi_args "")
|
|
cmake_parse_arguments(PARSE_ARGV 1 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()
|
|
|
|
get_target_property(repo_source_dir "${target}" SOURCE_DIR)
|
|
|
|
# Get the path relative to the PROJECT_SOURCE_DIR
|
|
file(RELATIVE_PATH relative_repo_source_dir "${PROJECT_SOURCE_DIR}" "${repo_source_dir}")
|
|
|
|
set(sub_path "${relative_repo_source_dir}")
|
|
set(${arg_OUT_VAR} "${sub_path}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Handles purl arguments specified to functions like qt_internal_add_sbom.
|
|
# Currently accepts arguments for 3 variants of purls, each of which will generate a separate purl.
|
|
# If no arguments are specified, for qt entity types, default values will be chosen.
|
|
#
|
|
# Purl variants:
|
|
# - PURL_QT_ARGS
|
|
# args to override Qt's generic purl for Qt modules or patched 3rd party libs
|
|
# defaults to something like pkg:generic/TheQtCompany/${repo_name}-${target}@SHA1
|
|
# - PURL_MIRROR_ARGS
|
|
# args to override Qt's mirror purl, which is hosted on github
|
|
# defaults to something like pkg:github/qt/${repo_name}@SHA1
|
|
# - PURL_3RDPARTY_UPSTREAM_ARGS
|
|
# args to specify a purl pointing to an upstream repo, usually to github or another forge
|
|
# no defaults, but could look like: pkg:github/harfbuzz/harfbuzz@v8.5.0
|
|
# Example values for harfbuzz:
|
|
# PURL_3RDPARTY_UPSTREAM_ARGS
|
|
# PURL_TYPE "github"
|
|
# PURL_NAMESPACE "harfbuzz"
|
|
# PURL_NAME "harfbuzz"
|
|
# PURL_VERSION "v8.5.0" # tag
|
|
function(_qt_internal_sbom_handle_purl_values target)
|
|
_qt_internal_get_sbom_purl_handling_options(opt_args single_args multi_args)
|
|
list(APPEND single_args OUT_VAR)
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 1 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()
|
|
|
|
# List of purl variants to process.
|
|
set(purl_variants "")
|
|
|
|
_qt_internal_sbom_get_git_version_vars()
|
|
|
|
set(third_party_types
|
|
QT_THIRD_PARTY_MODULE
|
|
QT_THIRD_PARTY_SOURCES
|
|
)
|
|
|
|
if(arg_IS_QT_ENTITY_TYPE)
|
|
# Qt entities have two purls by default, a QT generic one and a MIRROR hosted on github.
|
|
list(APPEND purl_variants MIRROR QT)
|
|
elseif(arg_TYPE IN_LIST third_party_types)
|
|
# Third party libraries vendored in Qt also have at least two purls, like regular Qt
|
|
# libraries, but might also have an upstream one.
|
|
|
|
# The order in which the purls are generated matters for tools that consume the SBOM. Some
|
|
# tools can only handle one PURL per package, so the first one should be the important one.
|
|
# For now, I deem that the upstream one if present. Otherwise the github mirror.
|
|
if(arg_PURL_3RDPARTY_UPSTREAM_ARGS)
|
|
list(APPEND purl_variants 3RDPARTY_UPSTREAM)
|
|
endif()
|
|
|
|
list(APPEND purl_variants MIRROR QT)
|
|
else()
|
|
# If handling another entity type, handle based on whether any of the purl arguments are
|
|
# set.
|
|
set(known_purl_variants QT MIRROR 3RDPARTY_UPSTREAM)
|
|
foreach(known_purl_variant IN LISTS known_purl_variants)
|
|
if(arg_PURL_${known_purl_variant}_ARGS)
|
|
list(APPEND purl_variants ${known_purl_variant})
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
if(arg_IS_QT_ENTITY_TYPE
|
|
OR arg_TYPE STREQUAL "QT_THIRD_PARTY_MODULE"
|
|
OR arg_TYPE STREQUAL "QT_THIRD_PARTY_SOURCES"
|
|
)
|
|
set(is_qt_purl_entity_type TRUE)
|
|
else()
|
|
set(is_qt_purl_entity_type FALSE)
|
|
endif()
|
|
|
|
_qt_internal_get_sbom_purl_parsing_options(purl_opt_args purl_single_args purl_multi_args)
|
|
|
|
set(project_package_options "")
|
|
|
|
foreach(purl_variant IN LISTS purl_variants)
|
|
# Clear previous values.
|
|
foreach(option_name IN LISTS purl_opt_args purl_single_args purl_multi_args)
|
|
unset(arg_${option_name})
|
|
endforeach()
|
|
|
|
_qt_internal_sbom_parse_purl_variant_options(arg arg_PURL_${purl_variant}_ARGS)
|
|
|
|
# Check if custom purl args were specified.
|
|
set(purl_args_available FALSE)
|
|
if(arg_PURL_${purl_variant}_ARGS)
|
|
set(purl_args_available TRUE)
|
|
endif()
|
|
|
|
# We want to create a purl either if it's one of Qt's entities and one of it's default
|
|
# purl types, or if custom args were specified.
|
|
set(consider_purl_processing FALSE)
|
|
if((purl_args_available OR is_qt_purl_entity_type) AND NOT arg_NO_PURL)
|
|
set(consider_purl_processing TRUE)
|
|
endif()
|
|
|
|
if(consider_purl_processing)
|
|
set(purl_args "")
|
|
|
|
# Override the purl version with the package version.
|
|
if(arg_PURL_USE_PACKAGE_VERSION AND arg_VERSION)
|
|
set(arg_PURL_VERSION "${arg_VERSION}")
|
|
endif()
|
|
|
|
# Append a vcs_url to the qualifiers if specified.
|
|
if(arg_PURL_VCS_URL)
|
|
list(APPEND arg_PURL_QUALIFIERS "vcs_url=${arg_PURL_VCS_URL}")
|
|
endif()
|
|
|
|
_qt_internal_forward_function_args(
|
|
FORWARD_APPEND
|
|
FORWARD_PREFIX arg
|
|
FORWARD_OUT_VAR purl_args
|
|
FORWARD_OPTIONS
|
|
${purl_opt_args}
|
|
FORWARD_SINGLE
|
|
${purl_single_args}
|
|
FORWARD_MULTI
|
|
${purl_multi_args}
|
|
)
|
|
|
|
# Qt entity types get special treatment purl.
|
|
if(is_qt_purl_entity_type AND NOT arg_NO_DEFAULT_QT_PURL AND
|
|
(purl_variant STREQUAL "QT" OR purl_variant STREQUAL "MIRROR"))
|
|
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
|
|
|
|
# Add a vcs_url to the generic QT variant.
|
|
if(purl_variant STREQUAL "QT")
|
|
set(entity_vcs_url_version_option "")
|
|
# Can be empty.
|
|
if(QT_SBOM_GIT_HASH_SHORT)
|
|
set(entity_vcs_url_version_option VERSION "${QT_SBOM_GIT_HASH_SHORT}")
|
|
endif()
|
|
|
|
_qt_internal_sbom_get_qt_entity_vcs_url(${target}
|
|
REPO_NAME "${repo_project_name_lowercase}"
|
|
${entity_vcs_url_version_option}
|
|
OUT_VAR vcs_url)
|
|
list(APPEND purl_args PURL_QUALIFIERS "vcs_url=${vcs_url}")
|
|
endif()
|
|
|
|
# Add the subdirectory path where the target was created as a custom qualifier.
|
|
_qt_internal_sbom_get_qt_entity_repo_source_dir(${target} OUT_VAR sub_path)
|
|
if(sub_path)
|
|
list(APPEND purl_args PURL_SUBPATH "${sub_path}")
|
|
endif()
|
|
|
|
# Add the target name as a custom qualifer.
|
|
list(APPEND purl_args PURL_QUALIFIERS "library_name=${target}")
|
|
|
|
# Can be empty.
|
|
if(QT_SBOM_GIT_HASH_SHORT)
|
|
list(APPEND purl_args VERSION "${QT_SBOM_GIT_HASH_SHORT}")
|
|
endif()
|
|
|
|
# Get purl args the Qt entity type, taking into account defaults.
|
|
_qt_internal_sbom_get_qt_entity_purl_args(${target}
|
|
NAME "${repo_project_name_lowercase}-${target}"
|
|
REPO_NAME "${repo_project_name_lowercase}"
|
|
SUPPLIER "${arg_SUPPLIER}"
|
|
PURL_VARIANT "${purl_variant}"
|
|
${purl_args}
|
|
OUT_VAR purl_args
|
|
)
|
|
endif()
|
|
|
|
_qt_internal_sbom_assemble_purl(${target}
|
|
${purl_args}
|
|
OUT_VAR package_manager_external_ref
|
|
)
|
|
list(APPEND project_package_options ${package_manager_external_ref})
|
|
endif()
|
|
endforeach()
|
|
|
|
set(direct_values
|
|
PURL_QT_VALUES
|
|
PURL_MIRROR_VALUES
|
|
PURL_3RDPARTY_UPSTREAM_VALUES
|
|
)
|
|
|
|
foreach(direct_value IN LISTS direct_values)
|
|
if(arg_${direct_value})
|
|
set(direct_values_per_type "")
|
|
foreach(direct_value IN LISTS arg_${direct_value})
|
|
_qt_internal_sbom_get_purl_value_extref(
|
|
VALUE "${direct_value}" OUT_VAR package_manager_external_ref)
|
|
|
|
list(APPEND direct_values_per_type ${package_manager_external_ref})
|
|
endforeach()
|
|
# The order in which the purls are generated, matters for tools that consume the SBOM.
|
|
# Some tools can only handle one PURL per package, so the first one should be the
|
|
# important one.
|
|
# For now, I deem that the directly specified ones (probably via a qt_attribution.json
|
|
# file) are the more important ones. So we prepend them.
|
|
list(PREPEND project_package_options ${direct_values_per_type})
|
|
endif()
|
|
endforeach()
|
|
|
|
set(${arg_OUT_VAR} "${project_package_options}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Gets a list of arguments to pass to _qt_internal_sbom_assemble_purl when handling a Qt entity
|
|
# type. The purl for Qt entity types have Qt-specific defaults, but can be overridden per purl
|
|
# component.
|
|
# The arguments are saved in OUT_VAR.
|
|
function(_qt_internal_sbom_get_qt_entity_purl_args target)
|
|
set(opt_args "")
|
|
set(single_args
|
|
NAME
|
|
REPO_NAME
|
|
SUPPLIER
|
|
VERSION
|
|
PURL_VARIANT
|
|
OUT_VAR
|
|
)
|
|
set(multi_args "")
|
|
|
|
_qt_internal_get_sbom_purl_parsing_options(purl_opt_args purl_single_args purl_multi_args)
|
|
list(APPEND opt_args ${purl_opt_args})
|
|
list(APPEND single_args ${purl_single_args})
|
|
list(APPEND multi_args ${purl_multi_args})
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
set(supported_purl_variants QT MIRROR)
|
|
if(NOT arg_PURL_VARIANT IN_LIST supported_purl_variants)
|
|
message(FATAL_ERROR "PURL_VARIANT unknown: ${arg_PURL_VARIANT}")
|
|
endif()
|
|
|
|
if(arg_PURL_VARIANT STREQUAL "QT")
|
|
set(purl_type "generic")
|
|
set(purl_namespace "${arg_SUPPLIER}")
|
|
set(purl_name "${arg_NAME}")
|
|
set(purl_version "${arg_VERSION}")
|
|
elseif(arg_PURL_VARIANT STREQUAL "MIRROR")
|
|
set(purl_type "github")
|
|
set(purl_namespace "qt")
|
|
set(purl_name "${arg_REPO_NAME}")
|
|
set(purl_version "${arg_VERSION}")
|
|
endif()
|
|
|
|
if(arg_PURL_TYPE)
|
|
set(purl_type "${arg_PURL_TYPE}")
|
|
endif()
|
|
|
|
if(arg_PURL_NAMESPACE)
|
|
set(purl_namespace "${arg_PURL_NAMESPACE}")
|
|
endif()
|
|
|
|
if(arg_PURL_NAME)
|
|
set(purl_name "${arg_PURL_NAME}")
|
|
endif()
|
|
|
|
if(arg_PURL_VERSION)
|
|
set(purl_version "${arg_PURL_VERSION}")
|
|
endif()
|
|
|
|
set(purl_version_option "")
|
|
if(purl_version)
|
|
set(purl_version_option PURL_VERSION "${purl_version}")
|
|
endif()
|
|
|
|
set(purl_args
|
|
PURL_TYPE "${purl_type}"
|
|
PURL_NAMESPACE "${purl_namespace}"
|
|
PURL_NAME "${purl_name}"
|
|
${purl_version_option}
|
|
)
|
|
|
|
if(arg_PURL_QUALIFIERS)
|
|
list(APPEND purl_args PURL_QUALIFIERS "${arg_PURL_QUALIFIERS}")
|
|
endif()
|
|
|
|
if(arg_PURL_SUBPATH)
|
|
list(APPEND purl_args PURL_SUBPATH "${arg_PURL_SUBPATH}")
|
|
endif()
|
|
|
|
set(${arg_OUT_VAR} "${purl_args}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Assembles an external reference purl identifier.
|
|
# PURL_TYPE and PURL_NAME are required.
|
|
# Stores the result in the OUT_VAR.
|
|
# Accepted options:
|
|
# PURL_TYPE
|
|
# PURL_NAME
|
|
# PURL_NAMESPACE
|
|
# PURL_VERSION
|
|
# PURL_SUBPATH
|
|
# PURL_QUALIFIERS
|
|
function(_qt_internal_sbom_assemble_purl target)
|
|
set(opt_args "")
|
|
set(single_args
|
|
OUT_VAR
|
|
)
|
|
set(multi_args "")
|
|
|
|
_qt_internal_get_sbom_purl_parsing_options(purl_opt_args purl_single_args purl_multi_args)
|
|
list(APPEND opt_args ${purl_opt_args})
|
|
list(APPEND single_args ${purl_single_args})
|
|
list(APPEND multi_args ${purl_multi_args})
|
|
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
set(purl_scheme "pkg")
|
|
|
|
if(NOT arg_PURL_TYPE)
|
|
message(FATAL_ERROR "PURL_TYPE must be set")
|
|
endif()
|
|
|
|
if(NOT arg_PURL_NAME)
|
|
message(FATAL_ERROR "PURL_NAME must be set")
|
|
endif()
|
|
|
|
if(NOT arg_OUT_VAR)
|
|
message(FATAL_ERROR "OUT_VAR must be set")
|
|
endif()
|
|
|
|
# https://github.com/package-url/purl-spec
|
|
# Spec is 'scheme:type/namespace/name@version?qualifiers#subpath'
|
|
set(purl "${purl_scheme}:${arg_PURL_TYPE}")
|
|
|
|
if(arg_PURL_NAMESPACE)
|
|
string(APPEND purl "/${arg_PURL_NAMESPACE}")
|
|
endif()
|
|
|
|
string(APPEND purl "/${arg_PURL_NAME}")
|
|
|
|
if(arg_PURL_VERSION)
|
|
string(APPEND purl "@${arg_PURL_VERSION}")
|
|
endif()
|
|
|
|
if(arg_PURL_QUALIFIERS)
|
|
# TODO: Note that the qualifiers are expected to be URL encoded, which this implementation
|
|
# is not doing at the moment.
|
|
list(JOIN arg_PURL_QUALIFIERS "&" qualifiers)
|
|
string(APPEND purl "?${qualifiers}")
|
|
endif()
|
|
|
|
if(arg_PURL_SUBPATH)
|
|
string(APPEND purl "#${arg_PURL_SUBPATH}")
|
|
endif()
|
|
|
|
_qt_internal_sbom_get_purl_value_extref(VALUE "${purl}" OUT_VAR result)
|
|
|
|
set(${arg_OUT_VAR} "${result}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Takes a PURL VALUE and returns an SBOM purl external reference in OUT_VAR.
|
|
function(_qt_internal_sbom_get_purl_value_extref)
|
|
set(opt_args "")
|
|
set(single_args
|
|
OUT_VAR
|
|
VALUE
|
|
)
|
|
set(multi_args "")
|
|
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()
|
|
|
|
if(NOT arg_VALUE)
|
|
message(FATAL_ERROR "VALUE must be set")
|
|
endif()
|
|
|
|
# SPDX SBOM External reference type.
|
|
set(ext_ref_prefix "PACKAGE-MANAGER purl")
|
|
set(external_ref "${ext_ref_prefix} ${arg_VALUE}")
|
|
set(result "EXTREF" "${external_ref}")
|
|
set(${arg_OUT_VAR} "${result}" PARENT_SCOPE)
|
|
endfunction()
|