qt/qtbase: finish 6.11.0->6.11.1 vendored bump left half-done by build system
The recipe tar= and blake3 were bumped to 6.11.1 at the 0.3.1 branch cut, but the vendored source/ tree was never propagated: it was a muddle of 6.11.0 and 6.11.1 files plus injected line-duplication corruption. Rebase the Redox port onto a clean pristine 6.11.1 tree: - pristine 6.11.1 + redox.patch + in6-pktinfo/ifreq/forkfd_qt patches - re-apply the 6 baked-only Redox shims never captured as patches (forkfd waitid, qprocess vfork, posix-sem decls, qassert, qplugin, qnet) - qtwaylandscanner null-guard one-liner (its patch file was malformed) - drop the sys/ioctl.h / assert.h / QT_CONFIG(opengl) line-duplication corruption Result differs from pristine 6.11.1 by exactly the 36-file Redox port.
This commit is contained in:
@@ -4,10 +4,13 @@
|
||||
# ensure that when this file is included the second time in a toplevel buld
|
||||
# (directly by qtbase) we actually add the extra definitions
|
||||
if (NOT DEFINED QT_SUPERBUILD OR DEFINED QT_REPO_MODULE_VERSION)
|
||||
set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1")
|
||||
set(QT_EXTRA_INTERNAL_TARGET_DEFINES
|
||||
"QT_LEAN_HEADERS=1"
|
||||
"QT_NO_SINGLE_ARGUMENT_QHASH_OVERLOAD=1"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(QT_REPO_MODULE_VERSION "6.11.0")
|
||||
set(QT_REPO_MODULE_VERSION "6.11.1")
|
||||
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
|
||||
|
||||
set(QT_COPYRIGHT "Copyright (C) The Qt Company Ltd. and other contributors.")
|
||||
|
||||
@@ -1 +1 @@
|
||||
QT_PACKAGEDATE_STR=2026-03-17
|
||||
QT_PACKAGEDATE_STR=2026-05-08
|
||||
@@ -1 +1 @@
|
||||
8ba7ea4b77a4b8f1948760221e264917ddc9e1c8
|
||||
59c81a3c2247b821b9b84b4eb8d939b77e07e276
|
||||
|
||||
@@ -459,6 +459,13 @@ elseif(WASM)
|
||||
"${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py" @ONLY)
|
||||
qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py"
|
||||
DESTINATION "${INSTALL_LIBEXECDIR}")
|
||||
elseif(WIN32)
|
||||
qt_copy_or_install(FILES "cmake/windows/app.exe.manifest.in"
|
||||
DESTINATION "${__GlobalConfig_install_dir}/windows"
|
||||
)
|
||||
file(COPY "cmake/windows/app.exe.manifest.in"
|
||||
DESTINATION "${__GlobalConfig_install_dir}/windows"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Install CI support files to libexec.
|
||||
|
||||
@@ -298,11 +298,13 @@ function(qt_internal_get_qt_build_public_helpers out_var)
|
||||
QtPublicPluginHelpers
|
||||
QtPublicPluginHelpers_v2
|
||||
QtPublicSbomAttributionHelpers
|
||||
QtPublicSbomBuildToolHelpers
|
||||
QtPublicSbomCommonGenerationHelpers
|
||||
QtPublicSbomCpeHelpers
|
||||
QtPublicSbomCycloneDXHelpers
|
||||
QtPublicSbomDocumentNamespaceHelpers
|
||||
QtPublicSbomDepHelpers
|
||||
QtPublicSbomExternalReferenceHelpers
|
||||
QtPublicSbomFileHelpers
|
||||
QtPublicSbomGenerationHelpers
|
||||
QtPublicSbomGenerationCycloneDXHelpers
|
||||
@@ -312,6 +314,7 @@ function(qt_internal_get_qt_build_public_helpers out_var)
|
||||
QtPublicSbomPurlHelpers
|
||||
QtPublicSbomPythonHelpers
|
||||
QtPublicSbomQtEntityHelpers
|
||||
QtPublicSbomRelationshipHelpers
|
||||
QtPublicSbomSystemDepHelpers
|
||||
QtPublicTargetHelpers
|
||||
QtPublicTestHelpers
|
||||
|
||||
@@ -243,6 +243,14 @@ macro(qt_internal_set_qt_apple_support_files_path)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(qt_internal_set_qt_windows_support_files_path)
|
||||
# This is analogous to what we have in QtConfig.cmake.in. It's copied here so that
|
||||
# tests can be built in tree.
|
||||
if(WIN32)
|
||||
set(__qt_internal_cmake_windows_support_files_path "${QT_CMAKE_DIR}/windows")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(qt_internal_set_qt_staging_prefix)
|
||||
if(NOT "${CMAKE_STAGING_PREFIX}" STREQUAL "")
|
||||
set(QT_STAGING_PREFIX "${CMAKE_STAGING_PREFIX}")
|
||||
@@ -267,6 +275,8 @@ macro(qt_internal_setup_paths_and_prefixes)
|
||||
qt_internal_set_qt_cmake_dir()
|
||||
|
||||
qt_internal_set_qt_apple_support_files_path()
|
||||
|
||||
qt_internal_set_qt_windows_support_files_path()
|
||||
endmacro()
|
||||
|
||||
# Returns the prefix for the variables defined by HostInfo package. The prefix
|
||||
|
||||
@@ -370,10 +370,6 @@ function(qt_internal_add_example_external_project subdir)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${singleOpts}" "${multiOpts}")
|
||||
|
||||
if(QT_FEATURE_developer_build)
|
||||
set(QT_LINT_EXAMPLES ON)
|
||||
endif()
|
||||
|
||||
_qt_internal_get_build_vars_for_external_projects(
|
||||
CMAKE_DIR_VAR qt_cmake_dir
|
||||
PREFIXES_VAR qt_prefixes
|
||||
@@ -388,10 +384,6 @@ function(qt_internal_add_example_external_project subdir)
|
||||
list(APPEND var_defs
|
||||
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${qt_cmake_dir}/qt.toolchain.cmake
|
||||
)
|
||||
if(QT_LINT_EXAMPLES)
|
||||
list(APPEND var_defs -DQT_LINT_EXAMPLES=ON)
|
||||
endif()
|
||||
|
||||
else()
|
||||
list(PREPEND CMAKE_PREFIX_PATH ${qt_prefixes})
|
||||
|
||||
@@ -404,6 +396,16 @@ function(qt_internal_add_example_external_project subdir)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(QT_FEATURE_developer_build)
|
||||
set(QT_LINT_EXAMPLES ON)
|
||||
endif()
|
||||
|
||||
# This might be set even without a developer build, e.g. in CI options, so it's a separate
|
||||
# check.
|
||||
if(QT_LINT_EXAMPLES)
|
||||
list(APPEND var_defs -DQT_LINT_EXAMPLES:BOOL=ON)
|
||||
endif()
|
||||
|
||||
# We we need to augment the CMAKE_MODULE_PATH with the current repo cmake build dir, to find
|
||||
# files like FindWrapBundledFooConfigExtra.cmake.
|
||||
set(module_paths "${qt_prefixes}")
|
||||
|
||||
@@ -376,12 +376,12 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL \"4.0\"
|
||||
set(CMAKE_POLICY_VERSION_MINIMUM \"\${__qt_toolchain_cmake_policy_version_minimum}\")
|
||||
message(DEBUG
|
||||
\"Setting CMAKE_POLICY_VERSION_MINIMUM to \"
|
||||
\"\${__qt_toolchain_cmake_policy_version_minimum}\ in toolchain file.\")
|
||||
\"\${__qt_toolchain_cmake_policy_version_minimum} in toolchain file.\")
|
||||
endif()
|
||||
")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown TYPE value '${arg_TYPE}'. "
|
||||
" Supported values are COMMAND_LINE and VAR_ASSIGNMENT.")
|
||||
" Supported values are COMMAND_LINE and TOOLCHAIN_FILE_ASSIGNMENT.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -79,6 +79,10 @@ if(APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(__qt_internal_cmake_windows_support_files_path "${_qt_import_prefix}/windows")
|
||||
endif()
|
||||
|
||||
# Public helpers available to all Qt packages.
|
||||
set(__qt_public_files_to_include
|
||||
@QT_PUBLIC_FILES_TO_INCLUDE@
|
||||
|
||||
@@ -1462,8 +1462,8 @@ function(qt_internal_set_up_config_optimizations_like_in_qmake)
|
||||
# Todo:
|
||||
# When the new toolchain is fixed, and it doesn't fall back to the legacy
|
||||
# anymore by default, then we should be able to remove this workaround.
|
||||
if(ANDROID AND ANDROID_COMPILER_FLAGS MATCHES "(^| )-g")
|
||||
qt_internal_remove_compiler_flags("-g")
|
||||
if(ANDROID AND ANDROID_COMPILER_FLAGS MATCHES "(^| )-g( |$)")
|
||||
qt_internal_remove_compiler_flags("(^| )-g( |$)" REGEX)
|
||||
qt_internal_add_compiler_flags(FLAGS "-g" CONFIGS DEBUG RELWITHDEBINFO)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -682,7 +682,7 @@ function(qt_internal_add_module target)
|
||||
foreach(public_lib IN LISTS arg_PUBLIC_LIBRARIES)
|
||||
if("${public_lib}" MATCHES "^(Qt|${QT_CMAKE_EXPORT_NAMESPACE})::(.*)Private$")
|
||||
message(AUTHOR_WARNING
|
||||
"${target} specfies ${public_lib} in its PUBLIC_LIBRARIES option. "
|
||||
"${target} specifies ${public_lib} in its PUBLIC_LIBRARIES option. "
|
||||
"Public modules should not link publicly to Private modules, because that "
|
||||
"exposes private module headers to user projects without them opting into it. "
|
||||
"Update the project to use the LIBRARIES keyword instead."
|
||||
|
||||
@@ -344,10 +344,9 @@ function(qt_internal_add_plugin target)
|
||||
qt_internal_wrap_private_modules("${target}"
|
||||
OUT_VAR qt_libs_private
|
||||
LIBRARIES ${qt_libs_private})
|
||||
list(APPEND qt_register_target_depentdencies_args PRIVATE ${qt_libs_private})
|
||||
list(APPEND qt_register_target_dependencies_args PRIVATE ${qt_libs_private})
|
||||
endif()
|
||||
qt_internal_register_target_dependencies("${target}"
|
||||
${qt_register_target_dependencies_args})
|
||||
qt_internal_register_target_dependencies("${target}" ${qt_register_target_dependencies_args})
|
||||
|
||||
if(target_type STREQUAL STATIC_LIBRARY)
|
||||
if(qt_module_target)
|
||||
@@ -392,6 +391,28 @@ function(qt_internal_add_plugin target)
|
||||
set(test_plugin_arg TEST_PLUGIN)
|
||||
endif()
|
||||
|
||||
# Some plugins like the sqldriver ones can be built as part of the main repo or as a
|
||||
# separate standalone project. We need to make sure we exclude loading the Qt prebuilt
|
||||
# plugins when loading such a project regardless which way it is built.
|
||||
if(QT_STANDALONE_PROJECT_NAME)
|
||||
set(project_names_arg "${QT_STANDALONE_PROJECT_NAME}")
|
||||
|
||||
if(QT_PARENT_PROJECT_NAME)
|
||||
list(APPEND project_names_arg "${QT_PARENT_PROJECT_NAME}")
|
||||
else()
|
||||
message(WARNING
|
||||
"QT_STANDALONE_PROJECT_NAME is set to '${QT_STANDALONE_PROJECT_NAME}', but "
|
||||
"QT_PARENT_PROJECT_NAME is not set. This can cause issues when building the "
|
||||
"standalone project. Set QT_PARENT_PROJECT_NAME to the submodule's "
|
||||
"root CMakeLists.txt project name to avoid this warning."
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(project_names_arg "${PROJECT_NAME}")
|
||||
endif()
|
||||
list(PREPEND project_names_arg "PROJECT_NAMES")
|
||||
string(REPLACE ";" " " project_names_arg "${project_names_arg}")
|
||||
|
||||
configure_package_config_file(
|
||||
"${QT_CMAKE_DIR}/QtPluginConfig.cmake.in"
|
||||
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
|
||||
@@ -406,7 +427,7 @@ function(qt_internal_add_plugin target)
|
||||
_qt_internal_should_include_targets(
|
||||
TARGETS ${target}
|
||||
NAMESPACE ${INSTALL_CMAKE_NAMESPACE}::
|
||||
PROJECT_NAME ${PROJECT_NAME}
|
||||
${project_names_arg}
|
||||
OUT_VAR_SHOULD_SKIP __qt_${target}_skip_include_targets_file
|
||||
${test_plugin_arg}
|
||||
)
|
||||
|
||||
@@ -685,6 +685,30 @@ function(_qt_internal_set_apple_localizations target)
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(plist_file "${target}" MACOSX_BUNDLE_INFO_PLIST)
|
||||
if (NOT plist_file)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT "${QT_I18N_SOURCE_LANGUAGE}" STREQUAL "")
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS "print CFBundleDevelopmentRegion"
|
||||
OUTPUT_VARIABLE existing_dev_region
|
||||
)
|
||||
if(existing_dev_region)
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS
|
||||
"Set CFBundleDevelopmentRegion ${QT_I18N_SOURCE_LANGUAGE}"
|
||||
)
|
||||
else()
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS
|
||||
"Add CFBundleDevelopmentRegion string"
|
||||
"Set CFBundleDevelopmentRegion ${QT_I18N_SOURCE_LANGUAGE}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(supported_languages "${QT_I18N_TRANSLATED_LANGUAGES}")
|
||||
if("${QT_I18N_TRANSLATED_LANGUAGES}" STREQUAL "")
|
||||
get_target_property(supported_languages "${target}" _qt_apple_supported_languages)
|
||||
@@ -692,10 +716,6 @@ function(_qt_internal_set_apple_localizations target)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
get_target_property(plist_file "${target}" MACOSX_BUNDLE_INFO_PLIST)
|
||||
if (NOT plist_file)
|
||||
return()
|
||||
endif()
|
||||
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS "print CFBundleLocalizations"
|
||||
@@ -712,20 +732,6 @@ function(_qt_internal_set_apple_localizations target)
|
||||
"Delete CFBundleAllowMixedLocalizations"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT "${QT_I18N_SOURCE_LANGUAGE}" STREQUAL "")
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS "print CFBundleDevelopmentRegion"
|
||||
OUTPUT_VARIABLE existing_dev_region
|
||||
)
|
||||
if(NOT existing_dev_region)
|
||||
_qt_internal_plist_buddy("${plist_file}"
|
||||
COMMANDS
|
||||
"Add CFBundleDevelopmentRegion string"
|
||||
"Set CFBundleDevelopmentRegion ${QT_I18N_SOURCE_LANGUAGE}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_set_ios_simulator_arch target)
|
||||
|
||||
@@ -104,6 +104,9 @@ function(__qt_internal_get_possible_cmake_dirs out_paths prefix_path)
|
||||
if(EXISTS "${prefix_path}/lib/cmake")
|
||||
list(APPEND ${out_paths} "${prefix_path}/lib/cmake")
|
||||
endif()
|
||||
if(EXISTS "${prefix_path}/lib64/cmake")
|
||||
list(APPEND ${out_paths} "${prefix_path}/lib64/cmake")
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES ${out_paths})
|
||||
set(${out_paths} "${${out_paths}}" PARENT_SCOPE)
|
||||
@@ -1055,9 +1058,14 @@ endfunction()
|
||||
# `OUT_VAR_SHOULD_SKIP`
|
||||
# Output variable indicating if the `include(*Targets.cmake)` should be skipped
|
||||
#
|
||||
# `PROJECT_NAMES`
|
||||
# The original project names that defined the current Config.cmake file at the time of the
|
||||
# generation. For now only used for plugins. Mostly it's just the main repo project name,
|
||||
# but for some projects like the sqldriver one that can be built standalone, there can be
|
||||
# more than one.
|
||||
#
|
||||
# `PROJECT_NAME`
|
||||
# The original project that defined the current Config.cmake file at the time of the
|
||||
# generation. For now only used for plugins.
|
||||
# Deprecated. Same as the one above, but only takes a single value. Kept for compatibility.
|
||||
#
|
||||
# `TEST_PLUGIN`
|
||||
# If the module is a TEST_PLUGIN, we do additional checks based on
|
||||
@@ -1073,10 +1081,11 @@ function(_qt_internal_should_include_targets)
|
||||
set(single_args
|
||||
NAMESPACE
|
||||
OUT_VAR_SHOULD_SKIP
|
||||
PROJECT_NAME
|
||||
PROJECT_NAME # deprecated
|
||||
)
|
||||
set(multi_args
|
||||
TARGETS
|
||||
PROJECT_NAMES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${option_args}" "${single_args}" "${multi_args}")
|
||||
|
||||
@@ -1124,7 +1133,15 @@ function(_qt_internal_should_include_targets)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(project_names "")
|
||||
if(arg_PROJECT_NAMES)
|
||||
list(APPEND project_names ${arg_PROJECT_NAMES})
|
||||
endif()
|
||||
if(arg_PROJECT_NAME)
|
||||
list(APPEND project_names ${arg_PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
if(project_names)
|
||||
# Check if we are in a top-level build and which submodules will be included
|
||||
if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS AND DEFINED Qt_SOURCE_DIR)
|
||||
# All projects will be rebuilt so skip any include targets
|
||||
@@ -1134,7 +1151,7 @@ function(_qt_internal_should_include_targets)
|
||||
|
||||
# Check if we are building the current project
|
||||
if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS AND
|
||||
PROJECT_NAME STREQUAL arg_PROJECT_NAME)
|
||||
PROJECT_NAME IN_LIST project_names)
|
||||
# We are currently building the project, so skip the include targets
|
||||
set(${arg_OUT_VAR_SHOULD_SKIP} ON PARENT_SCOPE)
|
||||
return()
|
||||
@@ -1142,7 +1159,7 @@ function(_qt_internal_should_include_targets)
|
||||
|
||||
# Check if we are building the standalone tests
|
||||
if(arg_TEST_PLUGIN AND QT_BUILD_STANDALONE_TESTS AND
|
||||
PROJECT_NAME STREQUAL arg_PROJECT_NAME)
|
||||
PROJECT_NAME IN_LIST project_names)
|
||||
set(${arg_OUT_VAR_SHOULD_SKIP} ON PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Copyright (C) 2025 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Save the 'macros base dir' in a global property instead of a variable, to allow access in a
|
||||
# deferred function where the variable might not be accessible by the function scope.
|
||||
# We explicitly don't set CMAKE_CURRENT_FUNCTION_LIST_DIR as we do in some other cases, because
|
||||
# that can be overridden by a different file depending on the inclusion order of the
|
||||
# cmake files, and thus pointing to a different dir.
|
||||
set_property(GLOBAL PROPERTY __qt_sbom_generation_helpers_base_dir "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
# Helper function to get common project related variables for SBOM generation for both SPDX and
|
||||
# CycloneDX formats.
|
||||
function(_qt_internal_sbom_get_common_project_variables)
|
||||
@@ -70,7 +77,9 @@ function(_qt_internal_sbom_get_common_project_variables)
|
||||
# Prevent collision with other generated SPDXID with -[0-9]+ suffix.
|
||||
string(REGEX REPLACE "-([0-9]+)$" "\\1" arg_PROJECT_FOR_SPDX_ID "${arg_PROJECT_FOR_SPDX_ID}")
|
||||
|
||||
set(project_spdx_id "SPDXRef-${arg_PROJECT_FOR_SPDX_ID}")
|
||||
_qt_internal_sbom_get_spdx_id_unique_suffix(spdx_id_unique_suffix)
|
||||
set(project_spdx_id "SPDXRef-${arg_PROJECT_FOR_SPDX_ID}${spdx_id_unique_suffix}")
|
||||
|
||||
set(${arg_OUT_VAR_PROJECT_FOR_SPDX_ID} "${project_spdx_id}" PARENT_SCOPE)
|
||||
|
||||
_qt_internal_sbom_set_default_option_value_and_error_if_empty(SUPPLIER "")
|
||||
@@ -105,7 +114,7 @@ function(_qt_internal_sbom_save_common_path_variables_in_global_properties)
|
||||
OUTPUT
|
||||
OUTPUT_RELATIVE_PATH
|
||||
SBOM_DIR
|
||||
PROPERTY_SUFFIX
|
||||
SBOM_FORMAT
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -137,7 +146,13 @@ function(_qt_internal_sbom_save_common_path_variables_in_global_properties)
|
||||
get_filename_component(install_sbom_dir "${install_sbom_path}" DIRECTORY)
|
||||
set(install_sbom_path_without_ext "${install_sbom_dir}/${output_file_name_without_ext}")
|
||||
|
||||
set(suffix "${arg_PROPERTY_SUFFIX}")
|
||||
if(arg_SBOM_FORMAT STREQUAL "SPDX_V2")
|
||||
set(suffix "")
|
||||
elseif(arg_SBOM_FORMAT STREQUAL "CYDX_V1_6")
|
||||
set(suffix "_cydx")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown SBOM_FORMAT: ${arg_SBOM_FORMAT}")
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_build_output_path${suffix} "${build_sbom_path}")
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_build_output_path_without_ext${suffix}
|
||||
@@ -204,6 +219,25 @@ function(_qt_internal_sbom_get_common_path_variables_from_global_properties)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Create the build directory that will contain all the intermediate generated sbom files.
|
||||
function(_qt_internal_sbom_create_sbom_staging_dir)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
OUT_VAR_SBOM_DIR
|
||||
)
|
||||
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_SBOM_DIR)
|
||||
message(FATAL_ERROR "OUT_VAR_SBOM_DIR argument is required.")
|
||||
endif()
|
||||
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
file(MAKE_DIRECTORY "${sbom_dir}")
|
||||
set(${arg_OUT_VAR_SBOM_DIR} "${sbom_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Helper function to create a staging file for SBOM generation.
|
||||
# It is the file that will be incrementally assembled by having content appended to it.
|
||||
# Also creates the intro file that will add the assembled content for the SBOM project, aka for the
|
||||
@@ -211,19 +245,20 @@ endfunction()
|
||||
function(_qt_internal_sbom_create_sbom_staging_file)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
CONTENT
|
||||
SBOM_FORMAT
|
||||
REPO_PROJECT_NAME_LOWERCASE
|
||||
OUT_VAR_CREATE_STAGING_FILE
|
||||
OUT_VAR_SBOM_DIR
|
||||
SBOM_DIR
|
||||
)
|
||||
set(multi_args
|
||||
RELATIONSHIP_STRINGS
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
# Create the directory that will contain all sbom related files.
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
file(MAKE_DIRECTORY "${sbom_dir}")
|
||||
if(NOT arg_SBOM_DIR)
|
||||
message(FATAL_ERROR "SBOM_DIR argument is required")
|
||||
endif()
|
||||
set(sbom_dir "${arg_SBOM_DIR}")
|
||||
|
||||
if(arg_SBOM_FORMAT STREQUAL "SPDX_V2")
|
||||
set(doc_base_name "SPDXRef-DOCUMENT")
|
||||
@@ -234,6 +269,14 @@ function(_qt_internal_sbom_create_sbom_staging_file)
|
||||
")
|
||||
_qt_internal_get_staging_area_spdx_file_path(staging_area_file)
|
||||
set(starting_message "Starting SPDX SBOM generation in build dir: ${staging_area_file}")
|
||||
set(extra_intro_content "")
|
||||
set(project_relationship_strings "")
|
||||
if(arg_RELATIONSHIP_STRINGS)
|
||||
list(JOIN arg_RELATIONSHIP_STRINGS "\n" arg_RELATIONSHIP_STRINGS)
|
||||
|
||||
# Prepend a newline to separate from the very first always there relationship.
|
||||
string(APPEND extra_intro_content "\n${arg_RELATIONSHIP_STRINGS}")
|
||||
endif()
|
||||
elseif(arg_SBOM_FORMAT STREQUAL "CYDX_V1_6")
|
||||
set(doc_base_name "cydx-document")
|
||||
set(doc_extension "cdx.in.toml")
|
||||
@@ -242,12 +285,40 @@ function(_qt_internal_sbom_create_sbom_staging_file)
|
||||
_qt_internal_get_staging_area_cydx_file_path(staging_area_file)
|
||||
set(starting_message
|
||||
"Starting CycloneDX SBOM TOML file generation in build dir: ${staging_area_file}")
|
||||
set(extra_intro_content "")
|
||||
|
||||
if(arg_RELATIONSHIP_STRINGS)
|
||||
list(JOIN arg_RELATIONSHIP_STRINGS "\n" arg_RELATIONSHIP_STRINGS)
|
||||
set(project_relationship_strings "${arg_RELATIONSHIP_STRINGS}")
|
||||
else()
|
||||
set(project_relationship_strings "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Generate project document intro spdx file.
|
||||
# Assemble final intro content.
|
||||
get_property(intro_content GLOBAL PROPERTY _qt_sbom_project_intro_content${suffix})
|
||||
if(NOT intro_content)
|
||||
message(FATAL_ERROR "Missing intro content for SBOM generation.")
|
||||
endif()
|
||||
|
||||
if(arg_SBOM_FORMAT STREQUAL "CYDX_V1_6")
|
||||
string(REPLACE "<PROJECT_RELATIONSHIP_PLACEHOLDER>" "${project_relationship_strings}"
|
||||
intro_content "${intro_content}")
|
||||
endif()
|
||||
|
||||
if(extra_intro_content)
|
||||
string(APPEND intro_content "${extra_intro_content}")
|
||||
endif()
|
||||
|
||||
# Final new line to separate the main package from rest of info.
|
||||
if(arg_SBOM_FORMAT STREQUAL "SPDX_V2")
|
||||
string(APPEND intro_content "\n")
|
||||
endif()
|
||||
|
||||
# Generate project document intro file.
|
||||
set(document_intro_file_name
|
||||
"${sbom_dir}/${doc_base_name}-${arg_REPO_PROJECT_NAME_LOWERCASE}.${doc_extension}")
|
||||
file(GENERATE OUTPUT "${document_intro_file_name}" CONTENT "${content}")
|
||||
file(GENERATE OUTPUT "${document_intro_file_name}" CONTENT "${intro_content}")
|
||||
|
||||
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
|
||||
if(is_multi_config)
|
||||
@@ -256,7 +327,7 @@ function(_qt_internal_sbom_create_sbom_staging_file)
|
||||
set(multi_config_suffix "")
|
||||
endif()
|
||||
|
||||
# Create cmake file to append the document intro spdx to the staging file.
|
||||
# Create cmake file to append the document intro to the staging file.
|
||||
set(create_staging_file
|
||||
"${sbom_dir}/append_document_to_staging${suffix}${multi_config_suffix}.cmake")
|
||||
|
||||
@@ -269,9 +340,7 @@ function(_qt_internal_sbom_create_sbom_staging_file)
|
||||
file(WRITE \"${staging_area_file}\" \"\${content}\")
|
||||
")
|
||||
file(GENERATE OUTPUT "${create_staging_file}" CONTENT "${content}")
|
||||
|
||||
set(${arg_OUT_VAR_CREATE_STAGING_FILE} "${create_staging_file}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_SBOM_DIR} "${sbom_dir}" PARENT_SCOPE)
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_cmake_intro_file${suffix} "${create_staging_file}")
|
||||
endfunction()
|
||||
|
||||
# Helper function to save common project info like supplier, project name, spdx id in global
|
||||
@@ -285,7 +354,9 @@ function(_qt_internal_sbom_save_project_info_in_global_properties)
|
||||
PROJECT
|
||||
PROJECT_SPDX_ID
|
||||
)
|
||||
set(multi_args "")
|
||||
set(multi_args
|
||||
EXTERNAL_REFERENCE_SBOM_DIRS
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
@@ -295,6 +366,34 @@ function(_qt_internal_sbom_save_project_info_in_global_properties)
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_project_name "${arg_PROJECT}")
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_project_spdx_id "${arg_PROJECT_SPDX_ID}")
|
||||
|
||||
# Used for finding external reference spdx documents.
|
||||
if(arg_EXTERNAL_REFERENCE_SBOM_DIRS)
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_internal_sbom_dirs
|
||||
${arg_EXTERNAL_REFERENCE_SBOM_DIRS})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper function to save sbom intro content (per sbom format) in a global property.
|
||||
function(_qt_internal_sbom_save_intro_content)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
SBOM_FORMAT
|
||||
CONTENT
|
||||
)
|
||||
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(arg_SBOM_FORMAT STREQUAL "SPDX_V2")
|
||||
set(suffix "")
|
||||
elseif(arg_SBOM_FORMAT STREQUAL "CYDX_V1_6")
|
||||
set(suffix "_cydx")
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown SBOM_FORMAT: ${arg_SBOM_FORMAT}")
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_project_intro_content${suffix} "${content}")
|
||||
endfunction()
|
||||
|
||||
# Helper function to get cmake include files for SBOM generation from global properties.
|
||||
@@ -320,7 +419,10 @@ function(_qt_internal_sbom_get_cmake_include_files)
|
||||
|
||||
_qt_internal_sbom_collect_cmake_include_files(includes
|
||||
JOIN_WITH_NEWLINES
|
||||
PROPERTIES _qt_sbom_cmake_include_files${suffix} _qt_sbom_cmake_end_include_files${suffix}
|
||||
PROPERTIES
|
||||
_qt_sbom_cmake_intro_file${suffix}
|
||||
_qt_sbom_cmake_include_files${suffix}
|
||||
_qt_sbom_cmake_end_include_files${suffix}
|
||||
)
|
||||
|
||||
# Before checksum includes are included after the verification codes have been collected
|
||||
@@ -443,8 +545,13 @@ function(_qt_internal_sbom_create_build_time_sbom_targets)
|
||||
set(build_sbom_install_prefix "${CMAKE_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
get_property(macro_module_base_dir GLOBAL PROPERTY __qt_sbom_generation_helpers_base_dir)
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
set(content "
|
||||
# Include helpers functions.
|
||||
include(\"${macro_module_base_dir}/QtPublicCMakeHelpers.cmake\")
|
||||
include(\"${macro_module_base_dir}/QtPublicSbomExternalReferenceHelpers.cmake\")
|
||||
|
||||
# QT_SBOM_BUILD_TIME be set to FALSE at install time, so don't override if it's set.
|
||||
# This allows reusing the same cmake file for both build and install.
|
||||
if(NOT DEFINED QT_SBOM_BUILD_TIME)
|
||||
|
||||
@@ -239,12 +239,20 @@ function(_qt_internal_sbom_get_cyclone_bom_serial_number)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Returns the bom version field that is part of a bom-link to refer to the target in an
|
||||
# external CycloneDX document.
|
||||
function(_qt_internal_sbom_get_cydx_external_urn_bom_version target out_var)
|
||||
# Currently 1, but we might change it to be configurable in the future.
|
||||
set(bom_version "1")
|
||||
set(${out_var} "${bom_version}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# See https://github.com/CycloneDX/guides/blob/main/SBOM/en/0x52-Linking.md
|
||||
function(_qt_internal_sbom_get_cydx_external_bom_link target out_var)
|
||||
get_target_property(spdx_id "${target}" _qt_sbom_spdx_id)
|
||||
get_target_property(bom_serial_number "${target}" _qt_sbom_cydx_bom_serial_number_uuid)
|
||||
|
||||
set(bom_version "1")
|
||||
_qt_internal_sbom_get_cydx_external_urn_bom_version("${target}" bom_version)
|
||||
set(bom_link "urn:cdx:${bom_serial_number}/${bom_version}#${spdx_id}")
|
||||
|
||||
set(${out_var} "${bom_link}" PARENT_SCOPE)
|
||||
|
||||
@@ -4,21 +4,20 @@
|
||||
# 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.
|
||||
# For SPDX documents, it collects the relationships in OUT_SPDX_RELATIONSHIPS.
|
||||
# For CYDX documents, it collects the dependencies (not relationship info) in OUT_CYDX_DEPENDENCIES.
|
||||
# Collects the relationships in OUT_SBOM_RELATIONSHIP_ENTRIES.
|
||||
# If a CYDX dependency is in an external docuemnt, the dependency is added to
|
||||
# OUT_EXTERNAL_TARGET_DEPENDENCIES instead.
|
||||
# OUT_EXTERNAL_TARGET_DEPENDENCIES as well.
|
||||
function(_qt_internal_sbom_handle_target_dependencies target)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
SPDX_ID
|
||||
OUT_CYDX_DEPENDENCIES
|
||||
OUT_SPDX_RELATIONSHIPS
|
||||
OUT_SBOM_RELATIONSHIP_ENTRIES
|
||||
OUT_EXTERNAL_TARGET_DEPENDENCIES
|
||||
)
|
||||
set(multi_args
|
||||
LIBRARIES
|
||||
PUBLIC_LIBRARIES
|
||||
SBOM_DEPENDENCIES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
@@ -81,15 +80,16 @@ function(_qt_internal_sbom_handle_target_dependencies target)
|
||||
set(all_direct_libraries ${libraries} ${public_libraries} ${sbom_dependencies})
|
||||
list(REMOVE_DUPLICATES all_direct_libraries)
|
||||
|
||||
set(regular_cydx_dependencies "")
|
||||
set(regular_spdx_dependencies "")
|
||||
|
||||
set(external_cydx_dependencies "")
|
||||
set(external_spdx_dependencies "")
|
||||
set(regular_relationship_entries "")
|
||||
set(external_relationship_entries "")
|
||||
set(external_target_dependencies "")
|
||||
|
||||
# Go through each direct linked lib.
|
||||
foreach(direct_lib IN LISTS all_direct_libraries)
|
||||
set(libs_to_process ${all_direct_libraries})
|
||||
|
||||
# Go through all collected direct linked libs. More might be added during the processing
|
||||
# due to handling of WrapFoo -> Qt::BundledFoo libraries.
|
||||
while(libs_to_process)
|
||||
list(POP_FRONT libs_to_process direct_lib)
|
||||
if(NOT TARGET "${direct_lib}")
|
||||
continue()
|
||||
endif()
|
||||
@@ -114,12 +114,13 @@ function(_qt_internal_sbom_handle_target_dependencies target)
|
||||
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 regular_cydx_dependencies "${lib_spdx_id}")
|
||||
list(APPEND regular_spdx_dependencies "${lib_spdx_id}")
|
||||
if(is_3rdparty_bundled_lib)
|
||||
message(DEBUG
|
||||
"Replacing SBOM dependency on '${direct_lib}' with dependency on "
|
||||
"bundled target '${lib_walked_target}' for target '${target}'")
|
||||
list(APPEND libs_to_process "${lib_walked_target}")
|
||||
set(bundled_targets_found TRUE)
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -159,37 +160,36 @@ function(_qt_internal_sbom_handle_target_dependencies target)
|
||||
if(NOT is_dependency_in_external_document)
|
||||
# If the target is not in the external document, it must be one built as part of the
|
||||
# current project.
|
||||
list(APPEND regular_cydx_dependencies "${lib_spdx_id}")
|
||||
list(APPEND regular_spdx_dependencies "${lib_spdx_id}")
|
||||
set(relationship_entry
|
||||
SBOM_RELATIONSHIP_ENTRY
|
||||
SBOM_RELATIONSHIP_FROM
|
||||
"${target}"
|
||||
SBOM_RELATIONSHIP_TYPE
|
||||
DEPENDS_ON
|
||||
SBOM_RELATIONSHIP_TO
|
||||
"${direct_lib}"
|
||||
)
|
||||
list(APPEND regular_relationship_entries ${relationship_entry})
|
||||
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, or for any
|
||||
# regular dependency that was built as part of a different project.
|
||||
_qt_internal_sbom_add_external_target_dependency("${direct_lib}"
|
||||
OUT_CYDX_DEPENDENCIES extra_cydx_dependencies
|
||||
OUT_SPDX_DEPENDENCIES extra_spdx_dependencies
|
||||
_qt_internal_sbom_add_external_target_dependency(
|
||||
TARGET "${target}"
|
||||
DEPENDENCY_TARGET "${direct_lib}"
|
||||
OUT_SBOM_RELATIONSHIP_ENTRIES extra_relationship_entries
|
||||
OUT_TARGET_DEPENDENCIES extra_target_dependencies
|
||||
)
|
||||
if(extra_spdx_dependencies)
|
||||
list(APPEND external_cydx_dependencies ${extra_cydx_dependencies})
|
||||
list(APPEND external_spdx_dependencies ${extra_spdx_dependencies})
|
||||
if(extra_relationship_entries)
|
||||
list(APPEND external_relationship_entries ${extra_relationship_entries})
|
||||
list(APPEND external_target_dependencies ${extra_target_dependencies})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endwhile()
|
||||
|
||||
set(spdx_relationships "")
|
||||
set(all_relationship_entries ${external_relationship_entries} ${regular_relationship_entries})
|
||||
|
||||
# Keep the external dependencies first, so they are neatly ordered.
|
||||
foreach(dep_spdx_id IN LISTS external_spdx_dependencies regular_spdx_dependencies)
|
||||
set(relationship "${package_spdx_id} DEPENDS_ON ${dep_spdx_id}")
|
||||
list(APPEND spdx_relationships "${relationship}")
|
||||
endforeach()
|
||||
|
||||
set(all_cydx_dependencies ${external_cydx_dependencies} ${regular_cydx_dependencies})
|
||||
|
||||
set(${arg_OUT_CYDX_DEPENDENCIES} "${all_cydx_dependencies}" PARENT_SCOPE)
|
||||
set(${arg_OUT_SPDX_RELATIONSHIPS} "${spdx_relationships}" PARENT_SCOPE)
|
||||
set(${arg_OUT_SBOM_RELATIONSHIP_ENTRIES} "${all_relationship_entries}" PARENT_SCOPE)
|
||||
set(${arg_OUT_EXTERNAL_TARGET_DEPENDENCIES} "${external_target_dependencies}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -223,25 +223,39 @@ function(_qt_internal_sbom_is_external_target_dependency target)
|
||||
set(${arg_OUT_VAR} "${part_of_other_repo}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Handles generating an external document reference SDPX element for each target package that is
|
||||
# located in a different spdx document, and collects the reference in OUT_SPDX_DEPENDENCIES.
|
||||
# In case of CycloneDX, we just collect the dependency spdx id (bom-ref) and the target name,
|
||||
# which are added to OUT_CYDX_DEPENDENCIES and OUT_TARGET_DEPENDENCIES respectively.
|
||||
function(_qt_internal_sbom_add_external_target_dependency target)
|
||||
# Handles generating an external document reference SDPX element for each ${arg_DEPENDENCY_TARGET}
|
||||
# package that is located in a different spdx document, and collects the relationship in
|
||||
# OUT_SBOM_RELATIONSHIP_ENTRIES.
|
||||
# In case of CycloneDX, we also collect the dependency target name in OUT_TARGET_DEPENDENCIES.
|
||||
function(_qt_internal_sbom_add_external_target_dependency)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
OUT_CYDX_DEPENDENCIES
|
||||
OUT_SPDX_DEPENDENCIES
|
||||
TARGET
|
||||
DEPENDENCY_TARGET
|
||||
OUT_SBOM_RELATIONSHIP_ENTRIES
|
||||
OUT_TARGET_DEPENDENCIES
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
_qt_internal_sbom_get_spdx_id_for_target("${target}" dep_spdx_id)
|
||||
if(NOT arg_TARGET)
|
||||
message(FATAL_ERROR "TARGET is required.")
|
||||
endif()
|
||||
if(NOT arg_DEPENDENCY_TARGET)
|
||||
message(FATAL_ERROR "DEPENDENCY_TARGET is required.")
|
||||
endif()
|
||||
if(NOT arg_OUT_SBOM_RELATIONSHIP_ENTRIES)
|
||||
message(FATAL_ERROR "OUT_SBOM_RELATIONSHIP_ENTRIES is required.")
|
||||
endif()
|
||||
if(NOT arg_OUT_TARGET_DEPENDENCIES)
|
||||
message(FATAL_ERROR "OUT_TARGET_DEPENDENCIES is required.")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_get_spdx_id_for_target("${arg_DEPENDENCY_TARGET}" dep_spdx_id)
|
||||
|
||||
if(NOT dep_spdx_id)
|
||||
message(DEBUG "Could not add external target dependency on ${target} "
|
||||
message(DEBUG "Could not add external target dependency on ${arg_DEPENDENCY_TARGET} "
|
||||
"because no spdx id could be found")
|
||||
set(${arg_OUT_CYDX_DEPENDENCIES} "" PARENT_SCOPE)
|
||||
set(${arg_OUT_SPDX_DEPENDENCIES} "" PARENT_SCOPE)
|
||||
@@ -249,29 +263,45 @@ function(_qt_internal_sbom_add_external_target_dependency target)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(cydx_dependencies "")
|
||||
set(spdx_dependencies "")
|
||||
set(relationship_entries "")
|
||||
set(target_depdendencies "")
|
||||
|
||||
# Get the external document path and the repo it belongs to for the given target.
|
||||
get_property(relative_installed_repo_document_path TARGET ${target}
|
||||
# Get the external document path and the repo it belongs to for the given target,
|
||||
# as well as the spdx document namespace, and cydx serial number.
|
||||
get_property(relative_installed_repo_document_path TARGET "${arg_DEPENDENCY_TARGET}"
|
||||
PROPERTY _qt_sbom_spdx_relative_installed_repo_document_path)
|
||||
|
||||
get_property(project_name_lowercase TARGET ${target}
|
||||
get_property(project_name_lowercase TARGET "${arg_DEPENDENCY_TARGET}"
|
||||
PROPERTY _qt_sbom_spdx_repo_project_name_lowercase)
|
||||
|
||||
get_property(spdx_document_namespace TARGET "${arg_DEPENDENCY_TARGET}"
|
||||
PROPERTY _qt_sbom_spdx_repo_document_namespace)
|
||||
|
||||
get_property(bom_serial_number_uuid TARGET "${arg_DEPENDENCY_TARGET}"
|
||||
PROPERTY _qt_sbom_cydx_bom_serial_number_uuid)
|
||||
|
||||
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)
|
||||
_qt_internal_sbom_get_external_document_ref_spdx_id_from_sbom_target(
|
||||
TARGET "${arg_DEPENDENCY_TARGET}"
|
||||
OUT_VAR external_document_ref
|
||||
CREATE_TEMPORARY_REF_WHEN_MISSING
|
||||
)
|
||||
|
||||
get_cmake_property(known_external_document
|
||||
_qt_known_external_documents_${external_document_ref})
|
||||
|
||||
set(spdx_dependency "${external_document_ref}:${dep_spdx_id}")
|
||||
set(relationship_entry
|
||||
SBOM_RELATIONSHIP_ENTRY
|
||||
SBOM_RELATIONSHIP_FROM
|
||||
"${arg_TARGET}"
|
||||
SBOM_RELATIONSHIP_TYPE
|
||||
DEPENDS_ON
|
||||
SBOM_RELATIONSHIP_TO
|
||||
"${arg_DEPENDENCY_TARGET}"
|
||||
)
|
||||
|
||||
list(APPEND cydx_dependencies "${dep_spdx_id}")
|
||||
list(APPEND spdx_dependencies "${spdx_dependency}")
|
||||
list(APPEND target_depdendencies "${target}")
|
||||
list(APPEND relationship_entries "${relationship_entry}")
|
||||
list(APPEND target_depdendencies "${arg_DEPENDENCY_TARGET}")
|
||||
|
||||
# Only add a reference to the external document package, if we haven't done so already.
|
||||
if(NOT known_external_document)
|
||||
@@ -282,23 +312,38 @@ function(_qt_internal_sbom_add_external_target_dependency target)
|
||||
|
||||
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}"
|
||||
)
|
||||
# We need to make the external document target name unique to the currently active
|
||||
# project, otherwise we risk trying to override the document details of an existing
|
||||
# external document target when processing multiple SBOM projects within a single
|
||||
# cmake invocation.
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(current_project_name)
|
||||
set(external_document_target
|
||||
"ExternalDocumentProject-${project_name_lowercase}-for-${current_project_name}")
|
||||
|
||||
set_property(GLOBAL PROPERTY
|
||||
_qt_known_external_documents_${external_document_ref} TRUE)
|
||||
set_property(GLOBAL APPEND PROPERTY
|
||||
_qt_known_external_documents "${external_document_ref}")
|
||||
if(QT_SBOM_GENERATE_SPDX_V2)
|
||||
_qt_internal_sbom_add_external_reference_document("${external_document_target}"
|
||||
NO_SCAN_FILE_AT_CONFIGURE_TIME
|
||||
SBOM_FORMAT SPDX_V2_TAG_VALUE
|
||||
SPDX_V2_DOCUMENT_REF_ID "${external_document_ref}"
|
||||
SPDX_V2_DOCUMENT_NAMESPACE "${spdx_document_namespace}"
|
||||
EXTERNAL_DOCUMENT_FILE_PATH "${external_document}"
|
||||
EXTERNAL_DOCUMENT_SEARCH_PATHS ${install_prefixes}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(QT_SBOM_GENERATE_CYDX_V1_6)
|
||||
_qt_internal_sbom_add_external_reference_document("${external_document_target}"
|
||||
NO_SCAN_FILE_AT_CONFIGURE_TIME
|
||||
SBOM_FORMAT CYDX_V1_JSON
|
||||
CYDX_URN_SERIAL_NUMBER "${bom_serial_number_uuid}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(AUTHOR_WARNING
|
||||
"Missing spdx document path for external target dependency: ${target}")
|
||||
"Missing spdx document path for external target dependency: ${arg_DEPENDENCY_TARGET}")
|
||||
endif()
|
||||
|
||||
set(${arg_OUT_CYDX_DEPENDENCIES} "${cydx_dependencies}" PARENT_SCOPE)
|
||||
set(${arg_OUT_SPDX_DEPENDENCIES} "${spdx_dependencies}" PARENT_SCOPE)
|
||||
set(${arg_OUT_SBOM_RELATIONSHIP_ENTRIES} "${relationship_entries}" PARENT_SCOPE)
|
||||
set(${arg_OUT_TARGET_DEPENDENCIES} "${target_depdendencies}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -304,6 +304,74 @@ function(_qt_internal_sbom_compute_qt_uniqueish_document_namespace_infix)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Computes a short unique-ish sha suffix to embed into all Qt package SPDX ids, to ensure that each
|
||||
# package spdx id is distinct enough between Qt builds.
|
||||
#
|
||||
# The use case is referencing multiple versions of the the same target, within a project that
|
||||
# mixes multiple Qt versions, like Qt for MCU.
|
||||
#
|
||||
# E.g. record that MyLib depends on Qt Core 6.8.3 from qtbase spdx doc1, and that MyLib2 depends
|
||||
# on Qt Core 6.9.1 from qtbase spdx doc2, within the generated project doc that contains MyLib and
|
||||
# MyLib2.
|
||||
#
|
||||
# Default hash length chosen to be 12, to have a probability of 0.001 collisions when there are
|
||||
# 750000 releases of the same Qt repo, according to
|
||||
# https://en.wikipedia.org/wiki/Birthday_problem#Probability_table
|
||||
function(_qt_internal_sbom_compute_uniqueish_spdx_id_suffix)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
SPDX_NAMESPACE
|
||||
OUT_VAR_UNIQUE_SUFFIX
|
||||
HASH_LENGTH
|
||||
)
|
||||
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(QT_SBOM_NO_AUTO_SPDX_SUFFIX)
|
||||
set(${arg_OUT_VAR_UNIQUE_SUFFIX} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT arg_OUT_VAR_UNIQUE_SUFFIX)
|
||||
message(FATAL_ERROR "OUT_VAR_UNIQUE_SUFFIX must be set")
|
||||
endif()
|
||||
|
||||
if(NOT arg_SPDX_NAMESPACE)
|
||||
message(FATAL_ERROR "SPDX_NAMESPACE must be set")
|
||||
endif()
|
||||
|
||||
if(QT_SBOM_FAKE_DETERMINISTIC_BUILD
|
||||
# This is to allow developers test a fake build, without a fake uuid
|
||||
AND NOT QT_SBOM_NO_FAKE_DETERMINISTIC_BUILD_SPDX_IDX_SUFFIX
|
||||
)
|
||||
set(unique_suffix "fake-id-suffix")
|
||||
else()
|
||||
string(SHA1 namespace_sha "${arg_SPDX_NAMESPACE}")
|
||||
|
||||
if(arg_HASH_LENGTH)
|
||||
set(sha_suffix_length "${arg_HASH_LENGTH}")
|
||||
else()
|
||||
set(sha_suffix_length "12")
|
||||
endif()
|
||||
|
||||
string(SUBSTRING "${namespace_sha}" 0 "${sha_suffix_length}" unique_suffix)
|
||||
endif()
|
||||
|
||||
set(${arg_OUT_VAR_UNIQUE_SUFFIX} "${unique_suffix}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Retrieves the current project's spdx id suffix.
|
||||
function(_qt_internal_sbom_get_spdx_id_unique_suffix out_var)
|
||||
get_cmake_property(unique_suffix _qt_internal_sbom_repo_spdx_id_unique_suffix)
|
||||
if(NOT unique_suffix)
|
||||
set(unique_suffix "")
|
||||
endif()
|
||||
|
||||
set(${out_var} "${unique_suffix}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Returns a lower case host platform name for sbom document namespace purposes.
|
||||
function(_qt_internal_sbom_get_host_platform_name out_var)
|
||||
string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" main_value)
|
||||
|
||||
@@ -52,10 +52,12 @@ function(_qt_internal_sbom_handle_target_binary_files target)
|
||||
THIRD_PARTY_LIBRARY
|
||||
THIRD_PARTY_LIBRARY_WITH_FILES
|
||||
THIRD_PARTY_SOURCES
|
||||
SBOM_PROJECT
|
||||
EXECUTABLE
|
||||
LIBRARY
|
||||
TRANSLATIONS
|
||||
RESOURCES
|
||||
BUILD_TOOL
|
||||
CUSTOM
|
||||
CUSTOM_NO_INFIX
|
||||
)
|
||||
@@ -75,8 +77,10 @@ function(_qt_internal_sbom_handle_target_binary_files target)
|
||||
SYSTEM_LIBRARY
|
||||
THIRD_PARTY_LIBRARY
|
||||
THIRD_PARTY_SOURCES
|
||||
SBOM_PROJECT
|
||||
TRANSLATIONS
|
||||
RESOURCES
|
||||
BUILD_TOOL
|
||||
CUSTOM
|
||||
CUSTOM_NO_INFIX
|
||||
)
|
||||
@@ -797,6 +801,7 @@ function(_qt_internal_sbom_map_path_to_reproducible_relative_path out_var)
|
||||
|
||||
set(is_in_source_dir FALSE)
|
||||
set(is_in_build_dir FALSE)
|
||||
set(is_in_sysroot_dir FALSE)
|
||||
|
||||
if(NOT arg_REPO_PROJECT_NAME_LOWERCASE)
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name)
|
||||
@@ -827,6 +832,12 @@ function(_qt_internal_sbom_map_path_to_reproducible_relative_path out_var)
|
||||
if(NOT is_in_source_dir AND NOT is_in_build_dir)
|
||||
_qt_internal_path_is_prefix(CMAKE_INSTALL_PREFIX "${path}" is_in_prefix_dir)
|
||||
endif()
|
||||
if(CMAKE_SYSROOT
|
||||
AND NOT is_in_source_dir
|
||||
AND NOT is_in_build_dir
|
||||
AND NOT is_in_prefix_dir)
|
||||
_qt_internal_path_is_prefix(CMAKE_SYSROOT "${path}" is_in_sysroot_dir)
|
||||
endif()
|
||||
else()
|
||||
# We consider relative paths to be relative to the current source dir.
|
||||
set(is_in_source_dir TRUE)
|
||||
@@ -854,6 +865,10 @@ function(_qt_internal_sbom_map_path_to_reproducible_relative_path out_var)
|
||||
set(handled TRUE)
|
||||
set(marker "/install_dir")
|
||||
string(REPLACE "${CMAKE_INSTALL_PREFIX}/" "${marker}/" path_out "${path_in_real}")
|
||||
elseif(is_in_sysroot_dir)
|
||||
set(handled TRUE)
|
||||
set(marker "/sysroot_dir")
|
||||
string(REPLACE "${CMAKE_SYSROOT}/" "${marker}/" path_out "${path_in_real}")
|
||||
else()
|
||||
# If it's not a source dir, a build dir, or install dir, it might be some kind of
|
||||
# weird genex or marker that we don't handle yet.
|
||||
@@ -1159,6 +1174,8 @@ endfunction()
|
||||
# Get a sanitized spdx id for a file.
|
||||
# For consistency, we prefix the id with SPDXRef-PackagedFile-. This is not a requirement.
|
||||
function(_qt_internal_sbom_get_file_spdx_id target out_var)
|
||||
_qt_internal_sbom_get_sanitized_spdx_id(spdx_id "SPDXRef-PackagedFile-${target}")
|
||||
_qt_internal_sbom_get_spdx_id_unique_suffix(spdx_id_unique_suffix)
|
||||
_qt_internal_sbom_get_sanitized_spdx_id(spdx_id
|
||||
"SPDXRef-PackagedFile-${target}${spdx_id_unique_suffix}")
|
||||
set(${out_var} "${spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -85,6 +85,8 @@ serial_number_uuid = \"${arg_BOM_SERIAL_NUMBER_UUID}\" # Required
|
||||
description = '''${project_comment}
|
||||
'''
|
||||
|
||||
<PROJECT_RELATIONSHIP_PLACEHOLDER>
|
||||
|
||||
[[project_build_tools]]
|
||||
name = \"cmake\"
|
||||
version = \"${CMAKE_VERSION}\"
|
||||
@@ -92,14 +94,12 @@ component_type = \"application\"
|
||||
description = \"Build system tool used to build the project.\"
|
||||
")
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
|
||||
_qt_internal_sbom_create_sbom_staging_file(
|
||||
CONTENT "${content}"
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
OUT_VAR_CREATE_STAGING_FILE create_staging_file
|
||||
OUT_VAR_SBOM_DIR sbom_dir
|
||||
)
|
||||
set(sbom_format "CYDX_V1_6")
|
||||
_qt_internal_sbom_save_intro_content(
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
CONTENT "${content}")
|
||||
|
||||
_qt_internal_sbom_create_sbom_staging_dir(OUT_VAR_SBOM_DIR sbom_dir)
|
||||
|
||||
_qt_internal_sbom_save_project_info_in_global_properties(
|
||||
SUPPLIER "${arg_SUPPLIER}"
|
||||
@@ -113,16 +113,16 @@ description = \"Build system tool used to build the project.\"
|
||||
OUTPUT "${arg_OUTPUT}"
|
||||
OUTPUT_RELATIVE_PATH "${arg_OUTPUT_RELATIVE_PATH}"
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
PROPERTY_SUFFIX _cydx
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
)
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_sbom_cmake_include_files_cydx "${create_staging_file}")
|
||||
endfunction()
|
||||
|
||||
# Finalizes the CycloneDX sbom generation for a project.
|
||||
function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
set(sbom_format "CYDX_V1_6")
|
||||
|
||||
_qt_internal_sbom_get_common_path_variables_from_global_properties(
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_PATH sbom_build_output_path
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_PATH_WITHOUT_EXT sbom_build_output_path_without_ext
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_DIR sbom_build_output_dir
|
||||
@@ -137,18 +137,34 @@ function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
|
||||
_qt_internal_sbom_get_qt_repo_project_name_lower_case(real_qt_repo_project_name_lowercase)
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
|
||||
_qt_internal_sbom_handle_project_relationships(
|
||||
OUTPUT_SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_RELATIONSHIP_STRINGS relationship_strings
|
||||
)
|
||||
|
||||
set(staging_file_args "")
|
||||
if(relationship_strings)
|
||||
list(APPEND staging_file_args RELATIONSHIP_STRINGS "${relationship_strings}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_create_sbom_staging_file(
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
${staging_file_args}
|
||||
)
|
||||
|
||||
# Process licenses before getting the includes.
|
||||
_qt_internal_sbom_add_recorded_licenses_cydx()
|
||||
|
||||
_qt_internal_sbom_get_cmake_include_files(
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_INCLUDES includes
|
||||
OUT_VAR_POST_GENERATION_INCLUDES post_generation_includes
|
||||
)
|
||||
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
|
||||
set(build_time_args "")
|
||||
if(includes)
|
||||
list(APPEND build_time_args INCLUDES "${includes}")
|
||||
@@ -157,7 +173,7 @@ function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
list(APPEND build_time_args POST_GENERATION_INCLUDES "${post_generation_includes}")
|
||||
endif()
|
||||
_qt_internal_sbom_create_build_time_sbom_targets(
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
REAL_QT_REPO_PROJECT_NAME_LOWERCASE "${real_qt_repo_project_name_lowercase}"
|
||||
SBOM_BUILD_OUTPUT_PATH "${sbom_build_output_path}"
|
||||
@@ -169,7 +185,7 @@ function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
|
||||
_qt_internal_sbom_setup_multi_config_install_markers(
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
OUT_VAR_EXTRA_CODE_BEGIN extra_code_begin
|
||||
OUT_VAR_EXTRA_CODE_INNER_END extra_code_inner_end
|
||||
@@ -203,7 +219,7 @@ function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_setup_sbom_install_code(
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
SBOM_INSTALL_OUTPUT_PATH "${sbom_install_output_path}"
|
||||
SBOM_INSTALL_OUTPUT_PATH_WITHOUT_EXT "${sbom_install_output_path_without_ext}"
|
||||
@@ -213,7 +229,7 @@ function(_qt_internal_sbom_end_project_generate_cyclone)
|
||||
)
|
||||
|
||||
_qt_internal_sbom_clear_cmake_include_files(
|
||||
SBOM_FORMAT "CYDX_V1_6"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@@ -241,8 +257,8 @@ function(_qt_internal_sbom_generate_cyclone_add_package)
|
||||
|
||||
# Additions compared to spdx function signature.
|
||||
PURL_VALUES
|
||||
DEPENDENCIES
|
||||
CYDX_PROPERTIES
|
||||
SBOM_RELATIONSHIP_ENTRIES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
@@ -298,14 +314,21 @@ function(_qt_internal_sbom_generate_cyclone_add_package)
|
||||
set(version_field "version = \\\"${arg_VERSION}\\\"")
|
||||
endif()
|
||||
|
||||
set(dependency_field "")
|
||||
set(dependency_list ${arg_DEPENDENCIES})
|
||||
if(dependency_list)
|
||||
# Wrap values in double quotes.
|
||||
list(TRANSFORM dependency_list PREPEND "\\\"")
|
||||
list(TRANSFORM dependency_list APPEND "\\\"")
|
||||
list(JOIN dependency_list ", " dependency_string)
|
||||
set(dependency_field "dependencies = [${dependency_string}]")
|
||||
set(relationships_field "")
|
||||
if(arg_SBOM_RELATIONSHIP_ENTRIES)
|
||||
_qt_internal_sbom_serialize_relationship_entries(
|
||||
OUTPUT_SBOM_FORMAT "CYDX_V1_6"
|
||||
CYDX_TOML_KEY "components."
|
||||
ESCAPE_CYDX_QUOTES
|
||||
OUT_VAR_RELATIONSHIPS_STRINGS entries_relationships_strings
|
||||
SBOM_RELATIONSHIP_ENTRIES ${arg_SBOM_RELATIONSHIP_ENTRIES}
|
||||
)
|
||||
if(entries_relationships_strings)
|
||||
# Remove duplicates, because apparently we sometimes get them for some system libraries.
|
||||
list(REMOVE_DUPLICATES entries_relationships_strings)
|
||||
|
||||
list(JOIN entries_relationships_strings "\n" relationships_field)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(copyright_field "")
|
||||
@@ -367,8 +390,8 @@ ${external_bom_link_field}
|
||||
${cpe_field}
|
||||
${purl_field}
|
||||
${license_concluded_field}
|
||||
${dependency_field}
|
||||
${properties_field}
|
||||
${relationships_field}
|
||||
\"
|
||||
)
|
||||
")
|
||||
|
||||
@@ -141,6 +141,8 @@ PackageVersion: ${QT_SBOM_GIT_VERSION}")
|
||||
|
||||
set(project_comment "<text>${project_comment}</text>")
|
||||
|
||||
_qt_internal_sbom_get_spdx_id_unique_suffix(spdx_id_unique_suffix)
|
||||
|
||||
set(content
|
||||
"SPDXVersion: SPDX-2.3
|
||||
DataLicense: CC0-1.0
|
||||
@@ -152,20 +154,6 @@ CreatorComment: <text>This SPDX document was created from CMake ${CMAKE_VERSION}
|
||||
build system from https://code.qt.io/cgit/qt/qtbase.git/tree/cmake/QtPublicSbomHelpers.cmake</text>
|
||||
Created: ${current_utc}\${QT_SBOM_EXTERNAL_DOC_REFS}
|
||||
|
||||
PackageName: ${CMAKE_CXX_COMPILER_ID}
|
||||
SPDXID: SPDXRef-compiler
|
||||
PackageVersion: ${CMAKE_CXX_COMPILER_VERSION}
|
||||
PackageDownloadLocation: NOASSERTION
|
||||
PackageLicenseConcluded: NOASSERTION
|
||||
PackageLicenseDeclared: NOASSERTION
|
||||
PackageCopyrightText: NOASSERTION
|
||||
PackageSupplier: Organization: Anonymous
|
||||
FilesAnalyzed: false
|
||||
PackageSummary: <text>The compiler as identified by CMake, running on ${CMAKE_HOST_SYSTEM_NAME} (${CMAKE_HOST_SYSTEM_PROCESSOR})</text>
|
||||
PrimaryPackagePurpose: APPLICATION
|
||||
Relationship: SPDXRef-compiler BUILD_DEPENDENCY_OF ${project_spdx_id}
|
||||
RelationshipComment: <text>${project_spdx_id} is built by compiler ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}</text>
|
||||
|
||||
PackageName: ${arg_PROJECT}
|
||||
SPDXID: ${project_spdx_id}${fields}
|
||||
ExternalRef: PACKAGE-MANAGER purl pkg:generic/${arg_SUPPLIER}/${arg_PROJECT}@${QT_SBOM_GIT_VERSION}
|
||||
@@ -178,19 +166,14 @@ PackageHomePage: ${arg_SUPPLIER_URL}
|
||||
PackageComment: ${project_comment}
|
||||
FilesAnalyzed: false
|
||||
BuiltDate: ${current_utc}
|
||||
Relationship: SPDXRef-DOCUMENT DESCRIBES ${project_spdx_id}
|
||||
")
|
||||
Relationship: SPDXRef-DOCUMENT DESCRIBES ${project_spdx_id}")
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
|
||||
_qt_internal_sbom_create_sbom_staging_file(
|
||||
CONTENT "${content}"
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
OUT_VAR_CREATE_STAGING_FILE create_staging_file
|
||||
OUT_VAR_SBOM_DIR sbom_dir
|
||||
)
|
||||
set(sbom_format "SPDX_V2")
|
||||
_qt_internal_sbom_save_intro_content(
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
CONTENT "${content}")
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_internal_sbom_dirs "${sbom_dir}")
|
||||
_qt_internal_sbom_create_sbom_staging_dir(OUT_VAR_SBOM_DIR sbom_dir)
|
||||
|
||||
_qt_internal_sbom_save_project_info_in_global_properties(
|
||||
SUPPLIER "${arg_SUPPLIER}"
|
||||
@@ -198,16 +181,16 @@ Relationship: SPDXRef-DOCUMENT DESCRIBES ${project_spdx_id}
|
||||
NAMESPACE "${arg_NAMESPACE}"
|
||||
PROJECT "${arg_PROJECT}"
|
||||
PROJECT_SPDX_ID "${project_spdx_id}"
|
||||
EXTERNAL_REFERENCE_SBOM_DIRS "${sbom_dir}"
|
||||
)
|
||||
|
||||
_qt_internal_sbom_save_common_path_variables_in_global_properties(
|
||||
OUTPUT "${arg_OUTPUT}"
|
||||
OUTPUT_RELATIVE_PATH "${arg_OUTPUT_RELATIVE_PATH}"
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
)
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_sbom_cmake_include_files "${create_staging_file}")
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_spdx_id_count 0)
|
||||
set_property(GLOBAL PROPERTY _qt_sbom_relationship_counter 0)
|
||||
endfunction()
|
||||
@@ -216,8 +199,9 @@ endfunction()
|
||||
# Creates an 'sbom' custom target to generate an incomplete sbom at build time (no checksums).
|
||||
# Creates install rules to install a complete (with checksums) sbom.
|
||||
function(_qt_internal_sbom_end_project_generate)
|
||||
set(sbom_format "SPDX_V2")
|
||||
_qt_internal_sbom_get_common_path_variables_from_global_properties(
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_PATH sbom_build_output_path
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_PATH_WITHOUT_EXT sbom_build_output_path_without_ext
|
||||
OUT_VAR_SBOM_BUILD_OUTPUT_DIR sbom_build_output_dir
|
||||
@@ -232,9 +216,27 @@ function(_qt_internal_sbom_end_project_generate)
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(repo_project_name_lowercase)
|
||||
_qt_internal_sbom_get_qt_repo_project_name_lower_case(real_qt_repo_project_name_lowercase)
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
|
||||
_qt_internal_sbom_handle_project_relationships(
|
||||
OUTPUT_SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_RELATIONSHIP_STRINGS relationship_strings
|
||||
)
|
||||
|
||||
set(staging_file_args "")
|
||||
if(relationship_strings)
|
||||
list(APPEND staging_file_args RELATIONSHIP_STRINGS "${relationship_strings}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_create_sbom_staging_file(
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
${staging_file_args}
|
||||
)
|
||||
|
||||
_qt_internal_sbom_get_cmake_include_files(
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
OUT_VAR_INCLUDES includes
|
||||
OUT_VAR_BEFORE_CHECKSUM_INCLUDES before_checksum_includes
|
||||
OUT_VAR_AFTER_CHECKSUM_INCLUDES after_checksum_includes
|
||||
@@ -242,8 +244,6 @@ function(_qt_internal_sbom_end_project_generate)
|
||||
OUT_VAR_VERIFY_INCLUDES verify_includes
|
||||
)
|
||||
|
||||
_qt_internal_get_current_project_sbom_dir(sbom_dir)
|
||||
|
||||
set(build_time_args "")
|
||||
if(includes)
|
||||
list(APPEND build_time_args INCLUDES "${includes}")
|
||||
@@ -252,7 +252,7 @@ function(_qt_internal_sbom_end_project_generate)
|
||||
list(APPEND build_time_args POST_GENERATION_INCLUDES "${post_generation_includes}")
|
||||
endif()
|
||||
_qt_internal_sbom_create_build_time_sbom_targets(
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
REAL_QT_REPO_PROJECT_NAME_LOWERCASE "${real_qt_repo_project_name_lowercase}"
|
||||
SBOM_BUILD_OUTPUT_PATH "${sbom_build_output_path}"
|
||||
@@ -280,7 +280,7 @@ function(_qt_internal_sbom_end_project_generate)
|
||||
|
||||
_qt_internal_sbom_setup_multi_config_install_markers(
|
||||
SBOM_DIR "${sbom_dir}"
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
OUT_VAR_EXTRA_CODE_BEGIN extra_code_begin
|
||||
OUT_VAR_EXTRA_CODE_INNER_END extra_code_inner_end
|
||||
@@ -335,7 +335,7 @@ unset(_verification_code)
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_setup_sbom_install_code(
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
REPO_PROJECT_NAME_LOWERCASE "${repo_project_name_lowercase}"
|
||||
SBOM_INSTALL_OUTPUT_PATH "${sbom_install_output_path}"
|
||||
SBOM_INSTALL_OUTPUT_PATH_WITHOUT_EXT "${sbom_install_output_path_without_ext}"
|
||||
@@ -346,7 +346,7 @@ unset(_verification_code)
|
||||
)
|
||||
|
||||
_qt_internal_sbom_clear_cmake_include_files(
|
||||
SBOM_FORMAT "SPDX_V2"
|
||||
SBOM_FORMAT "${sbom_format}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@@ -529,22 +529,29 @@ Relationship: ${arg_RELATIONSHIP}
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_sbom_cmake_include_files "${file_sbom_to_install}")
|
||||
endfunction()
|
||||
|
||||
# Helper to add a reference to an external SPDX document.
|
||||
# Helper to add a reference to an external SPDX v2 document.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_SPDX_ID: The spdx id by which the external document should be referenced in
|
||||
# the current project SPDX document. This semantically serves as a pointer to the external document
|
||||
# URI.
|
||||
# e.g. DocumentRef-qtbase.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_FILE_PATH: The relative file path of the external sbom document.
|
||||
# e.g. "sbom/qtbase-6.9.0.spdx"
|
||||
# SBOM_FORMAT: Type of SPDX file, SPDX_V2_TAG_VALUE or SPDX_V2_JSON.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_NAMESPACE: Explicit SPDX namespace to embed, instead of parsing the file passed
|
||||
# in EXTERNAL_DOCUMENT_FILE_PATH.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_SHA1: Explicit sha1 to embed, instead of calculating it from the file passed
|
||||
# in EXTERNAL_DOCUMENT_FILE_PATH.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_FILE_PATH: An absolute or relative file path of the external sbom document.
|
||||
# In case of a relative file path, it will be searched for in the directories specified by the
|
||||
# EXTERNAL_DOCUMENT_INSTALL_PREFIXES option.
|
||||
# e.g. "sbom/qtbase-6.9.0.spdx"
|
||||
# Can contain generator expressions.
|
||||
# The file path is searched for in the directories specified by the
|
||||
# EXTERNAL_DOCUMENT_INSTALL_PREFIXES option during sbom generation, to compute the file checksum.
|
||||
# The file path is NOT embedded into the current project spdx document.
|
||||
# Only its spdx id and namespace is embedded. The namespace is extracted from the contents of the
|
||||
# referenced file.
|
||||
# The file path is NOT embedded into the current project spdx document. Only the document ref id,
|
||||
# spdx namespace and sha1 is embedded.
|
||||
# The namespace is extracted from the contents of the referenced file.
|
||||
#
|
||||
# EXTERNAL_DOCUMENT_INSTALL_PREFIXES: A list of directories where the external document file path
|
||||
# is searched for. The first existing file is used. Additionally the following locations are
|
||||
@@ -571,6 +578,10 @@ function(_qt_internal_sbom_generate_add_external_reference)
|
||||
EXTERNAL_DOCUMENT_SPDX_ID
|
||||
EXTERNAL_PACKAGE_SPDX_ID
|
||||
RELATIONSHIP_STRING
|
||||
|
||||
EXTERNAL_DOCUMENT_NAMESPACE
|
||||
EXTERNAL_DOCUMENT_SHA1
|
||||
SBOM_FORMAT
|
||||
)
|
||||
set(multi_args
|
||||
EXTERNAL_DOCUMENT_INSTALL_PREFIXES
|
||||
@@ -578,7 +589,46 @@ function(_qt_internal_sbom_generate_add_external_reference)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
_qt_internal_sbom_set_default_option_value_and_error_if_empty(EXTERNAL_DOCUMENT_FILE_PATH "")
|
||||
# Default to tag:value if nothing is passed, because that was the previous behavior before this
|
||||
# option got introduced.
|
||||
if(NOT arg_SBOM_FORMAT)
|
||||
set(arg_SBOM_FORMAT "SPDX_V2_TAG_VALUE")
|
||||
endif()
|
||||
|
||||
if(NOT arg_EXTERNAL_DOCUMENT_FILE_PATH AND NOT arg_EXTERNAL_DOCUMENT_NAMESPACE)
|
||||
message(FATAL_ERROR "Either EXTERNAL_DOCUMENT_FILE_PATH or "
|
||||
"EXTERNAL_DOCUMENT_NAMESPACE together with EXTERNAL_DOCUMENT_SHA1 must be specified "
|
||||
"to be able to add an external reference for an external SPDX v2 tag:value document.")
|
||||
endif()
|
||||
|
||||
set(debug_var_assignments "
|
||||
EXTERNAL_DOCUMENT_FILE_PATH: '${arg_EXTERNAL_DOCUMENT_FILE_PATH}'
|
||||
EXTERNAL_DOCUMENT_NAMESPACE: '${arg_EXTERNAL_DOCUMENT_NAMESPACE}'
|
||||
EXTERNAL_DOCUMENT_SHA1: '${arg_EXTERNAL_DOCUMENT_SHA1}'
|
||||
EXTERNAL_DOCUMENT_SPDX_ID: '${arg_EXTERNAL_DOCUMENT_SPDX_ID}'
|
||||
")
|
||||
|
||||
if(NOT arg_EXTERNAL_DOCUMENT_FILE_PATH
|
||||
AND arg_EXTERNAL_DOCUMENT_NAMESPACE
|
||||
AND NOT arg_EXTERNAL_DOCUMENT_SHA1)
|
||||
message(FATAL_ERROR
|
||||
"EXTERNAL_DOCUMENT_SHA1 must be specified if EXTERNAL_DOCUMENT_NAMESPACE is specified "
|
||||
"and EXTERNAL_DOCUMENT_FILE_PATH is not specified.\n${debug_var_assignments}")
|
||||
endif()
|
||||
|
||||
if(NOT arg_EXTERNAL_DOCUMENT_FILE_PATH
|
||||
AND NOT arg_EXTERNAL_DOCUMENT_NAMESPACE
|
||||
AND arg_EXTERNAL_DOCUMENT_SHA1)
|
||||
message(FATAL_ERROR
|
||||
"EXTERNAL_DOCUMENT_NAMESPACE must be specified if EXTERNAL_DOCUMENT_SHA1 is specified "
|
||||
"and EXTERNAL_DOCUMENT_FILE_PATH is not specified.\n${debug_var_assignments}")
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_DOCUMENT_FILE_PATH)
|
||||
set(find_external_document TRUE)
|
||||
else()
|
||||
set(find_external_document FALSE)
|
||||
endif()
|
||||
|
||||
if(NOT arg_EXTERNAL_DOCUMENT_SPDX_ID)
|
||||
get_property(spdx_id_count GLOBAL PROPERTY _qt_sbom_spdx_id_count)
|
||||
@@ -594,7 +644,8 @@ function(_qt_internal_sbom_generate_add_external_reference)
|
||||
if(NOT sbom_project_spdx_id)
|
||||
message(FATAL_ERROR "Call _qt_internal_sbom_begin_project() first")
|
||||
endif()
|
||||
if(arg_RELATIONSHIP_STRING STREQUAL "")
|
||||
|
||||
if(NOT arg_RELATIONSHIP_STRING)
|
||||
if(arg_EXTERNAL_PACKAGE_SPDX_ID)
|
||||
set(external_package "${arg_EXTERNAL_DOCUMENT_SPDX_ID}:${arg_EXTERNAL_PACKAGE_SPDX_ID}")
|
||||
set(arg_RELATIONSHIP_STRING
|
||||
@@ -608,43 +659,23 @@ function(_qt_internal_sbom_generate_add_external_reference)
|
||||
|
||||
_qt_internal_get_staging_area_spdx_file_path(staging_area_spdx_file)
|
||||
|
||||
set(install_prefixes "")
|
||||
set(document_search_paths "")
|
||||
if(find_external_document)
|
||||
set(search_path_args "")
|
||||
if(arg_EXTERNAL_DOCUMENT_INSTALL_PREFIXES)
|
||||
list(APPEND search_path_args
|
||||
EXTERNAL_DOCUMENT_SEARCH_PATHS ${arg_EXTERNAL_DOCUMENT_INSTALL_PREFIXES}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add the current sbom build dirs as install prefixes, so that we can use ninja 'sbom'
|
||||
# in top-level builds. This is needed because the external references will point
|
||||
# to sbom docs in different build dirs, not just one.
|
||||
# We also need it in case we are converting a json document to a tag/value format in the
|
||||
# current build dir of the project, and want it to be found.
|
||||
get_cmake_property(build_sbom_dirs _qt_internal_sbom_dirs)
|
||||
if(build_sbom_dirs)
|
||||
foreach(build_sbom_dir IN LISTS build_sbom_dirs)
|
||||
list(APPEND install_prefixes "${build_sbom_dir}")
|
||||
endforeach()
|
||||
_qt_internal_sbom_get_external_reference_search_paths(document_search_paths
|
||||
SEARCH_IN_BUILD_SBOM_DIRS
|
||||
SEARCH_IN_QT_PREFIXES
|
||||
SEARCH_IN_DESTDIR_INSTALL_PREFIX_AT_INSTALL_TIME
|
||||
${search_path_args}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Always append the install time install prefix.
|
||||
# The variable is escaped, so it is evaluated during cmake install time, so that the value
|
||||
# can be overridden with cmake --install . --prefix <path>.
|
||||
list(APPEND install_prefixes "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
if(arg_EXTERNAL_DOCUMENT_INSTALL_PREFIXES)
|
||||
list(APPEND install_prefixes ${arg_EXTERNAL_DOCUMENT_INSTALL_PREFIXES})
|
||||
endif()
|
||||
|
||||
if(QT6_INSTALL_PREFIX)
|
||||
list(APPEND install_prefixes ${QT6_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
if(QT_ADDITIONAL_PACKAGES_PREFIX_PATH)
|
||||
list(APPEND install_prefixes ${QT_ADDITIONAL_PACKAGES_PREFIX_PATH})
|
||||
endif()
|
||||
|
||||
if(QT_ADDITIONAL_SBOM_DOCUMENT_PATHS)
|
||||
list(APPEND install_prefixes ${QT_ADDITIONAL_SBOM_DOCUMENT_PATHS})
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES install_prefixes)
|
||||
|
||||
set(relationship_content "")
|
||||
if(arg_RELATIONSHIP_STRING)
|
||||
set(relationship_content "
|
||||
@@ -655,29 +686,40 @@ function(_qt_internal_sbom_generate_add_external_reference)
|
||||
|
||||
# File path may not exist yet, and it could be a generator expression.
|
||||
set(content "
|
||||
set(relative_file_name \"${arg_EXTERNAL_DOCUMENT_FILE_PATH}\")
|
||||
set(document_dir_paths ${install_prefixes})
|
||||
list(JOIN document_dir_paths \"\\n\" document_dir_paths_per_line)
|
||||
foreach(document_dir_path IN LISTS document_dir_paths)
|
||||
set(document_file_path \"\${document_dir_path}/\${relative_file_name}\")
|
||||
if(EXISTS \"\${document_file_path}\")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT EXISTS \"\${document_file_path}\")
|
||||
message(FATAL_ERROR \"Could not find external SBOM document \${relative_file_name}\"
|
||||
\" in any of the document dir paths: \${document_dir_paths_per_line} \"
|
||||
set(find_external_document \"${find_external_document}\")
|
||||
set(document_search_paths \"${document_search_paths}\")
|
||||
set(maybe_external_document_file_path \"${arg_EXTERNAL_DOCUMENT_FILE_PATH}\")
|
||||
set(sbom_format \"${arg_SBOM_FORMAT}\")
|
||||
|
||||
set(explicit_external_document_namespace \"${arg_EXTERNAL_DOCUMENT_NAMESPACE}\")
|
||||
set(explicit_external_document_sha1 \"${arg_EXTERNAL_DOCUMENT_SHA1}\")
|
||||
|
||||
# The helper functions below are used both during configure time and SBOM generation
|
||||
# time. During configure time, QT_GENERATE_SBOM is checked to return early gracefully,
|
||||
# to allow projects to skip SBOM generation.
|
||||
# During SBOM generation time, we need to ensure these functions run properly.
|
||||
# Backup the var instead of setting it directly, because some of the testing infrastructure
|
||||
# does checks of the variable at the end of sbom generation.
|
||||
set(backup_qt_generate_sbom \"${QT_GENERATE_SBOM}\")
|
||||
set(QT_GENERATE_SBOM ON)
|
||||
if(find_external_document)
|
||||
_qt_internal_sbom_find_external_reference_document(document_file_path
|
||||
EXTERNAL_DOCUMENT_FILE_PATH \"\${maybe_external_document_file_path}\"
|
||||
EXTERNAL_DOCUMENT_SEARCH_PATHS \${document_search_paths}
|
||||
)
|
||||
endif()
|
||||
file(SHA1 \"\${document_file_path}\" ext_sha1)
|
||||
file(READ \"\${document_file_path}\" ext_content)
|
||||
|
||||
if(NOT \"\${ext_content}\" MATCHES \"[\\r\\n]DocumentNamespace:\")
|
||||
message(FATAL_ERROR \"Missing DocumentNamespace in \${document_file_path}\")
|
||||
endif()
|
||||
_qt_internal_sbom_parse_spdx_v2_document_namespace(
|
||||
EXTERNAL_DOCUMENT_FILE_PATH \"\${document_file_path}\"
|
||||
SBOM_FORMAT \"\${sbom_format}\"
|
||||
OUT_VAR_DOCUMENT_NAMESPACE ext_ns
|
||||
)
|
||||
|
||||
string(REGEX REPLACE \"^.*[\\r\\n]DocumentNamespace:[ \\t]*([^#\\r\\n]*).*$\"
|
||||
\"\\\\1\" ext_ns \"\${ext_content}\")
|
||||
file(SHA1 \"\${document_file_path}\" ext_sha1)
|
||||
else()
|
||||
set(ext_ns \"\${explicit_external_document_namespace}\")
|
||||
set(ext_sha1 \"\${explicit_external_document_sha1}\")
|
||||
endif()
|
||||
set(QT_GENERATE_SBOM \${backup_qt_generate_sbom})
|
||||
|
||||
string(APPEND QT_SBOM_EXTERNAL_DOC_REFS \"
|
||||
ExternalDocumentRef: ${arg_EXTERNAL_DOCUMENT_SPDX_ID} \${ext_ns} SHA1: \${ext_sha1}\")
|
||||
@@ -699,12 +741,12 @@ function(_qt_internal_sbom_generate_add_package)
|
||||
)
|
||||
set(single_args
|
||||
PACKAGE
|
||||
PACKAGE_SUMMARY
|
||||
VERSION
|
||||
LICENSE_DECLARED
|
||||
LICENSE_CONCLUDED
|
||||
COPYRIGHT
|
||||
DOWNLOAD_LOCATION
|
||||
RELATIONSHIP
|
||||
SPDXID
|
||||
SUPPLIER
|
||||
PURPOSE
|
||||
@@ -713,6 +755,8 @@ function(_qt_internal_sbom_generate_add_package)
|
||||
set(multi_args
|
||||
EXTREF
|
||||
CPE
|
||||
RELATIONSHIPS # Deprecated, SBOM_RELATIONSHIP_ENTRIES is preferred
|
||||
SBOM_RELATIONSHIP_ENTRIES
|
||||
)
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
@@ -776,6 +820,12 @@ PackageCopyrightText: NOASSERTION"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(arg_PACKAGE_SUMMARY)
|
||||
set(fields "${fields}
|
||||
PackageSummary: <text>${arg_PACKAGE_SUMMARY}</text>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(arg_PURPOSE)
|
||||
set(fields "${fields}
|
||||
PrimaryPackagePurpose: ${arg_PURPOSE}"
|
||||
@@ -802,12 +852,33 @@ ExternalRef: SECURITY cpe23Type ${cpe}"
|
||||
if(NOT sbom_project_spdx_id)
|
||||
message(FATAL_ERROR "Call _qt_internal_sbom_begin_project() first")
|
||||
endif()
|
||||
if(NOT arg_RELATIONSHIP)
|
||||
set(arg_RELATIONSHIP "${sbom_project_spdx_id} CONTAINS ${arg_SPDXID}")
|
||||
else()
|
||||
string(REPLACE "@QT_SBOM_LAST_SPDXID@" "${arg_SPDXID}" arg_RELATIONSHIP "${arg_RELATIONSHIP}")
|
||||
|
||||
set(relationship_strings "")
|
||||
if(arg_SBOM_RELATIONSHIP_ENTRIES)
|
||||
_qt_internal_sbom_serialize_relationship_entries(
|
||||
OUTPUT_SBOM_FORMAT "SPDX_V2"
|
||||
OUT_VAR_RELATIONSHIPS_STRINGS entries_relationships_strings
|
||||
SBOM_RELATIONSHIP_ENTRIES ${arg_SBOM_RELATIONSHIP_ENTRIES}
|
||||
)
|
||||
if(entries_relationships_strings)
|
||||
list(APPEND relationship_strings ${entries_relationships_strings})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TODO: This is deprecated, SBOM_RELATIONSHIP_ENTRIES is preferred, remove it once qtwebengine
|
||||
# is ported over.
|
||||
if(arg_RELATIONSHIPS)
|
||||
string(REPLACE
|
||||
"@QT_SBOM_LAST_SPDXID@" "${arg_SPDXID}" arg_RELATIONSHIPS "${arg_RELATIONSHIPS}")
|
||||
list(APPEND relationship_strings ${arg_RELATIONSHIPS})
|
||||
endif()
|
||||
|
||||
# Remove duplicates, because apparently we sometimes get them for some system libraries.
|
||||
list(REMOVE_DUPLICATES relationship_strings)
|
||||
|
||||
list(JOIN relationship_strings "\n" relationships_str)
|
||||
string(PREPEND relationships_str "\n")
|
||||
|
||||
set(fields "${fields}\\\${QT_SBOM_VERIFICATION_CODE_${arg_SPDXID}}")
|
||||
|
||||
_qt_internal_get_staging_area_spdx_file_path(staging_area_spdx_file)
|
||||
@@ -824,8 +895,7 @@ SPDXID: ${arg_SPDXID}
|
||||
PackageDownloadLocation: ${arg_DOWNLOAD_LOCATION}
|
||||
PackageVersion: ${arg_VERSION}
|
||||
PackageSupplier: ${arg_SUPPLIER}${fields}
|
||||
FilesAnalyzed: \\\${QT_SBOM_PACKAGE_HAS_FILES_${arg_SPDXID}}
|
||||
Relationship: ${arg_RELATIONSHIP}
|
||||
FilesAnalyzed: \\\${QT_SBOM_PACKAGE_HAS_FILES_${arg_SPDXID}}${relationships_str}
|
||||
\"
|
||||
)
|
||||
")
|
||||
|
||||
@@ -31,6 +31,9 @@ function(_qt_internal_sbom_begin_project)
|
||||
USE_GIT_VERSION
|
||||
__QT_INTERNAL_HANDLE_QT_REPO
|
||||
NO_AUTO_DOCUMENT_NAMESPACE_INFIX
|
||||
NO_AUTO_SEARCH_EXTERNAL_DOCUMENTS_IN_CMAKE_PATHS
|
||||
NO_AUTO_SPDX_ID_SUFFIX
|
||||
NO_AUTO_ADD_BUILD_TOOLS
|
||||
)
|
||||
set(single_args
|
||||
INSTALL_PREFIX
|
||||
@@ -43,6 +46,8 @@ function(_qt_internal_sbom_begin_project)
|
||||
DOCUMENT_NAMESPACE_INFIX
|
||||
DOCUMENT_NAMESPACE_SUFFIX
|
||||
DOCUMENT_NAMESPACE_URL_PREFIX
|
||||
SPDX_ID_SUFFIX
|
||||
SPDX_ID_SUFFIX_HASH_LENGTH
|
||||
VERSION
|
||||
SBOM_PROJECT_NAME
|
||||
QT_REPO_PROJECT_NAME
|
||||
@@ -52,6 +57,7 @@ function(_qt_internal_sbom_begin_project)
|
||||
set(multi_args
|
||||
COPYRIGHTS
|
||||
LICENSE_DIR_PATHS
|
||||
EXTERNAL_DOCUMENT_SEARCH_PATHS
|
||||
)
|
||||
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -123,6 +129,20 @@ function(_qt_internal_sbom_begin_project)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_DOCUMENT_SEARCH_PATHS)
|
||||
set_property(GLOBAL APPEND PROPERTY _qt_internal_sbom_external_document_search_paths
|
||||
${arg_EXTERNAL_DOCUMENT_SEARCH_PATHS})
|
||||
endif()
|
||||
|
||||
if(arg_NO_AUTO_SEARCH_EXTERNAL_DOCUMENTS_IN_CMAKE_PATHS)
|
||||
set(auto_search_external_documents_in_cmake_paths FALSE)
|
||||
else()
|
||||
set(auto_search_external_documents_in_cmake_paths TRUE)
|
||||
endif()
|
||||
set_property(GLOBAL PROPERTY
|
||||
_qt_internal_sbom_auto_search_external_documents_in_paths
|
||||
"${auto_search_external_documents_in_cmake_paths}")
|
||||
|
||||
if(arg_DOCUMENT_NAMESPACE)
|
||||
set(repo_spdx_namespace "${arg_DOCUMENT_NAMESPACE}")
|
||||
|
||||
@@ -186,15 +206,41 @@ function(_qt_internal_sbom_begin_project)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(QT_SBOM_GENERATE_CYDX_V1_6)
|
||||
_qt_internal_sbom_get_cyclone_bom_serial_number(
|
||||
_qt_internal_sbom_get_cyclone_bom_serial_number(
|
||||
SPDX_NAMESPACE "${repo_spdx_namespace}"
|
||||
OUT_VAR_UUID cyclone_dx_bom_serial_number_uuid
|
||||
)
|
||||
list(APPEND begin_project_generate_args_cydx
|
||||
BOM_SERIAL_NUMBER_UUID "${cyclone_dx_bom_serial_number_uuid}")
|
||||
|
||||
if(QT_SBOM_SPDX_ID_SUFFIX)
|
||||
set(spdx_id_unique_suffix "-${QT_SBOM_SPDX_ID_SUFFIX}")
|
||||
elseif(arg_SPDX_ID_SUFFIX)
|
||||
set(spdx_id_unique_suffix "-${arg_SPDX_ID_SUFFIX}")
|
||||
elseif(NOT arg_NO_AUTO_SPDX_ID_SUFFIX AND NOT QT_SBOM_NO_AUTO_SPDX_SUFFIX)
|
||||
set(compute_unique_spdx_id_suffix_args "")
|
||||
|
||||
if(QT_SBOM_SPDX_ID_SUFFIX_HASH_LENGTH)
|
||||
list(APPEND compute_unique_spdx_id_suffix_args
|
||||
HASH_LENGTH "${QT_SBOM_SPDX_ID_SUFFIX_HASH_LENGTH}")
|
||||
elseif(arg_SPDX_ID_SUFFIX_HASH_LENGTH)
|
||||
list(APPEND compute_unique_spdx_id_suffix_args
|
||||
HASH_LENGTH "${arg_SPDX_ID_SUFFIX_HASH_LENGTH}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_compute_uniqueish_spdx_id_suffix(
|
||||
SPDX_NAMESPACE "${repo_spdx_namespace}"
|
||||
OUT_VAR_UUID cyclone_dx_bom_serial_number_uuid
|
||||
OUT_VAR_UNIQUE_SUFFIX spdx_id_unique_suffix
|
||||
${compute_unique_spdx_id_suffix_args}
|
||||
)
|
||||
list(APPEND begin_project_generate_args_cydx
|
||||
BOM_SERIAL_NUMBER_UUID "${cyclone_dx_bom_serial_number_uuid}")
|
||||
string(PREPEND spdx_id_unique_suffix "-")
|
||||
else()
|
||||
set(spdx_id_unique_suffix "")
|
||||
endif()
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_spdx_id_unique_suffix
|
||||
"${spdx_id_unique_suffix}")
|
||||
|
||||
if(arg_INSTALL_SBOM_DIR)
|
||||
set(install_sbom_dir "${arg_INSTALL_SBOM_DIR}")
|
||||
elseif(INSTALL_SBOMDIR)
|
||||
@@ -231,9 +277,22 @@ function(_qt_internal_sbom_begin_project)
|
||||
|
||||
_qt_internal_path_join(repo_spdx_relative_install_path_spdx
|
||||
"${install_sbom_dir}" "${repo_project_file_name_spdx}")
|
||||
|
||||
# Currently only used for exporting as a target's property.
|
||||
_qt_internal_path_join(repo_spdx_relative_install_path_spdx_json
|
||||
"${install_sbom_dir}" "${repo_project_file_name_spdx}.json")
|
||||
|
||||
# This is actually the path to the intermediate CycloneDX toml file.
|
||||
_qt_internal_path_join(repo_spdx_relative_install_path_cydx
|
||||
"${install_sbom_dir}" "${repo_project_file_name_cydx}")
|
||||
|
||||
# Compute the relative path to the final cydx json file, to be exported in a target's property.
|
||||
get_filename_component(repo_project_file_name_cydx_without_ext
|
||||
"${repo_project_file_name_cydx}" NAME_WLE)
|
||||
|
||||
_qt_internal_path_join(repo_spdx_relative_install_path_cydx_json
|
||||
"${install_sbom_dir}" "${repo_project_file_name_cydx_without_ext}.json")
|
||||
|
||||
# Prepend DESTDIR, to allow relocating installed sbom. Needed for CI.
|
||||
_qt_internal_path_join(repo_spdx_install_path_spdx
|
||||
"\$ENV{DESTDIR}${install_prefix}" "${repo_spdx_relative_install_path_spdx}")
|
||||
@@ -353,9 +412,20 @@ function(_qt_internal_sbom_begin_project)
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_cyclone_dx_bom_serial_number_uuid
|
||||
"${cyclone_dx_bom_serial_number_uuid}")
|
||||
|
||||
# TODO: Figure out what needs to be done to fully port usage to the tag value var below,
|
||||
# taking into account compatibility for Qt Creator, etc.
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_relative_installed_repo_document_path
|
||||
"${repo_spdx_relative_install_path_spdx}")
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_document_spdx_v2_tag_value_relative_path
|
||||
"${repo_spdx_relative_install_path_spdx}")
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_document_spdx_v2_json_relative_path
|
||||
"${repo_spdx_relative_install_path_spdx_json}")
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_document_cydx_v1_6_json_relative_path
|
||||
"${repo_spdx_relative_install_path_cydx_json}")
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_project_name_lowercase
|
||||
"${repo_project_name_lowercase}")
|
||||
|
||||
@@ -365,6 +435,8 @@ function(_qt_internal_sbom_begin_project)
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_project_spdx_id
|
||||
"${repo_project_spdx_id}")
|
||||
|
||||
_qt_internal_create_project_sbom_target()
|
||||
|
||||
# Collect project licenses.
|
||||
set(license_dirs "")
|
||||
|
||||
@@ -413,6 +485,10 @@ function(_qt_internal_sbom_begin_project)
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_begin_called TRUE)
|
||||
|
||||
_qt_internal_sbom_setup_project_ops()
|
||||
|
||||
if(NOT arg_NO_AUTO_ADD_BUILD_TOOLS)
|
||||
_qt_internal_sbom_add_project_default_build_tools()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Check various internal options to decide which sbom generation operations should be setup.
|
||||
@@ -774,9 +850,13 @@ function(_qt_internal_sbom_end_project)
|
||||
set_property(GLOBAL PROPERTY _qt_known_external_documents "")
|
||||
foreach(external_document IN LISTS known_external_documents)
|
||||
set_property(GLOBAL PROPERTY _qt_known_external_documents_${external_document} "")
|
||||
set_property(GLOBAL PROPERTY _qt_known_external_documents_${external_document}_target "")
|
||||
endforeach()
|
||||
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_begin_called FALSE)
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_repo_spdx_id_unique_suffix "")
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_external_document_search_paths "")
|
||||
set_property(GLOBAL PROPERTY _qt_internal_sbom_auto_search_external_documents_in_paths "")
|
||||
|
||||
# Add configure-time dependency on project attribution files.
|
||||
get_property(attribution_files GLOBAL PROPERTY _qt_internal_project_attribution_files)
|
||||
@@ -937,6 +1017,7 @@ macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi
|
||||
NO_DEFAULT_QT_SUPPLIER
|
||||
SBOM_INCOMPLETE_3RD_PARTY_DEPENDENCIES
|
||||
IS_QT_3RD_PARTY_HEADER_MODULE
|
||||
IS_EXTERNAL_SBOM_ENTITY
|
||||
USE_ATTRIBUTION_FILES
|
||||
CREATE_SBOM_FOR_EACH_ATTRIBUTION
|
||||
__QT_INTERNAL_HANDLE_QT_ENTITY_TYPE_PACKAGE_VERSION
|
||||
@@ -948,9 +1029,12 @@ macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi
|
||||
__QT_INTERNAL_HANDLE_QT_ENTITY_ATTRIBUTION_FILES
|
||||
)
|
||||
set(${single_args}
|
||||
SPDX_ID
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET
|
||||
DEFAULT_SBOM_ENTITY_TYPE
|
||||
SBOM_ENTITY_TYPE
|
||||
PACKAGE_VERSION
|
||||
PACKAGE_SUMMARY
|
||||
FRIENDLY_PACKAGE_NAME
|
||||
SUPPLIER
|
||||
CPE_VENDOR
|
||||
@@ -970,6 +1054,9 @@ macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi
|
||||
ATTRIBUTION_FILE_PATHS
|
||||
ATTRIBUTION_FILE_DIR_PATHS
|
||||
ATTRIBUTION_IDS
|
||||
SBOM_RELATIONSHIP_ENTRIES
|
||||
# deprecated, previously used for SPDX v2 string-ified relationships
|
||||
# still used by WebEngine.
|
||||
SBOM_RELATIONSHIPS
|
||||
)
|
||||
|
||||
@@ -1114,13 +1201,7 @@ function(_qt_internal_sbom_add_target target)
|
||||
string(APPEND package_comment "Contained in CMake package: ${qt_package_name}\n")
|
||||
endif()
|
||||
|
||||
# Record the target spdx id right now, so we can refer to it in later attribution targets
|
||||
# if needed.
|
||||
_qt_internal_sbom_record_target_spdx_id(${target}
|
||||
SBOM_ENTITY_TYPE "${sbom_entity_type}"
|
||||
PACKAGE_NAME "${package_name_for_spdx_id}"
|
||||
OUT_VAR package_spdx_id
|
||||
)
|
||||
_qt_internal_sbom_get_spdx_id_for_target("${target}" package_spdx_id)
|
||||
|
||||
if(arg_USE_ATTRIBUTION_FILES)
|
||||
set(attribution_args
|
||||
@@ -1471,35 +1552,39 @@ function(_qt_internal_sbom_add_target target)
|
||||
list(APPEND project_package_options_cydx COMMENT "\n${package_comment}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_handle_target_dependencies("${target}"
|
||||
SPDX_ID "${package_spdx_id}"
|
||||
LIBRARIES "${arg_LIBRARIES}"
|
||||
PUBLIC_LIBRARIES "${arg_PUBLIC_LIBRARIES}"
|
||||
OUT_CYDX_DEPENDENCIES cydx_dependencies
|
||||
OUT_SPDX_RELATIONSHIPS spdx_relationships
|
||||
OUT_EXTERNAL_TARGET_DEPENDENCIES external_target_dependencies
|
||||
)
|
||||
if(cydx_dependencies)
|
||||
list(APPEND project_package_options_cydx DEPENDENCIES ${cydx_dependencies})
|
||||
endif()
|
||||
|
||||
# These are processed at the end of document generation.
|
||||
if(external_target_dependencies)
|
||||
_qt_internal_sbom_record_external_target_dependecies(
|
||||
TARGETS ${external_target_dependencies}
|
||||
)
|
||||
endif()
|
||||
|
||||
get_cmake_property(project_spdx_id _qt_internal_sbom_project_spdx_id)
|
||||
list(APPEND spdx_relationships "${project_spdx_id} CONTAINS ${package_spdx_id}")
|
||||
|
||||
if(arg_SBOM_RELATIONSHIPS)
|
||||
list(APPEND spdx_relationships "${arg_SBOM_RELATIONSHIPS}")
|
||||
_qt_internal_forward_function_args(
|
||||
FORWARD_PREFIX arg
|
||||
FORWARD_OUT_VAR relationship_args
|
||||
FORWARD_MULTI
|
||||
LIBRARIES
|
||||
PUBLIC_LIBRARIES
|
||||
SBOM_DEPENDENCIES
|
||||
SBOM_RELATIONSHIP_ENTRIES
|
||||
SBOM_RELATIONSHIPS # deprecated, still used by WebEngine
|
||||
)
|
||||
|
||||
_qt_internal_sbom_handle_target_relationships("${target}"
|
||||
SPDX_ID "${package_spdx_id}"
|
||||
PROJECT_SPDX_ID "${project_spdx_id}"
|
||||
${relationship_args}
|
||||
OUT_VAR_SBOM_RELATIONSHIP_ENTRIES sbom_relationship_entries
|
||||
OUT_VAR_SPDX_V2_RELATIONSHIPS spdx_relationships # deprecated, still used by WebEngine
|
||||
)
|
||||
if(sbom_relationship_entries)
|
||||
list(APPEND project_package_options_cydx
|
||||
SBOM_RELATIONSHIP_ENTRIES ${sbom_relationship_entries})
|
||||
list(APPEND project_package_options_spdx
|
||||
SBOM_RELATIONSHIP_ENTRIES ${sbom_relationship_entries})
|
||||
endif()
|
||||
if(spdx_relationships)
|
||||
list(APPEND project_package_options_spdx RELATIONSHIPS ${spdx_relationships})
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES spdx_relationships)
|
||||
list(JOIN spdx_relationships "\nRelationship: " relationships)
|
||||
list(APPEND project_package_options_spdx RELATIONSHIP "${relationships}")
|
||||
if(arg_PACKAGE_SUMMARY)
|
||||
list(APPEND project_package_options_spdx PACKAGE_SUMMARY "${arg_PACKAGE_SUMMARY}")
|
||||
endif()
|
||||
|
||||
if(QT_SBOM_GENERATE_SPDX_V2)
|
||||
_qt_internal_sbom_generate_add_package(
|
||||
@@ -1604,9 +1689,7 @@ function(_qt_internal_add_sbom target)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(opt_args
|
||||
IMMEDIATE_FINALIZATION
|
||||
)
|
||||
set(opt_args "")
|
||||
set(single_args "")
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -1619,10 +1702,6 @@ function(_qt_internal_add_sbom target)
|
||||
_qt_internal_create_sbom_target("${target}" ${forward_args})
|
||||
endif()
|
||||
|
||||
if(arg_IMMEDIATE_FINALIZATION)
|
||||
list(APPEND forward_args IMMEDIATE_FINALIZATION)
|
||||
endif()
|
||||
|
||||
# Save the passed options.
|
||||
_qt_internal_extend_sbom("${target}" ${forward_args})
|
||||
endfunction()
|
||||
@@ -1654,9 +1733,99 @@ function(_qt_internal_create_sbom_target target)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Creates a custom target to represent a project's root SBOM package / component.
|
||||
# For SPDX it represents what we consider the root package, although the spec doesn't define
|
||||
# any such package.
|
||||
# For CYDX it represents the root component, which is defined by the spec.
|
||||
# The target is currently intended to be used in sbom relationship entries, but might be expanded
|
||||
# with further information.
|
||||
function(_qt_internal_create_project_sbom_target)
|
||||
if(NOT QT_GENERATE_SBOM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(opt_args "")
|
||||
set(single_args "")
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
_qt_internal_sbom_get_current_project_target(target)
|
||||
if(TARGET "${target}")
|
||||
message(FATAL_ERROR "The target ${target} already exists.")
|
||||
endif()
|
||||
|
||||
add_library("${target}" INTERFACE IMPORTED)
|
||||
|
||||
get_property(project_spdx_id GLOBAL PROPERTY _qt_internal_sbom_project_spdx_id)
|
||||
if(NOT project_spdx_id)
|
||||
message(FATAL_ERROR "The global property _qt_internal_sbom_project_spdx_id was not set, "
|
||||
"which is required to create a project sbom target.")
|
||||
endif()
|
||||
|
||||
get_property(repo_document_namespace
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_document_namespace)
|
||||
|
||||
if(NOT repo_document_namespace)
|
||||
message(FATAL_ERROR "The global property _qt_internal_sbom_repo_document_namespace was not"
|
||||
" set, which is required to create a project sbom target.")
|
||||
endif()
|
||||
|
||||
get_property(bom_serial_number_uuid
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_cyclone_dx_bom_serial_number_uuid)
|
||||
|
||||
if(NOT bom_serial_number_uuid)
|
||||
message(FATAL_ERROR "The global property "
|
||||
"_qt_internal_sbom_repo_cyclone_dx_bom_serial_number_uuid"
|
||||
" was not set, which is required to create a project sbom target.")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id(
|
||||
"${arg_PROJECT_NAME}" external_document_ref)
|
||||
|
||||
set_target_properties("${target}" PROPERTIES
|
||||
IMPORTED_GLOBAL TRUE
|
||||
|
||||
_qt_sbom_is_custom_sbom_target "TRUE"
|
||||
_qt_sbom_is_project_sbom_target "TRUE"
|
||||
|
||||
_qt_sbom_spdx_id "${project_spdx_id}"
|
||||
_qt_sbom_entity_type "SBOM_PROJECT"
|
||||
_qt_sbom_spdx_repo_document_namespace "${repo_document_namespace}"
|
||||
_qt_sbom_spdx_v2_external_document_ref "${external_document_ref}"
|
||||
_qt_sbom_cydx_bom_serial_number_uuid "${bom_serial_number_uuid}"
|
||||
_qt_sbom_spdx_repo_project_name_lowercase "${project_name}"
|
||||
)
|
||||
|
||||
# The computation for these relies on the previous ones being set.
|
||||
_qt_internal_sbom_compute_external_spdx_v2_id("${target}" external_spdx_v2_id)
|
||||
_qt_internal_sbom_get_cydx_external_bom_link("${target}" external_bom_link)
|
||||
|
||||
set_target_properties("${target}" PROPERTIES
|
||||
_qt_sbom_spdx_v2_external_spdx_id "${external_spdx_v2_id}"
|
||||
_qt_sbom_cydx_external_bom_link "${external_bom_link}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Returns the target name representing the current sbom project.
|
||||
# When SBOM generation is disable, returns an empty string.
|
||||
function(_qt_internal_sbom_get_current_project_target out_var)
|
||||
if(NOT QT_GENERATE_SBOM)
|
||||
set(${out_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_lower_case(project_name)
|
||||
|
||||
set(target "${project_name}ProjectSbom")
|
||||
set(${out_var} "${target}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Helper to add additional sbom information for an existing target.
|
||||
# Just appends the options to the target's sbom args property, which will will be evaluated
|
||||
# during finalization.
|
||||
# For external sbom targets there is no finalization, so the relevant info is recorded
|
||||
# immediately.
|
||||
function(_qt_internal_extend_sbom target)
|
||||
if(NOT QT_GENERATE_SBOM)
|
||||
return()
|
||||
@@ -1671,12 +1840,15 @@ function(_qt_internal_extend_sbom target)
|
||||
set(opt_args
|
||||
NO_FINALIZATION
|
||||
IMMEDIATE_FINALIZATION
|
||||
IS_EXTERNAL_SBOM_ENTITY
|
||||
)
|
||||
set(single_args
|
||||
TYPE # deprecated
|
||||
SBOM_ENTITY_TYPE
|
||||
DEFAULT_SBOM_ENTITY_TYPE
|
||||
FRIENDLY_PACKAGE_NAME
|
||||
SPDX_ID
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -1692,29 +1864,69 @@ function(_qt_internal_extend_sbom target)
|
||||
|
||||
_qt_internal_map_sbom_entity_type(sbom_entity_type ${ARGN})
|
||||
|
||||
# Make sure a spdx id is recorded for the target right now, so it is "known" when handling
|
||||
# relationships for other targets, even if the target was not yet finalized.
|
||||
if(sbom_entity_type)
|
||||
# Friendly package name is allowed to be empty.
|
||||
set(package_name_option "")
|
||||
if(arg_FRIENDLY_PACKAGE_NAME)
|
||||
set(package_name_option PACKAGE_NAME "${arg_FRIENDLY_PACKAGE_NAME}")
|
||||
# Make sure the spdx id and sbom entity type is recorded for the target right now, so it is
|
||||
# "known" when handling relationships for other targets, even if the target was not yet
|
||||
# finalized.
|
||||
# The SBOM entity type is expected to be passed when creating a new SBOM target, or extending
|
||||
# an existing target for the first time with SBOM info.
|
||||
_qt_internal_sbom_get_spdx_id_for_target("${target}" spdx_id)
|
||||
if(NOT spdx_id)
|
||||
set(record_spdx_id_args "")
|
||||
|
||||
# Isn't allowed to be empty when creating the spdx id.
|
||||
if(sbom_entity_type)
|
||||
list(APPEND record_spdx_id_args SBOM_ENTITY_TYPE "${sbom_entity_type}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Target '${target}' is missing an SBOM_ENTITY_TYPE value. "
|
||||
"Make sure to pass it when creating new SBOM target or extending an existing "
|
||||
"target with SBOM infromation for the first time.")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_record_target_spdx_id(${target}
|
||||
SBOM_ENTITY_TYPE "${sbom_entity_type}"
|
||||
${package_name_option}
|
||||
)
|
||||
# Friendly package name is allowed to be empty.
|
||||
if(arg_FRIENDLY_PACKAGE_NAME)
|
||||
list(APPEND record_spdx_id_args PACKAGE_NAME "${arg_FRIENDLY_PACKAGE_NAME}")
|
||||
endif()
|
||||
|
||||
# Allow specifying a custom spdx id, even for non-external targets.
|
||||
if(arg_SPDX_ID)
|
||||
list(APPEND record_spdx_id_args SPDX_ID "${arg_SPDX_ID}")
|
||||
endif()
|
||||
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
list(APPEND record_spdx_id_args IS_EXTERNAL_SBOM_ENTITY)
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_SBOM_DOCUMENT_TARGET)
|
||||
list(APPEND record_spdx_id_args
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET ${arg_EXTERNAL_SBOM_DOCUMENT_TARGET})
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_record_target_spdx_id(${target} ${record_spdx_id_args})
|
||||
endif()
|
||||
|
||||
get_target_property(is_system_library "${target}" _qt_internal_sbom_is_system_library)
|
||||
get_target_property(is_project_sbom_target "${target}" _qt_sbom_is_project_sbom_target)
|
||||
|
||||
set_property(TARGET ${target} APPEND PROPERTY _qt_finalize_sbom_args "${forward_args}")
|
||||
|
||||
# Record the target's relevant properties immediately after they are saved above, so we can
|
||||
# refer to it in relationship entries.
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY AND QT_SBOM_GENERATE_CYDX_V1_6)
|
||||
_qt_internal_sbom_record_external_target_dependecies(TARGETS "${target}")
|
||||
endif()
|
||||
|
||||
# If requested via NO_FINALIZATION or the target is a system library, don't run finalization.
|
||||
# This is necessary for system libraries because those are handled in special code path just
|
||||
# before finishing project sbom generation, and finalizing them would cause issues because they
|
||||
# don't actually have a TYPE until a later point in time.
|
||||
if(NOT arg_NO_FINALIZATION AND NOT is_system_library)
|
||||
# Also skip regular processing / finalization for external targets, because their handling
|
||||
# is different, and we don't create a regular package for them.
|
||||
if(NOT arg_NO_FINALIZATION
|
||||
AND NOT is_system_library
|
||||
AND NOT is_project_sbom_target
|
||||
AND NOT arg_IS_EXTERNAL_SBOM_ENTITY
|
||||
)
|
||||
# Defer finalization. In case it was already deferred, it will be a no-op.
|
||||
# Some targets need immediate finalization, like the PlatformInternal ones,
|
||||
# because otherwise they would be finalized after the sbom was already generated.
|
||||
@@ -1884,7 +2096,7 @@ function(_qt_internal_sbom_get_root_project_name_lower_case out_var)
|
||||
get_cmake_property(project_name _qt_internal_sbom_repo_project_name)
|
||||
|
||||
if(NOT project_name)
|
||||
message(FATAL_ERROR "No SBOM project name was set.")
|
||||
message(FATAL_ERROR "The current active SBOM project name was not found.")
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${project_name}" repo_project_name_lowercase)
|
||||
@@ -1904,9 +2116,110 @@ function(_qt_internal_sbom_get_qt_repo_project_name_lower_case out_var)
|
||||
set(${out_var} "${repo_project_name_lowercase}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Get a spdx id to reference an external document.
|
||||
# Compute a SPDX v2.3 DocumentRef ID, to reference the current project's SPDX document as an
|
||||
# external document in another project.
|
||||
#
|
||||
# This is only meant to be used for exporting the value as part of the target's properties, to be
|
||||
# used in a different project.
|
||||
#
|
||||
# It should NOT be used to refer to already exported targets.
|
||||
#
|
||||
# For querying the DocumentRef of an exported target, use
|
||||
# _qt_internal_sbom_get_external_document_ref_spdx_id_from_sbom_target.
|
||||
function(_qt_internal_sbom_compute_external_document_ref_spdx_id repo_name out_var)
|
||||
_qt_internal_sbom_get_spdx_id_unique_suffix(spdx_id_unique_suffix)
|
||||
set(${out_var} "DocumentRef-${repo_name}${spdx_id_unique_suffix}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Older deprecated function name of the function above, kept for compatibility in case it is used.
|
||||
function(_qt_internal_sbom_get_external_document_ref_spdx_id repo_name out_var)
|
||||
set(${out_var} "DocumentRef-${repo_name}" PARENT_SCOPE)
|
||||
if(NOT QT_NO_DEPRECATED_GET_EXTERNAL_DOCUMENT_REF_SPDX_ID)
|
||||
message(DEPRECATION
|
||||
"This function is deprecated. "
|
||||
"Please use _qt_internal_sbom_compute_external_document_ref_spdx_id() instead."
|
||||
"To silence this deprecation, pass "
|
||||
"-DQT_NO_DEPRECATED_GET_EXTERNAL_DOCUMENT_REF_SPDX_ID=ON "
|
||||
"when configuring the project."
|
||||
)
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id("${repo_name}" external_document_ref)
|
||||
|
||||
set(${out_var} "${external_document_ref}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Query the external reference DocumentRef ID from the given SBOM target.
|
||||
#
|
||||
# If CREATE_TEMPORARY_REF_WHEN_MISSING is set, it instead of erroring out when the document ref
|
||||
# isn't found, create one and assign it to the to the usual target property.
|
||||
# This allows referring to older targets that were exported without the necessary properties.
|
||||
function(_qt_internal_sbom_get_external_document_ref_spdx_id_from_sbom_target)
|
||||
if(NOT QT_GENERATE_SBOM)
|
||||
set(${out_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(opt_args
|
||||
CREATE_TEMPORARY_REF_WHEN_MISSING
|
||||
)
|
||||
set(single_args
|
||||
TARGET
|
||||
OUT_VAR
|
||||
)
|
||||
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_TARGET)
|
||||
message(FATAL_ERROR "TARGET argument is required.")
|
||||
endif()
|
||||
|
||||
if(NOT arg_OUT_VAR)
|
||||
message(FATAL_ERROR "OUT_VAR argument is required.")
|
||||
endif()
|
||||
|
||||
set(target_unaliased "${arg_TARGET}")
|
||||
_qt_internal_dealias_target(target_unaliased)
|
||||
|
||||
get_target_property(external_document_ref
|
||||
"${target_unaliased}" _qt_sbom_spdx_v2_external_document_ref)
|
||||
|
||||
if(NOT external_document_ref)
|
||||
if(NOT arg_CREATE_TEMPORARY_REF_WHEN_MISSING
|
||||
AND NOT QT_SBOM_NO_AUTO_CREATE_DOCUMENT_REF_WHEN_MISSING)
|
||||
message(FATAL_ERROR
|
||||
"The target '${arg_TARGET}' does not have a recorded SPDX v2.3 external document "
|
||||
"DocumentRef value recorded.")
|
||||
else()
|
||||
string(MAKE_C_IDENTIFIER "${arg_TARGET}" target_c_identifier)
|
||||
string(REPLACE "_" "-" target_c_identifier "${target_c_identifier}")
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id("${target_c_identifier}"
|
||||
external_document_ref
|
||||
)
|
||||
set_target_properties("${target_unaliased}" PROPERTIES
|
||||
_qt_sbom_spdx_v2_external_document_ref "${external_document_ref}")
|
||||
|
||||
message(DEBUG "Did not find external document ref for target '${arg_TARGET}'. "
|
||||
"Created one and set it to '${external_document_ref}' in the target's properties.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${arg_OUT_VAR} "${external_document_ref}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Computes a spdx id to reference the target's spdx v2 package via a SPDX v2 external document ref.
|
||||
# To be used for exporting the value as part of the target's metadata.
|
||||
function(_qt_internal_sbom_compute_external_spdx_v2_id target out_var)
|
||||
get_target_property(project_name_lowercase "${target}"
|
||||
_qt_sbom_spdx_repo_project_name_lowercase)
|
||||
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id(
|
||||
"${project_name_lowercase}" external_document_ref)
|
||||
|
||||
_qt_internal_sbom_get_spdx_id_for_target("${target}" spdx_id)
|
||||
|
||||
set(ext_spdx_id "${external_document_ref}:${spdx_id}")
|
||||
set(${out_var} "${ext_spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Sanitize a given value to be used as a SPDX id.
|
||||
@@ -1920,12 +2233,21 @@ function(_qt_internal_sbom_get_sanitized_spdx_id out_var hint)
|
||||
set(${out_var} "${spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Generates a spdx id for a target and saves it its properties.
|
||||
# Generates a spdx id for a target and saves it, the passed in SBOM_ENTITY_TYPE value, and a lot
|
||||
# of other project specific properties like the spdx document namespace, cyclone dx document serial
|
||||
# number, etc, in its target properties.
|
||||
# A SBOM_ENTITY_TYPE value is required when saving the spdx id.
|
||||
|
||||
# Exits early and returns the spdx id if it was already created earlier.
|
||||
function(_qt_internal_sbom_record_target_spdx_id target)
|
||||
set(opt_args "")
|
||||
set(opt_args
|
||||
IS_EXTERNAL_SBOM_ENTITY
|
||||
)
|
||||
set(single_args
|
||||
SPDX_ID
|
||||
PACKAGE_NAME
|
||||
SBOM_ENTITY_TYPE
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET
|
||||
OUT_VAR
|
||||
)
|
||||
set(multi_args "")
|
||||
@@ -1948,14 +2270,32 @@ function(_qt_internal_sbom_record_target_spdx_id target)
|
||||
set(package_name_for_spdx_id "${target}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_generate_target_package_spdx_id(package_spdx_id
|
||||
SBOM_ENTITY_TYPE "${arg_SBOM_ENTITY_TYPE}"
|
||||
PACKAGE_NAME "${package_name_for_spdx_id}"
|
||||
)
|
||||
# If an explicit SPDX_ID is set, use it rather than generating one.
|
||||
if(arg_SPDX_ID)
|
||||
set(package_spdx_id "${arg_SPDX_ID}")
|
||||
else()
|
||||
_qt_internal_sbom_generate_target_package_spdx_id(package_spdx_id
|
||||
SBOM_ENTITY_TYPE "${arg_SBOM_ENTITY_TYPE}"
|
||||
PACKAGE_NAME "${package_name_for_spdx_id}"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(save_spdx_id_args "")
|
||||
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
list(APPEND save_spdx_id_args IS_EXTERNAL_SBOM_ENTITY)
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_SBOM_DOCUMENT_TARGET)
|
||||
list(APPEND save_spdx_id_args
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET ${arg_EXTERNAL_SBOM_DOCUMENT_TARGET})
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_save_spdx_id_for_target("${target}"
|
||||
SPDX_ID "${package_spdx_id}"
|
||||
PACKAGE_NAME "${package_name_for_spdx_id}"
|
||||
SBOM_ENTITY_TYPE "${arg_SBOM_ENTITY_TYPE}"
|
||||
${save_spdx_id_args}
|
||||
)
|
||||
|
||||
_qt_internal_sbom_is_qt_entity_type("${arg_SBOM_ENTITY_TYPE}" is_qt_entity_type)
|
||||
@@ -1987,9 +2327,13 @@ function(_qt_internal_sbom_generate_target_package_spdx_id out_var)
|
||||
|
||||
_qt_internal_sbom_get_root_project_name_for_spdx_id(repo_project_name_spdx_id)
|
||||
_qt_internal_sbom_get_package_infix("${arg_SBOM_ENTITY_TYPE}" package_infix)
|
||||
_qt_internal_sbom_get_spdx_id_unique_suffix(spdx_id_unique_suffix)
|
||||
|
||||
_qt_internal_sbom_get_sanitized_spdx_id(spdx_id
|
||||
"SPDXRef-${repo_project_name_spdx_id}-${package_infix}-${arg_PACKAGE_NAME}")
|
||||
string(CONCAT spdx_id_input
|
||||
"SPDXRef-${repo_project_name_spdx_id}-${package_infix}-${arg_PACKAGE_NAME}"
|
||||
"${spdx_id_unique_suffix}"
|
||||
)
|
||||
_qt_internal_sbom_get_sanitized_spdx_id(spdx_id "${spdx_id_input}")
|
||||
|
||||
set(${out_var} "${spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -2001,11 +2345,14 @@ endfunction()
|
||||
# Also saves the sbom entity type and package name, because it's needed when creating CycloneDX
|
||||
# components where the target is in an external document.
|
||||
function(_qt_internal_sbom_save_spdx_id_for_target target)
|
||||
set(opt_args "")
|
||||
set(opt_args
|
||||
IS_EXTERNAL_SBOM_ENTITY
|
||||
)
|
||||
set(single_args
|
||||
SPDX_ID
|
||||
PACKAGE_NAME
|
||||
SBOM_ENTITY_TYPE
|
||||
EXTERNAL_SBOM_DOCUMENT_TARGET
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -2023,6 +2370,32 @@ function(_qt_internal_sbom_save_spdx_id_for_target target)
|
||||
message(FATAL_ERROR "SBOM_ENTITY_TYPE must be set")
|
||||
endif()
|
||||
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY AND NOT arg_EXTERNAL_SBOM_DOCUMENT_TARGET)
|
||||
message(FATAL_ERROR
|
||||
"Target '${target}' is marked as external with the IS_EXTERNAL_SBOM_ENTITY option "
|
||||
"but no EXTERNAL_SBOM_DOCUMENT_TARGET was provided. Make sure to pass in a "
|
||||
"an external sbom document target created by "
|
||||
"_qt_internal_sbom_add_external_reference_document() to the mentioned option.")
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_SBOM_DOCUMENT_TARGET AND NOT TARGET "${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}")
|
||||
message(FATAL_ERROR
|
||||
"The target '${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}' passed to "
|
||||
"EXTERNAL_SBOM_DOCUMENT_TARGET does not exist.")
|
||||
endif()
|
||||
|
||||
if(arg_EXTERNAL_SBOM_DOCUMENT_TARGET)
|
||||
get_target_property(is_external_project_sbom_target "${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}"
|
||||
_qt_sbom_is_external_project_sbom_target)
|
||||
if(NOT is_external_project_sbom_target)
|
||||
message(FATAL_ERROR
|
||||
"The target '${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}' passed to "
|
||||
"EXTERNAL_SBOM_DOCUMENT_TARGET is not marked as an external project sbom target. "
|
||||
"Make sure to create the target with "
|
||||
"_qt_internal_sbom_add_external_reference_document().")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(spdx_id "${arg_SPDX_ID}")
|
||||
|
||||
message(DEBUG "Saving spdx id for target ${target}: ${spdx_id}")
|
||||
@@ -2033,18 +2406,50 @@ function(_qt_internal_sbom_save_spdx_id_for_target target)
|
||||
set_target_properties(${target_unaliased} PROPERTIES
|
||||
_qt_sbom_spdx_id "${spdx_id}")
|
||||
|
||||
# Retrieve repo specific properties.
|
||||
get_property(repo_document_namespace
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_document_namespace)
|
||||
# Retrieve some of the repo / project specific properties.
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_target_property(repo_document_namespace
|
||||
"${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}" _qt_sbom_spdx_repo_document_namespace)
|
||||
else()
|
||||
get_property(repo_document_namespace
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_document_namespace)
|
||||
endif()
|
||||
|
||||
get_property(bom_serial_number_uuid
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_cyclone_dx_bom_serial_number_uuid)
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_target_property(bom_serial_number_uuid
|
||||
"${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}" _qt_sbom_cydx_bom_serial_number_uuid)
|
||||
else()
|
||||
get_property(bom_serial_number_uuid
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_cyclone_dx_bom_serial_number_uuid)
|
||||
endif()
|
||||
|
||||
get_property(relative_installed_repo_document_path
|
||||
GLOBAL PROPERTY _qt_internal_sbom_relative_installed_repo_document_path)
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_target_property(relative_installed_repo_document_path
|
||||
"${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}"
|
||||
_qt_sbom_spdx_relative_installed_repo_document_path)
|
||||
else()
|
||||
get_property(relative_installed_repo_document_path
|
||||
GLOBAL PROPERTY _qt_internal_sbom_relative_installed_repo_document_path)
|
||||
endif()
|
||||
|
||||
get_property(project_name_lowercase
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_project_name_lowercase)
|
||||
if(NOT arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_property(document_spdx_v2_tag_value_relative_path
|
||||
GLOBAL PROPERTY _qt_internal_sbom_document_spdx_v2_tag_value_relative_path)
|
||||
|
||||
get_property(document_spdx_v2_json_relative_path
|
||||
GLOBAL PROPERTY _qt_internal_sbom_document_spdx_v2_json_relative_path)
|
||||
|
||||
get_property(document_cydx_v1_6_json_relative_path
|
||||
GLOBAL PROPERTY _qt_internal_sbom_document_cydx_v1_6_json_relative_path)
|
||||
endif()
|
||||
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_target_property(project_name_lowercase
|
||||
"${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}" _qt_sbom_spdx_repo_project_name_lowercase)
|
||||
else()
|
||||
get_property(project_name_lowercase
|
||||
GLOBAL PROPERTY _qt_internal_sbom_repo_project_name_lowercase)
|
||||
endif()
|
||||
|
||||
# And save them on the target.
|
||||
set_property(TARGET ${target_unaliased} PROPERTY
|
||||
@@ -2055,6 +2460,20 @@ function(_qt_internal_sbom_save_spdx_id_for_target target)
|
||||
_qt_sbom_cydx_bom_serial_number_uuid
|
||||
"${bom_serial_number_uuid}")
|
||||
|
||||
if(NOT arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
set_property(TARGET ${target_unaliased} PROPERTY
|
||||
_qt_sbom_spdx_v2_document_tag_value_relative_path
|
||||
"${document_spdx_v2_tag_value_relative_path}")
|
||||
|
||||
set_property(TARGET ${target_unaliased} PROPERTY
|
||||
_qt_sbom_spdx_v2_document_json_relative_path
|
||||
"${document_spdx_v2_json_relative_path}")
|
||||
|
||||
set_property(TARGET ${target_unaliased} PROPERTY
|
||||
_qt_sbom_cydx_v1_6_document_json_relative_path
|
||||
"${document_cydx_v1_6_json_relative_path}")
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${target_unaliased} PROPERTY
|
||||
_qt_sbom_spdx_relative_installed_repo_document_path
|
||||
"${relative_installed_repo_document_path}")
|
||||
@@ -2071,19 +2490,53 @@ function(_qt_internal_sbom_save_spdx_id_for_target target)
|
||||
_qt_sbom_entity_type
|
||||
"${arg_SBOM_ENTITY_TYPE}")
|
||||
|
||||
# Export the properties, so they can be queried by other repos.
|
||||
# We also do it for versionless targets.
|
||||
set(export_properties
|
||||
_qt_sbom_entity_type
|
||||
_qt_sbom_package_name
|
||||
_qt_sbom_spdx_id
|
||||
_qt_sbom_spdx_repo_document_namespace
|
||||
_qt_sbom_cydx_bom_serial_number_uuid
|
||||
_qt_sbom_spdx_relative_installed_repo_document_path
|
||||
_qt_sbom_spdx_repo_project_name_lowercase
|
||||
)
|
||||
set_property(TARGET "${target_unaliased}" APPEND PROPERTY
|
||||
EXPORT_PROPERTIES "${export_properties}")
|
||||
if(arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
get_target_property(external_document_ref
|
||||
"${arg_EXTERNAL_SBOM_DOCUMENT_TARGET}" _qt_sbom_spdx_v2_external_document_ref)
|
||||
else()
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id(
|
||||
"${project_name_lowercase}" external_document_ref)
|
||||
endif()
|
||||
|
||||
set_property(TARGET "${target_unaliased}" PROPERTY
|
||||
_qt_sbom_spdx_v2_external_document_ref "${external_document_ref}")
|
||||
|
||||
_qt_internal_sbom_compute_external_spdx_v2_id("${target_unaliased}" external_spdx_v2_id)
|
||||
|
||||
set_property(TARGET "${target_unaliased}" PROPERTY
|
||||
_qt_sbom_spdx_v2_external_spdx_id "${external_spdx_v2_id}")
|
||||
|
||||
_qt_internal_sbom_get_cydx_external_bom_link("${target_unaliased}" external_bom_link)
|
||||
set_property(TARGET "${target_unaliased}" PROPERTY
|
||||
_qt_sbom_cydx_external_bom_link "${external_bom_link}")
|
||||
|
||||
_qt_internal_sbom_get_cydx_external_urn_bom_version("${target_unaliased}"
|
||||
external_urn_bom_version)
|
||||
set_property(TARGET "${target_unaliased}" PROPERTY
|
||||
_qt_sbom_cydx_external_urn_bom_version "${external_urn_bom_version}")
|
||||
|
||||
if(NOT arg_IS_EXTERNAL_SBOM_ENTITY)
|
||||
# Export the properties, so they can be queried by other repos.
|
||||
# We also do it for versionless targets.
|
||||
set(export_properties
|
||||
_qt_sbom_entity_type
|
||||
_qt_sbom_package_name
|
||||
_qt_sbom_spdx_id
|
||||
_qt_sbom_spdx_repo_document_namespace
|
||||
_qt_sbom_spdx_v2_external_document_ref
|
||||
_qt_sbom_spdx_v2_external_spdx_id
|
||||
_qt_sbom_spdx_v2_document_tag_value_relative_path
|
||||
_qt_sbom_spdx_v2_document_json_relative_path
|
||||
_qt_sbom_cydx_bom_serial_number_uuid
|
||||
_qt_sbom_cydx_external_bom_link
|
||||
_qt_sbom_cydx_external_urn_bom_version
|
||||
_qt_sbom_cydx_v1_6_document_json_relative_path
|
||||
_qt_sbom_spdx_relative_installed_repo_document_path
|
||||
_qt_sbom_spdx_repo_project_name_lowercase
|
||||
)
|
||||
set_property(TARGET "${target_unaliased}" APPEND PROPERTY
|
||||
EXPORT_PROPERTIES "${export_properties}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Returns whether the given sbom type is considered to be a Qt type like a module or a tool.
|
||||
@@ -2165,6 +2618,12 @@ function(_qt_internal_sbom_get_spdx_id_for_target target out_var)
|
||||
set(${out_var} "${spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Retrieves a saved spdx id for the current project. Might be empty.
|
||||
function(_qt_internal_sbom_get_current_project_spdx_id out_var)
|
||||
get_cmake_property(spdx_id _qt_internal_sbom_project_spdx_id)
|
||||
set(${out_var} "${spdx_id}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Returns a package infix for a given target sbom type to be used in spdx package id generation.
|
||||
function(_qt_internal_sbom_get_package_infix type out_infix)
|
||||
if(type STREQUAL "QT_MODULE")
|
||||
@@ -2201,10 +2660,14 @@ function(_qt_internal_sbom_get_package_infix type out_infix)
|
||||
set(package_infix "3rdparty-library-with-files")
|
||||
elseif(type STREQUAL "THIRD_PARTY_SOURCES")
|
||||
set(package_infix "3rdparty-sources")
|
||||
elseif(type STREQUAL "SBOM_PROJECT")
|
||||
set(package_infix "sbom-project")
|
||||
elseif(type STREQUAL "TRANSLATIONS")
|
||||
set(package_infix "translations")
|
||||
elseif(type STREQUAL "RESOURCES")
|
||||
set(package_infix "resource")
|
||||
elseif(type STREQUAL "BUILD_TOOL")
|
||||
set(package_infix "build-tool")
|
||||
elseif(type STREQUAL "CUSTOM")
|
||||
set(package_infix "custom")
|
||||
elseif(type STREQUAL "CUSTOM_NO_INFIX")
|
||||
@@ -2252,10 +2715,14 @@ function(_qt_internal_sbom_get_package_purpose type out_purpose)
|
||||
set(package_purpose "LIBRARY")
|
||||
elseif(type STREQUAL "THIRD_PARTY_SOURCES")
|
||||
set(package_purpose "LIBRARY")
|
||||
elseif(type STREQUAL "SBOM_PROJECT")
|
||||
set(package_purpose "OTHER")
|
||||
elseif(type STREQUAL "TRANSLATIONS")
|
||||
set(package_purpose "OTHER")
|
||||
elseif(type STREQUAL "RESOURCES")
|
||||
set(package_purpose "OTHER")
|
||||
elseif(type STREQUAL "BUILD_TOOL")
|
||||
set(package_purpose "OTHER")
|
||||
elseif(type STREQUAL "CUSTOM")
|
||||
set(package_purpose "OTHER")
|
||||
elseif(type STREQUAL "CUSTOM_NO_INFIX")
|
||||
|
||||
@@ -166,7 +166,7 @@ endfunction()
|
||||
# DEPS_FOUND_FOR_$OP_KEY var.
|
||||
function(_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found)
|
||||
set(opt_args
|
||||
REQUIRED
|
||||
FINAL_ATTEMPT
|
||||
PYTHON_FOUND
|
||||
DEP_FOUND
|
||||
EVERYTHING_FOUND
|
||||
@@ -176,17 +176,20 @@ function(_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found)
|
||||
PYTHON_PATH
|
||||
PYTHON_VERSION
|
||||
DEP_PACKAGE_NAME
|
||||
PYTHON_FIND_OUTPUT
|
||||
DEP_FIND_OUTPUT
|
||||
ATTEMPT_MESSAGE
|
||||
ATTEMPT_RESULT_MESSAGE
|
||||
OP_KEY
|
||||
ATTEMPT_LOG_OUT_VAR
|
||||
ATTEMPT_COUNT_OUT_VAR
|
||||
)
|
||||
set(multi_args
|
||||
PYTHON_COMMON_ARGS
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
|
||||
if(arg_EVERYTHING_FOUND)
|
||||
message(DEBUG "Python Dependencies found. "
|
||||
"Using Python '${arg_PYTHON_PATH}' for running SBOM op '${arg_OP_KEY}'.")
|
||||
message(DEBUG "Python and dependencies found for SBOM operation '${arg_OP_KEY}'. "
|
||||
"Using Python '${arg_PYTHON_PATH}'")
|
||||
|
||||
if(NOT QT_INTERNAL_SBOM_PYTHON_EXECUTABLE)
|
||||
message(DEBUG "Setting QT_INTERNAL_SBOM_PYTHON_EXECUTABLE to ${arg_PYTHON_PATH}")
|
||||
@@ -202,40 +205,110 @@ function(_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(arg_REQUIRED)
|
||||
set(message_type "FATAL_ERROR")
|
||||
else()
|
||||
set(message_type "DEBUG")
|
||||
endif()
|
||||
|
||||
if(NOT arg_PYTHON_FOUND)
|
||||
# Look for python one more time, this time without QUIET, to show an error why it
|
||||
# wasn't found.
|
||||
if(arg_REQUIRED)
|
||||
_qt_internal_sbom_find_python_helper(${arg_PYTHON_COMMON_ARGS}
|
||||
OUT_VAR_PYTHON_PATH unused_python
|
||||
OUT_VAR_PYTHON_FOUND unused_found
|
||||
)
|
||||
endif()
|
||||
message(${message_type}
|
||||
"Python ${arg_REQUIRED_VERSION} for running SBOM op '${arg_OP_KEY}' NOT found.")
|
||||
string(CONCAT not_found_message
|
||||
"Python ${arg_REQUIRED_VERSION} for running SBOM operation '${arg_OP_KEY}' NOT found.\n"
|
||||
"Suggestion: In case if Python is installed, but not in a standard location, specify "
|
||||
"it using -DQT_SBOM_PYTHON_INTERP=<python-executable-path>\n"
|
||||
)
|
||||
elseif(NOT arg_DEP_FOUND)
|
||||
|
||||
set(extra_install_message "")
|
||||
if(message_type STREQUAL "FATAL_ERROR")
|
||||
set(extra_install_message
|
||||
"Install it using pip install '${arg_DEP_PACKAGE_NAME}' \n")
|
||||
endif()
|
||||
message(${message_type}
|
||||
"Python dependency for running SBOM op '${arg_OP_KEY}' NOT found:\n"
|
||||
${extra_install_message}
|
||||
"Details:\n"
|
||||
"Python interpreter: ${arg_PYTHON_PATH}\n"
|
||||
"Error output: \n${arg_DEP_FIND_OUTPUT}\n\n"
|
||||
string(CONCAT not_found_message
|
||||
"Python dependency for running SBOM operation '${arg_OP_KEY}' NOT found.\n"
|
||||
"Suggestion: Install it using pip install '${arg_DEP_PACKAGE_NAME}'\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(dep_find_contents "")
|
||||
if(arg_DEP_FIND_OUTPUT)
|
||||
string(CONCAT dep_find_contents
|
||||
"Dependency find log begins.\n${arg_DEP_FIND_OUTPUT}\n"
|
||||
"Dependency find log ends.\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
string(CONCAT current_attempt_message
|
||||
"${arg_ATTEMPT_MESSAGE}\n"
|
||||
"Result: ${not_found_message}\n"
|
||||
"Python find log begins.\n${arg_PYTHON_FIND_OUTPUT}\n"
|
||||
"Python find log ends.\n"
|
||||
"${dep_find_contents}"
|
||||
"${arg_ATTEMPT_RESULT_MESSAGE}\n\n"
|
||||
)
|
||||
|
||||
message(DEBUG "${current_attempt_message}")
|
||||
|
||||
if(arg_FINAL_ATTEMPT)
|
||||
set(previous_attempts_contents "")
|
||||
if(${arg_ATTEMPT_LOG_OUT_VAR})
|
||||
string(CONCAT previous_attempts_contents
|
||||
"Previous attempts log below.\n"
|
||||
"${${arg_ATTEMPT_LOG_OUT_VAR}}"
|
||||
)
|
||||
endif()
|
||||
string(CONCAT final_attempt_message
|
||||
"${current_attempt_message}\n"
|
||||
"${previous_attempts_contents}"
|
||||
)
|
||||
message(FATAL_ERROR "${final_attempt_message}")
|
||||
endif()
|
||||
|
||||
# Collect all previous attempt messages, to show them at the very end if not found.
|
||||
if(arg_ATTEMPT_LOG_OUT_VAR)
|
||||
set(attempt_count "${${arg_ATTEMPT_COUNT_OUT_VAR}}")
|
||||
math(EXPR attempt_count "${attempt_count} + 1")
|
||||
set(${arg_ATTEMPT_COUNT_OUT_VAR} "${attempt_count}" PARENT_SCOPE)
|
||||
|
||||
string(CONCAT attempt_log_content
|
||||
"${${arg_ATTEMPT_LOG_OUT_VAR}}\n"
|
||||
"Attempt ${attempt_count}:\n"
|
||||
"${current_attempt_message}")
|
||||
set(${arg_ATTEMPT_LOG_OUT_VAR} "${attempt_log_content}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper macro to output a debug or error message when python or one of its dependencies are not
|
||||
# found. Expects the caller to set all of the vars.
|
||||
# Meant to reduce the line noise due to the repeated calls.
|
||||
macro(_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda)
|
||||
# Assemble args to show what wasn't found.
|
||||
if(python_found)
|
||||
list(APPEND verify_args PYTHON_FOUND)
|
||||
endif()
|
||||
if(dep_found)
|
||||
list(APPEND verify_args DEP_FOUND)
|
||||
endif()
|
||||
if(everything_found)
|
||||
list(APPEND verify_args EVERYTHING_FOUND)
|
||||
endif()
|
||||
if(python_path)
|
||||
list(APPEND verify_args PYTHON_PATH "${python_path}")
|
||||
endif()
|
||||
if(python_version)
|
||||
list(APPEND verify_args PYTHON_VERSION "${python_version}")
|
||||
endif()
|
||||
if(python_find_output)
|
||||
list(APPEND verify_args PYTHON_FIND_OUTPUT "${python_find_output}")
|
||||
endif()
|
||||
if(dep_find_output)
|
||||
list(APPEND verify_args DEP_FIND_OUTPUT "${dep_find_output}")
|
||||
endif()
|
||||
if(attempt_message)
|
||||
list(APPEND verify_args ATTEMPT_MESSAGE "${attempt_message}")
|
||||
endif()
|
||||
if(attempt_result_message)
|
||||
list(APPEND verify_args ATTEMPT_RESULT_MESSAGE "${attempt_result_message}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found(
|
||||
${verify_args}
|
||||
REQUIRED_VERSION "${required_version}"
|
||||
OP_KEY "${arg_OP_KEY}"
|
||||
DEP_PACKAGE_NAME "${dep_package_name}"
|
||||
ATTEMPT_LOG_OUT_VAR attempt_log
|
||||
ATTEMPT_COUNT_OUT_VAR attempt_count
|
||||
)
|
||||
endmacro()
|
||||
|
||||
# Helper to find a python interpreter and a specific python dependency, e.g. to be able to generate
|
||||
# a SPDX JSON SBOM, or run post-installation steps like NTIA verification.
|
||||
# The exact dependency should be specified as the OP_KEY.
|
||||
@@ -244,6 +317,8 @@ endfunction()
|
||||
# avoid conflicts with any other found python interpreter.
|
||||
# Also caches the python version found in QT_INTERNAL_SBOM_PYTHON_VERSION, so we can show it
|
||||
# in the configure summary.
|
||||
# Attempts to find a suitable Python interpreter with multiple searching approaches, due to platform
|
||||
# specifics and quirks in how the FindPython module behaves.
|
||||
function(_qt_internal_sbom_find_and_handle_sbom_op_dependencies)
|
||||
set(opt_args
|
||||
REQUIRED
|
||||
@@ -298,86 +373,151 @@ function(_qt_internal_sbom_find_and_handle_sbom_op_dependencies)
|
||||
|
||||
set(everything_found FALSE)
|
||||
|
||||
# On macOS FindPython prefers looking in the system framework location, but that usually would
|
||||
# Used by the macro calls below below to collect debug find output across attempts.
|
||||
set(attempt_log "")
|
||||
set(attempt_count 0)
|
||||
|
||||
# Look in QT_SBOM_PYTHON_INTERP if it's set.
|
||||
# If QT_SBOM_FORCE_USE_PYTHON_INTERP is set, attempt to use it without preforming a search.
|
||||
_qt_internal_sbom_get_sbom_python_interp_path(
|
||||
OUT_VAR_FOUND sbom_python_interp_found
|
||||
OUT_VAR_PYTHON_PATH sbom_python_interp_path
|
||||
OUT_VAR_PYTHON_DIR_PATH sbom_python_interp_dir
|
||||
)
|
||||
if(sbom_python_interp_found)
|
||||
set(extra_python_args
|
||||
QUIET
|
||||
)
|
||||
# It's possible that sbom_python_interp_path can't be computed, so we need to check that
|
||||
# it's not empty.
|
||||
if(QT_SBOM_FORCE_USE_PYTHON_INTERP AND sbom_python_interp_path)
|
||||
set(using_message "${sbom_python_interp_path}")
|
||||
list(APPEND extra_python_args
|
||||
FORCE_SET_INTERPRETER_PATH
|
||||
INTERPRETER_PATH "${sbom_python_interp_path}"
|
||||
)
|
||||
else()
|
||||
set(using_message "${sbom_python_interp_dir}")
|
||||
list(APPEND extra_python_args
|
||||
SEARCH_IN_INTERPRETER_DIR_PATH
|
||||
INTERPRETER_DIR_PATH "${sbom_python_interp_dir}"
|
||||
)
|
||||
endif()
|
||||
string(CONCAT attempt_message
|
||||
"Looking for Python and dependencies for operation '${arg_OP_KEY}'. "
|
||||
"Searching using QT_SBOM_PYTHON_INTERP: '${using_message}'.")
|
||||
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
|
||||
|
||||
set(attempt_result_message "")
|
||||
if(NOT everything_found)
|
||||
string(CONCAT attempt_result_message
|
||||
"SBOM Python search using QT_SBOM_PYTHON_INTERP did "
|
||||
"not find all dependencies for operation '${arg_OP_KEY}'. Search continues."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(verify_args "")
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda()
|
||||
endif()
|
||||
|
||||
# Look into any custom provided root dir, that was not set via QT_SBOM_PYTHON_INTERP.
|
||||
_qt_internal_sbom_get_python_root_dir(OUT_VAR_PYTHON_ROOT_DIR_PATH python_root_dir)
|
||||
if(NOT everything_found AND python_root_dir)
|
||||
set(extra_python_args
|
||||
QUIET
|
||||
SEARCH_IN_ROOT_DIR
|
||||
)
|
||||
string(CONCAT attempt_message
|
||||
"Looking for Python and dependencies for operation '${arg_OP_KEY}'. "
|
||||
"Searching using Python3_ROOT_DIR: '${python_root_dir}'.")
|
||||
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
|
||||
|
||||
set(attempt_result_message "")
|
||||
if(NOT everything_found)
|
||||
string(CONCAT attempt_result_message
|
||||
"SBOM Python search using Python3_ROOT_DIR did not find all dependencies "
|
||||
"for operation '${arg_OP_KEY}'. Search continues."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(verify_args "")
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda()
|
||||
endif()
|
||||
|
||||
# On macOS, FindPython prefers looking in the system framework location, but that usually would
|
||||
# not have the required dependencies. So we first look in it, and then fallback to any other
|
||||
# non-framework python found.
|
||||
if(CMAKE_HOST_APPLE)
|
||||
set(extra_python_args SEARCH_IN_FRAMEWORKS QUIET)
|
||||
message(DEBUG "Looking for Python and dependencies for op '${arg_OP_KEY}' "
|
||||
"in the system framework location first.")
|
||||
if(NOT everything_found AND CMAKE_HOST_APPLE)
|
||||
set(extra_python_args
|
||||
QUIET
|
||||
SEARCH_IN_FRAMEWORKS
|
||||
)
|
||||
string(CONCAT attempt_message
|
||||
"Looking for Python and dependencies for operation '${arg_OP_KEY}'. "
|
||||
"Searching in the macOS framework locations.")
|
||||
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
|
||||
|
||||
set(attempt_result_message "")
|
||||
if(NOT everything_found)
|
||||
# Assemble args to show what wasn't found.
|
||||
set(verify_args "")
|
||||
if(python_found)
|
||||
list(APPEND verify_args PYTHON_FOUND)
|
||||
endif()
|
||||
if(dep_found)
|
||||
list(APPEND verify_args DEP_FOUND)
|
||||
endif()
|
||||
if(everything_found)
|
||||
list(APPEND verify_args EVERYTHING_FOUND)
|
||||
endif()
|
||||
if(python_path)
|
||||
list(APPEND verify_args PYTHON_PATH "${python_path}")
|
||||
endif()
|
||||
if(python_version)
|
||||
list(APPEND verify_args PYTHON_VERSION "${python_version}")
|
||||
endif()
|
||||
if(dep_find_output)
|
||||
list(APPEND verify_args DEP_FIND_OUTPUT "${dep_find_output}")
|
||||
endif()
|
||||
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found(
|
||||
${verify_args}
|
||||
REQUIRED_VERSION "${required_version}"
|
||||
OP_KEY "${arg_OP_KEY}"
|
||||
DEP_PACKAGE_NAME "${dep_package_name}"
|
||||
PYTHON_COMMON_ARGS ${python_common_args}
|
||||
string(CONCAT attempt_result_message
|
||||
"macOS-specific SBOM Python search did not find all dependencies "
|
||||
"for operation '${arg_OP_KEY}'. Search continues."
|
||||
)
|
||||
|
||||
message(DEBUG "Initial Apple-specific SBOM Python search did not find all dependencies "
|
||||
"for op ${arg_OP_KEY}, looking again outside of the system framework location.")
|
||||
endif()
|
||||
|
||||
set(verify_args "")
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda()
|
||||
endif()
|
||||
|
||||
# Looking again if something wasn't found, or a search was not done yet.
|
||||
if(NOT everything_found)
|
||||
set(extra_python_args QUIET)
|
||||
message(DEBUG "Looking for Python and dependencies for op '${arg_OP_KEY}'.")
|
||||
# On Windows, also look in the registry first.
|
||||
if(NOT everything_found AND CMAKE_HOST_WIN32)
|
||||
set(extra_python_args
|
||||
QUIET
|
||||
SEARCH_IN_REGISTRY
|
||||
)
|
||||
string(CONCAT attempt_message
|
||||
"Looking for Python and dependencies for operation '${arg_OP_KEY}'. Searching in the "
|
||||
" Windows registry.")
|
||||
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
|
||||
|
||||
set(attempt_result_message "")
|
||||
if(NOT everything_found)
|
||||
string(CONCAT attempt_result_message
|
||||
"Windows-specific SBOM Python search did not find all dependencies "
|
||||
"for operation '${arg_OP_KEY}'. Search continues."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(verify_args "")
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda()
|
||||
endif()
|
||||
|
||||
# Assemble args to show what was or wasn't found.
|
||||
set(verify_args "")
|
||||
if(arg_REQUIRED)
|
||||
list(APPEND verify_args REQUIRED)
|
||||
# Do a final lookup without all the approaches above.
|
||||
if(NOT everything_found)
|
||||
set(extra_python_args
|
||||
QUIET
|
||||
)
|
||||
string(CONCAT attempt_message
|
||||
"Looking for Python and dependencies for operation "
|
||||
"'${arg_OP_KEY}'. Searching default locations as per FindPython3 docs.")
|
||||
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
|
||||
|
||||
set(attempt_result_message "")
|
||||
if(NOT everything_found)
|
||||
string(CONCAT attempt_result_message
|
||||
"SBOM Python search in default locations did not find all dependencies "
|
||||
"for operation '${arg_OP_KEY}'. Search concluded."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(verify_args "")
|
||||
# If REQUIRED was passed, and we are at the final attempt, in case we didn't find the
|
||||
# deps, we will error out.
|
||||
if(arg_REQUIRED)
|
||||
list(APPEND verify_args FINAL_ATTEMPT)
|
||||
endif()
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found_lambda()
|
||||
endif()
|
||||
if(python_found)
|
||||
list(APPEND verify_args PYTHON_FOUND)
|
||||
endif()
|
||||
if(dep_found)
|
||||
list(APPEND verify_args DEP_FOUND)
|
||||
endif()
|
||||
if(everything_found)
|
||||
list(APPEND verify_args EVERYTHING_FOUND)
|
||||
endif()
|
||||
if(python_path)
|
||||
list(APPEND verify_args PYTHON_PATH "${python_path}")
|
||||
endif()
|
||||
if(python_version)
|
||||
list(APPEND verify_args PYTHON_VERSION "${python_version}")
|
||||
endif()
|
||||
if(dep_find_output)
|
||||
list(APPEND verify_args DEP_FIND_OUTPUT "${dep_find_output}")
|
||||
endif()
|
||||
_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found(
|
||||
${verify_args}
|
||||
REQUIRED_VERSION "${required_version}"
|
||||
OP_KEY "${arg_OP_KEY}"
|
||||
DEP_PACKAGE_NAME "${dep_package_name}"
|
||||
PYTHON_COMMON_ARGS ${python_common_args}
|
||||
)
|
||||
|
||||
if(arg_OUT_VAR_DEPS_FOUND)
|
||||
set(${arg_OUT_VAR_DEPS_FOUND} "${QT_INTERNAL_SBOM_DEPS_FOUND_FOR_${arg_OP_KEY}}"
|
||||
|
||||
@@ -15,6 +15,7 @@ macro(_qt_internal_sbom_find_python_and_dependency_helper_lambda)
|
||||
OUT_VAR_PYTHON_VERSION python_version
|
||||
OUT_VAR_DEP_FOUND dep_found
|
||||
OUT_VAR_PYTHON_AND_DEP_FOUND everything_found
|
||||
OUT_VAR_PYTHON_FIND_OUTPUT python_find_output
|
||||
OUT_VAR_DEP_FIND_OUTPUT dep_find_output
|
||||
)
|
||||
endmacro()
|
||||
@@ -31,6 +32,7 @@ function(_qt_internal_sbom_find_python_and_dependency_helper)
|
||||
OUT_VAR_PYTHON_VERSION
|
||||
OUT_VAR_DEP_FOUND
|
||||
OUT_VAR_PYTHON_AND_DEP_FOUND
|
||||
OUT_VAR_PYTHON_FIND_OUTPUT
|
||||
OUT_VAR_DEP_FIND_OUTPUT
|
||||
)
|
||||
set(multi_args
|
||||
@@ -68,6 +70,7 @@ function(_qt_internal_sbom_find_python_and_dependency_helper)
|
||||
OUT_VAR_PYTHON_PATH python_path_inner
|
||||
OUT_VAR_PYTHON_FOUND python_found_inner
|
||||
OUT_VAR_PYTHON_VERSION python_version_inner
|
||||
OUT_VAR_PYTHON_FIND_OUTPUT python_find_output_inner
|
||||
)
|
||||
|
||||
if(python_found_inner AND python_path_inner)
|
||||
@@ -90,14 +93,14 @@ function(_qt_internal_sbom_find_python_and_dependency_helper)
|
||||
endif()
|
||||
set(${arg_OUT_VAR_DEP_FOUND} "${dep_found_inner}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_PYTHON_AND_DEP_FOUND} "${everything_found_inner}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_PYTHON_FIND_OUTPUT} "${python_find_output_inner}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_DEP_FIND_OUTPUT} "${dep_find_output_inner}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Tries to find the python intrepreter, given the QT_SBOM_PYTHON_INTERP path hint, as well as
|
||||
# other options.
|
||||
# Tries to find a python intrepreter for SBOM handling.
|
||||
# Ignores any previously found python.
|
||||
# Returns the python interpreter path and whether it was successfully found, along with the version
|
||||
# found.
|
||||
# found and some debug output when not found.
|
||||
#
|
||||
# This is intentionally a function, and not a macro, to prevent overriding the Python3_EXECUTABLE
|
||||
# non-cache variable in a global scope in case if a different python is found and used for a
|
||||
@@ -110,13 +113,20 @@ endfunction()
|
||||
function(_qt_internal_sbom_find_python_helper)
|
||||
set(opt_args
|
||||
SEARCH_IN_FRAMEWORKS
|
||||
SEARCH_IN_REGISTRY
|
||||
SEARCH_IN_ROOT_DIR
|
||||
SEARCH_IN_INTERPRETER_DIR_PATH
|
||||
FORCE_SET_INTERPRETER_PATH
|
||||
QUIET
|
||||
)
|
||||
set(single_args
|
||||
VERSION
|
||||
INTERPRETER_PATH
|
||||
INTERPRETER_DIR_PATH
|
||||
OUT_VAR_PYTHON_PATH
|
||||
OUT_VAR_PYTHON_FOUND
|
||||
OUT_VAR_PYTHON_VERSION
|
||||
OUT_VAR_PYTHON_FIND_OUTPUT
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
@@ -130,9 +140,59 @@ function(_qt_internal_sbom_find_python_helper)
|
||||
message(FATAL_ERROR "OUT_VAR_PYTHON_FOUND var is required")
|
||||
endif()
|
||||
|
||||
set(find_output "Search options:")
|
||||
|
||||
# Allow disabling looking for a python interpreter shipped as part of a macOS system framework.
|
||||
if(NOT arg_SEARCH_IN_FRAMEWORKS)
|
||||
set(Python3_FIND_FRAMEWORK NEVER)
|
||||
list(APPEND find_output "Will NOT search in macOS framework locations.")
|
||||
else()
|
||||
list(APPEND find_output "Will search in macOS framework locations.")
|
||||
endif()
|
||||
|
||||
if(NOT arg_SEARCH_IN_REGISTRY)
|
||||
set(Python3_FIND_REGISTRY NEVER)
|
||||
list(APPEND find_output "Will NOT search in Windows registry.")
|
||||
else()
|
||||
list(APPEND find_output "Will search in Windows registry.")
|
||||
endif()
|
||||
|
||||
# Remove the root dir if not requested to search in it.
|
||||
if(NOT arg_SEARCH_IN_ROOT_DIR)
|
||||
unset(Python3_ROOT_DIR)
|
||||
|
||||
# Temporarily also unset the env var.
|
||||
set(python_root_dir_env_value "$ENV{Python3_ROOT_DIR}")
|
||||
if(python_root_dir_env_value AND NOT QT_SBOM_NO_PYTHON_ROOT_DIR_ENV_BACKUP)
|
||||
set(python_root_dir_env_backup "$ENV{Python3_ROOT_DIR}")
|
||||
unset(ENV{Python3_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
list(APPEND find_output "Will NOT search in Python3_ROOT_DIR.")
|
||||
else()
|
||||
_qt_internal_sbom_get_python_root_dir(OUT_VAR_PYTHON_ROOT_DIR_PATH Python3_ROOT_DIR)
|
||||
list(APPEND find_output "Will search in Python3_ROOT_DIR: '${Python3_ROOT_DIR}'.")
|
||||
endif()
|
||||
|
||||
set(force_python_interpreter_path "")
|
||||
if(arg_FORCE_SET_INTERPRETER_PATH AND arg_INTERPRETER_PATH)
|
||||
set(force_python_interpreter_path "${arg_INTERPRETER_PATH}")
|
||||
|
||||
list(APPEND find_output
|
||||
"Will force set Python3_EXECUTABLE to provided INTERPRETER_PATH: "
|
||||
"'${arg_INTERPRETER_PATH}'.")
|
||||
else()
|
||||
list(APPEND find_output "Will NOT force set Python3_EXECUTABLE to INTERPRETER_PATH.")
|
||||
endif()
|
||||
|
||||
if(arg_SEARCH_IN_INTERPRETER_DIR_PATH AND arg_INTERPRETER_DIR_PATH)
|
||||
# Python3_ROOT_DIR expects a directory, not a path.
|
||||
set(Python3_ROOT_DIR "${arg_INTERPRETER_DIR_PATH}")
|
||||
|
||||
list(APPEND find_output
|
||||
"Will search in provided INTERPRETER_DIR_PATH: '${arg_INTERPRETER_DIR_PATH}'.")
|
||||
else()
|
||||
list(APPEND find_output "Will NOT search in INTERPRETER_DIR_PATH.")
|
||||
endif()
|
||||
|
||||
set(required_version "")
|
||||
@@ -141,32 +201,158 @@ function(_qt_internal_sbom_find_python_helper)
|
||||
endif()
|
||||
|
||||
set(find_quiet "")
|
||||
if(arg_QUIET)
|
||||
if(arg_QUIET AND NOT QT_INTERNAL_SBOM_NO_FIND_PYTHON_QUIET)
|
||||
set(find_quiet "QUIET")
|
||||
endif()
|
||||
|
||||
# Locally reset any executable that was possibly already found.
|
||||
# We do this to ensure we always re-do the lookup/
|
||||
# This needs to be set to an empty string, to override any cache variable
|
||||
set(Python3_EXECUTABLE "")
|
||||
# When performing a search, locally reset any executable that was possibly already found.
|
||||
# We do this to ensure we always re-do the lookup.
|
||||
# This needs to be set to an empty string, to override any cache variable.
|
||||
# When FORCE_SET_INTERPRETER_PATH is used, set the executable to the given path.
|
||||
if(arg_FORCE_SET_INTERPRETER_PATH
|
||||
AND force_python_interpreter_path
|
||||
AND EXISTS "${force_python_interpreter_path}"
|
||||
)
|
||||
set(Python3_EXECUTABLE "${force_python_interpreter_path}")
|
||||
else()
|
||||
set(Python3_EXECUTABLE "")
|
||||
endif()
|
||||
|
||||
# This needs to be unset, because the Python module checks whether the variable is defined, not
|
||||
# whether it is empty.
|
||||
unset(_Python3_EXECUTABLE)
|
||||
|
||||
if(QT_SBOM_PYTHON_INTERP)
|
||||
set(Python3_ROOT_DIR ${QT_SBOM_PYTHON_INTERP})
|
||||
find_package(Python3 ${required_version} ${find_quiet} COMPONENTS Interpreter)
|
||||
|
||||
if(python_root_dir_env_backup AND NOT QT_SBOM_NO_PYTHON_ROOT_DIR_ENV_BACKUP)
|
||||
set(ENV{Python3_ROOT_DIR} "${python_root_dir_env_backup}")
|
||||
endif()
|
||||
|
||||
find_package(Python3 ${required_version} ${find_quiet} COMPONENTS Interpreter)
|
||||
if(Python3_Interpreter_FOUND)
|
||||
list(APPEND find_output
|
||||
"Python interpreter found: '${Python3_EXECUTABLE} (version ${Python3_VERSION})'.")
|
||||
else()
|
||||
list(APPEND find_output "Python interpreter NOT found.")
|
||||
endif()
|
||||
|
||||
list(JOIN find_output "\n" find_output_joined)
|
||||
|
||||
set(${arg_OUT_VAR_PYTHON_PATH} "${Python3_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_PYTHON_FOUND} "${Python3_Interpreter_FOUND}" PARENT_SCOPE)
|
||||
set(${arg_OUT_VAR_PYTHON_FIND_OUTPUT} "${find_output_joined}" PARENT_SCOPE)
|
||||
if(arg_OUT_VAR_PYTHON_VERSION)
|
||||
set(${arg_OUT_VAR_PYTHON_VERSION} "${Python3_VERSION}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper to retrieve the python interpreter path and directory, from either a QT_SBOM_PYTHON_INTERP
|
||||
# env var or regular (cache) var, when one of them is set.
|
||||
# The regular var is preferred.
|
||||
# In the very first implementation, the var expected to be passed an interpreter path, rather
|
||||
# than the dir where the python interpreter is located, which is what FindPython's
|
||||
# Python3_ROOT_DIR expects.
|
||||
# Now both cases are handled properly. So the variable name is a bit of a misnomer, but we keep
|
||||
# it for compatibility.
|
||||
function(_qt_internal_sbom_get_sbom_python_interp_path)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
OUT_VAR_FOUND
|
||||
OUT_VAR_PYTHON_PATH
|
||||
OUT_VAR_PYTHON_DIR_PATH
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
set(candidates
|
||||
"${QT_SBOM_PYTHON_INTERP}"
|
||||
"$ENV{QT_SBOM_PYTHON_INTERP}"
|
||||
)
|
||||
|
||||
set(something_found FALSE)
|
||||
foreach(candidate IN LISTS candidates)
|
||||
if(NOT candidate OR NOT EXISTS "${candidate}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(something_found TRUE)
|
||||
if(IS_DIRECTORY "${candidate}")
|
||||
set(interp_dir "${candidate}")
|
||||
set(interp_path "")
|
||||
|
||||
# We need to find the executable in the given directory, to be able to assign it to
|
||||
# Python3_EXECUTABLE when FORCE_SET_INTERPRETER_PATH is used.
|
||||
find_program(QT_INTERNAL_SBOM_PYTHON_INTERP_PATH
|
||||
NAMES python3 python
|
||||
HINTS "${interp_dir}"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if(QT_INTERNAL_SBOM_PYTHON_INTERP_PATH)
|
||||
set(interp_path "${QT_INTERNAL_SBOM_PYTHON_INTERP_PATH}")
|
||||
message(DEBUG
|
||||
"Found python executable '${interp_path}' in QT_SBOM_PYTHON_INTERP candidate "
|
||||
"directory: '${candidate}'.")
|
||||
else()
|
||||
message(DEBUG
|
||||
"QT_SBOM_PYTHON_INTERP '${candidate}' is a directory, but no "
|
||||
"python executable found in it.")
|
||||
endif()
|
||||
else()
|
||||
get_filename_component(interp_dir "${candidate}" DIRECTORY)
|
||||
set(interp_path "${candidate}")
|
||||
endif()
|
||||
break()
|
||||
endforeach()
|
||||
|
||||
if(arg_OUT_VAR_FOUND)
|
||||
set(${arg_OUT_VAR_FOUND} "${something_found}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(arg_OUT_VAR_PYTHON_PATH)
|
||||
set(${arg_OUT_VAR_PYTHON_PATH} "${interp_path}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(arg_OUT_VAR_PYTHON_DIR_PATH)
|
||||
set(${arg_OUT_VAR_PYTHON_DIR_PATH} "${interp_dir}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Returns the value of Python3_ROOT_DIR if it's set, either as a cmake variable or env var.
|
||||
function(_qt_internal_sbom_get_python_root_dir)
|
||||
set(opt_args "")
|
||||
set(single_args
|
||||
OUT_VAR_FOUND
|
||||
OUT_VAR_PYTHON_ROOT_DIR_PATH
|
||||
)
|
||||
set(multi_args "")
|
||||
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
set(candidates
|
||||
"${Python3_ROOT_DIR}"
|
||||
"$ENV{Python3_ROOT_DIR}"
|
||||
)
|
||||
|
||||
set(python_root_dir "")
|
||||
|
||||
set(something_found FALSE)
|
||||
foreach(candidate IN LISTS candidates)
|
||||
if(NOT candidate OR NOT EXISTS "${candidate}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(something_found TRUE)
|
||||
if(IS_DIRECTORY "${candidate}")
|
||||
set(python_root_dir "${candidate}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(arg_OUT_VAR_FOUND)
|
||||
set(${arg_OUT_VAR_FOUND} "${something_found}" PARENT_SCOPE)
|
||||
endif()
|
||||
if(arg_OUT_VAR_PYTHON_ROOT_DIR_PATH)
|
||||
set(${arg_OUT_VAR_PYTHON_ROOT_DIR_PATH} "${python_root_dir}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Helper that takes an python import statement to run using the given python interpreter path,
|
||||
# to confirm that the given python dependency can be found.
|
||||
# Returns whether the dependency was found and the output of running the import, for error handling.
|
||||
@@ -205,11 +391,14 @@ function(_qt_internal_sbom_find_python_dependency_helper)
|
||||
|
||||
if("${res}" STREQUAL "0")
|
||||
set(found TRUE)
|
||||
set(output "${output}")
|
||||
string(CONCAT output
|
||||
"SBOM Python dependency '${arg_DEPENDENCY_IMPORT_STATEMENT}' found. "
|
||||
"Output:\n${output}")
|
||||
else()
|
||||
set(found FALSE)
|
||||
string(CONCAT output "SBOM Python dependency ${arg_DEPENDENCY_IMPORT_STATEMENT} NOT found. "
|
||||
"Error:\n${output}")
|
||||
string(CONCAT output
|
||||
"SBOM Python dependency '${arg_DEPENDENCY_IMPORT_STATEMENT}' NOT found. "
|
||||
"Output:\n${output} Exit code: ${res}")
|
||||
endif()
|
||||
|
||||
set(${arg_OUT_VAR_FOUND} "${found}" PARENT_SCOPE)
|
||||
@@ -240,13 +429,26 @@ function(_qt_internal_sbom_find_python_dependency_program)
|
||||
# The path to python installed apps is different on Windows compared to UNIX, so we use
|
||||
# a different path than where the python interpreter might be located.
|
||||
if(QT_SBOM_PYTHON_APPS_PATH)
|
||||
list(APPEND hints ${QT_SBOM_PYTHON_APPS_PATH})
|
||||
list(APPEND hints "${QT_SBOM_PYTHON_APPS_PATH}")
|
||||
endif()
|
||||
|
||||
# Sometimes the installed apps might be in the same location as the interepreter, if both are
|
||||
# in a virtual env. So look also in the interpreter path.
|
||||
if(QT_SBOM_PYTHON_INTERP)
|
||||
list(APPEND hints ${QT_SBOM_PYTHON_INTERP})
|
||||
_qt_internal_sbom_get_sbom_python_interp_path(OUT_VAR_PYTHON_DIR_PATH sbom_python_interp_dir)
|
||||
if(sbom_python_interp_dir)
|
||||
list(APPEND hints "${sbom_python_interp_dir}")
|
||||
endif()
|
||||
|
||||
# If we already found a python interpreter for sbom purposes, look in its parent directory.
|
||||
if(QT_INTERNAL_SBOM_PYTHON_EXECUTABLE)
|
||||
get_filename_component(python_interp_dir "${QT_INTERNAL_SBOM_PYTHON_EXECUTABLE}" DIRECTORY)
|
||||
list(APPEND hints "${python_interp_dir}")
|
||||
endif()
|
||||
|
||||
# Look in the python root dir as well.
|
||||
_qt_internal_sbom_get_python_root_dir(OUT_VAR_PYTHON_ROOT_DIR_PATH python_root_dir)
|
||||
if(python_root_dir)
|
||||
list(APPEND hints "${python_root_dir}")
|
||||
endif()
|
||||
|
||||
find_program(${cache_var}
|
||||
|
||||
@@ -288,6 +288,13 @@ function(_qt_internal_sbom_get_qt_entity_repo_source_dir target)
|
||||
message(FATAL_ERROR "OUT_VAR must be set")
|
||||
endif()
|
||||
|
||||
# Interface libs don't have a source dir. Return early, to avoid errors with CMake 3.16.
|
||||
get_target_property(target_type "${target}" TYPE)
|
||||
if(target_type STREQUAL "INTERFACE_LIBRARY")
|
||||
set(${arg_OUT_VAR} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(repo_source_dir "${target}" SOURCE_DIR)
|
||||
|
||||
# Get the path relative to the PROJECT_SOURCE_DIR
|
||||
|
||||
@@ -1,6 +1,134 @@
|
||||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
function(_qt_internal_normalize_project_version)
|
||||
# Default if PROJECT_VERSION is missing or empty
|
||||
if(NOT DEFINED PROJECT_VERSION OR PROJECT_VERSION STREQUAL "")
|
||||
set(_version "1.0.0.0")
|
||||
else()
|
||||
set(_version "${PROJECT_VERSION}")
|
||||
endif()
|
||||
|
||||
# Split version into components
|
||||
string(REPLACE "." ";" _ver_list "${_version}")
|
||||
|
||||
# Ensure exactly 4 segments
|
||||
list(LENGTH _ver_list _len)
|
||||
while(_len LESS 4)
|
||||
list(APPEND _ver_list "0")
|
||||
list(LENGTH _ver_list _len)
|
||||
endwhile()
|
||||
|
||||
# Truncate extra segments
|
||||
list(SUBLIST _ver_list 0 4 _ver_list)
|
||||
|
||||
# Windows Application Manifests require a 4-part version (Major.Minor.Build.Revision).
|
||||
# Each segment is stored as a 16-bit unsigned integer (USHORT).
|
||||
# Values exceeding 65535 will cause schema validation errors in MSIX/AppX
|
||||
# or 'Side-by-Side' (SxS) configuration failures in Win32 executables.
|
||||
# We clamp the values here to ensure the manifest remains valid for Windows.
|
||||
set(_ver_list_tmp "")
|
||||
foreach(i RANGE 0 3)
|
||||
list(GET _ver_list ${i} seg)
|
||||
if(seg LESS 0)
|
||||
message(WARNING
|
||||
"Version segment '${seg}' is less than 0. Capping to 0.")
|
||||
set(seg 0)
|
||||
elseif(seg GREATER 65535)
|
||||
message(WARNING
|
||||
"Version segment '${seg}' exceeds 65535. Capping to 65535.")
|
||||
set(seg 65535)
|
||||
endif()
|
||||
list(APPEND _ver_list_tmp ${seg})
|
||||
endforeach()
|
||||
|
||||
list(GET _ver_list_tmp 0 _maj)
|
||||
list(GET _ver_list_tmp 1 _min)
|
||||
list(GET _ver_list_tmp 2 _build)
|
||||
list(GET _ver_list_tmp 3 _revision)
|
||||
|
||||
# Recompose canonical version
|
||||
set(PROJECT_VERSION
|
||||
"${_maj}.${_min}.${_build}.${_revision}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_qt_internal_finalize_windows_app target)
|
||||
get_target_property(targetType "${target}" TYPE)
|
||||
if(NOT "${targetType}" STREQUAL "EXECUTABLE" AND
|
||||
NOT "${targetType}" STREQUAL "SHARED_LIBRARY")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# If the project already specifies a custom file, we don't override it.
|
||||
get_target_property(sources "${target}" SOURCES)
|
||||
set(manifest_in "${sources}")
|
||||
list(FILTER manifest_in INCLUDE REGEX ".*\.manifest")
|
||||
if(manifest_in)
|
||||
message(DEBUG "Skipping manifest magic due to user specified manifest file")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(manifest_in "${__qt_internal_cmake_windows_support_files_path}/app.exe.manifest.in")
|
||||
|
||||
get_target_property(output_name ${target} OUTPUT_NAME)
|
||||
if(NOT output_name)
|
||||
set(output_name "${target}")
|
||||
endif()
|
||||
string(MAKE_C_IDENTIFIER "${target}" target_identifier)
|
||||
set(manifest_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(manifest_out "${manifest_out_dir}/${output_name}.exe.manifest")
|
||||
|
||||
_qt_internal_normalize_project_version()
|
||||
|
||||
get_target_property(project_identifier "${target}" QT_WINDOWS_APP_PROJECT_IDENTIFIER)
|
||||
if(NOT project_identifier)
|
||||
message(DEBUG "QT_WINDOWS_APP_PROJECT_IDENTIFIER not set, "
|
||||
"using com.yourcompany.${target_identifier} as fallback")
|
||||
set(QT_WINDOWS_APP_PROJECT_IDENTIFIER "com.yourcompany.${target_identifier}")
|
||||
else()
|
||||
set(QT_WINDOWS_APP_PROJECT_IDENTIFIER "${project_identifier}")
|
||||
endif()
|
||||
|
||||
get_target_property(project_executionlevel "${target}" QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL)
|
||||
if(NOT project_executionlevel)
|
||||
message(DEBUG "QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL not set, "
|
||||
"using asInvoker as fallback")
|
||||
set(QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL "asInvoker")
|
||||
else()
|
||||
# Validate value
|
||||
set(_valid_execution_levels
|
||||
"asInvoker"
|
||||
"highestAvailable"
|
||||
"requireAdministrator"
|
||||
)
|
||||
|
||||
if(NOT project_executionlevel IN_LIST _valid_execution_levels)
|
||||
message(WARNING
|
||||
"Invalid QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL value: "
|
||||
"'${project_executionlevel}' for target '${target}'."
|
||||
"Defaulting to 'asInvoker'."
|
||||
" Valid values are: asInvoker, highestAvailable, requireAdministrator."
|
||||
)
|
||||
set(QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL "asInvoker")
|
||||
else()
|
||||
set(QT_WINDOWS_APP_PROJECT_EXECUTION_LEVEL "${project_executionlevel}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Call configure_file to substitute Qt-specific @FOO@ values, not ${FOO} values.
|
||||
configure_file(
|
||||
"${manifest_in}"
|
||||
"${manifest_out}"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
target_sources("${target}" PRIVATE "${manifest_out}")
|
||||
if(MSVC)
|
||||
target_link_options(${target} PRIVATE "/MANIFEST:NO")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(qt6_add_win_app_sdk target)
|
||||
if(NOT MSVC)
|
||||
message(WARNING
|
||||
|
||||
@@ -24,7 +24,7 @@ function(qt_internal_add_resource target resourceName)
|
||||
cmake_parse_arguments(PARSE_ARGV 2 arg
|
||||
""
|
||||
"PREFIX;LANG;BASE;OUTPUT_TARGETS"
|
||||
"FILES;OPTIONS")
|
||||
"FILES")
|
||||
_qt_internal_validate_all_args_are_parsed(arg)
|
||||
|
||||
_qt_internal_process_resource(${target} ${resourceName}
|
||||
@@ -32,7 +32,6 @@ function(qt_internal_add_resource target resourceName)
|
||||
LANG "${arg_LANG}"
|
||||
BASE "${arg_BASE}"
|
||||
FILES ${arg_FILES}
|
||||
OPTIONS ${arg_OPTIONS}
|
||||
OUTPUT_TARGETS out_targets
|
||||
)
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ function(qt_internal_sbom_get_project_spdx_id out_var)
|
||||
set(result "")
|
||||
|
||||
if(QT_GENERATE_SBOM)
|
||||
get_cmake_property(project_spdx_id _qt_internal_sbom_project_spdx_id)
|
||||
_qt_internal_sbom_get_current_project_spdx_id(project_spdx_id)
|
||||
if(project_spdx_id)
|
||||
set(result "${project_spdx_id}")
|
||||
endif()
|
||||
@@ -127,7 +127,7 @@ function(qt_internal_sbom_get_external_document_ref_spdx_id project_name out_var
|
||||
|
||||
if(QT_GENERATE_SBOM)
|
||||
string(TOLOWER "${project_name}" project_name_lowercase)
|
||||
_qt_internal_sbom_get_external_document_ref_spdx_id("${project_name_lowercase}" result)
|
||||
_qt_internal_sbom_compute_external_document_ref_spdx_id("${project_name_lowercase}" result)
|
||||
endif()
|
||||
|
||||
set(${out_var} "${result}" PARENT_SCOPE)
|
||||
@@ -242,12 +242,12 @@ function(qt_internal_add_sbom_summary_info)
|
||||
|
||||
qt_internal_is_sbom_option_enabled(QT_SBOM_GENERATE_CYDX_V1_6 value)
|
||||
qt_internal_get_sbom_option_required_suffix(QT_SBOM_REQUIRE_GENERATE_CYDX_V1_6 suffix)
|
||||
qt_configure_add_summary_entry(ARGS "Generate CyloneDX v1.6"
|
||||
qt_configure_add_summary_entry(ARGS "Generate CycloneDX v1.6"
|
||||
TYPE "message" MESSAGE "${value}${suffix}")
|
||||
|
||||
qt_internal_is_sbom_option_enabled(QT_SBOM_VERIFY_CYDX_V1_6 value)
|
||||
qt_internal_get_sbom_option_required_suffix(QT_SBOM_REQUIRE_VERIFY_CYDX_V1_6 suffix)
|
||||
qt_configure_add_summary_entry(ARGS "Verify CyloneDX v1.6"
|
||||
qt_configure_add_summary_entry(ARGS "Verify CycloneDX v1.6"
|
||||
TYPE "message" MESSAGE "${value}${suffix}")
|
||||
|
||||
# Python interpreter info.
|
||||
|
||||
@@ -682,12 +682,12 @@ function(qt_internal_add_test name)
|
||||
"This is fine if OpenSSL was built statically.")
|
||||
endif()
|
||||
endif()
|
||||
qt_internal_android_test_runner_arguments("${name}" test_executable extra_test_args)
|
||||
set(test_executable "$<TARGET_PROPERTY:${name},_qt_android_runner_wrapper_path>")
|
||||
list(APPEND extra_test_args "--timeout" "${android_timeout}")
|
||||
|
||||
set(build_environment "")
|
||||
if(DEFINED ENV{QT_BUILD_ENVIRONMENT})
|
||||
set(build_environment "$ENV{QT_BUILD_ENVIRONMENT}")
|
||||
if(DEFINED ENV{QTEST_ENVIRONMENT})
|
||||
set(build_environment "$ENV{QTEST_ENVIRONMENT}")
|
||||
endif()
|
||||
|
||||
if(QT_ENABLE_VERBOSE_DEPLOYMENT OR build_environment STREQUAL "ci")
|
||||
|
||||
@@ -11,6 +11,24 @@ gccsetup --cc gcc --cxx g++ --config "$BAUHAUS_CONFIG"
|
||||
cd "$CAFECC_BASEPATH"
|
||||
BAUHAUS_IR_COMPRESSION=none COMPILE_ONLY=1 cmake -G Ninja -DAXIVION_ANALYSIS_TOOLCHAIN_FILE=/home/qt/bauhaus-suite/profiles/cmake/axivion-launcher-toolchain.cmake -DCMAKE_PREFIX_PATH=/home/qt/work/qt/qtbase/build -DCMAKE_PROJECT_INCLUDE_BEFORE=/home/qt/bauhaus-suite/profiles/cmake/axivion-before-project-hook.cmake -B build -S . --fresh
|
||||
cmake --build build -j4
|
||||
|
||||
ROOT_DIR=src
|
||||
MAGIC="Qt-Security score:critical"
|
||||
|
||||
INCLUDE_FILES=""
|
||||
|
||||
while IFS= read -r file; do
|
||||
if [ -z "$INCLUDE_FILES" ]; then
|
||||
INCLUDE_FILES="$file"
|
||||
else
|
||||
INCLUDE_FILES="$INCLUDE_FILES:$file"
|
||||
fi
|
||||
done <<EOF
|
||||
$(grep -rl "$MAGIC" "$ROOT_DIR")
|
||||
EOF
|
||||
|
||||
export INCLUDE_FILES
|
||||
|
||||
for MODULE in qtconcurrent qtcore qtdbus qtgui qtnetwork qtopengl qtopenglwidgets qtsql qttest qtwidgets qtprintsupport qtxml; do
|
||||
export MODULE
|
||||
export PLUGINS=""
|
||||
|
||||
@@ -118,56 +118,56 @@ instructions:
|
||||
# Run CTest, with or without ignoring exit code
|
||||
# FIXME: De-duplicate the CTest instruction when
|
||||
# ignoreExitCode expands environment variables.
|
||||
- type: Group
|
||||
- type: ExecuteCommand
|
||||
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: false
|
||||
maxTimeInSeconds: 14400
|
||||
maxTimeBetweenOutput: 14400
|
||||
userMessageOnFailure: >
|
||||
Failed to run tests.
|
||||
enable_if:
|
||||
condition: runtime
|
||||
env_var: COIN_CTEST_IGNORE_EXIT_CODE
|
||||
not_equals_value: "1"
|
||||
instructions:
|
||||
- type: ExecuteCommand
|
||||
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: false
|
||||
maxTimeInSeconds: 14400
|
||||
maxTimeBetweenOutput: 14400
|
||||
userMessageOnFailure: >
|
||||
Failed to run tests.
|
||||
- type: ExecuteCommand
|
||||
command: >
|
||||
cmake -E copy
|
||||
Testing{{.Env.CI_PATH_SEP}}Temporary{{.Env.CI_PATH_SEP}}LastTest.log
|
||||
{{.Env.COIN_CTEST_RESULTSDIR}}{{.Env.CI_PATH_SEP}}CTest.log
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: false
|
||||
userMessageOnFailure: >
|
||||
Failed to copy LastTest.log to testresults directory.
|
||||
executeOn: always
|
||||
maxTimeInSeconds: 20
|
||||
maxTimeBetweenOutput: 20
|
||||
- type: Group
|
||||
- type: ExecuteCommand
|
||||
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: true
|
||||
maxTimeInSeconds: 14400
|
||||
maxTimeBetweenOutput: 14400
|
||||
userMessageOnFailure: >
|
||||
Failed to run tests.
|
||||
enable_if:
|
||||
condition: runtime
|
||||
env_var: COIN_CTEST_IGNORE_EXIT_CODE
|
||||
equals_value: "1"
|
||||
instructions:
|
||||
- type: ExecuteCommand
|
||||
command: "{{.Env.TESTS_ENV_PREFIX}} ctest {{.Env.CTEST_ARGS}}"
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: true
|
||||
maxTimeInSeconds: 14400
|
||||
maxTimeBetweenOutput: 14400
|
||||
userMessageOnFailure: >
|
||||
Failed to run tests.
|
||||
- type: ExecuteCommand
|
||||
command: >
|
||||
cmake -E copy
|
||||
Testing{{.Env.CI_PATH_SEP}}Temporary{{.Env.CI_PATH_SEP}}LastTest.log
|
||||
{{.Env.COIN_CTEST_RESULTSDIR}}{{.Env.CI_PATH_SEP}}CTest.log
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
ignoreExitCode: true
|
||||
userMessageOnFailure: >
|
||||
Failed to copy LastTest.log to testresults directory.
|
||||
executeOn: always
|
||||
|
||||
- type: ExecuteCommand
|
||||
instructionName: "Copy ctest log"
|
||||
command: >
|
||||
cmake -E copy
|
||||
Testing{{.Env.CI_PATH_SEP}}Temporary{{.Env.CI_PATH_SEP}}LastTest.log
|
||||
{{.Env.COIN_CTEST_RESULTSDIR}}
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
userMessageOnFailure: >
|
||||
Failed to copy LastTest.log to testresults directory.
|
||||
executeOn: always
|
||||
maxTimeInSeconds: 60
|
||||
maxTimeBetweenOutput: 60
|
||||
- type: ExecuteCommand
|
||||
command: >
|
||||
cmake -E copy
|
||||
Testing{{.Env.CI_PATH_SEP}}Temporary{{.Env.CI_PATH_SEP}}LastTest.log.tmp
|
||||
{{.Env.COIN_CTEST_RESULTSDIR}}
|
||||
executeCommandArgumentSplitingBehavior: SplitAfterVariableSubstitution
|
||||
userMessageOnFailure: >
|
||||
Failed to copy LastTest.log.tmp to testresults directory.
|
||||
executeOn: failure
|
||||
executeOnTarget: Copy ctest log
|
||||
resetError: target
|
||||
maxTimeInSeconds: 60
|
||||
maxTimeBetweenOutput: 60
|
||||
|
||||
- type: Group
|
||||
instructions:
|
||||
|
||||
@@ -310,7 +310,13 @@ instructions:
|
||||
condition: property
|
||||
property: host.compiler
|
||||
equals_value: MSVC2022
|
||||
|
||||
- type: EnvironmentVariable
|
||||
variableName: VC_SCRIPT
|
||||
variableValue: "%ProgramFiles%\\Microsoft Visual Studio\\18\\Professional\\VC\\Auxiliary\\Build\\vcvarsall.bat"
|
||||
enable_if:
|
||||
condition: property
|
||||
property: host.compiler
|
||||
equals_value: MSVC2026
|
||||
|
||||
# With MSVC we need setup the environment before every subprocess call, the group below creates a script that
|
||||
# does it. It is enough to prepand it to every call (it is safe to add it even on other OSes)
|
||||
@@ -555,8 +561,12 @@ instructions:
|
||||
variableValue: >-
|
||||
-DQT_GENERATE_SBOM=ON
|
||||
-DQT_GENERATE_SOURCE_SBOM=ON
|
||||
-DQT_SBOM_REQUIRE_GENERATE_CYDX_V1_6=ON
|
||||
-DQT_SBOM_VERIFY_SPDX_V2=ON
|
||||
-DQT_SBOM_REQUIRE_GENERATE_SPDX_V2_JSON=ON
|
||||
-DQT_SBOM_REQUIRE_VERIFY_SPDX_V2=ON
|
||||
-DQT_SBOM_VERIFY_CYDX_V1_6=ON
|
||||
-DQT_SBOM_REQUIRE_GENERATE_CYDX_V1_6=ON
|
||||
-DQT_SBOM_REQUIRE_VERIFY_CYDX_V1_6=ON
|
||||
- type: AppendToEnvironmentVariable
|
||||
variableName: COMMON_CMAKE_ARGS
|
||||
variableValue: " {{.Env.SBOM_COMMON_ARGS}} "
|
||||
|
||||
@@ -1306,12 +1306,17 @@ qt_feature("coverage"
|
||||
CONDITION QT_FEATURE_coverage_gcov
|
||||
)
|
||||
|
||||
qt_feature("android_16kb_pages"
|
||||
# Mark the feature as PRIVATE because we want it to be exported, but we don't want modifications
|
||||
# in the public qconfig.h header in a patch release.
|
||||
qt_feature("android_16kb_pages" PRIVATE
|
||||
LABEL "Using 16KB page sizes in Android"
|
||||
CONDITION ANDROID AND (((CMAKE_ANDROID_NDK_VERSION VERSION_GREATER_EQUAL "25.0.0"))
|
||||
AND ((CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") OR
|
||||
(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")))
|
||||
)
|
||||
# The feature is saved into the public QT_CONFIG var in the qt_lib_core.pri file, to ensure it's
|
||||
# available automatically for testing with qtConfig() in all user projects.
|
||||
qt_feature_config("android_16kb_pages" QMAKE_PUBLIC_QT_CONFIG)
|
||||
|
||||
qt_configure_add_summary_build_type_and_config()
|
||||
qt_configure_add_summary_section(NAME "Build options")
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
/*!
|
||||
\externalpage http://qt.io/
|
||||
\externalpage https://qt.io/
|
||||
\title Qt Homepage
|
||||
*/
|
||||
/*!
|
||||
\externalpage http://bugreports.qt.io
|
||||
\externalpage https://qt-project.atlassian.net/jira/
|
||||
\title Qt Bug Tracker
|
||||
*/
|
||||
/*!
|
||||
\externalpage http://qt.io/download
|
||||
\externalpage https://qt.io/download
|
||||
\title Downloads
|
||||
*/
|
||||
/*!
|
||||
\externalpage http://www.qt.io/about-us/
|
||||
\externalpage https://www.qt.io/about-us/
|
||||
\title The Qt Company
|
||||
*/
|
||||
/*!
|
||||
\externalpage https://www.qt.io/about-us/
|
||||
\title The Qt Group
|
||||
*/
|
||||
/*!
|
||||
\externalpage https://code.qt.io/
|
||||
\title Public Qt Repository
|
||||
|
||||
@@ -57,7 +57,6 @@ macro.examplecategory = "\\meta category {\1}\n\\ingroup category \1"
|
||||
|
||||
macro.B2Q = "Boot to Qt"
|
||||
macro.B2QSS = "Boot to Qt Software Stack"
|
||||
macro.B2QST = "Boot to Qt Startup Screen"
|
||||
macro.IFW = "Qt Installer Framework"
|
||||
macro.Q4A = "Qt for Android"
|
||||
macro.QA = "Qt Assistant"
|
||||
@@ -81,6 +80,7 @@ macro.QOI = "Qt Online Installer"
|
||||
macro.QQEM = "Qt Quick Effect Maker"
|
||||
macro.QQV = "Qt QML Viewer"
|
||||
macro.QtAA = "Qt for Android Automotive"
|
||||
macro.QTOB = "Qt Onboard"
|
||||
macro.QUL = "Qt Quick Ultralite"
|
||||
macro.QtTAS = "Qt Tools for Android Studio"
|
||||
|
||||
|
||||
-1
@@ -16,7 +16,6 @@
|
||||
android:smallScreens="true" />
|
||||
<application
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:extractNativeLibs="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="Qt Notifier"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@
|
||||
|
||||
\snippet serialization/savegame/level.h 0
|
||||
|
||||
We want the levels in our game to each each have several NPCs, so we keep a QList
|
||||
We want the levels in our game to each have several NPCs, so we keep a QList
|
||||
of Character objects. We also provide the familiar fromJson() and toJson()
|
||||
functions.
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
\brief Demonstrates how to receive information sent to a multicast group.
|
||||
|
||||
This example demonstrates how to receive messages sent to a multicast group.
|
||||
\image multicastreceiver-example.webp
|
||||
\image multicastreceiver-example.webp {Received IPv4 and IPv6 multicast
|
||||
datagrams with Quit button}
|
||||
*/
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@
|
||||
classes to download a collection of images from the network and scale them,
|
||||
without blocking the UI.
|
||||
|
||||
\image imagescaling.webp
|
||||
\image imagescaling.webp {Downloaded images in a grid with Add URLs,
|
||||
Cancel buttons and Finished status}
|
||||
|
||||
The application consists of the following steps:
|
||||
|
||||
|
||||
+2
-1
@@ -18,7 +18,8 @@
|
||||
prime numbers within the list and display the total count of prime numbers
|
||||
found.
|
||||
|
||||
\image primecounter.png
|
||||
\image primecounter.png {Prime counter with reduce option selector,
|
||||
Start button, progress bars, and prime count result}
|
||||
|
||||
\include examples-run.qdocinc
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@
|
||||
an instance of \c TableModel to add, edit and remove entries to
|
||||
the address book.
|
||||
|
||||
\c{struct Contact} stores a contact and provides
|
||||
comparison and serialization via QDataStream:
|
||||
|
||||
\snippet itemviews/addressbook/contact.h 0
|
||||
|
||||
\c TableModel is a subclass of QAbstractTableModel that provides
|
||||
the standard model/view API to access data. It holds a list of
|
||||
added contacts.
|
||||
@@ -299,18 +304,10 @@
|
||||
|
||||
The constructor instantiates the \c addButton,
|
||||
\c descriptionLabel and connects the \c{addButton}'s signal to
|
||||
the \c{addEntry()} slot.
|
||||
the \c{triggered()} signal.
|
||||
|
||||
\snippet itemviews/addressbook/newaddresstab.cpp 0
|
||||
|
||||
The \c addEntry() function is similar to \c AddressWidget's
|
||||
\c addEntry() in the sense that both functions instantiate an
|
||||
\c AddDialog object. Data from the dialog is extracted and sent
|
||||
to \c AddressWidget's \c addEntry() slot by emitting the
|
||||
\c sendDetails() signal.
|
||||
|
||||
\snippet itemviews/addressbook/newaddresstab.cpp 1
|
||||
|
||||
\image signals-n-slots-aw-nat.png {Function calls for adding a new contact}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
\title Combo Widget Mapper Example
|
||||
\examplecategory {User Interface Components}
|
||||
\ingroup examples-itemviews
|
||||
\brief The Combo Widget Mapper example shows how to use a custom delegate to
|
||||
map information from a model to specific widgets on a form.
|
||||
\brief The Combo Widget Mapper example shows how to use a QDataWidgetMapper
|
||||
to map information from a model to specific widgets on a form.
|
||||
|
||||
\image combowidgetmapper-example.png
|
||||
{Application with various fields such as Name, Address, and Type}
|
||||
@@ -17,29 +17,44 @@
|
||||
can be entered, we provide a combo box to allow their addresses to be
|
||||
classified as "Home", "Work" or "Other".
|
||||
|
||||
\section1 Person Class Definition
|
||||
|
||||
We define a struct \c{Person} containing name, address and address type
|
||||
to represent the model data:
|
||||
|
||||
\snippet itemviews/combowidgetmapper/person.h Person
|
||||
|
||||
It is declared to be a Q_GADGET with the members to be properties
|
||||
of the same name to be able to conveniently populate a QRangeModel.
|
||||
|
||||
\section1 Window Class Definition
|
||||
|
||||
The class provides a constructor, a slot to keep the buttons up to date,
|
||||
and a private function to set up the model:
|
||||
The class provides a constructor and a slot to keep the buttons up to date:
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.h Window definition
|
||||
|
||||
In addition to the QDataWidgetMapper object and the controls used to make
|
||||
up the user interface, we use a QStandardItemModel to hold our data and
|
||||
a QStringListModel to hold information about the types of address that
|
||||
can be applied to each person's data.
|
||||
up the user interface, we use a QList<Person> to hold our data
|
||||
and a QRangeModel operating on it.
|
||||
|
||||
\section1 Window Class Implementation
|
||||
|
||||
The constructor of the \c Window class can be explained in three parts.
|
||||
The constructor of the \c Window class can be explained in several parts.
|
||||
In the first part, we set up the widgets used for the user interface:
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.cpp Set up widgets
|
||||
|
||||
The person list is populated using an initalizer list, which is then
|
||||
passed to the QRangeModel:
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.cpp Set up the model
|
||||
|
||||
Note that we set up the mapping the combo box in the same way as for other
|
||||
widgets, but that we apply its own model to it so that it will display
|
||||
data from its own model, the \c typeModel, rather than from the model
|
||||
containing data about each person.
|
||||
widgets, but that we apply its own model holding information about the
|
||||
types of address to it, so that it will display the address type string
|
||||
rather than the data from the model containing data about each person:
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.cpp Set up type combo box
|
||||
|
||||
Next, we set up the widget mapper, relating each input widget to a column
|
||||
in the model specified by the call to \l{QDataWidgetMapper::}{setModel()}:
|
||||
@@ -64,20 +79,6 @@
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.cpp Set up connections and layouts
|
||||
|
||||
The model is initialized in the window's \c{setupModel()} function. Here,
|
||||
we create a standard model with 5 rows and 3 columns. In each row, we
|
||||
insert a name, address, and a value that indicates the type of address.
|
||||
The address types are stored in a string list model.
|
||||
|
||||
\snippet itemviews/combowidgetmapper/window.cpp Set up the model
|
||||
|
||||
As we insert each row into the model, like a record in a database, we
|
||||
store values that correspond to items in \c typeModel for each person's
|
||||
address type. When the widget mapper reads these values from the final
|
||||
column of each row, it will need to use them as references to values in
|
||||
\c typeModel, as shown in the following diagram. This is where the
|
||||
delegate is used.
|
||||
|
||||
\image widgetmapper-combo-mapping.png
|
||||
{Mapping of the model to the view using a delegate}
|
||||
|
||||
|
||||
+15
-12
@@ -189,13 +189,13 @@
|
||||
data structure:
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 3
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 4
|
||||
|
||||
Note that whenever the user changes one of the filtering options,
|
||||
we must explicitly reapply the filter. This is done by connecting
|
||||
the various editors to functions that update the proxy model.
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 5
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 4
|
||||
|
||||
|
||||
The sorting will be handled by the view. All we have to do is to
|
||||
enable sorting for our proxy view by setting the
|
||||
@@ -203,12 +203,14 @@
|
||||
default). Then we add all the filtering widgets and the proxy view
|
||||
to a layout that we install on a corresponding group box.
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 6
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 5
|
||||
|
||||
Finally, after putting our two group boxes into another layout
|
||||
that we install on our main application widget, we customize the
|
||||
application window.
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/window.cpp 6
|
||||
|
||||
As mentioned above, we create the source model in the \c main ()
|
||||
function, calling the \c Window::setSourceModel() function to make
|
||||
the application use it:
|
||||
@@ -253,14 +255,15 @@
|
||||
model by creating the model in the \c main () function. First we
|
||||
create the application, then we create the source model:
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/main.cpp 0
|
||||
\snippet itemviews/customsortfiltermodel/main.cpp 1
|
||||
|
||||
The \c createMailModel() function is a convenience function
|
||||
provided to simplify the constructor. All it does is to create and
|
||||
return a model describing a collection of emails. The model is an
|
||||
instance of the QStandardItemModel class, i.e., a generic model
|
||||
for storing custom data typically used as a repository for
|
||||
standard Qt data types. Each mail description is added to the
|
||||
model using \c addMail(), another convenience function. See \c
|
||||
{itemviews/customsortfiltermodel/main.cpp} for details.
|
||||
We create a class \c MailModel, which inherits QRangeModel and
|
||||
reimplements QAbstractItemView::headerData() to return the correct section
|
||||
headings. It is populated from an array of struct \c MailHeader.
|
||||
\c MailHeader is declared to be a \c Q_GADGET with properties to be used
|
||||
by QRangeModel:
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/mailheader.h 0
|
||||
|
||||
\snippet itemviews/customsortfiltermodel/main.cpp 0
|
||||
*/
|
||||
|
||||
@@ -153,6 +153,10 @@ WidgetGallery::WidgetGallery(QWidget *parent)
|
||||
QGuiApplication::styleHints()->setColorScheme(static_cast<Qt::ColorScheme>(index));
|
||||
});
|
||||
|
||||
connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [colorSchemeComboBox](Qt::ColorScheme colorScheme) {
|
||||
colorSchemeComboBox->setCurrentIndex(static_cast<int>(colorScheme));
|
||||
});
|
||||
|
||||
const QKeySequence helpKeySequence(QKeySequence::HelpContents);
|
||||
const QString helpText = tr("Press <kbd>%1</kbd> over a widget to see Documentation")
|
||||
.arg(helpKeySequence.toString(QKeySequence::NativeText));
|
||||
|
||||
@@ -11,6 +11,7 @@ qt_standard_project_setup()
|
||||
qt_add_executable(addressbook
|
||||
adddialog.cpp adddialog.h
|
||||
addresswidget.cpp addresswidget.h
|
||||
contact.h
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h
|
||||
newaddresstab.cpp newaddresstab.h
|
||||
|
||||
+37
-32
@@ -2,58 +2,63 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "adddialog.h"
|
||||
#include "contact.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPushButton>
|
||||
|
||||
//! [0]
|
||||
AddDialog::AddDialog(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this)),
|
||||
nameText(new QLineEdit),
|
||||
addressText(new QTextEdit)
|
||||
addressText(new QPlainTextEdit)
|
||||
{
|
||||
auto nameLabel = new QLabel(tr("Name"));
|
||||
auto addressLabel = new QLabel(tr("Address"));
|
||||
auto okButton = new QPushButton(tr("OK"));
|
||||
auto cancelButton = new QPushButton(tr("Cancel"));
|
||||
auto *formLayout = new QFormLayout;
|
||||
formLayout->addRow(tr("Name"), nameText);
|
||||
formLayout->addRow(tr("Address"), addressText);
|
||||
|
||||
auto gLayout = new QGridLayout;
|
||||
gLayout->setColumnStretch(1, 2);
|
||||
gLayout->addWidget(nameLabel, 0, 0);
|
||||
gLayout->addWidget(nameText, 0, 1);
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addLayout(formLayout);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
|
||||
gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop);
|
||||
gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft);
|
||||
|
||||
auto buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->addWidget(okButton);
|
||||
buttonLayout->addWidget(cancelButton);
|
||||
|
||||
gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight);
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addLayout(gLayout);
|
||||
setLayout(mainLayout);
|
||||
|
||||
connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
|
||||
connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(nameText, &QLineEdit::textChanged, this, &AddDialog::updateEnabled);
|
||||
connect(addressText, &QPlainTextEdit::textChanged, this, &AddDialog::updateEnabled);
|
||||
|
||||
setWindowTitle(tr("Add a Contact"));
|
||||
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
QString AddDialog::name() const
|
||||
void AddDialog::updateEnabled()
|
||||
{
|
||||
return nameText->text();
|
||||
Contact c = contact();
|
||||
const bool valid = !c.name.isEmpty() && c.name.front().isLetter() && !c.address.isEmpty();
|
||||
buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
|
||||
}
|
||||
|
||||
QString AddDialog::address() const
|
||||
Contact AddDialog::contact() const
|
||||
{
|
||||
return addressText->toPlainText();
|
||||
return { nameText->text().trimmed(), addressText->toPlainText().trimmed() };
|
||||
}
|
||||
|
||||
void AddDialog::editAddress(const QString &name, const QString &address)
|
||||
void AddDialog::setContact(const Contact &c)
|
||||
{
|
||||
nameText->setText(c.name);
|
||||
addressText->setPlainText(c.address);
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void AddDialog::editAddress(const Contact &c)
|
||||
{
|
||||
nameText->setReadOnly(true);
|
||||
nameText->setText(name);
|
||||
addressText->setPlainText(address);
|
||||
setContact(c);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
@@ -7,27 +7,34 @@
|
||||
#include <QDialog>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QTextEdit;
|
||||
class QPlainTextEdit;
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
struct Contact;
|
||||
|
||||
//! [0]
|
||||
class AddDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AddDialog(QWidget *parent = nullptr);
|
||||
explicit AddDialog(QWidget *parent = nullptr);
|
||||
|
||||
QString name() const;
|
||||
QString address() const;
|
||||
void editAddress(const QString &name, const QString &address);
|
||||
Contact contact() const;
|
||||
void setContact(const Contact &c);
|
||||
|
||||
void editAddress(const Contact &c);
|
||||
|
||||
private slots:
|
||||
void updateEnabled();
|
||||
|
||||
private:
|
||||
QDialogButtonBox *buttonBox;
|
||||
QLineEdit *nameText;
|
||||
QTextEdit *addressText;
|
||||
QPlainTextEdit *addressText;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ SOURCES = adddialog.cpp \
|
||||
tablemodel.cpp
|
||||
HEADERS = adddialog.h \
|
||||
addresswidget.h \
|
||||
contact.h \
|
||||
mainwindow.h \
|
||||
newaddresstab.h \
|
||||
tablemodel.h
|
||||
|
||||
+98
-65
@@ -3,8 +3,33 @@
|
||||
|
||||
#include "addresswidget.h"
|
||||
#include "adddialog.h"
|
||||
#include "newaddresstab.h"
|
||||
#include "tablemodel.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QHeaderView>
|
||||
#include <QMessageBox>
|
||||
#include <QTableView>
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QItemSelection>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QModelIndex>
|
||||
#include <QRegularExpression>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QString AddressWidget::fileName()
|
||||
{
|
||||
static const QString result = QStandardPaths::standardLocations(QStandardPaths::TempLocation).value(0)
|
||||
+ "/addressbook.dat"_L1;
|
||||
return result;
|
||||
}
|
||||
|
||||
//! [0]
|
||||
AddressWidget::AddressWidget(QWidget *parent)
|
||||
@@ -12,8 +37,7 @@ AddressWidget::AddressWidget(QWidget *parent)
|
||||
table(new TableModel(this)),
|
||||
newAddressTab(new NewAddressTab(this))
|
||||
{
|
||||
connect(newAddressTab, &NewAddressTab::sendDetails,
|
||||
this, &AddressWidget::addEntry);
|
||||
connect(newAddressTab, &NewAddressTab::triggered, this, &AddressWidget::showAddEntryDialog);
|
||||
|
||||
addTab(newAddressTab, tr("Address Book"));
|
||||
|
||||
@@ -24,30 +48,33 @@ AddressWidget::AddressWidget(QWidget *parent)
|
||||
//! [2]
|
||||
void AddressWidget::showAddEntryDialog()
|
||||
{
|
||||
AddDialog aDialog;
|
||||
AddDialog aDialog(this);
|
||||
|
||||
if (aDialog.exec())
|
||||
addEntry(aDialog.name(), aDialog.address());
|
||||
if (aDialog.exec() == QDialog::Accepted)
|
||||
addEntry(aDialog.contact());
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
void AddressWidget::addEntry(const QString &name, const QString &address)
|
||||
void AddressWidget::addEntry(const Contact &contact)
|
||||
{
|
||||
if (!name.front().isLetter()) {
|
||||
QMessageBox::information(this, tr("Invalid name"),
|
||||
tr("The name must start with a letter."));
|
||||
} else if (!table->getContacts().contains({ name, address })) {
|
||||
table->insertRows(0, 1, QModelIndex());
|
||||
|
||||
QModelIndex index = table->index(0, 0, QModelIndex());
|
||||
table->setData(index, name, Qt::EditRole);
|
||||
index = table->index(0, 1, QModelIndex());
|
||||
table->setData(index, address, Qt::EditRole);
|
||||
removeTab(indexOf(newAddressTab));
|
||||
} else {
|
||||
if (table->getContacts().contains(contact)) {
|
||||
QMessageBox::information(this, tr("Duplicate Name"),
|
||||
tr("The name \"%1\" already exists.").arg(name));
|
||||
tr("The name \"%1\" already exists.").arg(contact.name));
|
||||
return;
|
||||
}
|
||||
|
||||
table->insertRows(0, 1);
|
||||
table->setData(table->index(0, 0), contact.name, Qt::EditRole);
|
||||
table->setData(table->index(0, 1), contact.address, Qt::EditRole);
|
||||
removeTab(indexOf(newAddressTab));
|
||||
|
||||
const QChar firstChar = contact.name.at(0).toUpper();
|
||||
for (int t = 0, tabCount = count(); t < tabCount; ++t) {
|
||||
if (tabText(t).contains(firstChar)) {
|
||||
setCurrentIndex(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//! [3]
|
||||
@@ -55,37 +82,35 @@ void AddressWidget::addEntry(const QString &name, const QString &address)
|
||||
//! [4a]
|
||||
void AddressWidget::editEntry()
|
||||
{
|
||||
QTableView *temp = static_cast<QTableView*>(currentWidget());
|
||||
QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
|
||||
QItemSelectionModel *selectionModel = temp->selectionModel();
|
||||
auto *tableView = static_cast<QTableView *>(currentWidget());
|
||||
auto *proxy = static_cast<QSortFilterProxyModel *>(tableView->model());
|
||||
QItemSelectionModel *selectionModel = tableView->selectionModel();
|
||||
|
||||
const QModelIndexList indexes = selectionModel->selectedRows();
|
||||
QString name;
|
||||
QString address;
|
||||
int row = -1;
|
||||
if (indexes.isEmpty())
|
||||
return;
|
||||
|
||||
for (const QModelIndex &index : indexes) {
|
||||
row = proxy->mapToSource(index).row();
|
||||
QModelIndex nameIndex = table->index(row, 0, QModelIndex());
|
||||
QVariant varName = table->data(nameIndex, Qt::DisplayRole);
|
||||
name = varName.toString();
|
||||
const int row = proxy->mapToSource(indexes.constFirst()).row();
|
||||
|
||||
QModelIndex addressIndex = table->index(row, 1, QModelIndex());
|
||||
QVariant varAddr = table->data(addressIndex, Qt::DisplayRole);
|
||||
address = varAddr.toString();
|
||||
}
|
||||
Contact contact;
|
||||
QModelIndex nameIndex = table->index(row, 0);
|
||||
QVariant varName = table->data(nameIndex, Qt::DisplayRole);
|
||||
contact.name = varName.toString();
|
||||
QModelIndex addressIndex = table->index(row, 1);
|
||||
QVariant varAddr = table->data(addressIndex, Qt::DisplayRole);
|
||||
contact.address = varAddr.toString();
|
||||
//! [4a]
|
||||
|
||||
//! [4b]
|
||||
AddDialog aDialog;
|
||||
AddDialog aDialog(this);
|
||||
aDialog.setWindowTitle(tr("Edit a Contact"));
|
||||
aDialog.editAddress(name, address);
|
||||
aDialog.editAddress(contact);
|
||||
|
||||
if (aDialog.exec()) {
|
||||
const QString newAddress = aDialog.address();
|
||||
if (newAddress != address) {
|
||||
const QModelIndex index = table->index(row, 1, QModelIndex());
|
||||
table->setData(index, newAddress, Qt::EditRole);
|
||||
if (aDialog.exec() == QDialog::Accepted) {
|
||||
const Contact newContact = aDialog.contact();
|
||||
if (newContact != contact) {
|
||||
const QModelIndex index = table->index(row, 1);
|
||||
table->setData(index, newContact.address, Qt::EditRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,18 +119,18 @@ void AddressWidget::editEntry()
|
||||
//! [5]
|
||||
void AddressWidget::removeEntry()
|
||||
{
|
||||
QTableView *temp = static_cast<QTableView*>(currentWidget());
|
||||
QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
|
||||
QItemSelectionModel *selectionModel = temp->selectionModel();
|
||||
auto *tableView = static_cast<QTableView *>(currentWidget());
|
||||
auto *proxy = static_cast<QSortFilterProxyModel *>(tableView->model());
|
||||
QItemSelectionModel *selectionModel = tableView->selectionModel();
|
||||
|
||||
const QModelIndexList indexes = selectionModel->selectedRows();
|
||||
if (indexes.isEmpty())
|
||||
return;
|
||||
|
||||
for (QModelIndex index : indexes) {
|
||||
int row = proxy->mapToSource(index).row();
|
||||
table->removeRows(row, 1, QModelIndex());
|
||||
}
|
||||
const int row = proxy->mapToSource(indexes.constFirst()).row();
|
||||
table->removeRows(row, 1);
|
||||
|
||||
if (table->rowCount(QModelIndex()) == 0)
|
||||
if (table->rowCount() == 0)
|
||||
insertTab(0, newAddressTab, tr("Address Book"));
|
||||
}
|
||||
//! [5]
|
||||
@@ -113,7 +138,6 @@ void AddressWidget::removeEntry()
|
||||
//! [1]
|
||||
void AddressWidget::setupTabs()
|
||||
{
|
||||
using namespace Qt::StringLiterals;
|
||||
const auto groups = { "ABC"_L1, "DEF"_L1, "GHI"_L1, "JKL"_L1, "MNO"_L1, "PQR"_L1,
|
||||
"STU"_L1, "VW"_L1, "XYZ"_L1 };
|
||||
|
||||
@@ -121,12 +145,12 @@ void AddressWidget::setupTabs()
|
||||
const auto regExp = QRegularExpression(QLatin1StringView("^[%1].*").arg(str),
|
||||
QRegularExpression::CaseInsensitiveOption);
|
||||
|
||||
auto proxyModel = new QSortFilterProxyModel(this);
|
||||
auto *proxyModel = new QSortFilterProxyModel(this);
|
||||
proxyModel->setSourceModel(table);
|
||||
proxyModel->setFilterRegularExpression(regExp);
|
||||
proxyModel->setFilterKeyColumn(0);
|
||||
|
||||
QTableView *tableView = new QTableView;
|
||||
auto *tableView = new QTableView;
|
||||
tableView->setModel(proxyModel);
|
||||
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
tableView->horizontalHeader()->setStretchLastSection(true);
|
||||
@@ -149,14 +173,15 @@ void AddressWidget::setupTabs()
|
||||
//! [1]
|
||||
|
||||
//! [7]
|
||||
void AddressWidget::readFromFile()
|
||||
bool AddressWidget::readFromFile()
|
||||
{
|
||||
QFile file(fileName);
|
||||
QFile file(fileName());
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::information(this, tr("Unable to open file"),
|
||||
file.errorString());
|
||||
return;
|
||||
tr("Cannot open %1: %2").arg(QDir::toNativeSeparators(fileName()),
|
||||
file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<Contact> contacts;
|
||||
@@ -166,24 +191,32 @@ void AddressWidget::readFromFile()
|
||||
if (contacts.isEmpty()) {
|
||||
QMessageBox::information(this, tr("No contacts in file"),
|
||||
tr("The file you are attempting to open contains no contacts."));
|
||||
} else {
|
||||
for (const auto &contact: std::as_const(contacts))
|
||||
addEntry(contact.name, contact.address);
|
||||
}
|
||||
|
||||
for (const auto &contact : std::as_const(contacts))
|
||||
addEntry(contact);
|
||||
|
||||
return true;
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [6]
|
||||
void AddressWidget::writeToFile()
|
||||
bool AddressWidget::writeToFile()
|
||||
{
|
||||
QFile file(fileName);
|
||||
QFile file(fileName());
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox::information(this, tr("Unable to open file"), file.errorString());
|
||||
return;
|
||||
QMessageBox::information(this, tr("Unable to open file"),
|
||||
tr("Cannot write to %1: %2").arg(QDir::toNativeSeparators(fileName()),
|
||||
file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sortedContacts = table->getContacts();
|
||||
std::sort(sortedContacts.begin(), sortedContacts.end());
|
||||
|
||||
QDataStream out(&file);
|
||||
out << table->getContacts();
|
||||
out << sortedContacts;
|
||||
return true;
|
||||
}
|
||||
//! [6]
|
||||
|
||||
+12
-13
@@ -4,31 +4,33 @@
|
||||
#ifndef ADDRESSWIDGET_H
|
||||
#define ADDRESSWIDGET_H
|
||||
|
||||
#include "newaddresstab.h"
|
||||
#include "tablemodel.h"
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QTabWidget>
|
||||
#include <QStandardPaths>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSortFilterProxyModel;
|
||||
class QItemSelectionModel;
|
||||
class QItemSelection;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
struct Contact;
|
||||
class NewAddressTab;
|
||||
class TableModel;
|
||||
|
||||
//! [0]
|
||||
class AddressWidget : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AddressWidget(QWidget *parent = nullptr);
|
||||
void readFromFile();
|
||||
void writeToFile();
|
||||
explicit AddressWidget(QWidget *parent = nullptr);
|
||||
|
||||
bool readFromFile();
|
||||
bool writeToFile();
|
||||
|
||||
static QString fileName();
|
||||
|
||||
public slots:
|
||||
void showAddEntryDialog();
|
||||
void addEntry(const QString &name, const QString &address);
|
||||
void addEntry(const Contact &contact);
|
||||
void editEntry();
|
||||
void removeEntry();
|
||||
|
||||
@@ -38,9 +40,6 @@ signals:
|
||||
private:
|
||||
void setupTabs();
|
||||
|
||||
inline static QString fileName =
|
||||
QStandardPaths::standardLocations(QStandardPaths::TempLocation).value(0)
|
||||
+ QStringLiteral("/addressbook.dat");
|
||||
TableModel *table;
|
||||
NewAddressTab *newAddressTab;
|
||||
};
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include <QScreen>
|
||||
|
||||
#include <QRect>
|
||||
|
||||
//! [0]
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
MainWindow mw;
|
||||
const auto availableGeometry = mw.screen()->availableGeometry();
|
||||
mw.resize(availableGeometry.width() / 3, availableGeometry.height() / 3);
|
||||
mw.show();
|
||||
return app.exec();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
//! [0]
|
||||
|
||||
+27
-15
@@ -6,6 +6,14 @@
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
#include <QMenuBar>
|
||||
#include <QStatusBar>
|
||||
|
||||
#include <QIcon>
|
||||
#include <QKeySequence>
|
||||
|
||||
#include <QDir>
|
||||
#include <QItemSelection>
|
||||
#include <QModelIndexList>
|
||||
|
||||
//! [0]
|
||||
MainWindow::MainWindow()
|
||||
@@ -23,24 +31,31 @@ void MainWindow::createMenus()
|
||||
{
|
||||
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
||||
|
||||
QAction *openAct = new QAction(tr("&Open..."), this);
|
||||
auto *openAct = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpen),
|
||||
tr("&Open..."), this);
|
||||
openAct->setShortcut(QKeySequence(QKeySequence::Open));
|
||||
fileMenu->addAction(openAct);
|
||||
connect(openAct, &QAction::triggered, this, &MainWindow::openFile);
|
||||
//! [1a]
|
||||
|
||||
QAction *saveAct = new QAction(tr("&Save"), this);
|
||||
auto *saveAct = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::DocumentSave),
|
||||
tr("&Save"), this);
|
||||
saveAct->setShortcut(QKeySequence(QKeySequence::Save));
|
||||
fileMenu->addAction(saveAct);
|
||||
connect(saveAct, &QAction::triggered, this, &MainWindow::saveFile);
|
||||
|
||||
fileMenu->addSeparator();
|
||||
|
||||
QAction *exitAct = new QAction(tr("E&xit"), this);
|
||||
auto *exitAct = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit),
|
||||
tr("E&xit"), this);
|
||||
exitAct->setShortcut(QKeySequence(QKeySequence::Quit));
|
||||
fileMenu->addAction(exitAct);
|
||||
connect(exitAct, &QAction::triggered, this, &QWidget::close);
|
||||
|
||||
QMenu *toolMenu = menuBar()->addMenu(tr("&Tools"));
|
||||
|
||||
QAction *addAct = new QAction(tr("&Add Entry..."), this);
|
||||
auto *addAct = new QAction(tr("&Add Entry..."), this);
|
||||
addAct->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_A));
|
||||
toolMenu->addAction(addAct);
|
||||
connect(addAct, &QAction::triggered,
|
||||
addressWidget, &AddressWidget::showAddEntryDialog);
|
||||
@@ -59,35 +74,32 @@ void MainWindow::createMenus()
|
||||
connect(removeAct, &QAction::triggered, addressWidget, &AddressWidget::removeEntry);
|
||||
|
||||
connect(addressWidget, &AddressWidget::selectionChanged,
|
||||
this, &MainWindow::updateActions);
|
||||
this, &MainWindow::updateActions);
|
||||
}
|
||||
//! [1b]
|
||||
|
||||
//! [2]
|
||||
void MainWindow::openFile()
|
||||
{
|
||||
addressWidget->readFromFile();
|
||||
if (addressWidget->readFromFile())
|
||||
statusBar()->showMessage(tr("Read %1").arg(QDir::toNativeSeparators(AddressWidget::fileName())));
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
void MainWindow::saveFile()
|
||||
{
|
||||
addressWidget->writeToFile();
|
||||
if (addressWidget->writeToFile())
|
||||
statusBar()->showMessage(tr("Wrote %1").arg(QDir::toNativeSeparators(AddressWidget::fileName())));
|
||||
}
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
void MainWindow::updateActions(const QItemSelection &selection)
|
||||
{
|
||||
QModelIndexList indexes = selection.indexes();
|
||||
const QModelIndexList indexes = selection.indexes();
|
||||
|
||||
if (!indexes.isEmpty()) {
|
||||
removeAct->setEnabled(true);
|
||||
editAct->setEnabled(true);
|
||||
} else {
|
||||
removeAct->setEnabled(false);
|
||||
editAct->setEnabled(false);
|
||||
}
|
||||
removeAct->setEnabled(!indexes.isEmpty());
|
||||
editAct->setEnabled(!indexes.isEmpty());
|
||||
}
|
||||
//! [4]
|
||||
|
||||
+9
-20
@@ -2,35 +2,24 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "newaddresstab.h"
|
||||
#include "adddialog.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
//! [0]
|
||||
NewAddressTab::NewAddressTab(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
auto descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
|
||||
"\nClick Add to add new contacts."));
|
||||
auto *descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
|
||||
"\nClick Add to add new contacts."));
|
||||
|
||||
auto addButton = new QPushButton(tr("Add"));
|
||||
auto *addButton = new QPushButton(tr("Add"));
|
||||
|
||||
connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::addEntry);
|
||||
connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::triggered);
|
||||
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(descriptionLabel);
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(descriptionLabel, 0, Qt::AlignCenter);
|
||||
mainLayout->addWidget(addButton, 0, Qt::AlignCenter);
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void NewAddressTab::addEntry()
|
||||
{
|
||||
AddDialog aDialog;
|
||||
|
||||
if (aDialog.exec())
|
||||
emit sendDetails(aDialog.name(), aDialog.address());
|
||||
}
|
||||
//! [1]
|
||||
|
||||
+2
-11
@@ -6,25 +6,16 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
//! [0]
|
||||
class NewAddressTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NewAddressTab(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void addEntry();
|
||||
explicit NewAddressTab(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void sendDetails(const QString &name, const QString &address);
|
||||
void triggered();
|
||||
};
|
||||
//! [0]
|
||||
|
||||
|
||||
+5
-8
@@ -31,10 +31,10 @@ int TableModel::columnCount(const QModelIndex &parent) const
|
||||
QVariant TableModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
return {};
|
||||
|
||||
if (index.row() >= contacts.size() || index.row() < 0)
|
||||
return QVariant();
|
||||
return {};
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
const auto &contact = contacts.at(index.row());
|
||||
@@ -48,17 +48,14 @@ QVariant TableModel::data(const QModelIndex &index, int role) const
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
if (orientation == Qt::Horizontal) {
|
||||
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Name");
|
||||
@@ -68,7 +65,7 @@ QVariant TableModel::headerData(int section, Qt::Orientation orientation, int ro
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
//! [3]
|
||||
|
||||
|
||||
+10
-29
@@ -4,48 +4,29 @@
|
||||
#ifndef TABLEMODEL_H
|
||||
#define TABLEMODEL_H
|
||||
|
||||
#include "contact.h"
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QList>
|
||||
|
||||
//! [0]
|
||||
|
||||
struct Contact
|
||||
{
|
||||
QString name;
|
||||
QString address;
|
||||
|
||||
bool operator==(const Contact &other) const
|
||||
{
|
||||
return name == other.name && address == other.address;
|
||||
}
|
||||
};
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &stream, const Contact &contact)
|
||||
{
|
||||
return stream << contact.name << contact.address;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &stream, Contact &contact)
|
||||
{
|
||||
return stream >> contact.name >> contact.address;
|
||||
}
|
||||
|
||||
class TableModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TableModel(QObject *parent = nullptr);
|
||||
explicit TableModel(QObject *parent = nullptr);
|
||||
TableModel(const QList<Contact> &contacts, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
int rowCount(const QModelIndex &parent = {}) const override;
|
||||
int columnCount(const QModelIndex &parent = {}) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
|
||||
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
|
||||
bool insertRows(int position, int rows, const QModelIndex &index = {}) override;
|
||||
bool removeRows(int position, int rows, const QModelIndex &index = {}) override;
|
||||
|
||||
const QList<Contact> &getContacts() const;
|
||||
|
||||
private:
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(basicsortfiltermodel
|
||||
main.cpp
|
||||
window.cpp window.h
|
||||
window.cpp window.h mailheader.h
|
||||
)
|
||||
|
||||
set_target_properties(basicsortfiltermodel PROPERTIES
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
QT += widgets
|
||||
requires(qtConfig(combobox))
|
||||
|
||||
HEADERS = window.h
|
||||
HEADERS = window.h \
|
||||
mailheader.h
|
||||
SOURCES = main.cpp \
|
||||
window.cpp
|
||||
|
||||
|
||||
+55
-41
@@ -1,58 +1,72 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mailheader.h"
|
||||
#include "window.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QStandardItemModel>
|
||||
#include <QTime>
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
void addMail(QAbstractItemModel *model, const QString &subject,
|
||||
const QString &sender, const QDateTime &date)
|
||||
#include <QtCore/QRangeModel>
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class MailModel : public QRangeModel
|
||||
{
|
||||
model->insertRow(0);
|
||||
model->setData(model->index(0, 0), subject);
|
||||
model->setData(model->index(0, 1), sender);
|
||||
model->setData(model->index(0, 2), date);
|
||||
}
|
||||
public:
|
||||
explicit MailModel(QObject *parent = nullptr) : QRangeModel(mails, parent) {}
|
||||
|
||||
QAbstractItemModel *createMailModel(QObject *parent)
|
||||
{
|
||||
QStandardItemModel *model = new QStandardItemModel(0, 3, parent);
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return Window::tr("Subject");
|
||||
case 1:
|
||||
return Window::tr("Sender");
|
||||
case 2:
|
||||
return Window::tr("Date");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QRangeModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Subject"));
|
||||
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Sender"));
|
||||
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Date"));
|
||||
private:
|
||||
static const std::array<MailHeader, 10> mails;
|
||||
};
|
||||
|
||||
addMail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
|
||||
QDateTime(QDate(2006, 12, 31), QTime(17, 03)));
|
||||
addMail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
|
||||
QDateTime(QDate(2006, 12, 22), QTime(9, 44)));
|
||||
addMail(model, "Accounts", "pascale@nospam.com",
|
||||
QDateTime(QDate(2006, 12, 31), QTime(12, 50)));
|
||||
addMail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
|
||||
QDateTime(QDate(2006, 12, 25), QTime(11, 39)));
|
||||
addMail(model, "Re: Expenses", "Andy <andy@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 02), QTime(16, 05)));
|
||||
addMail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 18)));
|
||||
addMail(model, "Re: Accounts", "Andy <andy@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 26)));
|
||||
addMail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(11, 33)));
|
||||
addMail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 00)));
|
||||
addMail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 01)));
|
||||
|
||||
return model;
|
||||
}
|
||||
const std::array<MailHeader, 10> MailModel::mails = {
|
||||
MailHeader{ u"RE: Sports"_s, u"Petra Schmidt <petras@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 01)) },
|
||||
MailHeader{ u"AW: Sports"_s, u"Rolf Newschweinstein <rolfn@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 00)) },
|
||||
MailHeader{ u"Sports"_s, u"Linda Smith <linda.smith@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(11, 33)) },
|
||||
MailHeader{ u"Re: Accounts"_s, u"Andy <andy@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 26)) },
|
||||
MailHeader{ u"Re: Accounts"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 18)) },
|
||||
MailHeader{ u"Re: Expenses"_s, u"Andy <andy@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 02), QTime(16, 05)) },
|
||||
MailHeader{ u"Expenses"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 25), QTime(11, 39)) },
|
||||
MailHeader{ u"Accounts"_s, u"pascale@nospam.com"_s,
|
||||
QDateTime(QDate(2006, 12, 31), QTime(12, 50)) },
|
||||
MailHeader{ u"Radically new concept"_s, u"Grace K. <grace@software-inc.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 22), QTime(9, 44)) },
|
||||
MailHeader{ u"Happy New Year!"_s, u"Grace K. <grace@software-inc.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 31), QTime(17, 03)) }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
Window window;
|
||||
window.setSourceModel(createMailModel(&window));
|
||||
window.setSourceModel(new MailModel(&window));
|
||||
window.show();
|
||||
return app.exec();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
|
||||
+50
-38
@@ -1,10 +1,26 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include <QtWidgets/QCheckBox>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <QtWidgets/QGroupBox>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QTreeView>
|
||||
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
static inline QColor textColor(const QPalette &palette)
|
||||
{
|
||||
return palette.color(QPalette::Active, QPalette::Text);
|
||||
@@ -20,40 +36,32 @@ static void setTextColor(QWidget *w, const QColor &c)
|
||||
}
|
||||
|
||||
Window::Window()
|
||||
: proxyModel(new QSortFilterProxyModel),
|
||||
sourceView(new QTreeView),
|
||||
proxyView(new QTreeView),
|
||||
sortCaseSensitivityCheckBox(new QCheckBox(tr("Case sensitive sorting"))),
|
||||
filterCaseSensitivityCheckBox(new QCheckBox(tr("Case sensitive filter"))),
|
||||
filterPatternLineEdit(new QLineEdit),
|
||||
filterSyntaxComboBox(new QComboBox),
|
||||
filterColumnComboBox(new QComboBox)
|
||||
{
|
||||
proxyModel = new QSortFilterProxyModel;
|
||||
|
||||
sourceView = new QTreeView;
|
||||
sourceView->setRootIsDecorated(false);
|
||||
sourceView->setAlternatingRowColors(true);
|
||||
|
||||
proxyView = new QTreeView;
|
||||
proxyView->setRootIsDecorated(false);
|
||||
proxyView->setAlternatingRowColors(true);
|
||||
proxyView->setModel(proxyModel);
|
||||
proxyView->setSortingEnabled(true);
|
||||
|
||||
sortCaseSensitivityCheckBox = new QCheckBox(tr("Case sensitive sorting"));
|
||||
filterCaseSensitivityCheckBox = new QCheckBox(tr("Case sensitive filter"));
|
||||
|
||||
filterPatternLineEdit = new QLineEdit;
|
||||
filterPatternLineEdit->setClearButtonEnabled(true);
|
||||
filterPatternLabel = new QLabel(tr("&Filter pattern:"));
|
||||
filterPatternLabel->setBuddy(filterPatternLineEdit);
|
||||
|
||||
filterSyntaxComboBox = new QComboBox;
|
||||
filterSyntaxComboBox->addItem(tr("Regular expression"), RegularExpression);
|
||||
filterSyntaxComboBox->addItem(tr("Wildcard"), Wildcard);
|
||||
filterSyntaxComboBox->addItem(tr("Fixed string"), FixedString);
|
||||
filterSyntaxLabel = new QLabel(tr("Filter &syntax:"));
|
||||
filterSyntaxLabel->setBuddy(filterSyntaxComboBox);
|
||||
|
||||
filterColumnComboBox = new QComboBox;
|
||||
filterColumnComboBox->addItem(tr("Subject"));
|
||||
filterColumnComboBox->addItem(tr("Sender"));
|
||||
filterColumnComboBox->addItem(tr("Date"));
|
||||
filterColumnLabel = new QLabel(tr("Filter &column:"));
|
||||
filterColumnLabel->setBuddy(filterColumnComboBox);
|
||||
|
||||
connect(filterPatternLineEdit, &QLineEdit::textChanged,
|
||||
this, &Window::filterRegularExpressionChanged);
|
||||
@@ -66,39 +74,38 @@ Window::Window()
|
||||
connect(sortCaseSensitivityCheckBox, &QAbstractButton::toggled,
|
||||
this, &Window::sortChanged);
|
||||
|
||||
sourceGroupBox = new QGroupBox(tr("Original Model"));
|
||||
proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
|
||||
auto *sourceGroupBox = new QGroupBox(tr("Original Model"));
|
||||
auto *proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
|
||||
|
||||
QHBoxLayout *sourceLayout = new QHBoxLayout;
|
||||
auto *sourceLayout = new QHBoxLayout(sourceGroupBox);
|
||||
sourceLayout->addWidget(sourceView);
|
||||
sourceGroupBox->setLayout(sourceLayout);
|
||||
|
||||
QGridLayout *proxyLayout = new QGridLayout;
|
||||
proxyLayout->addWidget(proxyView, 0, 0, 1, 3);
|
||||
proxyLayout->addWidget(filterPatternLabel, 1, 0);
|
||||
proxyLayout->addWidget(filterPatternLineEdit, 1, 1, 1, 2);
|
||||
proxyLayout->addWidget(filterSyntaxLabel, 2, 0);
|
||||
proxyLayout->addWidget(filterSyntaxComboBox, 2, 1, 1, 2);
|
||||
proxyLayout->addWidget(filterColumnLabel, 3, 0);
|
||||
proxyLayout->addWidget(filterColumnComboBox, 3, 1, 1, 2);
|
||||
proxyLayout->addWidget(filterCaseSensitivityCheckBox, 4, 0, 1, 2);
|
||||
proxyLayout->addWidget(sortCaseSensitivityCheckBox, 4, 2);
|
||||
proxyGroupBox->setLayout(proxyLayout);
|
||||
auto *proxyLayout = new QVBoxLayout(proxyGroupBox);
|
||||
proxyLayout->addWidget(proxyView);
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *filterLayout = new QFormLayout;
|
||||
filterLayout->addRow(tr("&Filter pattern:"), filterPatternLineEdit);
|
||||
filterLayout->addRow(tr("Filter &syntax:"), filterSyntaxComboBox);
|
||||
filterLayout->addRow(tr("Filter &column:"), filterColumnComboBox);
|
||||
proxyLayout->addLayout(filterLayout);
|
||||
auto *checkBoxLayout = new QHBoxLayout();
|
||||
checkBoxLayout->addWidget(filterCaseSensitivityCheckBox);
|
||||
checkBoxLayout->addWidget(sortCaseSensitivityCheckBox);
|
||||
checkBoxLayout->addStretch();
|
||||
proxyLayout->addLayout(checkBoxLayout);
|
||||
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(sourceGroupBox);
|
||||
mainLayout->addWidget(proxyGroupBox);
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(tr("Basic Sort/Filter Model"));
|
||||
resize(500, 450);
|
||||
auto screenGeometry = screen()->geometry();
|
||||
resize(screenGeometry.width() / 2, screenGeometry.height() * 2 / 3);
|
||||
|
||||
proxyView->sortByColumn(1, Qt::AscendingOrder);
|
||||
filterColumnComboBox->setCurrentIndex(1);
|
||||
|
||||
filterPatternLineEdit->setText("Andy|Grace");
|
||||
filterPatternLineEdit->setText(u"Andy|Grace"_s);
|
||||
filterCaseSensitivityCheckBox->setChecked(true);
|
||||
sortCaseSensitivityCheckBox->setChecked(true);
|
||||
}
|
||||
@@ -107,6 +114,11 @@ void Window::setSourceModel(QAbstractItemModel *model)
|
||||
{
|
||||
proxyModel->setSourceModel(model);
|
||||
sourceView->setModel(model);
|
||||
|
||||
for (int i = 0; i < proxyModel->columnCount(); ++i)
|
||||
proxyView->resizeColumnToContents(i);
|
||||
for (int i = 0; i < model->columnCount(); ++i)
|
||||
sourceView->resizeColumnToContents(i);
|
||||
}
|
||||
|
||||
void Window::filterRegularExpressionChanged()
|
||||
|
||||
+2
-9
@@ -4,14 +4,12 @@
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtWidgets/QWidget>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractItemModel;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QSortFilterProxyModel;
|
||||
class QTreeView;
|
||||
@@ -34,15 +32,10 @@ private slots:
|
||||
private:
|
||||
QSortFilterProxyModel *proxyModel;
|
||||
|
||||
QGroupBox *sourceGroupBox;
|
||||
QGroupBox *proxyGroupBox;
|
||||
QTreeView *sourceView;
|
||||
QTreeView *proxyView;
|
||||
QCheckBox *filterCaseSensitivityCheckBox;
|
||||
QCheckBox *sortCaseSensitivityCheckBox;
|
||||
QLabel *filterPatternLabel;
|
||||
QLabel *filterSyntaxLabel;
|
||||
QLabel *filterColumnLabel;
|
||||
QCheckBox *filterCaseSensitivityCheckBox;
|
||||
QLineEdit *filterPatternLineEdit;
|
||||
enum Syntax {
|
||||
RegularExpression,
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ qt_standard_project_setup()
|
||||
qt_add_executable(combowidgetmapper
|
||||
main.cpp
|
||||
window.cpp window.h
|
||||
person.h
|
||||
)
|
||||
|
||||
set_target_properties(combowidgetmapper PROPERTIES
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
QT += widgets
|
||||
requires(qtConfig(combobox))
|
||||
|
||||
HEADERS = window.h
|
||||
HEADERS = window.h person.h
|
||||
SOURCES = main.cpp \
|
||||
window.cpp
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QApplication>
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
@@ -10,5 +10,5 @@ int main(int argc, char **argv)
|
||||
QApplication app(argc, argv);
|
||||
Window window;
|
||||
window.show();
|
||||
return app.exec();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
|
||||
+46
-62
@@ -1,33 +1,46 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QtWidgets>
|
||||
#include "window.h"
|
||||
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDataWidgetMapper>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <QtWidgets/QTextEdit>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
|
||||
#include <QtCore/QRangeModel>
|
||||
#include <QtCore/QStringListModel>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
//! [Set up widgets]
|
||||
Window::Window(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupModel();
|
||||
|
||||
nameLabel = new QLabel(tr("Na&me:"));
|
||||
nameEdit = new QLineEdit();
|
||||
addressLabel = new QLabel(tr("&Address:"));
|
||||
addressEdit = new QTextEdit();
|
||||
typeLabel = new QLabel(tr("&Type:"));
|
||||
typeComboBox = new QComboBox();
|
||||
nextButton = new QPushButton(tr("&Next"));
|
||||
previousButton = new QPushButton(tr("&Previous"));
|
||||
|
||||
nameLabel->setBuddy(nameEdit);
|
||||
addressLabel->setBuddy(addressEdit);
|
||||
typeLabel->setBuddy(typeComboBox);
|
||||
|
||||
typeComboBox->setModel(typeModel);
|
||||
: QWidget(parent),
|
||||
nameEdit(new QLineEdit),
|
||||
addressEdit(new QTextEdit),
|
||||
typeComboBox(new QComboBox),
|
||||
nextButton(new QPushButton(tr("&Next"))),
|
||||
previousButton(new QPushButton(tr("&Previous"))),
|
||||
//! [Set up widgets]
|
||||
//! [Set up the model]
|
||||
data{Person{u"Alice"_s, u"<qt>123 Main Street<br/>Market Town</qt>"_s, u"0"_s},
|
||||
Person{u"Bob"_s, u"<qt>PO Box 32<br/>Mail Handling Service<br/>Service City</qt>"_s, u"1"_s},
|
||||
Person{u"Carol"_s, u"<qt>The Lighthouse<br/>Remote Island</qt>"_s, u"2"_s},
|
||||
Person{u"Donald"_s, u"<qt>47338 Park Avenue<br/>Big City</qt>"_s, u"0"_s},
|
||||
Person{u"Emma"_s, u"<qt>Research Station<br/>Base Camp<br/>Big Mountain</qt>"_s, u"2"_s}},
|
||||
model(new QRangeModel(data, this)),
|
||||
mapper(new QDataWidgetMapper(this))
|
||||
//! [Set up the model]
|
||||
{
|
||||
//! [Set up type combo box]
|
||||
typeComboBox->setModel(new QStringListModel({ tr("Home"), tr("Work"), tr("Other") }, this));
|
||||
//! [Set up type combo box]
|
||||
|
||||
//! [Set up the mapper]
|
||||
mapper = new QDataWidgetMapper(this);
|
||||
mapper->setModel(model);
|
||||
mapper->addMapping(nameEdit, 0);
|
||||
mapper->addMapping(addressEdit, 1);
|
||||
@@ -42,54 +55,25 @@ Window::Window(QWidget *parent)
|
||||
connect(mapper, &QDataWidgetMapper::currentIndexChanged,
|
||||
this, &Window::updateButtons);
|
||||
|
||||
QGridLayout *layout = new QGridLayout();
|
||||
layout->addWidget(nameLabel, 0, 0, 1, 1);
|
||||
layout->addWidget(nameEdit, 0, 1, 1, 1);
|
||||
layout->addWidget(previousButton, 0, 2, 1, 1);
|
||||
layout->addWidget(addressLabel, 1, 0, 1, 1);
|
||||
layout->addWidget(addressEdit, 1, 1, 2, 1);
|
||||
layout->addWidget(nextButton, 1, 2, 1, 1);
|
||||
layout->addWidget(typeLabel, 3, 0, 1, 1);
|
||||
layout->addWidget(typeComboBox, 3, 1, 1, 1);
|
||||
setLayout(layout);
|
||||
auto *formLayout = new QFormLayout;
|
||||
formLayout->addRow(tr("Na&me:"), nameEdit);
|
||||
formLayout->addRow(tr("&Address:"), addressEdit);
|
||||
formLayout->addRow(tr("&Type:"), typeComboBox);
|
||||
|
||||
auto *buttonLayout = new QVBoxLayout;
|
||||
buttonLayout->addWidget(previousButton);
|
||||
buttonLayout->addWidget(nextButton);
|
||||
buttonLayout->addStretch();
|
||||
|
||||
auto *mainLayout = new QHBoxLayout(this);
|
||||
mainLayout->addLayout(formLayout);
|
||||
mainLayout->addLayout(buttonLayout);
|
||||
|
||||
setWindowTitle(tr("Delegate Widget Mapper"));
|
||||
mapper->toFirst();
|
||||
}
|
||||
//! [Set up connections and layouts]
|
||||
|
||||
//! [Set up the model]
|
||||
void Window::setupModel()
|
||||
{
|
||||
QStringList items;
|
||||
items << tr("Home") << tr("Work") << tr("Other");
|
||||
typeModel = new QStringListModel(items, this);
|
||||
|
||||
model = new QStandardItemModel(5, 3, this);
|
||||
QStringList names;
|
||||
names << "Alice" << "Bob" << "Carol" << "Donald" << "Emma";
|
||||
QStringList addresses;
|
||||
addresses << "<qt>123 Main Street<br/>Market Town</qt>"
|
||||
<< "<qt>PO Box 32<br/>Mail Handling Service"
|
||||
"<br/>Service City</qt>"
|
||||
<< "<qt>The Lighthouse<br/>Remote Island</qt>"
|
||||
<< "<qt>47338 Park Avenue<br/>Big City</qt>"
|
||||
<< "<qt>Research Station<br/>Base Camp<br/>Big Mountain</qt>";
|
||||
|
||||
QStringList types;
|
||||
types << "0" << "1" << "2" << "0" << "2";
|
||||
|
||||
for (int row = 0; row < 5; ++row) {
|
||||
QStandardItem *item = new QStandardItem(names[row]);
|
||||
model->setItem(row, 0, item);
|
||||
item = new QStandardItem(addresses[row]);
|
||||
model->setItem(row, 1, item);
|
||||
item = new QStandardItem(types[row]);
|
||||
model->setItem(row, 2, item);
|
||||
}
|
||||
}
|
||||
//! [Set up the model]
|
||||
|
||||
//! [Slot for updating the buttons]
|
||||
void Window::updateButtons(int row)
|
||||
{
|
||||
|
||||
+7
-12
@@ -4,16 +4,17 @@
|
||||
#ifndef WINDOW_H
|
||||
#define WINDOW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "person.h"
|
||||
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtCore/QList>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QDataWidgetMapper;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QStandardItemModel;
|
||||
class QStringListModel;
|
||||
class QRangeModel;
|
||||
class QTextEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -29,19 +30,13 @@ private slots:
|
||||
void updateButtons(int row);
|
||||
|
||||
private:
|
||||
void setupModel();
|
||||
|
||||
QLabel *nameLabel;
|
||||
QLabel *addressLabel;
|
||||
QLabel *typeLabel;
|
||||
QLineEdit *nameEdit;
|
||||
QTextEdit *addressEdit;
|
||||
QComboBox *typeComboBox;
|
||||
QPushButton *nextButton;
|
||||
QPushButton *previousButton;
|
||||
|
||||
QStandardItemModel *model;
|
||||
QStringListModel *typeModel;
|
||||
QList<Person> data;
|
||||
QRangeModel *model;
|
||||
QDataWidgetMapper *mapper;
|
||||
};
|
||||
//! [Window definition]
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(customsortfiltermodel
|
||||
filterwidget.cpp filterwidget.h
|
||||
main.cpp
|
||||
main.cpp mailheader.h
|
||||
mysortfilterproxymodel.cpp mysortfilterproxymodel.h
|
||||
window.cpp window.h
|
||||
)
|
||||
|
||||
+2
-1
@@ -3,7 +3,8 @@ requires(qtConfig(treeview))
|
||||
|
||||
HEADERS = mysortfilterproxymodel.h \
|
||||
window.h \
|
||||
filterwidget.h
|
||||
filterwidget.h \
|
||||
mailheader.h
|
||||
SOURCES = main.cpp \
|
||||
mysortfilterproxymodel.cpp \
|
||||
window.cpp \
|
||||
|
||||
+16
-14
@@ -3,13 +3,15 @@
|
||||
|
||||
#include "filterwidget.h"
|
||||
|
||||
#include <QIcon>
|
||||
#include <QPixmap>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QToolButton>
|
||||
#include <QWidgetAction>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QToolButton>
|
||||
#include <QtWidgets/QWidgetAction>
|
||||
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QActionGroup>
|
||||
#include <QtGui/QIcon>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
FilterWidget::FilterWidget(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
@@ -18,30 +20,30 @@ FilterWidget::FilterWidget(QWidget *parent)
|
||||
setClearButtonEnabled(true);
|
||||
connect(this, &QLineEdit::textChanged, this, &FilterWidget::filterChanged);
|
||||
|
||||
QMenu *menu = new QMenu(this);
|
||||
auto *menu = new QMenu(this);
|
||||
m_caseSensitivityAction = menu->addAction(tr("Case Sensitive"));
|
||||
m_caseSensitivityAction->setCheckable(true);
|
||||
connect(m_caseSensitivityAction, &QAction::toggled, this, &FilterWidget::filterChanged);
|
||||
|
||||
menu->addSeparator();
|
||||
m_patternGroup->setExclusive(true);
|
||||
QAction *patternAction = menu->addAction("Regular Expression");
|
||||
QAction *patternAction = menu->addAction(tr("Regular Expression"));
|
||||
patternAction->setCheckable(true);
|
||||
patternAction->setChecked(true);
|
||||
patternAction->setData(QVariant(int(RegularExpression)));
|
||||
m_patternGroup->addAction(patternAction);
|
||||
patternAction = menu->addAction("Wildcard");
|
||||
patternAction = menu->addAction(tr("Wildcard"));
|
||||
patternAction->setCheckable(true);
|
||||
patternAction->setData(QVariant(int(Wildcard)));
|
||||
m_patternGroup->addAction(patternAction);
|
||||
patternAction = menu->addAction("Fixed String");
|
||||
patternAction = menu->addAction(tr("Fixed String"));
|
||||
patternAction->setData(QVariant(int(FixedString)));
|
||||
patternAction->setCheckable(true);
|
||||
m_patternGroup->addAction(patternAction);
|
||||
connect(m_patternGroup, &QActionGroup::triggered, this, &FilterWidget::filterChanged);
|
||||
|
||||
const QIcon icon = QIcon(QPixmap(":/images/find.png"));
|
||||
QToolButton *optionsButton = new QToolButton;
|
||||
const QIcon icon = QIcon(u":/images/find.png"_s);
|
||||
auto *optionsButton = new QToolButton;
|
||||
#ifndef QT_NO_CURSOR
|
||||
optionsButton->setCursor(Qt::ArrowCursor);
|
||||
#endif
|
||||
@@ -50,7 +52,7 @@ FilterWidget::FilterWidget(QWidget *parent)
|
||||
optionsButton->setMenu(menu);
|
||||
optionsButton->setPopupMode(QToolButton::InstantPopup);
|
||||
|
||||
QWidgetAction *optionsAction = new QWidgetAction(this);
|
||||
auto *optionsAction = new QWidgetAction(this);
|
||||
optionsAction->setDefaultWidget(optionsButton);
|
||||
addAction(optionsAction, QLineEdit::LeadingPosition);
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
#ifndef FILTERWIDGET_H
|
||||
#define FILTERWIDGET_H
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
|
||||
+60
-42
@@ -1,58 +1,76 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "mailheader.h"
|
||||
#include "window.h"
|
||||
|
||||
void addMail(QAbstractItemModel *model, const QString &subject,
|
||||
const QString &sender, const QDateTime &date)
|
||||
{
|
||||
model->insertRow(0);
|
||||
model->setData(model->index(0, 0), subject);
|
||||
model->setData(model->index(0, 1), sender);
|
||||
model->setData(model->index(0, 2), date);
|
||||
}
|
||||
#include <QtWidgets/QApplication>
|
||||
|
||||
QAbstractItemModel *createMailModel(QObject *parent)
|
||||
{
|
||||
QStandardItemModel *model = new QStandardItemModel(0, 3, parent);
|
||||
#include <QtCore/QRangeModel>
|
||||
|
||||
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Subject"));
|
||||
model->setHeaderData(1, Qt::Horizontal, QObject::tr("Sender"));
|
||||
model->setHeaderData(2, Qt::Horizontal, QObject::tr("Date"));
|
||||
#include <array>
|
||||
|
||||
addMail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
|
||||
QDateTime(QDate(2006, 12, 31), QTime(17, 03)));
|
||||
addMail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
|
||||
QDateTime(QDate(2006, 12, 22), QTime(9, 44)));
|
||||
addMail(model, "Accounts", "pascale@nospam.com",
|
||||
QDateTime(QDate(2006, 12, 31), QTime(12, 50)));
|
||||
addMail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
|
||||
QDateTime(QDate(2006, 12, 25), QTime(11, 39)));
|
||||
addMail(model, "Re: Expenses", "Andy <andy@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 02), QTime(16, 05)));
|
||||
addMail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 18)));
|
||||
addMail(model, "Re: Accounts", "Andy <andy@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 26)));
|
||||
addMail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(11, 33)));
|
||||
addMail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 00)));
|
||||
addMail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 01)));
|
||||
|
||||
return model;
|
||||
}
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
//! [0]
|
||||
class MailModel : public QRangeModel
|
||||
{
|
||||
public:
|
||||
explicit MailModel(QObject *parent = nullptr) : QRangeModel(mails, parent) {}
|
||||
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return Window::tr("Subject");
|
||||
case 1:
|
||||
return Window::tr("Sender");
|
||||
case 2:
|
||||
return Window::tr("Date");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QRangeModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
private:
|
||||
static const std::array<MailHeader, 10> mails;
|
||||
};
|
||||
|
||||
const std::array<MailHeader, 10> MailModel::mails = {
|
||||
MailHeader{ u"RE: Sports"_s, u"Petra Schmidt <petras@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 01)) },
|
||||
MailHeader{ u"AW: Sports"_s, u"Rolf Newschweinstein <rolfn@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(12, 00)) },
|
||||
MailHeader{ u"Sports"_s, u"Linda Smith <linda.smith@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 05), QTime(11, 33)) },
|
||||
MailHeader{ u"Re: Accounts"_s, u"Andy <andy@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 26)) },
|
||||
MailHeader{ u"Re: Accounts"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 03), QTime(14, 18)) },
|
||||
MailHeader{ u"Re: Expenses"_s, u"Andy <andy@nospam.com>"_s,
|
||||
QDateTime(QDate(2007, 01, 02), QTime(16, 05)) },
|
||||
MailHeader{ u"Expenses"_s, u"Joe Bloggs <joe@bloggs.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 25), QTime(11, 39)) },
|
||||
MailHeader{ u"Accounts"_s, u"pascale@nospam.com"_s,
|
||||
QDateTime(QDate(2006, 12, 31), QTime(12, 50)) },
|
||||
MailHeader{ u"Radically new concept"_s, u"Grace K. <grace@software-inc.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 22), QTime(9, 44)) },
|
||||
MailHeader{ u"Happy New Year!"_s, u"Grace K. <grace@software-inc.com>"_s,
|
||||
QDateTime(QDate(2006, 12, 31), QTime(17, 03)) }
|
||||
};
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
Window window;
|
||||
window.setSourceModel(createMailModel(&window));
|
||||
window.setSourceModel(new MailModel(&window));
|
||||
window.show();
|
||||
return app.exec();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
//! [0]
|
||||
//! [1]
|
||||
|
||||
+19
-17
@@ -3,7 +3,10 @@
|
||||
|
||||
#include "mysortfilterproxymodel.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtCore/QModelIndex>
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
//! [0]
|
||||
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
|
||||
@@ -53,26 +56,25 @@ bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
|
||||
//! [4]
|
||||
|
||||
//! [6]
|
||||
if (leftData.userType() == QMetaType::QDateTime) {
|
||||
if (leftData.userType() == QMetaType::QDateTime)
|
||||
return leftData.toDateTime() < rightData.toDateTime();
|
||||
} else {
|
||||
static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");
|
||||
|
||||
QString leftString = leftData.toString();
|
||||
if (left.column() == 1) {
|
||||
const QRegularExpressionMatch match = emailPattern.match(leftString);
|
||||
if (match.hasMatch())
|
||||
leftString = match.captured(0);
|
||||
}
|
||||
QString rightString = rightData.toString();
|
||||
if (right.column() == 1) {
|
||||
const QRegularExpressionMatch match = emailPattern.match(rightString);
|
||||
if (match.hasMatch())
|
||||
rightString = match.captured(0);
|
||||
}
|
||||
static const QRegularExpression emailPattern(u"[\\w\\.]*@[\\w\\.]*"_s);
|
||||
|
||||
return QString::localeAwareCompare(leftString, rightString) < 0;
|
||||
QString leftString = leftData.toString();
|
||||
if (left.column() == 1) {
|
||||
const QRegularExpressionMatch match = emailPattern.match(leftString);
|
||||
if (match.hasMatch())
|
||||
leftString = match.captured(0);
|
||||
}
|
||||
QString rightString = rightData.toString();
|
||||
if (right.column() == 1) {
|
||||
const QRegularExpressionMatch match = emailPattern.match(rightString);
|
||||
if (match.hasMatch())
|
||||
rightString = match.captured(0);
|
||||
}
|
||||
|
||||
return QString::localeAwareCompare(leftString, rightString) < 0;
|
||||
}
|
||||
//! [5] //! [6]
|
||||
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
#ifndef MYSORTFILTERPROXYMODEL_H
|
||||
#define MYSORTFILTERPROXYMODEL_H
|
||||
|
||||
#include <QDate>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QtCore/QDate>
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
|
||||
//! [0]
|
||||
class MySortFilterProxyModel : public QSortFilterProxyModel
|
||||
|
||||
+38
-37
@@ -5,83 +5,84 @@
|
||||
#include "mysortfilterproxymodel.h"
|
||||
#include "filterwidget.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QtWidgets/QCheckBox>
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDateEdit>
|
||||
#include <QtWidgets/QFormLayout>
|
||||
#include <QtWidgets/QGroupBox>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QTreeView>
|
||||
|
||||
#include <QtGui/QScreen>
|
||||
|
||||
#include <QtCore/QDate>
|
||||
|
||||
//! [0]
|
||||
Window::Window()
|
||||
: proxyModel(new MySortFilterProxyModel(this)),
|
||||
//! [0]
|
||||
sourceView(new QTreeView),
|
||||
proxyView(new QTreeView),
|
||||
filterWidget(new FilterWidget),
|
||||
fromDateEdit(new QDateEdit),
|
||||
toDateEdit(new QDateEdit)
|
||||
{
|
||||
proxyModel = new MySortFilterProxyModel(this);
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
sourceView = new QTreeView;
|
||||
sourceView->setRootIsDecorated(false);
|
||||
sourceView->setAlternatingRowColors(true);
|
||||
//! [1]
|
||||
|
||||
QHBoxLayout *sourceLayout = new QHBoxLayout;
|
||||
//! [2]
|
||||
auto *sourceGroupBox = new QGroupBox(tr("Original Model"));
|
||||
auto *sourceLayout = new QHBoxLayout(sourceGroupBox);
|
||||
sourceLayout->addWidget(sourceView);
|
||||
sourceGroupBox = new QGroupBox(tr("Original Model"));
|
||||
sourceGroupBox->setLayout(sourceLayout);
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
filterWidget = new FilterWidget;
|
||||
filterWidget->setText(tr("Grace|Sports"));
|
||||
connect(filterWidget, &FilterWidget::filterChanged, this, &Window::textFilterChanged);
|
||||
|
||||
filterPatternLabel = new QLabel(tr("&Filter pattern:"));
|
||||
filterPatternLabel->setBuddy(filterWidget);
|
||||
|
||||
fromDateEdit = new QDateEdit;
|
||||
fromDateEdit->setDate(QDate(1970, 01, 01));
|
||||
fromLabel = new QLabel(tr("F&rom:"));
|
||||
fromLabel->setBuddy(fromDateEdit);
|
||||
|
||||
toDateEdit = new QDateEdit;
|
||||
toDateEdit->setDate(QDate(2099, 12, 31));
|
||||
toLabel = new QLabel(tr("&To:"));
|
||||
toLabel->setBuddy(toDateEdit);
|
||||
|
||||
//! [3]
|
||||
//! [4]
|
||||
connect(filterWidget, &FilterWidget::filterChanged,
|
||||
this, &Window::textFilterChanged);
|
||||
connect(filterWidget, &QLineEdit::textChanged,
|
||||
this, &Window::textFilterChanged);
|
||||
connect(fromDateEdit, &QDateTimeEdit::dateChanged,
|
||||
this, &Window::dateFilterChanged);
|
||||
connect(toDateEdit, &QDateTimeEdit::dateChanged,
|
||||
//! [3] //! [4]
|
||||
this, &Window::dateFilterChanged);
|
||||
//! [4]
|
||||
|
||||
//! [5]
|
||||
proxyView = new QTreeView;
|
||||
proxyView->setRootIsDecorated(false);
|
||||
proxyView->setAlternatingRowColors(true);
|
||||
proxyView->setModel(proxyModel);
|
||||
proxyView->setSortingEnabled(true);
|
||||
proxyView->sortByColumn(1, Qt::AscendingOrder);
|
||||
|
||||
QGridLayout *proxyLayout = new QGridLayout;
|
||||
proxyLayout->addWidget(proxyView, 0, 0, 1, 3);
|
||||
proxyLayout->addWidget(filterPatternLabel, 1, 0);
|
||||
proxyLayout->addWidget(filterWidget, 1, 1);
|
||||
proxyLayout->addWidget(fromLabel, 3, 0);
|
||||
proxyLayout->addWidget(fromDateEdit, 3, 1, 1, 2);
|
||||
proxyLayout->addWidget(toLabel, 4, 0);
|
||||
proxyLayout->addWidget(toDateEdit, 4, 1, 1, 2);
|
||||
auto *proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
|
||||
auto *proxyLayout = new QVBoxLayout(proxyGroupBox);
|
||||
proxyLayout->addWidget(proxyView);
|
||||
auto *formLayout = new QFormLayout;
|
||||
proxyLayout->addLayout(formLayout);
|
||||
formLayout->addRow(tr("&Filter pattern:"), filterWidget);
|
||||
formLayout->addRow(tr("F&rom:"), fromDateEdit);
|
||||
formLayout->addRow(tr("&To:"), toDateEdit);
|
||||
|
||||
proxyGroupBox = new QGroupBox(tr("Sorted/Filtered Model"));
|
||||
proxyGroupBox->setLayout(proxyLayout);
|
||||
//! [5]
|
||||
|
||||
//! [6]
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
auto *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(sourceGroupBox);
|
||||
mainLayout->addWidget(proxyGroupBox);
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle(tr("Custom Sort/Filter Model"));
|
||||
resize(500, 450);
|
||||
auto screenGeometry = screen()->geometry();
|
||||
resize(screenGeometry.width() / 2, screenGeometry.height() * 2 / 3);
|
||||
}
|
||||
//! [6]
|
||||
|
||||
|
||||
+2
-10
@@ -8,16 +8,13 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAbstractItemModel;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDateEdit;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QTreeView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MySortFilterProxyModel;
|
||||
class FilterWidget;
|
||||
|
||||
//! [0]
|
||||
class Window : public QWidget
|
||||
{
|
||||
@@ -35,13 +32,8 @@ private slots:
|
||||
private:
|
||||
MySortFilterProxyModel *proxyModel;
|
||||
|
||||
QGroupBox *sourceGroupBox;
|
||||
QGroupBox *proxyGroupBox;
|
||||
QTreeView *sourceView;
|
||||
QTreeView *proxyView;
|
||||
QLabel *filterPatternLabel;
|
||||
QLabel *fromLabel;
|
||||
QLabel *toLabel;
|
||||
FilterWidget *filterWidget;
|
||||
QDateEdit *fromDateEdit;
|
||||
QDateEdit *toDateEdit;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
the files. The \l {QXmlStream Bookmarks Example} provides an alternative
|
||||
way to read this type of file.
|
||||
|
||||
\image screenshot.png
|
||||
\image screenshot.png {Bookmark tree with Title and Location columns}
|
||||
|
||||
\section1 The XbelTree Class Definition
|
||||
|
||||
|
||||
@@ -36,7 +36,11 @@ build_pass|if(single_android_abi:!single_arch) {
|
||||
else:TARGET = lib$${TARGET}_$${QT_ARCH}.so
|
||||
}
|
||||
QMAKE_LFLAGS += -Wl,-soname,$$shell_quote($$TARGET)
|
||||
|
||||
contains(ANDROID_TARGET_ARCH, "arm64-v8a|x86_64") {
|
||||
qtConfig(android_16kb_pages) {
|
||||
QMAKE_LFLAGS += -Wl,-z,max-page-size=16384
|
||||
}
|
||||
}
|
||||
android_install {
|
||||
target.path=/libs/$$ANDROID_TARGET_ARCH/
|
||||
INSTALLS *= target
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <qfileinfo.h>
|
||||
#include <qhash.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
@@ -40,7 +42,15 @@ struct FixStringCacheKey
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
inline size_t qHash(const FixStringCacheKey &f) { return f.hashCode(); }
|
||||
QT_BEGIN_INCLUDE_NAMESPACE
|
||||
namespace std {
|
||||
template <> struct hash<QT_PREPEND_NAMESPACE(FixStringCacheKey)>
|
||||
{
|
||||
size_t operator()(const QT_PREPEND_NAMESPACE(FixStringCacheKey) &key) const noexcept
|
||||
{ return key.hashCode(); }
|
||||
};
|
||||
} // namespace std
|
||||
QT_END_INCLUDE_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
struct FileInfoCacheKey
|
||||
@@ -78,7 +88,15 @@ struct FileInfoCacheKey
|
||||
|| (c0 == QLatin1Char('\\') && c1 == QLatin1Char('\\')));
|
||||
}
|
||||
};
|
||||
inline size_t qHash(const FileInfoCacheKey &f) { return f.hashCode(); }
|
||||
QT_BEGIN_INCLUDE_NAMESPACE
|
||||
namespace std {
|
||||
template <> struct hash<QT_PREPEND_NAMESPACE(FileInfoCacheKey)>
|
||||
{
|
||||
size_t operator()(const QT_PREPEND_NAMESPACE(FileInfoCacheKey) &key) const noexcept
|
||||
{ return key.hashCode(); }
|
||||
};
|
||||
} // namespace std
|
||||
QT_END_INCLUDE_NAMESPACE
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
|
||||
@@ -2820,20 +2820,22 @@ MakefileGenerator::writeMakeQmake(QTextStream &t, bool noDummyQmakeAll)
|
||||
QFileInfo
|
||||
MakefileGenerator::fileInfo(QString file) const
|
||||
{
|
||||
Q_CONSTINIT static QHash<FileInfoCacheKey, QFileInfo> *cache = nullptr;
|
||||
static QFileInfo noInfo = QFileInfo();
|
||||
using HashMap = std::unordered_map<FileInfoCacheKey, QFileInfo>;
|
||||
Q_CONSTINIT static HashMap *cache = nullptr;
|
||||
if(!cache) {
|
||||
cache = new QHash<FileInfoCacheKey, QFileInfo>;
|
||||
qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FileInfoCacheKey, QFileInfo> >, (void**)&cache);
|
||||
cache = new HashMap;
|
||||
qmakeAddCacheClear(qmakeDeleteCacheClear<HashMap>, (void**)&cache);
|
||||
}
|
||||
FileInfoCacheKey cacheKey(file);
|
||||
QFileInfo value = cache->value(cacheKey, noInfo);
|
||||
if (value != noInfo)
|
||||
return value;
|
||||
|
||||
const auto it = cache->find(cacheKey);
|
||||
|
||||
if (it != cache->end())
|
||||
return it->second;
|
||||
|
||||
QFileInfo fi(file);
|
||||
if (fi.exists())
|
||||
cache->insert(cacheKey, fi);
|
||||
cache->emplace(cacheKey, fi);
|
||||
return fi;
|
||||
}
|
||||
|
||||
|
||||
@@ -306,8 +306,9 @@ struct ReplaceExtraCompilerCacheKey
|
||||
hash = (size_t)forShell ^ qHash(var) ^ qHash(in) ^ qHash(out) /*^ qHash(pwd)*/;
|
||||
return hash;
|
||||
}
|
||||
friend size_t qHash(const ReplaceExtraCompilerCacheKey &key, size_t seed = 0) noexcept
|
||||
{ return key.hashCode() ^ seed; }
|
||||
};
|
||||
inline size_t qHash(const ReplaceExtraCompilerCacheKey &f) { return f.hashCode(); }
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
@@ -92,11 +92,11 @@ size_t ProString::updatedHash() const
|
||||
return (m_hash = hash(m_string.constData() + m_offset, m_length));
|
||||
}
|
||||
|
||||
size_t qHash(const ProString &str)
|
||||
size_t qHash(const ProString &str, size_t seed)
|
||||
{
|
||||
if (!(str.m_hash & 0x80000000))
|
||||
return str.m_hash;
|
||||
return str.updatedHash();
|
||||
return str.m_hash ^ seed;
|
||||
return str.updatedHash() ^ seed;
|
||||
}
|
||||
|
||||
ProKey::ProKey(const QString &str) :
|
||||
|
||||
@@ -161,7 +161,8 @@ private:
|
||||
int m_file;
|
||||
mutable size_t m_hash;
|
||||
size_t updatedHash() const;
|
||||
friend size_t qHash(const ProString &str);
|
||||
friend size_t qHash(const ProString &str, size_t seed);
|
||||
friend size_t qHash(const ProString &str) { return qHash(str, size_t{0}); }
|
||||
friend QString operator+(const ProString &one, const ProString &two);
|
||||
friend class ProKey;
|
||||
};
|
||||
@@ -195,6 +196,9 @@ public:
|
||||
ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; }
|
||||
|
||||
private:
|
||||
friend size_t qHash(const ProKey &key, size_t seed = 0) noexcept
|
||||
{ return qHash(key.toString(), seed); }
|
||||
|
||||
ProKey(const ProString &other);
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(ProKey, Q_RELOCATABLE_TYPE);
|
||||
@@ -231,9 +235,6 @@ template <> struct QConcatenable<ProKey> : private QAbstractConcatenable
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
size_t qHash(const ProString &str);
|
||||
|
||||
inline QString &operator+=(QString &that, const ProString &other)
|
||||
{ return that += other.toQStringView(); }
|
||||
|
||||
|
||||
@@ -82,9 +82,9 @@ QMakeBaseKey::QMakeBaseKey(const QString &_root, const QString &_stash, bool _ho
|
||||
{
|
||||
}
|
||||
|
||||
size_t qHash(const QMakeBaseKey &key)
|
||||
size_t qHash(const QMakeBaseKey &key, size_t seed) noexcept
|
||||
{
|
||||
return qHash(key.root) ^ qHash(key.stash) ^ (uint)key.hostBuild;
|
||||
return qHashMulti(seed, key.root, key.stash, uint{key.hostBuild});
|
||||
}
|
||||
|
||||
bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two)
|
||||
|
||||
@@ -35,9 +35,11 @@ public:
|
||||
QString root;
|
||||
QString stash;
|
||||
bool hostBuild;
|
||||
|
||||
friend size_t qHash(const QMakeBaseKey &key, size_t seed) noexcept;
|
||||
friend size_t qHash(const QMakeBaseKey &key) noexcept { return qHash(key, size_t{0}); }
|
||||
};
|
||||
|
||||
size_t qHash(const QMakeBaseKey &key);
|
||||
bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two);
|
||||
|
||||
class QMakeBaseEnv
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <qtversion.h>
|
||||
#include <private/qlibraryinfo_p.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace QMakeInternal;
|
||||
@@ -474,18 +476,19 @@ QString
|
||||
Option::fixString(QString string, uchar flags)
|
||||
{
|
||||
//const QString orig_string = string;
|
||||
static QHash<FixStringCacheKey, QString> *cache = nullptr;
|
||||
using HashMap = std::unordered_map<FixStringCacheKey, QString>;
|
||||
Q_CONSTINIT static HashMap *cache = nullptr;
|
||||
if(!cache) {
|
||||
cache = new QHash<FixStringCacheKey, QString>;
|
||||
qmakeAddCacheClear(qmakeDeleteCacheClear<QHash<FixStringCacheKey, QString> >, (void**)&cache);
|
||||
cache = new HashMap();
|
||||
qmakeAddCacheClear(qmakeDeleteCacheClear<HashMap>, (void**)&cache);
|
||||
}
|
||||
FixStringCacheKey cacheKey(string, flags);
|
||||
|
||||
QHash<FixStringCacheKey, QString>::const_iterator it = cache->constFind(cacheKey);
|
||||
const auto it = cache->find(cacheKey);
|
||||
|
||||
if (it != cache->constEnd()) {
|
||||
if (it != cache->end()) {
|
||||
//qDebug() << "Fix (cached) " << orig_string << "->" << it.value();
|
||||
return it.value();
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//fix the environment variables
|
||||
@@ -532,7 +535,7 @@ Option::fixString(QString string, uchar flags)
|
||||
|
||||
//cache
|
||||
//qDebug() << "Fix" << orig_string << "->" << string;
|
||||
cache->insert(cacheKey, string);
|
||||
cache->emplace(cacheKey, string);
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -141,6 +141,7 @@ especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h> // to get the POSIX flags
|
||||
|
||||
#if _POSIX_SPAWN > 0
|
||||
|
||||
+12
-9
@@ -1,4 +1,4 @@
|
||||
FreeType 2.14.2
|
||||
FreeType 2.14.3
|
||||
===============
|
||||
|
||||
Homepage: https://freetype.org
|
||||
@@ -32,9 +32,9 @@ sites. Go to
|
||||
|
||||
and download one of the following files.
|
||||
|
||||
freetype-doc-2.14.2.tar.xz
|
||||
freetype-doc-2.14.2.tar.gz
|
||||
ftdoc2142.zip
|
||||
freetype-doc-2.14.3.tar.xz
|
||||
freetype-doc-2.14.3.tar.gz
|
||||
ftdoc2143.zip
|
||||
|
||||
To view the documentation online, go to
|
||||
|
||||
@@ -66,10 +66,13 @@ Please submit bug reports at
|
||||
|
||||
https://gitlab.freedesktop.org/freetype/freetype/-/issues
|
||||
|
||||
Alternatively, you might report bugs by e-mail to
|
||||
`freetype-devel@nongnu.org`. Don't forget to send a detailed
|
||||
explanation of the problem -- there is nothing worse than receiving a
|
||||
terse message that only says 'it doesn't work'.
|
||||
This gitlab instance also supports confidential reports.
|
||||
|
||||
Alternatively, but less preferable, you might report bugs by sending
|
||||
an e-mail to the public mailing list `freetype-devel@nongnu.org`.
|
||||
Don't forget to submit a detailed explanation of the problem -- there
|
||||
is nothing worse than receiving a terse message that only says 'it
|
||||
doesn't work'.
|
||||
|
||||
|
||||
Patches
|
||||
@@ -94,7 +97,7 @@ Enjoy!
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2006-2025 by
|
||||
Copyright (C) 2006-2026 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -482,14 +482,14 @@ SPDX-License-Identifier = "FTL OR GPL-2.0-only"
|
||||
path = ["src/autofit/afgsub.*"]
|
||||
comment = "Copyright continuation line ignored by reuse for lack of word Copyright at start."
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = ["Copyright (C) 2025 by David Turner, Robert Wilhelm, and Werner Lemberg."]
|
||||
SPDX-FileCopyrightText = ["Copyright (C) 2026 by David Turner, Robert Wilhelm, and Werner Lemberg."]
|
||||
SPDX-License-Identifier = "FTL OR GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = ["src/autofit/afadjust.*"]
|
||||
comment = "Copyright continuation line ignored by reuse for lack of word Copyright at start."
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = ["Copyright (C) 2023-2025 by David Turner, Robert Wilhelm, and Werner Lemberg and Craig White"]
|
||||
SPDX-FileCopyrightText = ["Copyright (C) 2023-2026 by David Turner, Robert Wilhelm, and Werner Lemberg and Craig White"]
|
||||
SPDX-License-Identifier = "FTL OR GPL-2.0-only"
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Unix-specific FreeType low-level system interface (body).
|
||||
*
|
||||
* Copyright (C) 1996-2025 by
|
||||
* Copyright (C) 1996-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Debugging and logging component for Win32 (body).
|
||||
*
|
||||
* Copyright (C) 1996-2025 by
|
||||
* Copyright (C) 1996-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
CHANGES BETWEEN 2.14.2 and 2.14.3 (2026-Mar-22)
|
||||
|
||||
I. IMPORTANT BUG FIXES
|
||||
|
||||
- A bunch of potential security problems have been found. All users
|
||||
should update.
|
||||
|
||||
|
||||
II. MISCELLANEOUS
|
||||
|
||||
- If configuration option `TT_CONFIG_OPTION_GPOS_KERNING` is active,
|
||||
GPOS-based kerning could miss some value pairs (bug introduced in
|
||||
version 2.14.0).
|
||||
|
||||
|
||||
======================================================================
|
||||
|
||||
CHANGES BETWEEN 2.14.1 and 2.14.2 (2026-Mar-01)
|
||||
|
||||
I. IMPORTANT CHANGES
|
||||
@@ -5867,7 +5884,7 @@ Extensions support:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2000-2025 by
|
||||
Copyright (C) 2000-2026 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -139,7 +139,7 @@ IV. Overriding default configuration and module headers
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2003-2025 by
|
||||
Copyright (C) 2003-2026 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -297,7 +297,7 @@ to access them.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2002-2025 by
|
||||
Copyright (C) 2002-2026 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -27,7 +27,7 @@ Other bugs have been registered at the savannah bugzilla of FreeType.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Copyright (C) 2001-2025 by
|
||||
Copyright (C) 2001-2026 by
|
||||
David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
|
||||
This file is part of the FreeType project, and may only be used,
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
"Description": "FreeType is a freely available software library to render fonts.",
|
||||
"Homepage": "http://www.freetype.org",
|
||||
"Version": "2.14.2",
|
||||
"DownloadLocation": "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.2.tar.gz",
|
||||
"Version": "2.14.3",
|
||||
"DownloadLocation": "https://download.savannah.gnu.org/releases/freetype/freetype-2.14.3.tar.gz",
|
||||
"PURL": [
|
||||
"pkg:github/freetype/freetype@VER-$<VERSION_DASHED>"
|
||||
],
|
||||
@@ -21,34 +21,34 @@
|
||||
"LicenseId": "FTL OR GPL-2.0-only",
|
||||
"LicenseFile": "LICENSE.txt",
|
||||
"Copyright": ["Copyright (c) 2007-2014 Adobe Systems Incorporated",
|
||||
"Copyright (c) 2004-2025 Albert Chin-A-Young",
|
||||
"Copyright (c) 2018-2025 Armin Hasitzka, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2004-2026 Albert Chin-A-Young",
|
||||
"Copyright (c) 2018-2026 Armin Hasitzka, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2000 Computing Research Labs, New Mexico State University",
|
||||
"Copyright (c) 1996-2025 David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg",
|
||||
"Copyright (c) 2004-2025 David Turner, Robert Wilhelm, Werner Lemberg and George Williams",
|
||||
"Copyright (c) 2022-2025 David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti",
|
||||
"Copyright (c) 2008-2025 David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya",
|
||||
"Copyright (c) 2003-2025 David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2005-2025 David Turner",
|
||||
"Copyright (c) 2007-2025 Derek Clegg and Michael Toftdal",
|
||||
"Copyright (c) 1996-2026 David Turner, Robert Wilhelm, Dominik Röttsches, and Werner Lemberg",
|
||||
"Copyright (c) 2004-2026 David Turner, Robert Wilhelm, Werner Lemberg and George Williams",
|
||||
"Copyright (c) 2022-2026 David Turner, Robert Wilhelm, Werner Lemberg, and Moazin Khatti",
|
||||
"Copyright (c) 2008-2026 David Turner, Robert Wilhelm, Werner Lemberg, and suzuki toshiya",
|
||||
"Copyright (c) 2003-2026 David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2005-2026 David Turner",
|
||||
"Copyright (c) 2007-2026 Derek Clegg and Michael Toftdal",
|
||||
"Copyright (c) 2007 Dmitry Timoshkov for Codeweavers",
|
||||
"Copyright (c) 2001-2015 Francesco Zappa Nardelli",
|
||||
"Copyright (c) 2005, 2007, 2008, 2013 George Williams",
|
||||
"Copyright (c) 2013-2025 Google, Inc. Google Author(s) Behdad Esfahbod and Stuart Gill",
|
||||
"Copyright (c) 2013-2026 Google, Inc. Google Author(s) Behdad Esfahbod and Stuart Gill",
|
||||
"Copyright (c) 2013-2022 Google, Inc.",
|
||||
"Copyright (c) 2003 Huw D M Davies for Codeweavers",
|
||||
"Copyright (c) 2010-2025 Joel Klinghed",
|
||||
"Copyright (c) 1996-2025 Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2003-2025 Masatake YAMATO and Redhat K.K.",
|
||||
"Copyright (c) 2004-2025 Masatake YAMATO, Redhat K.K, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2019-2025 Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2009-2025 Oran Agra and Mickey Gabel",
|
||||
"Copyright (c) 2007-2025 Rahul Bhalerao <rahul.bhalerao@redhat.com>",
|
||||
"Copyright (c) 2002-2025 Roberto Alameda",
|
||||
"Copyright (c) 2015-2025 Werner Lemberg",
|
||||
"Copyright (c) 2004-2025 suzuki toshiya, Masatake YAMATO, Red Hat K.K., David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2010-2026 Joel Klinghed",
|
||||
"Copyright (c) 1996-2026 Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2003-2026 Masatake YAMATO and Redhat K.K.",
|
||||
"Copyright (c) 2004-2026 Masatake YAMATO, Redhat K.K, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2019-2026 Nikhil Ramakrishnan, David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2009-2026 Oran Agra and Mickey Gabel",
|
||||
"Copyright (c) 2007-2026 Rahul Bhalerao <rahul.bhalerao@redhat.com>",
|
||||
"Copyright (c) 2002-2026 Roberto Alameda",
|
||||
"Copyright (c) 2015-2026 Werner Lemberg",
|
||||
"Copyright (c) 2004-2026 suzuki toshiya, Masatake YAMATO, Red Hat K.K., David Turner, Robert Wilhelm, and Werner Lemberg",
|
||||
"Copyright (c) 2019 nyorain",
|
||||
"Copyright (c) 2022-2025 David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and Dominik Röttsches",
|
||||
"Copyright (c) 2022-2026 David Turner, Robert Wilhelm, Werner Lemberg, George Williams, and Dominik Röttsches",
|
||||
"Copyright (C) 2009, 2023 Red Hat, Inc."]
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Auto-fitter routines to adjust components based on charcode (body).
|
||||
*
|
||||
* Copyright (C) 2023-2025 by
|
||||
* Copyright (C) 2023-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* Written by Craig White <gerzytet@gmail.com>.
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Auto-fitter routines to adjust components based on charcode (header).
|
||||
*
|
||||
* Copyright (C) 2023-2025 by
|
||||
* Copyright (C) 2023-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* Written by Craig White <gerzytet@gmail.com>.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* Auto-fitter data for blue strings (body).
|
||||
*
|
||||
* Copyright (C) 2013-2025 by
|
||||
* Copyright (C) 2013-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Auto-fitter data for blue strings (body).
|
||||
*
|
||||
* Copyright (C) 2013-2025 by
|
||||
* Copyright (C) 2013-2026 by
|
||||
* David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
*
|
||||
* This file is part of the FreeType project, and may only be used,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user