From aea52288d6daf4e722fe53e480f0cbe3f7914ae1 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Fri, 14 Jun 2024 17:51:12 +0200 Subject: [PATCH] CMake: Improve license handling for SBOMs Add a NO_ATTRIBUTION_LICENSE_ID option to allow to skip including license references from qt_attribution.json files. Add a QT_LICENSE_ID option that can take an "id" value and return one of the common license expressions we use throughout Qt repositories: - GPL3 with exception for tools / apps - GFDL for documentation - GPL3 only modules etc. Add a QT_SBOM_DEFAULT_QT_LICENSE_ID_LIBRARIES variable that can be set at directory scope (or root repo scope) to use a specific license id for all qt modules / plugins created within that repo, e.g. the Commercial + GPL3 variant. These can be opted out, to fallback to the "default" license by passing NO_DEFAULT_QT_LICENSE_ID_LIBRARIES. Add a similar variable QT_SBOM_DEFAULT_QT_LICENSE_ID_EXECUTABLES for tools / apps, including a NO_DEFAULT_QT_LICENSE_ID_EXECUTABLES option. Modify the logic for setting licenses for qt targets: - use whatever is set in QT_SBOM_DEFAULT_QT_LICENSE_EXECUTABLES for tools and apps - or use the 'GPL3 with exception' variant for tools and apps - use whatever is set in QT_SBOM_DEFAULT_QT_LICENSE_LIBARRIES for modules ands plugins - or use the default Commercial + LGPL3 + GPL variant for modules and plugins Amends 37a5e001277db9e1392a242171ab2b88cb6c3049 Task-number: QTBUG-122899 Change-Id: I3a8abac62b9f4b342f91ef139064884f02aa935e Reviewed-by: Alexey Edelev (cherry picked from commit fe90ba704168d47cf88d0c6512380b525f38f5d3) Reviewed-by: Qt Cherry-pick Bot --- cmake/QtPublicSbomHelpers.cmake | 98 +++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 10 deletions(-) diff --git a/cmake/QtPublicSbomHelpers.cmake b/cmake/QtPublicSbomHelpers.cmake index 794447c8e7f..f7944eb9923 100644 --- a/cmake/QtPublicSbomHelpers.cmake +++ b/cmake/QtPublicSbomHelpers.cmake @@ -267,7 +267,10 @@ endfunction() macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi_args) set(${opt_args} NO_CURRENT_DIR_ATTRIBUTION + NO_ATTRIBUTION_LICENSE_ID NO_DEFAULT_QT_LICENSE + NO_DEFAULT_QT_LICENSE_ID_LIBRARIES + NO_DEFAULT_QT_LICENSE_ID_EXECUTABLES NO_DEFAULT_DIRECTORY_QT_LICENSE NO_DEFAULT_QT_COPYRIGHTS NO_DEFAULT_QT_PACKAGE_VERSION @@ -279,6 +282,7 @@ macro(_qt_internal_get_sbom_add_target_common_options opt_args single_args multi CPE_VENDOR CPE_PRODUCT LICENSE_EXPRESSION + QT_LICENSE_ID DOWNLOAD_LOCATION ATTRIBUTION_ENTRY_INDEX ) @@ -392,21 +396,58 @@ function(_qt_internal_sbom_add_target target) _qt_internal_sbom_handle_qt_attribution_files(qa ${attribution_args}) - # Collect license expressions, but each expression needs to be abided, so we AND them. + # Collect license expressions, but in most cases, each expression needs to be abided, so we + # AND the accumulated license expressions. set(license_expression "") if(arg_LICENSE_EXPRESSION) set(license_expression "${arg_LICENSE_EXPRESSION}") endif() - if(is_qt_entity_type AND NOT arg_NO_DEFAULT_QT_LICENSE) - _qt_internal_sbom_get_default_qt_license_id(qt_license_expression) + # For Qt entities, we have some special handling. + if(is_qt_entity_type AND NOT arg_NO_DEFAULT_QT_LICENSE AND NOT arg_QT_LICENSE_ID) + if(arg_TYPE STREQUAL "QT_TOOL" OR arg_TYPE STREQUAL "QT_APP") + if(QT_SBOM_DEFAULT_QT_LICENSE_ID_EXECUTABLES + AND NOT arg_NO_DEFAULT_QT_LICENSE_ID_EXECUTABLES) + # A repo might contain only the "gpl3" license variant as the default for all + # executables, so allow setting it at the repo level to avoid having to repeat it + # for each target. + _qt_internal_sbom_get_spdx_license_expression( + "${QT_SBOM_DEFAULT_QT_LICENSE_ID_EXECUTABLES}" qt_license_expression) + else() + # For tools and apps, we use the gpl exception variant by default. + _qt_internal_sbom_get_spdx_license_expression("QT_COMMERCIAL_OR_GPL3_WITH_EXCEPTION" + qt_license_expression) + endif() + + elseif(QT_SBOM_DEFAULT_QT_LICENSE_ID_LIBRARIES + AND NOT arg_NO_DEFAULT_QT_LICENSE_ID_LIBRARIES) + # A repo might contain only the "gpl3" license variant as the default for all modules + # and plugins, so allow setting it at the repo level to avoid having to repeat it + # for each target. + _qt_internal_sbom_get_spdx_license_expression( + "${QT_SBOM_DEFAULT_QT_LICENSE_ID_LIBRARIES}" qt_license_expression) + + else() + # Otherwise, for modules and plugins we use the default qt license. + _qt_internal_sbom_get_spdx_license_expression("QT_DEFAULT" qt_license_expression) + endif() + _qt_internal_sbom_join_two_license_ids_with_op( "${license_expression}" "AND" "${qt_license_expression}" license_expression) endif() - # Allow setting a license expression per directory scope via a variable. + # Some Qt entities might request a specific license from the subset that we usually use. + if(arg_QT_LICENSE_ID) + _qt_internal_sbom_get_spdx_license_expression("${arg_QT_LICENSE_ID}" + requested_license_expression) + _qt_internal_sbom_join_two_license_ids_with_op( + "${license_expression}" "AND" "${requested_license_expression}" + license_expression) + endif() + + # Allow setting a license expression string per directory scope via a variable. if(is_qt_entity_type AND QT_SBOM_LICENSE_EXPRESSION AND NOT arg_NO_DEFAULT_DIRECTORY_QT_LICENSE) set(qt_license_expression "${QT_SBOM_LICENSE_EXPRESSION}") _qt_internal_sbom_join_two_license_ids_with_op( @@ -414,7 +455,8 @@ function(_qt_internal_sbom_add_target target) license_expression) endif() - if(qa_license_id) + # Read a license expression from the attribution json file. + if(qa_license_id AND NOT arg_NO_ATTRIBUTION_LICENSE_ID) if(NOT qa_license_id MATCHES "urn:dje:license") _qt_internal_sbom_join_two_license_ids_with_op( "${license_expression}" "AND" "${qa_license_id}" @@ -431,6 +473,8 @@ function(_qt_internal_sbom_add_target target) if(license_expression) list(APPEND project_package_options LICENSE_CONCLUDED "${license_expression}") + + # For qt entities we know the license we provide, so we mark it as declared as well. if(is_qt_entity_type) list(APPEND project_package_options LICENSE_DECLARED "${license_expression}") endif() @@ -2332,11 +2376,45 @@ function(_qt_internal_sbom_get_package_purpose type out_purpose) set(${out_purpose} "${package_purpose}" PARENT_SCOPE) endfunction() -# Get the default qt spdx license expression. -function(_qt_internal_sbom_get_default_qt_license_id out_var) - set(${out_var} - "LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only" - PARENT_SCOPE) +# Get a qt spdx license expression given the id. +function(_qt_internal_sbom_get_spdx_license_expression id out_var) + set(license "") + + # The default for modules / plugins + if(id STREQUAL "QT_DEFAULT" OR id STREQUAL "QT_COMMERCIAL_OR_LGPL3") + set(license "LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only") + + # For commercial only entities + elseif(id STREQUAL "QT_COMMERCIAL") + set(license "LicenseRef-Qt-Commercial") + + # For GPL3 only modules + elseif(id STREQUAL "QT_COMMERCIAL_OR_GPL3") + set(license "LicenseRef-Qt-Commercial OR GPL-3.0-only") + + # For tools and apps + elseif(id STREQUAL "QT_COMMERCIAL_OR_GPL3_WITH_EXCEPTION") + set(license "LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0") + + # For things like the qtmain library + elseif(id STREQUAL "QT_COMMERCIAL_OR_BSD3") + set(license "LicenseRef-Qt-Commercial OR BSD-3-Clause") + + # For documentation + elseif(id STREQUAL "QT_COMMERCIAL_OR_GFDL1_3") + set(license "LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only") + + # For examples and the like + elseif(id STREQUAL "BSD3") + set(license "BSD-3-Clause") + + endif() + + if(NOT license) + message(FATAL_ERROR "No SPDX license expression found for id: ${id}") + endif() + + set(${out_var} "${license}" PARENT_SCOPE) endfunction() # Get the default qt copyright.