The SBOM implementation got somewhat large. Split the code into several new QtPublicSbomFooHelpers.cmake files, to make it more manageable. No code or behavior was changed. Pick-to: 6.8 6.9 Task-number: QTBUG-122899 Change-Id: Ia0ca1792eec21d12c4bb4cabe63279e1f5c07e3d Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
328 lines
13 KiB
CMake
328 lines
13 KiB
CMake
# Copyright (C) 2024 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# Walks a target's direct dependencies and assembles a list of relationships between the packages
|
|
# of the target dependencies.
|
|
# Currently handles various Qt targets and system libraries.
|
|
function(_qt_internal_sbom_handle_target_dependencies target)
|
|
set(opt_args "")
|
|
set(single_args
|
|
SPDX_ID
|
|
OUT_RELATIONSHIPS
|
|
)
|
|
set(multi_args
|
|
LIBRARIES
|
|
PUBLIC_LIBRARIES
|
|
)
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_SPDX_ID)
|
|
message(FATAL_ERROR "SPDX_ID must be set")
|
|
endif()
|
|
set(package_spdx_id "${arg_SPDX_ID}")
|
|
|
|
|
|
set(libraries "")
|
|
if(arg_LIBRARIES)
|
|
list(APPEND libraries "${arg_LIBRARIES}")
|
|
endif()
|
|
|
|
get_target_property(extend_libraries "${target}" _qt_extend_target_libraries)
|
|
if(extend_libraries)
|
|
list(APPEND libraries ${extend_libraries})
|
|
endif()
|
|
|
|
get_target_property(target_type ${target} TYPE)
|
|
set(valid_target_types
|
|
EXECUTABLE
|
|
SHARED_LIBRARY
|
|
MODULE_LIBRARY
|
|
STATIC_LIBRARY
|
|
OBJECT_LIBRARY
|
|
)
|
|
if(target_type IN_LIST valid_target_types)
|
|
get_target_property(link_libraries "${target}" LINK_LIBRARIES)
|
|
if(link_libraries)
|
|
list(APPEND libraries ${link_libraries})
|
|
endif()
|
|
endif()
|
|
|
|
set(public_libraries "")
|
|
if(arg_PUBLIC_LIBRARIES)
|
|
list(APPEND public_libraries "${arg_PUBLIC_LIBRARIES}")
|
|
endif()
|
|
|
|
get_target_property(extend_public_libraries "${target}" _qt_extend_target_public_libraries)
|
|
if(extend_public_libraries)
|
|
list(APPEND public_libraries ${extend_public_libraries})
|
|
endif()
|
|
|
|
set(sbom_dependencies "")
|
|
if(arg_SBOM_DEPENDENCIES)
|
|
list(APPEND sbom_dependencies "${arg_SBOM_DEPENDENCIES}")
|
|
endif()
|
|
|
|
get_target_property(extend_sbom_dependencies "${target}" _qt_extend_target_sbom_dependencies)
|
|
if(extend_sbom_dependencies)
|
|
list(APPEND sbom_dependencies ${extend_sbom_dependencies})
|
|
endif()
|
|
|
|
list(REMOVE_DUPLICATES libraries)
|
|
list(REMOVE_DUPLICATES public_libraries)
|
|
list(REMOVE_DUPLICATES sbom_dependencies)
|
|
|
|
set(all_direct_libraries ${libraries} ${public_libraries} ${sbom_dependencies})
|
|
list(REMOVE_DUPLICATES all_direct_libraries)
|
|
|
|
set(spdx_dependencies "")
|
|
set(relationships "")
|
|
|
|
# Go through each direct linked lib.
|
|
foreach(direct_lib IN LISTS all_direct_libraries)
|
|
if(NOT TARGET "${direct_lib}")
|
|
continue()
|
|
endif()
|
|
|
|
# Some targets are Qt modules, even though they are not prefixed with Qt::, targets
|
|
# like Bootstrap and QtLibraryInfo. We use the property to differentiate them.
|
|
get_target_property(is_marked_as_qt_module "${direct_lib}" _qt_sbom_is_qt_module)
|
|
|
|
# Custom sbom targets created by _qt_internal_create_sbom_target are always imported, so we
|
|
# need to differentiate them via this property.
|
|
get_target_property(is_custom_sbom_target "${direct_lib}" _qt_sbom_is_custom_sbom_target)
|
|
|
|
if("${direct_lib}" MATCHES "^(Qt::.*)|(${QT_CMAKE_EXPORT_NAMESPACE}::.*)")
|
|
set(is_qt_prefixed TRUE)
|
|
else()
|
|
set(is_qt_prefixed FALSE)
|
|
endif()
|
|
|
|
# is_qt_dependency is not strictly only a qt dependency, it applies to custom sbom
|
|
# targets as well. But I'm having a hard time to come up with a better name.
|
|
if(is_marked_as_qt_module OR is_custom_sbom_target OR is_qt_prefixed)
|
|
set(is_qt_dependency TRUE)
|
|
else()
|
|
set(is_qt_dependency FALSE)
|
|
endif()
|
|
|
|
# Regular Qt dependency, depend on the relevant package, either within the current
|
|
# document or via an external document.
|
|
if(is_qt_dependency)
|
|
_qt_internal_sbom_is_external_target_dependency("${direct_lib}"
|
|
OUT_VAR is_dependency_in_external_document
|
|
)
|
|
|
|
if(is_dependency_in_external_document)
|
|
# External document case.
|
|
_qt_internal_sbom_add_external_target_dependency(
|
|
"${package_spdx_id}" "${direct_lib}"
|
|
extra_spdx_dependencies
|
|
extra_spdx_relationships
|
|
)
|
|
if(extra_spdx_dependencies)
|
|
list(APPEND spdx_dependencies "${extra_spdx_dependencies}")
|
|
endif()
|
|
if(extra_spdx_relationships)
|
|
list(APPEND relationships "${extra_spdx_relationships}")
|
|
endif()
|
|
else()
|
|
# Dependency is part of current repo build.
|
|
_qt_internal_sbom_get_spdx_id_for_target("${direct_lib}" dep_spdx_id)
|
|
if(dep_spdx_id)
|
|
list(APPEND spdx_dependencies "${dep_spdx_id}")
|
|
else()
|
|
message(DEBUG "Could not add target dependency on ${direct_lib} "
|
|
"because no spdx id could be found")
|
|
endif()
|
|
endif()
|
|
else()
|
|
# If it's not a Qt dependency, then it's most likely a 3rd party dependency.
|
|
# If we are looking at a FindWrap dependency, we need to depend on either
|
|
# the system or vendored lib, whichever one the FindWrap script points to.
|
|
# If we are looking at a non-Wrap dependency, it's 99% a system lib.
|
|
__qt_internal_walk_libs(
|
|
"${direct_lib}"
|
|
lib_walked_targets
|
|
_discarded_out_var
|
|
"sbom_targets"
|
|
"collect_targets")
|
|
|
|
# Detect if we are dealing with a vendored / bundled lib.
|
|
set(bundled_targets_found FALSE)
|
|
if(lib_walked_targets)
|
|
foreach(lib_walked_target IN LISTS lib_walked_targets)
|
|
get_target_property(is_3rdparty_bundled_lib
|
|
"${lib_walked_target}" _qt_module_is_3rdparty_library)
|
|
_qt_internal_sbom_get_spdx_id_for_target("${lib_walked_target}" lib_spdx_id)
|
|
|
|
# Add a dependency on the vendored lib instead of the Wrap target.
|
|
if(is_3rdparty_bundled_lib AND lib_spdx_id)
|
|
list(APPEND spdx_dependencies "${lib_spdx_id}")
|
|
set(bundled_targets_found TRUE)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
# If no bundled libs were found as a result of walking the Wrap lib, we consider this
|
|
# a system lib, and add a dependency on it directly.
|
|
if(NOT bundled_targets_found)
|
|
_qt_internal_sbom_get_spdx_id_for_target("${direct_lib}" lib_spdx_id)
|
|
_qt_internal_sbom_is_external_target_dependency("${direct_lib}"
|
|
SYSTEM_LIBRARY
|
|
OUT_VAR is_dependency_in_external_document
|
|
)
|
|
|
|
if(lib_spdx_id)
|
|
if(NOT is_dependency_in_external_document)
|
|
list(APPEND spdx_dependencies "${lib_spdx_id}")
|
|
|
|
# Mark the system library is used, so that we later generate an sbom for it.
|
|
_qt_internal_append_to_cmake_property_without_duplicates(
|
|
_qt_internal_sbom_consumed_system_library_targets
|
|
"${direct_lib}"
|
|
)
|
|
else()
|
|
# Refer to the package in the external document. This can be the case
|
|
# in a top-level build, where a system library is reused across repos.
|
|
_qt_internal_sbom_add_external_target_dependency(
|
|
"${package_spdx_id}" "${direct_lib}"
|
|
extra_spdx_dependencies
|
|
extra_spdx_relationships
|
|
)
|
|
if(extra_spdx_dependencies)
|
|
list(APPEND spdx_dependencies "${extra_spdx_dependencies}")
|
|
endif()
|
|
if(extra_spdx_relationships)
|
|
list(APPEND relationships "${extra_spdx_relationships}")
|
|
endif()
|
|
endif()
|
|
else()
|
|
message(DEBUG "Could not add target dependency on system library ${direct_lib} "
|
|
"because no spdx id could be found")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
foreach(dep_spdx_id IN LISTS spdx_dependencies)
|
|
set(relationship
|
|
"${package_spdx_id} DEPENDS_ON ${dep_spdx_id}"
|
|
)
|
|
list(APPEND relationships "${relationship}")
|
|
endforeach()
|
|
|
|
set(${arg_OUT_RELATIONSHIPS} "${relationships}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Checks whether the current target will have its sbom generated into the current repo sbom
|
|
# document, or whether it is present in an external sbom document.
|
|
function(_qt_internal_sbom_is_external_target_dependency target)
|
|
set(opt_args
|
|
SYSTEM_LIBRARY
|
|
)
|
|
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)
|
|
|
|
get_target_property(is_imported "${target}" IMPORTED)
|
|
get_target_property(is_custom_sbom_target "${target}" _qt_sbom_is_custom_sbom_target)
|
|
|
|
_qt_internal_sbom_get_root_project_name_lower_case(current_repo_project_name)
|
|
get_property(target_repo_project_name TARGET ${target}
|
|
PROPERTY _qt_sbom_spdx_repo_project_name_lowercase)
|
|
|
|
if(NOT "${target_repo_project_name}" STREQUAL ""
|
|
AND NOT "${target_repo_project_name}" STREQUAL "${current_repo_project_name}")
|
|
set(part_of_other_repo TRUE)
|
|
else()
|
|
set(part_of_other_repo FALSE)
|
|
endif()
|
|
|
|
# A target is in an external document if
|
|
# 1) it is imported, and not a custom sbom target, and not a system library
|
|
# 2) it was created as part of another repo in a top-level build
|
|
if((is_imported AND NOT is_custom_sbom_target AND NOT arg_SYSTEM_LIBRARY)
|
|
OR part_of_other_repo)
|
|
set(is_dependency_in_external_document TRUE)
|
|
else()
|
|
set(is_dependency_in_external_document FALSE)
|
|
endif()
|
|
|
|
set(${arg_OUT_VAR} "${is_dependency_in_external_document}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Handles generating an external document reference SDPX element for each target package that is
|
|
# located in a different spdx document.
|
|
function(_qt_internal_sbom_add_external_target_dependency
|
|
current_package_spdx_id
|
|
target_dep
|
|
out_spdx_dependencies
|
|
out_spdx_relationships
|
|
)
|
|
set(target "${target_dep}")
|
|
|
|
_qt_internal_sbom_get_spdx_id_for_target("${target}" dep_spdx_id)
|
|
|
|
if(NOT dep_spdx_id)
|
|
message(DEBUG "Could not add external target dependency on ${target} "
|
|
"because no spdx id could be found")
|
|
set(${out_spdx_dependencies} "" PARENT_SCOPE)
|
|
set(${out_spdx_relationships} "" PARENT_SCOPE)
|
|
return()
|
|
endif()
|
|
|
|
set(spdx_dependencies "")
|
|
set(spdx_relationships "")
|
|
|
|
# Get the external document path and the repo it belongs to for the given target.
|
|
get_property(relative_installed_repo_document_path TARGET ${target}
|
|
PROPERTY _qt_sbom_spdx_relative_installed_repo_document_path)
|
|
|
|
get_property(project_name_lowercase TARGET ${target}
|
|
PROPERTY _qt_sbom_spdx_repo_project_name_lowercase)
|
|
|
|
if(relative_installed_repo_document_path AND project_name_lowercase)
|
|
_qt_internal_sbom_get_external_document_ref_spdx_id(
|
|
"${project_name_lowercase}" external_document_ref)
|
|
|
|
get_cmake_property(known_external_document
|
|
_qt_known_external_documents_${external_document_ref})
|
|
|
|
set(relationship
|
|
"${current_package_spdx_id} DEPENDS_ON ${external_document_ref}:${dep_spdx_id}")
|
|
|
|
list(APPEND spdx_relationships "${relationship}")
|
|
|
|
# Only add a reference to the external document package, if we haven't done so already.
|
|
if(NOT known_external_document)
|
|
set(install_prefixes "")
|
|
|
|
get_cmake_property(install_prefix _qt_internal_sbom_install_prefix)
|
|
list(APPEND install_prefixes "${install_prefix}")
|
|
|
|
set(external_document "${relative_installed_repo_document_path}")
|
|
|
|
_qt_internal_sbom_generate_add_external_reference(
|
|
EXTERNAL_DOCUMENT_FILE_PATH "${external_document}"
|
|
EXTERNAL_DOCUMENT_INSTALL_PREFIXES ${install_prefixes}
|
|
EXTERNAL_DOCUMENT_SPDX_ID "${external_document_ref}"
|
|
)
|
|
|
|
set_property(GLOBAL PROPERTY
|
|
_qt_known_external_documents_${external_document_ref} TRUE)
|
|
set_property(GLOBAL APPEND PROPERTY
|
|
_qt_known_external_documents "${external_document_ref}")
|
|
endif()
|
|
else()
|
|
message(WARNING "Missing spdx document path for external ref: "
|
|
"package_name_for_spdx_id ${package_name_for_spdx_id} direct_lib ${direct_lib}")
|
|
endif()
|
|
|
|
set(${out_spdx_dependencies} "${spdx_dependencies}" PARENT_SCOPE)
|
|
set(${out_spdx_relationships} "${spdx_relationships}" PARENT_SCOPE)
|
|
endfunction()
|