summaryrefslogtreecommitdiffstats
path: root/cmake/QtPublicSbomOpsHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtPublicSbomOpsHelpers.cmake')
-rw-r--r--cmake/QtPublicSbomOpsHelpers.cmake324
1 files changed, 288 insertions, 36 deletions
diff --git a/cmake/QtPublicSbomOpsHelpers.cmake b/cmake/QtPublicSbomOpsHelpers.cmake
index 58af2a57f1f..78c4eaaa5e6 100644
--- a/cmake/QtPublicSbomOpsHelpers.cmake
+++ b/cmake/QtPublicSbomOpsHelpers.cmake
@@ -6,14 +6,28 @@
# like NTIA validation, auditing, json generation, etc.
function(_qt_internal_sbom_setup_project_ops_generation)
set(opt_args
+ # spdx options
GENERATE_JSON
GENERATE_JSON_REQUIRED
+
+ # cydx options
+ GENERATE_CYCLONE_DX_V1_6
+ GENERATE_CYCLONE_DX_V1_6_REQUIRED
+ VERIFY_CYCLONE_DX_V1_6
+ VERIFY_CYCLONE_DX_V1_6_REQUIRED
+ VERBOSE_CYCLONE_DX_V1_6
+
+ # source spdx options
GENERATE_SOURCE_SBOM
+ LINT_SOURCE_SBOM
+ LINT_SOURCE_SBOM_NO_ERROR
+
+ # Extra spdx verification
VERIFY_SBOM
VERIFY_SBOM_REQUIRED
VERIFY_NTIA_COMPLIANT
- LINT_SOURCE_SBOM
- LINT_SOURCE_SBOM_NO_ERROR
+
+ # Extra spdx info
SHOW_TABLE
AUDIT
AUDIT_NO_ERROR
@@ -38,6 +52,33 @@ function(_qt_internal_sbom_setup_project_ops_generation)
endif()
endif()
+ if(arg_GENERATE_CYCLONE_DX_V1_6 AND NOT QT_INTERNAL_NO_SBOM_PYTHON_OPS)
+ set(op_args
+ OUT_VAR_DEPS_FOUND deps_found
+ )
+ if(arg_GENERATE_CYCLONE_DX_V1_6_REQUIRED)
+ list(APPEND op_args REQUIRED)
+ endif()
+
+ set(gen_args "")
+ if(arg_VERIFY_CYCLONE_DX_V1_6)
+ list(APPEND gen_args VERIFY)
+ endif()
+
+ if(arg_VERIFY_CYCLONE_DX_V1_6_REQUIRED)
+ list(APPEND gen_args VERIFY_REQUIRED)
+ endif()
+
+ if(arg_VERBOSE_CYCLONE_DX_V1_6)
+ list(APPEND gen_args VERBOSE)
+ endif()
+
+ _qt_internal_sbom_find_cydx_dependencies(${op_args})
+ if(deps_found)
+ _qt_internal_sbom_generate_cydx_json(${gen_args})
+ endif()
+ endif()
+
if(arg_VERIFY_SBOM AND NOT QT_INTERNAL_NO_SBOM_PYTHON_OPS)
set(op_args
OP_KEY "VERIFY_SBOM"
@@ -90,12 +131,119 @@ function(_qt_internal_sbom_setup_project_ops_generation)
endif()
endfunction()
+# Tries to find the dependencies for generating CycloneDX v1.6 SBOMs.
+# Sets OUT_VAR_DEPS_FOUND to TRUE if found, FALSE otherwise.
+function(_qt_internal_sbom_find_cydx_dependencies)
+ set(opt_args
+ REQUIRED
+ )
+ set(single_args
+ OUT_VAR_DEPS_FOUND
+ )
+ set(multi_args "")
+ cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
+
+ if(NOT arg_OUT_VAR_DEPS_FOUND)
+ message(FATAL_ERROR "OUT_VAR_DEPS_FOUND is required")
+ endif()
+
+ set(op_args
+ OP_KEY "GENERATE_CYCLONE_DX_V1_6"
+ OUT_VAR_DEPS_FOUND deps_found
+ )
+
+ if(arg_REQUIRED)
+ list(APPEND op_args REQUIRED)
+ endif()
+
+ _qt_internal_sbom_find_and_handle_sbom_op_dependencies(${op_args})
+
+ set(${arg_OUT_VAR_DEPS_FOUND} "${deps_found}" PARENT_SCOPE)
+endfunction()
+
+# Helper to output a debug or error message when python or one of its dependencies are not found.
+# When found, it also caches the found python interpreter path and version, as well as the
+# DEPS_FOUND_FOR_$OP_KEY var.
+function(_qt_internal_sbom_verify_if_sbom_op_dependencies_are_found)
+ set(opt_args
+ REQUIRED
+ PYTHON_FOUND
+ DEP_FOUND
+ EVERYTHING_FOUND
+ )
+ set(single_args
+ REQUIRED_VERSION
+ PYTHON_PATH
+ PYTHON_VERSION
+ DEP_PACKAGE_NAME
+ DEP_FIND_OUTPUT
+ OP_KEY
+ )
+ set(multi_args
+ PYTHON_COMMON_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}'.")
+
+ if(NOT QT_INTERNAL_SBOM_PYTHON_EXECUTABLE)
+ message(DEBUG "Setting QT_INTERNAL_SBOM_PYTHON_EXECUTABLE to ${arg_PYTHON_PATH}")
+ message(DEBUG "Setting QT_INTERNAL_SBOM_PYTHON_VERSION to ${arg_PYTHON_VERSION}")
+ set(QT_INTERNAL_SBOM_PYTHON_EXECUTABLE "${arg_PYTHON_PATH}" CACHE INTERNAL
+ "Python interpeter used for SBOM generation.")
+ set(QT_INTERNAL_SBOM_PYTHON_VERSION "${arg_PYTHON_VERSION}" CACHE INTERNAL
+ "Python interpeter version used for SBOM generation.")
+ endif()
+
+ set(QT_INTERNAL_SBOM_DEPS_FOUND_FOR_${arg_OP_KEY} "TRUE" CACHE INTERNAL
+ "All dependencies found to run SBOM op '${arg_OP_KEY}'")
+ 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.")
+ 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"
+ )
+ endif()
+endfunction()
+
# 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.
#
# Caches the found python executable in a separate cache var QT_INTERNAL_SBOM_PYTHON_EXECUTABLE, to
# 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.
function(_qt_internal_sbom_find_and_handle_sbom_op_dependencies)
set(opt_args
REQUIRED
@@ -112,12 +260,22 @@ function(_qt_internal_sbom_find_and_handle_sbom_op_dependencies)
message(FATAL_ERROR "OP_KEY is required")
endif()
- set(supported_ops "GENERATE_JSON" "VERIFY_SBOM" "RUN_NTIA")
+ set(supported_ops
+ "GENERATE_JSON"
+ "GENERATE_CYCLONE_DX_V1_6"
+ "VERIFY_SBOM"
+ "RUN_NTIA"
+ )
if(arg_OP_KEY STREQUAL "GENERATE_JSON" OR arg_OP_KEY STREQUAL "VERIFY_SBOM")
set(import_statement "import spdx_tools.spdx.clitools.pyspdxtools")
+ set(dep_package_name "spdx-tools")
+ elseif(arg_OP_KEY STREQUAL "GENERATE_CYCLONE_DX_V1_6")
+ set(import_statement "from cyclonedx.output.json import JsonV1Dot6")
+ set(dep_package_name "cyclonedx-python-lib[json-validation]")
elseif(arg_OP_KEY STREQUAL "RUN_NTIA")
set(import_statement "import ntia_conformance_checker.main")
+ set(dep_package_name "ntia-conformance-checker")
else()
message(FATAL_ERROR "OP_KEY must be one of ${supported_ops}")
endif()
@@ -145,48 +303,81 @@ function(_qt_internal_sbom_find_and_handle_sbom_op_dependencies)
# 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.")
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
+ 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}
+ )
+
+ 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()
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}'.")
_qt_internal_sbom_find_python_and_dependency_helper_lambda()
endif()
- # Always save the python interpreter path if it is found, even if the dependencies are not
- # found. This improves the error message workflow.
- if(python_found AND NOT QT_INTERNAL_SBOM_PYTHON_EXECUTABLE)
- set(QT_INTERNAL_SBOM_PYTHON_EXECUTABLE "${python_path}" CACHE INTERNAL
- "Python interpeter used for SBOM generation.")
+ # Assemble args to show what was or wasn't found.
+ set(verify_args "")
+ if(arg_REQUIRED)
+ list(APPEND verify_args REQUIRED)
endif()
-
- if(NOT everything_found)
- if(arg_REQUIRED)
- set(message_type "FATAL_ERROR")
- else()
- set(message_type "DEBUG")
- endif()
-
- if(NOT 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(${python_common_args}
- OUT_VAR_PYTHON_PATH unused_python
- OUT_VAR_PYTHON_FOUND unused_found
- )
- endif()
- message(${message_type} "Python ${required_version} for running SBOM ops not found.")
- elseif(NOT dep_found)
- message(${message_type} "Python dependency for running SBOM op ${arg_OP_KEY} "
- "not found:\n Python: ${python_path} \n Output: \n${dep_find_output}")
- endif()
- else()
- message(DEBUG "Using Python ${python_path} for running SBOM ops.")
-
- set(QT_INTERNAL_SBOM_DEPS_FOUND_FOR_${arg_OP_KEY} "TRUE" CACHE INTERNAL
- "All dependencies found to run SBOM OP ${arg_OP_KEY}")
+ 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}}"
@@ -298,7 +489,7 @@ function(_qt_internal_sbom_check_python_dependency_available)
_qt_internal_validate_all_args_are_parsed(arg)
set(failure_message
- "Required Python dependencies not found: ")
+ "Required Python dependencies NOT found: ")
if(arg_FAILURE_MESSAGE_PREFIX)
list(PREPEND failure_message ${arg_FAILURE_MESSAGE_PREFIX})
@@ -393,6 +584,67 @@ function(_qt_internal_sbom_generate_json)
set_property(GLOBAL APPEND PROPERTY _qt_sbom_cmake_verify_include_files "${verify_sbom}")
endfunction()
+# Helper to generate a CycloneDX JSON file from the intermediate .toml file created by the build
+# system.
+function(_qt_internal_sbom_generate_cydx_json)
+ set(opt_args
+ VERIFY
+ VERIFY_REQUIRED
+ VERBOSE
+ )
+ 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)
+
+ set(error_message_prefix "Failed to generate a CycloneDX json file.")
+ _qt_internal_sbom_assert_python_interpreter_available("${error_message_prefix}")
+ _qt_internal_sbom_assert_python_dependency_available(GENERATE_CYCLONE_DX_V1_6
+ "cyclonedx" ${error_message_prefix})
+
+ _qt_internal_sbom_get_cyclone_dx_generator_path(generator_path)
+
+ set(extra_args "")
+ if(arg_VERIFY)
+ list(APPEND extra_args "--validate-json")
+ endif()
+ if(arg_VERIFY_REQUIRED)
+ list(APPEND extra_args "--validate-json-required")
+ endif()
+ if(arg_VERBOSE)
+ list(APPEND extra_args "--verbose")
+ endif()
+ list(JOIN extra_args " " extra_args)
+
+ set(content "
+ message(STATUS
+ \"Generating final CycloneDX JSON: \${QT_SBOM_OUTPUT_PATH_WITHOUT_EXT}.json\")
+ execute_process(
+ COMMAND \"${QT_INTERNAL_SBOM_PYTHON_EXECUTABLE}\" \"${generator_path}\"
+ --input-path \"\${QT_SBOM_OUTPUT_PATH}\"
+ --output-path \"\${QT_SBOM_OUTPUT_PATH_WITHOUT_EXT}.json\"
+ ${extra_args}
+ RESULT_VARIABLE res
+ )
+ if(NOT res EQUAL 0)
+ message(FATAL_ERROR \"CycloneDX JSON generation failed: \${res}\")
+ endif()
+ # Remove the intermediate toml file, there's no point in installing it.
+ # Keep the one in the build dir, it's useful for debugging.
+ if(NOT QT_SBOM_BUILD_TIME)
+ message(STATUS \"Removing intermediate TOML file \${QT_SBOM_OUTPUT_PATH}\")
+ file(REMOVE \"\${QT_SBOM_OUTPUT_PATH}\")
+ endif()
+")
+
+ _qt_internal_get_current_project_sbom_dir(sbom_dir)
+ set(generate_cydx "${sbom_dir}/generate_cyclone_dx.cmake")
+ file(GENERATE OUTPUT "${generate_cydx}" CONTENT "${content}")
+
+ set_property(GLOBAL APPEND PROPERTY
+ _qt_sbom_cmake_post_generation_include_files_cydx "${generate_cydx}")
+endfunction()
+
# Helper to query whether the all required dependencies are available to generate a tag / value
# document from a json one.
function(_qt_internal_sbom_verify_deps_for_generate_tag_value_spdx_document)