From ba88d6819e3714b8871da87f1036103586a3cbc6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Apr 2026 20:33:44 -0500
Subject: [PATCH 1/8] ENH: Add SPDX 2.3 SBOM generation at configure time
Implement build-time generation of a Software Bill of Materials
(SBOM) in SPDX 2.3 JSON format. The SBOM documents ITK and all
enabled third-party dependencies with their names, licenses, and
dependency relationships.
- Add CMake/ITKSBOMGeneration.cmake with SBOM generation logic
- Add ITK_GENERATE_SBOM option (default ON) to CMakeLists.txt
- Generate sbom.spdx.json in the build directory at configure time
- Install sbom.spdx.json alongside LICENSE, NOTICE, and README.md
- Support FFTW as optional GPL dependency in SBOM
- Provide itk_sbom_register_package() for remote modules to extend
Co-authored-by: dzenanz <1792121+dzenanz@users.noreply.github.com>
---
CMake/ITKSBOMGeneration.cmake | 552 ++++++++++++++++++++++++++++++++++
CMakeLists.txt | 23 ++
2 files changed, 575 insertions(+)
create mode 100644 CMake/ITKSBOMGeneration.cmake
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
new file mode 100644
index 00000000000..b33583728b0
--- /dev/null
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -0,0 +1,552 @@
+#[=============================================================================[
+ ITKSBOMGeneration.cmake - Generate SPDX Software Bill of Materials (SBOM)
+
+ This module generates an SPDX 2.3 JSON SBOM document describing ITK and
+ its enabled third-party dependencies at build configuration time. The SBOM
+ includes component names, versions, licenses, and dependency relationships.
+
+ Usage:
+ option(ITK_GENERATE_SBOM "Generate SPDX SBOM at configure time" ON)
+ include(ITKSBOMGeneration)
+
+ The generated file is written to:
+ ${CMAKE_BINARY_DIR}/sbom.spdx.json
+#]=============================================================================]
+
+if(NOT ITK_GENERATE_SBOM)
+ return()
+endif()
+
+#-----------------------------------------------------------------------------
+# Allow remote modules to register SBOM package metadata.
+#
+# Usage in a remote module's CMakeLists.txt:
+# itk_sbom_register_package(
+# NAME "MyRemoteModule"
+# VERSION "1.0.0"
+# SPDX_LICENSE "Apache-2.0"
+# DOWNLOAD_LOCATION "https://github.com/example/MyRemoteModule"
+# SUPPLIER "Organization: Example"
+# COPYRIGHT "Copyright Example Inc."
+# )
+#
+define_property(
+ GLOBAL
+ PROPERTY ITK_SBOM_EXTRA_PACKAGES
+ BRIEF_DOCS
+ "Additional SBOM package entries registered by remote modules."
+ FULL_DOCS
+ "A list of JSON-formatted package entries for the SBOM."
+)
+
+function(itk_sbom_register_package)
+ set(_options "")
+ set(
+ _one_value
+ NAME
+ VERSION
+ SPDX_LICENSE
+ DOWNLOAD_LOCATION
+ SUPPLIER
+ COPYRIGHT
+ )
+ set(_multi_value "")
+ cmake_parse_arguments(
+ _pkg
+ "${_options}"
+ "${_one_value}"
+ "${_multi_value}"
+ ${ARGN}
+ )
+
+ if(NOT _pkg_NAME)
+ message(FATAL_ERROR "itk_sbom_register_package: NAME is required.")
+ endif()
+ if(NOT _pkg_SPDX_LICENSE)
+ set(_pkg_SPDX_LICENSE "NOASSERTION")
+ endif()
+ if(NOT _pkg_VERSION)
+ set(_pkg_VERSION "NOASSERTION")
+ endif()
+ if(NOT _pkg_DOWNLOAD_LOCATION)
+ set(_pkg_DOWNLOAD_LOCATION "NOASSERTION")
+ endif()
+ if(NOT _pkg_SUPPLIER)
+ set(_pkg_SUPPLIER "NOASSERTION")
+ endif()
+ if(NOT _pkg_COPYRIGHT)
+ set(_pkg_COPYRIGHT "NOASSERTION")
+ endif()
+
+ # Sanitize the name for use as SPDX ID (only alphanumeric and -)
+ string(REGEX REPLACE "[^A-Za-z0-9-]" "-" _spdx_id "${_pkg_NAME}")
+
+ set(_entry "")
+ string(APPEND _entry " {\n")
+ string(APPEND _entry " \"SPDXID\": \"SPDXRef-${_spdx_id}\",\n")
+ string(APPEND _entry " \"name\": \"${_pkg_NAME}\",\n")
+ string(APPEND _entry " \"versionInfo\": \"${_pkg_VERSION}\",\n")
+ string(
+ APPEND
+ _entry
+ " \"downloadLocation\": \"${_pkg_DOWNLOAD_LOCATION}\",\n"
+ )
+ string(APPEND _entry " \"supplier\": \"${_pkg_SUPPLIER}\",\n")
+ string(
+ APPEND
+ _entry
+ " \"licenseConcluded\": \"${_pkg_SPDX_LICENSE}\",\n"
+ )
+ string(APPEND _entry " \"licenseDeclared\": \"${_pkg_SPDX_LICENSE}\",\n")
+ string(APPEND _entry " \"copyrightText\": \"${_pkg_COPYRIGHT}\",\n")
+ string(APPEND _entry " \"filesAnalyzed\": false\n")
+ string(APPEND _entry " }")
+
+ set_property(
+ GLOBAL
+ APPEND
+ PROPERTY
+ ITK_SBOM_EXTRA_PACKAGES
+ "${_entry}"
+ )
+
+ # Also store the SPDX ID for relationship generation
+ set_property(
+ GLOBAL
+ APPEND
+ PROPERTY
+ ITK_SBOM_EXTRA_SPDX_IDS
+ "SPDXRef-${_spdx_id}"
+ )
+endfunction()
+
+#-----------------------------------------------------------------------------
+# Internal: Escape a string for use in a JSON value.
+# Handles backslashes, double quotes, and newlines.
+#
+function(_itk_sbom_json_escape input_string output_var)
+ set(_str "${input_string}")
+ # Escape backslashes first
+ string(REPLACE "\\" "\\\\" _str "${_str}")
+ # Escape double quotes
+ string(REPLACE "\"" "\\\"" _str "${_str}")
+ # Escape newlines (CMake uses \n for semicolons and actual newlines)
+ string(REPLACE "\n" "\\n" _str "${_str}")
+ # Escape tabs
+ string(REPLACE "\t" "\\t" _str "${_str}")
+ set(${output_var} "${_str}" PARENT_SCOPE)
+endfunction()
+
+#-----------------------------------------------------------------------------
+# Internal: Define SBOM metadata for a known ThirdParty module.
+# Sets variables in the caller's scope:
+# _spdx_license, _download_location, _supplier, _copyright, _version
+#
+function(
+ _itk_sbom_get_thirdparty_metadata
+ module_name
+ out_license
+ out_download
+ out_supplier
+ out_copyright
+ out_version
+)
+ # Defaults
+ set(_license "NOASSERTION")
+ set(_download "NOASSERTION")
+ set(_supplier "NOASSERTION")
+ set(_copyright "NOASSERTION")
+ set(_version "NOASSERTION")
+
+ if("${module_name}" STREQUAL "ITKDCMTK")
+ set(_license "BSD-3-Clause")
+ set(_download "https://dicom.offis.de/dcmtk")
+ set(_supplier "Organization: OFFIS e.V.")
+ set(_copyright "Copyright OFFIS e.V.")
+ set(_version "3.6.9")
+ elseif("${module_name}" STREQUAL "ITKDICOMParser")
+ set(_license "BSD-3-Clause")
+ set(_download "https://github.com/InsightSoftwareConsortium/ITK")
+ set(_supplier "Organization: Kitware Inc.")
+ set(_copyright "Copyright Kitware Inc.")
+ elseif("${module_name}" STREQUAL "ITKDoubleConversion")
+ set(_license "BSD-3-Clause")
+ set(_download "https://github.com/google/double-conversion")
+ set(_supplier "Organization: Google Inc.")
+ set(_copyright "Copyright Google Inc.")
+ set(_version "3.1.6")
+ elseif("${module_name}" STREQUAL "ITKEigen3")
+ set(_license "MPL-2.0")
+ set(_download "https://eigen.tuxfamily.org")
+ set(_supplier "Organization: Eigen")
+ set(_copyright "Copyright Eigen contributors")
+ # Try to detect Eigen version from CMake variable
+ if(DEFINED EIGEN3_VERSION_STRING)
+ set(_version "${EIGEN3_VERSION_STRING}")
+ elseif(DEFINED Eigen3_VERSION)
+ set(_version "${Eigen3_VERSION}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKExpat")
+ set(_license "MIT")
+ set(_download "https://libexpat.github.io")
+ set(_supplier "Organization: Expat development team")
+ set(_copyright "Copyright Expat development team")
+ elseif("${module_name}" STREQUAL "ITKGDCM")
+ set(_license "BSD-3-Clause")
+ set(_download "https://gdcm.sourceforge.net")
+ set(_supplier "Organization: GDCM contributors")
+ set(_copyright "Copyright GDCM contributors")
+ set(_version "3.2.2")
+ elseif("${module_name}" STREQUAL "ITKGIFTI")
+ set(_license "LicenseRef-NITRC-Public-Domain")
+ set(_download "https://www.nitrc.org/projects/gifti")
+ set(_supplier "Organization: NITRC")
+ set(_copyright "NOASSERTION")
+ elseif("${module_name}" STREQUAL "ITKGoogleTest")
+ set(_license "BSD-3-Clause")
+ set(_download "https://github.com/google/googletest")
+ set(_supplier "Organization: Google Inc.")
+ set(_copyright "Copyright Google Inc.")
+ set(_version "1.17.0")
+ elseif("${module_name}" STREQUAL "ITKHDF5")
+ set(_license "BSD-3-Clause")
+ set(_download "https://www.hdfgroup.org/solutions/hdf5")
+ set(_supplier "Organization: The HDF Group")
+ set(_copyright "Copyright The HDF Group")
+ # Try to detect HDF5 version from CMake variable
+ if(DEFINED HDF5_VERSION)
+ set(_version "${HDF5_VERSION}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKJPEG")
+ set(_license "IJG AND BSD-3-Clause AND Zlib")
+ set(_download "https://libjpeg-turbo.org")
+ set(_supplier "Organization: libjpeg-turbo")
+ set(_copyright "Copyright libjpeg-turbo contributors")
+ elseif("${module_name}" STREQUAL "ITKKWSys")
+ set(_license "BSD-3-Clause")
+ set(_download "https://gitlab.kitware.com/utils/kwsys")
+ set(_supplier "Organization: Kitware Inc.")
+ set(_copyright "Copyright Kitware Inc.")
+ elseif("${module_name}" STREQUAL "ITKMINC")
+ set(_license "LGPL-2.1-only")
+ set(_download "https://github.com/BIC-MNI/libminc")
+ set(_supplier "Organization: McConnell Brain Imaging Centre")
+ set(_copyright "Copyright McConnell Brain Imaging Centre")
+ elseif("${module_name}" STREQUAL "ITKMetaIO")
+ set(_license "Apache-2.0")
+ set(_download "https://github.com/Kitware/MetaIO")
+ set(_supplier "Organization: Kitware Inc.")
+ set(_copyright "Copyright Kitware Inc.")
+ elseif("${module_name}" STREQUAL "ITKNIFTI")
+ set(_license "LicenseRef-NIFTI-Public-Domain")
+ set(_download "https://nifti.nimh.nih.gov")
+ set(_supplier "Organization: NITRC")
+ set(_copyright "NOASSERTION")
+ elseif("${module_name}" STREQUAL "ITKNetlib")
+ set(_license "LicenseRef-Netlib-SLATEC")
+ set(_download "https://www.netlib.org/slatec")
+ set(_supplier "Organization: Netlib")
+ set(_copyright "NOASSERTION")
+ elseif("${module_name}" STREQUAL "ITKNrrdIO")
+ set(_license "LGPL-2.1-only")
+ set(_download "https://teem.sourceforge.net/nrrd")
+ set(_supplier "Organization: Teem")
+ set(_copyright "Copyright Teem contributors")
+ elseif("${module_name}" STREQUAL "ITKOpenJPEG")
+ set(_license "BSD-2-Clause")
+ set(_download "https://www.openjpeg.org")
+ set(_supplier "Organization: OpenJPEG contributors")
+ set(_copyright "Copyright OpenJPEG contributors")
+ set(_version "2.5.4")
+ elseif("${module_name}" STREQUAL "ITKPNG")
+ set(_license "Libpng-2.0")
+ set(_download "https://www.libpng.org/pub/png/libpng.html")
+ set(_supplier "Organization: libpng contributors")
+ set(_copyright "Copyright libpng contributors")
+ # Try to detect PNG version from CMake variable
+ if(DEFINED PNG_VERSION_STRING)
+ set(_version "${PNG_VERSION_STRING}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKTBB")
+ set(_license "Apache-2.0")
+ set(_download "https://github.com/oneapi-src/oneTBB")
+ set(_supplier "Organization: Intel Corporation")
+ set(_copyright "Copyright Intel Corporation")
+ # Try to detect TBB version from CMake variable
+ if(DEFINED TBB_VERSION)
+ set(_version "${TBB_VERSION}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKTIFF")
+ set(_license "libtiff")
+ set(_download "https://libtiff.maptools.org")
+ set(_supplier "Organization: libtiff contributors")
+ set(_copyright "Copyright libtiff contributors")
+ # Try to detect TIFF version from CMake variable
+ if(DEFINED TIFF_VERSION_STRING)
+ set(_version "${TIFF_VERSION_STRING}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKVNL")
+ set(_license "BSD-3-Clause")
+ set(_download "https://vxl.github.io")
+ set(_supplier "Organization: VXL contributors")
+ set(_copyright "Copyright VXL contributors")
+ elseif("${module_name}" STREQUAL "ITKZLIB")
+ set(_license "Zlib")
+ set(_download "https://github.com/zlib-ng/zlib-ng")
+ set(_supplier "Organization: zlib-ng contributors")
+ set(_copyright "Copyright zlib-ng contributors")
+ # Try to detect zlib version from CMake variable
+ if(DEFINED ZLIB_VERSION_STRING)
+ set(_version "${ZLIB_VERSION_STRING}")
+ endif()
+ elseif("${module_name}" STREQUAL "ITKLIBLBFGS")
+ set(_license "MIT")
+ set(_download "https://github.com/chokkan/liblbfgs")
+ set(_supplier "Organization: Naoaki Okazaki")
+ set(_copyright "Copyright Naoaki Okazaki")
+ endif()
+
+ set(${out_license} "${_license}" PARENT_SCOPE)
+ set(${out_download} "${_download}" PARENT_SCOPE)
+ set(${out_supplier} "${_supplier}" PARENT_SCOPE)
+ set(${out_copyright} "${_copyright}" PARENT_SCOPE)
+ set(${out_version} "${_version}" PARENT_SCOPE)
+endfunction()
+
+#-----------------------------------------------------------------------------
+# Main function: Generate the SPDX 2.3 SBOM JSON document.
+#
+function(itk_generate_sbom)
+ string(TIMESTAMP _sbom_timestamp "%Y-%m-%dT%H:%M:%SZ" UTC)
+ string(TIMESTAMP _sbom_uid "%Y%m%d%H%M%S" UTC)
+
+ set(
+ _sbom_namespace
+ "https://spdx.org/spdxdocs/ITK-${ITK_VERSION}-${_sbom_uid}"
+ )
+
+ # --- Begin JSON document ---
+ set(_json "")
+ string(APPEND _json "{\n")
+ string(APPEND _json " \"spdxVersion\": \"SPDX-2.3\",\n")
+ string(APPEND _json " \"dataLicense\": \"CC0-1.0\",\n")
+ string(APPEND _json " \"SPDXID\": \"SPDXRef-DOCUMENT\",\n")
+ string(APPEND _json " \"name\": \"ITK-${ITK_VERSION}-SBOM\",\n")
+ string(APPEND _json " \"documentNamespace\": \"${_sbom_namespace}\",\n")
+
+ # --- creationInfo ---
+ string(APPEND _json " \"creationInfo\": {\n")
+ string(APPEND _json " \"created\": \"${_sbom_timestamp}\",\n")
+ string(APPEND _json " \"creators\": [\n")
+ string(APPEND _json " \"Tool: CMake-${CMAKE_VERSION}\",\n")
+ string(APPEND _json " \"Organization: NumFOCUS\"\n")
+ string(APPEND _json " ],\n")
+ string(APPEND _json " \"licenseListVersion\": \"3.22\"\n")
+ string(APPEND _json " },\n")
+
+ # --- packages array ---
+ string(APPEND _json " \"packages\": [\n")
+
+ # ITK main package
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"SPDXID\": \"SPDXRef-ITK\",\n")
+ string(APPEND _json " \"name\": \"ITK\",\n")
+ string(APPEND _json " \"versionInfo\": \"${ITK_VERSION}\",\n")
+ string(
+ APPEND
+ _json
+ " \"downloadLocation\": \"https://github.com/InsightSoftwareConsortium/ITK\",\n"
+ )
+ string(APPEND _json " \"supplier\": \"Organization: NumFOCUS\",\n")
+ string(APPEND _json " \"licenseConcluded\": \"Apache-2.0\",\n")
+ string(APPEND _json " \"licenseDeclared\": \"Apache-2.0\",\n")
+ string(
+ APPEND
+ _json
+ " \"copyrightText\": \"Copyright 1999-2019 Insight Software Consortium, Copyright 2020-present NumFOCUS\",\n"
+ )
+ string(APPEND _json " \"filesAnalyzed\": false\n")
+ string(APPEND _json " }")
+
+ # Collect ThirdParty modules from enabled modules list
+ set(_thirdparty_spdx_ids "")
+ foreach(_mod ${ITK_MODULES_ENABLED})
+ if(${_mod}_IS_TEST)
+ continue()
+ endif()
+
+ # Check if this is a ThirdParty module by examining its source directory
+ if(NOT DEFINED ${_mod}_SOURCE_DIR)
+ continue()
+ endif()
+ string(FIND "${${_mod}_SOURCE_DIR}" "Modules/ThirdParty" _tp_pos)
+ if(_tp_pos EQUAL -1)
+ continue()
+ endif()
+
+ # Get metadata for this ThirdParty module
+ _itk_sbom_get_thirdparty_metadata("${_mod}"
+ _pkg_license _pkg_download _pkg_supplier _pkg_copyright _pkg_version
+ )
+
+ # Get description from module declaration and escape for JSON
+ set(_pkg_description "${ITK_MODULE_${_mod}_DESCRIPTION}")
+ if(_pkg_description)
+ _itk_sbom_json_escape("${_pkg_description}" _pkg_description)
+ endif()
+
+ # Sanitize module name for SPDX ID
+ string(REGEX REPLACE "[^A-Za-z0-9-]" "-" _spdx_id "${_mod}")
+ list(APPEND _thirdparty_spdx_ids "SPDXRef-${_spdx_id}")
+
+ string(APPEND _json ",\n")
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"SPDXID\": \"SPDXRef-${_spdx_id}\",\n")
+ string(APPEND _json " \"name\": \"${_mod}\",\n")
+ string(APPEND _json " \"versionInfo\": \"${_pkg_version}\",\n")
+ string(APPEND _json " \"downloadLocation\": \"${_pkg_download}\",\n")
+ string(APPEND _json " \"supplier\": \"${_pkg_supplier}\",\n")
+ string(APPEND _json " \"licenseConcluded\": \"${_pkg_license}\",\n")
+ string(APPEND _json " \"licenseDeclared\": \"${_pkg_license}\",\n")
+ string(APPEND _json " \"copyrightText\": \"${_pkg_copyright}\",\n")
+ if(_pkg_description)
+ string(APPEND _json " \"description\": \"${_pkg_description}\",\n")
+ endif()
+ string(APPEND _json " \"filesAnalyzed\": false\n")
+ string(APPEND _json " }")
+ endforeach()
+
+ # FFTW (not an ITK module, but an optional external dependency)
+ if(ITK_USE_FFTWD OR ITK_USE_FFTWF)
+ set(_fftw_license "GPL-2.0-or-later")
+ set(_fftw_version "NOASSERTION")
+ if(DEFINED _fftw_target_version)
+ set(_fftw_version "${_fftw_target_version}")
+ elseif(DEFINED FFTW_VERSION)
+ set(_fftw_version "${FFTW_VERSION}")
+ endif()
+
+ string(APPEND _json ",\n")
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"SPDXID\": \"SPDXRef-FFTW\",\n")
+ string(APPEND _json " \"name\": \"FFTW\",\n")
+ string(APPEND _json " \"versionInfo\": \"${_fftw_version}\",\n")
+ string(
+ APPEND
+ _json
+ " \"downloadLocation\": \"https://www.fftw.org\",\n"
+ )
+ string(APPEND _json " \"supplier\": \"Organization: MIT\",\n")
+ string(APPEND _json " \"licenseConcluded\": \"${_fftw_license}\",\n")
+ string(APPEND _json " \"licenseDeclared\": \"${_fftw_license}\",\n")
+ string(
+ APPEND
+ _json
+ " \"copyrightText\": \"Copyright Matteo Frigo and Massachusetts Institute of Technology\",\n"
+ )
+ string(
+ APPEND
+ _json
+ " \"description\": \"Fastest Fourier Transform in the West\",\n"
+ )
+ string(APPEND _json " \"filesAnalyzed\": false\n")
+ string(APPEND _json " }")
+ list(APPEND _thirdparty_spdx_ids "SPDXRef-FFTW")
+ endif()
+
+ # Append extra packages registered by remote modules
+ get_property(_extra_packages GLOBAL PROPERTY ITK_SBOM_EXTRA_PACKAGES)
+ foreach(_extra_pkg ${_extra_packages})
+ string(APPEND _json ",\n${_extra_pkg}")
+ endforeach()
+
+ string(APPEND _json "\n ],\n")
+
+ # --- relationships array ---
+ string(APPEND _json " \"relationships\": [\n")
+
+ # DOCUMENT describes ITK
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"spdxElementId\": \"SPDXRef-DOCUMENT\",\n")
+ string(APPEND _json " \"relationshipType\": \"DESCRIBES\",\n")
+ string(APPEND _json " \"relatedSpdxElement\": \"SPDXRef-ITK\"\n")
+ string(APPEND _json " }")
+
+ # ITK DEPENDS_ON each ThirdParty module
+ foreach(_spdx_id ${_thirdparty_spdx_ids})
+ string(APPEND _json ",\n")
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"spdxElementId\": \"SPDXRef-ITK\",\n")
+ string(APPEND _json " \"relationshipType\": \"DEPENDS_ON\",\n")
+ string(APPEND _json " \"relatedSpdxElement\": \"${_spdx_id}\"\n")
+ string(APPEND _json " }")
+ endforeach()
+
+ # Extra packages registered by remote modules
+ get_property(_extra_spdx_ids GLOBAL PROPERTY ITK_SBOM_EXTRA_SPDX_IDS)
+ foreach(_spdx_id ${_extra_spdx_ids})
+ string(APPEND _json ",\n")
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"spdxElementId\": \"SPDXRef-ITK\",\n")
+ string(APPEND _json " \"relationshipType\": \"DEPENDS_ON\",\n")
+ string(APPEND _json " \"relatedSpdxElement\": \"${_spdx_id}\"\n")
+ string(APPEND _json " }")
+ endforeach()
+
+ string(APPEND _json "\n ],\n")
+
+ # --- hasExtractedLicensingInfo for custom LicenseRef identifiers ---
+ string(APPEND _json " \"hasExtractedLicensingInfo\": [\n")
+ string(APPEND _json " {\n")
+ string(
+ APPEND
+ _json
+ " \"licenseId\": \"LicenseRef-NIFTI-Public-Domain\",\n"
+ )
+ string(APPEND _json " \"name\": \"NIFTI Public Domain License\",\n")
+ string(
+ APPEND
+ _json
+ " \"extractedText\": \"This software is in the public domain. The NIFTI header and library are released into the public domain.\"\n"
+ )
+ string(APPEND _json " },\n")
+ string(APPEND _json " {\n")
+ string(
+ APPEND
+ _json
+ " \"licenseId\": \"LicenseRef-NITRC-Public-Domain\",\n"
+ )
+ string(
+ APPEND
+ _json
+ " \"name\": \"NITRC GIFTI Public Domain License\",\n"
+ )
+ string(
+ APPEND
+ _json
+ " \"extractedText\": \"The GIFTI library is released into the public domain under the NITRC project.\"\n"
+ )
+ string(APPEND _json " },\n")
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"licenseId\": \"LicenseRef-Netlib-SLATEC\",\n")
+ string(
+ APPEND
+ _json
+ " \"name\": \"Netlib SLATEC Public Domain License\",\n"
+ )
+ string(
+ APPEND
+ _json
+ " \"extractedText\": \"The SLATEC Common Mathematical Library is issued by the U.S. Government and is in the public domain.\"\n"
+ )
+ string(APPEND _json " }\n")
+ string(APPEND _json " ]\n")
+
+ # --- Close JSON document ---
+ string(APPEND _json "}\n")
+
+ # Write SBOM to build directory
+ set(_sbom_file "${CMAKE_BINARY_DIR}/sbom.spdx.json")
+ file(WRITE "${_sbom_file}" "${_json}")
+ message(STATUS "SBOM generated: ${_sbom_file}")
+endfunction()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index aba2ec14dd4..7452f611271 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -247,6 +247,14 @@ If this is not set during the initial configuration, it will have no effect."
)
mark_as_advanced(ITK_USE_SYSTEM_LIBRARIES)
+#-----------------------------------------------------------------------------
+# Generate a Software Bill of Materials (SBOM) in SPDX 2.3 JSON format.
+option(
+ ITK_GENERATE_SBOM
+ "Generate SPDX Software Bill of Materials (SBOM) at configure time"
+ ON
+)
+
#-----------------------------------------------------------------------------
# Enable the download and use of BrainWeb datasets.
# When this data is available, additional 3D tests are enabled.
@@ -744,6 +752,12 @@ add_subdirectory(Modules/Remote)
# Enable modules according to user inputs and the module dependency DAG.
include(ITKModuleEnablement)
+# Generate SPDX Software Bill of Materials (SBOM) if enabled.
+include(ITKSBOMGeneration)
+if(ITK_GENERATE_SBOM)
+ itk_generate_sbom()
+endif()
+
# Setup clang-tidy for code best-practices enforcement for C++11
include(ITKClangTidySetup)
#----------------------------------------------------------------------
@@ -855,6 +869,15 @@ install(
COMPONENT Runtime
)
+if(ITK_GENERATE_SBOM)
+ install(
+ FILES
+ "${CMAKE_BINARY_DIR}/sbom.spdx.json"
+ DESTINATION ${ITK_INSTALL_DOC_DIR}
+ COMPONENT Runtime
+ )
+endif()
+
if(BUILD_TESTING)
# If building the testing, write the test costs (i.e. time to run)
# analysis to disk to more easily review long-running test times
From 229c2cecbeda28bbf0b0865da2eed2dc717bb02b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Apr 2026 20:34:13 -0500
Subject: [PATCH 2/8] ENH: Distribute SBOM metadata into per-module
itk-module.cmake files
Move SPDX license and per-module metadata from the centralized
lookup table in ITKSBOMGeneration.cmake into each ThirdParty
module's itk-module.cmake file for co-location and easier
maintenance.
- Extend itk_module() macro to accept SPDX_LICENSE,
SPDX_DOWNLOAD_LOCATION, SPDX_COPYRIGHT,
SPDX_CUSTOM_LICENSE_TEXT, and SPDX_CUSTOM_LICENSE_NAME
- Add SPDX metadata to all 23 ThirdParty itk-module.cmake files
- Simplify ITKSBOMGeneration.cmake to read from module declarations
Co-authored-by: dzenanz <1792121+dzenanz@users.noreply.github.com>
---
.gitignore | 1 +
CMake/ITKModuleMacros.cmake | 22 +-
CMake/ITKSBOMGeneration.cmake | 293 ++++--------------
Modules/ThirdParty/DCMTK/itk-module.cmake | 18 +-
.../ThirdParty/DICOMParser/itk-module.cmake | 11 +-
.../DoubleConversion/itk-module.cmake | 11 +-
Modules/ThirdParty/Eigen3/itk-module.cmake | 6 +
Modules/ThirdParty/Expat/itk-module.cmake | 11 +-
Modules/ThirdParty/GDCM/itk-module.cmake | 18 +-
Modules/ThirdParty/GIFTI/itk-module.cmake | 10 +
.../ThirdParty/GoogleTest/itk-module.cmake | 12 +-
Modules/ThirdParty/HDF5/itk-module.cmake | 13 +-
Modules/ThirdParty/JPEG/itk-module.cmake | 11 +-
Modules/ThirdParty/KWSys/itk-module.cmake | 11 +-
Modules/ThirdParty/MINC/itk-module.cmake | 18 +-
Modules/ThirdParty/MetaIO/itk-module.cmake | 13 +-
Modules/ThirdParty/NIFTI/itk-module.cmake | 17 +-
Modules/ThirdParty/Netlib/itk-module.cmake | 17 +-
Modules/ThirdParty/NrrdIO/itk-module.cmake | 13 +-
Modules/ThirdParty/OpenJPEG/itk-module.cmake | 12 +-
Modules/ThirdParty/PNG/itk-module.cmake | 13 +-
Modules/ThirdParty/TBB/itk-module.cmake | 12 +-
Modules/ThirdParty/TIFF/itk-module.cmake | 6 +
Modules/ThirdParty/VNL/itk-module.cmake | 11 +-
Modules/ThirdParty/ZLIB/itk-module.cmake | 11 +-
Modules/ThirdParty/libLBFGS/itk-module.cmake | 11 +-
26 files changed, 346 insertions(+), 256 deletions(-)
diff --git a/.gitignore b/.gitignore
index 634d6fc26c1..b4da296cf20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@ CMakeUserPresets.json
# pixi environments
.pixi
*.egg-info
+InsightData-*.tar.gz
diff --git a/CMake/ITKModuleMacros.cmake b/CMake/ITKModuleMacros.cmake
index 96ef854c4db..4a933db26d8 100644
--- a/CMake/ITKModuleMacros.cmake
+++ b/CMake/ITKModuleMacros.cmake
@@ -69,12 +69,17 @@ macro(itk_module _name)
set(ITK_MODULE_${itk-module}_DESCRIPTION "description")
set(ITK_MODULE_${itk-module}_EXCLUDE_FROM_DEFAULT 0)
set(ITK_MODULE_${itk-module}_ENABLE_SHARED 0)
+ set(ITK_MODULE_${itk-module}_SPDX_LICENSE "")
+ set(ITK_MODULE_${itk-module}_SPDX_DOWNLOAD_LOCATION "")
+ set(ITK_MODULE_${itk-module}_SPDX_COPYRIGHT "")
+ set(ITK_MODULE_${itk-module}_SPDX_CUSTOM_LICENSE_TEXT "")
+ set(ITK_MODULE_${itk-module}_SPDX_CUSTOM_LICENSE_NAME "")
foreach(arg ${ARGN})
### Parse itk_module named options
if(
"${arg}"
MATCHES
- "^((|COMPILE_|PRIVATE_|TEST_|)DEPENDS|DESCRIPTION|DEFAULT|FACTORY_NAMES)$"
+ "^((|COMPILE_|PRIVATE_|TEST_|)DEPENDS|DESCRIPTION|DEFAULT|FACTORY_NAMES|SPDX_LICENSE|SPDX_DOWNLOAD_LOCATION|SPDX_COPYRIGHT|SPDX_CUSTOM_LICENSE_TEXT|SPDX_CUSTOM_LICENSE_NAME)$"
)
set(_doing "${arg}")
elseif("${arg}" MATCHES "^EXCLUDE_FROM_DEFAULT$")
@@ -104,6 +109,21 @@ macro(itk_module _name)
elseif("${_doing}" MATCHES "^DESCRIPTION$")
set(_doing "")
set(ITK_MODULE_${itk-module}_DESCRIPTION "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_LICENSE$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_LICENSE "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_DOWNLOAD_LOCATION$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_DOWNLOAD_LOCATION "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_COPYRIGHT$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_COPYRIGHT "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_CUSTOM_LICENSE_TEXT$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_CUSTOM_LICENSE_TEXT "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_CUSTOM_LICENSE_NAME$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_CUSTOM_LICENSE_NAME "${arg}")
elseif("${_doing}" MATCHES "^DEFAULT")
message(FATAL_ERROR "Invalid argument [DEFAULT]")
else()
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
index b33583728b0..041b763bc83 100644
--- a/CMake/ITKSBOMGeneration.cmake
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -5,6 +5,14 @@
its enabled third-party dependencies at build configuration time. The SBOM
includes component names, versions, licenses, and dependency relationships.
+ Per-module SPDX metadata is declared in each module's itk-module.cmake via
+ the itk_module() macro arguments:
+ SPDX_LICENSE - SPDX license identifier (e.g. "Apache-2.0")
+ SPDX_DOWNLOAD_LOCATION - URL for the upstream source
+ SPDX_COPYRIGHT - Copyright text
+ SPDX_CUSTOM_LICENSE_TEXT - Extracted text for custom LicenseRef-* IDs
+ SPDX_CUSTOM_LICENSE_NAME - Human-readable name for custom license refs
+
Usage:
option(ITK_GENERATE_SBOM "Generate SPDX SBOM at configure time" ON)
include(ITKSBOMGeneration)
@@ -137,182 +145,6 @@ function(_itk_sbom_json_escape input_string output_var)
set(${output_var} "${_str}" PARENT_SCOPE)
endfunction()
-#-----------------------------------------------------------------------------
-# Internal: Define SBOM metadata for a known ThirdParty module.
-# Sets variables in the caller's scope:
-# _spdx_license, _download_location, _supplier, _copyright, _version
-#
-function(
- _itk_sbom_get_thirdparty_metadata
- module_name
- out_license
- out_download
- out_supplier
- out_copyright
- out_version
-)
- # Defaults
- set(_license "NOASSERTION")
- set(_download "NOASSERTION")
- set(_supplier "NOASSERTION")
- set(_copyright "NOASSERTION")
- set(_version "NOASSERTION")
-
- if("${module_name}" STREQUAL "ITKDCMTK")
- set(_license "BSD-3-Clause")
- set(_download "https://dicom.offis.de/dcmtk")
- set(_supplier "Organization: OFFIS e.V.")
- set(_copyright "Copyright OFFIS e.V.")
- set(_version "3.6.9")
- elseif("${module_name}" STREQUAL "ITKDICOMParser")
- set(_license "BSD-3-Clause")
- set(_download "https://github.com/InsightSoftwareConsortium/ITK")
- set(_supplier "Organization: Kitware Inc.")
- set(_copyright "Copyright Kitware Inc.")
- elseif("${module_name}" STREQUAL "ITKDoubleConversion")
- set(_license "BSD-3-Clause")
- set(_download "https://github.com/google/double-conversion")
- set(_supplier "Organization: Google Inc.")
- set(_copyright "Copyright Google Inc.")
- set(_version "3.1.6")
- elseif("${module_name}" STREQUAL "ITKEigen3")
- set(_license "MPL-2.0")
- set(_download "https://eigen.tuxfamily.org")
- set(_supplier "Organization: Eigen")
- set(_copyright "Copyright Eigen contributors")
- # Try to detect Eigen version from CMake variable
- if(DEFINED EIGEN3_VERSION_STRING)
- set(_version "${EIGEN3_VERSION_STRING}")
- elseif(DEFINED Eigen3_VERSION)
- set(_version "${Eigen3_VERSION}")
- endif()
- elseif("${module_name}" STREQUAL "ITKExpat")
- set(_license "MIT")
- set(_download "https://libexpat.github.io")
- set(_supplier "Organization: Expat development team")
- set(_copyright "Copyright Expat development team")
- elseif("${module_name}" STREQUAL "ITKGDCM")
- set(_license "BSD-3-Clause")
- set(_download "https://gdcm.sourceforge.net")
- set(_supplier "Organization: GDCM contributors")
- set(_copyright "Copyright GDCM contributors")
- set(_version "3.2.2")
- elseif("${module_name}" STREQUAL "ITKGIFTI")
- set(_license "LicenseRef-NITRC-Public-Domain")
- set(_download "https://www.nitrc.org/projects/gifti")
- set(_supplier "Organization: NITRC")
- set(_copyright "NOASSERTION")
- elseif("${module_name}" STREQUAL "ITKGoogleTest")
- set(_license "BSD-3-Clause")
- set(_download "https://github.com/google/googletest")
- set(_supplier "Organization: Google Inc.")
- set(_copyright "Copyright Google Inc.")
- set(_version "1.17.0")
- elseif("${module_name}" STREQUAL "ITKHDF5")
- set(_license "BSD-3-Clause")
- set(_download "https://www.hdfgroup.org/solutions/hdf5")
- set(_supplier "Organization: The HDF Group")
- set(_copyright "Copyright The HDF Group")
- # Try to detect HDF5 version from CMake variable
- if(DEFINED HDF5_VERSION)
- set(_version "${HDF5_VERSION}")
- endif()
- elseif("${module_name}" STREQUAL "ITKJPEG")
- set(_license "IJG AND BSD-3-Clause AND Zlib")
- set(_download "https://libjpeg-turbo.org")
- set(_supplier "Organization: libjpeg-turbo")
- set(_copyright "Copyright libjpeg-turbo contributors")
- elseif("${module_name}" STREQUAL "ITKKWSys")
- set(_license "BSD-3-Clause")
- set(_download "https://gitlab.kitware.com/utils/kwsys")
- set(_supplier "Organization: Kitware Inc.")
- set(_copyright "Copyright Kitware Inc.")
- elseif("${module_name}" STREQUAL "ITKMINC")
- set(_license "LGPL-2.1-only")
- set(_download "https://github.com/BIC-MNI/libminc")
- set(_supplier "Organization: McConnell Brain Imaging Centre")
- set(_copyright "Copyright McConnell Brain Imaging Centre")
- elseif("${module_name}" STREQUAL "ITKMetaIO")
- set(_license "Apache-2.0")
- set(_download "https://github.com/Kitware/MetaIO")
- set(_supplier "Organization: Kitware Inc.")
- set(_copyright "Copyright Kitware Inc.")
- elseif("${module_name}" STREQUAL "ITKNIFTI")
- set(_license "LicenseRef-NIFTI-Public-Domain")
- set(_download "https://nifti.nimh.nih.gov")
- set(_supplier "Organization: NITRC")
- set(_copyright "NOASSERTION")
- elseif("${module_name}" STREQUAL "ITKNetlib")
- set(_license "LicenseRef-Netlib-SLATEC")
- set(_download "https://www.netlib.org/slatec")
- set(_supplier "Organization: Netlib")
- set(_copyright "NOASSERTION")
- elseif("${module_name}" STREQUAL "ITKNrrdIO")
- set(_license "LGPL-2.1-only")
- set(_download "https://teem.sourceforge.net/nrrd")
- set(_supplier "Organization: Teem")
- set(_copyright "Copyright Teem contributors")
- elseif("${module_name}" STREQUAL "ITKOpenJPEG")
- set(_license "BSD-2-Clause")
- set(_download "https://www.openjpeg.org")
- set(_supplier "Organization: OpenJPEG contributors")
- set(_copyright "Copyright OpenJPEG contributors")
- set(_version "2.5.4")
- elseif("${module_name}" STREQUAL "ITKPNG")
- set(_license "Libpng-2.0")
- set(_download "https://www.libpng.org/pub/png/libpng.html")
- set(_supplier "Organization: libpng contributors")
- set(_copyright "Copyright libpng contributors")
- # Try to detect PNG version from CMake variable
- if(DEFINED PNG_VERSION_STRING)
- set(_version "${PNG_VERSION_STRING}")
- endif()
- elseif("${module_name}" STREQUAL "ITKTBB")
- set(_license "Apache-2.0")
- set(_download "https://github.com/oneapi-src/oneTBB")
- set(_supplier "Organization: Intel Corporation")
- set(_copyright "Copyright Intel Corporation")
- # Try to detect TBB version from CMake variable
- if(DEFINED TBB_VERSION)
- set(_version "${TBB_VERSION}")
- endif()
- elseif("${module_name}" STREQUAL "ITKTIFF")
- set(_license "libtiff")
- set(_download "https://libtiff.maptools.org")
- set(_supplier "Organization: libtiff contributors")
- set(_copyright "Copyright libtiff contributors")
- # Try to detect TIFF version from CMake variable
- if(DEFINED TIFF_VERSION_STRING)
- set(_version "${TIFF_VERSION_STRING}")
- endif()
- elseif("${module_name}" STREQUAL "ITKVNL")
- set(_license "BSD-3-Clause")
- set(_download "https://vxl.github.io")
- set(_supplier "Organization: VXL contributors")
- set(_copyright "Copyright VXL contributors")
- elseif("${module_name}" STREQUAL "ITKZLIB")
- set(_license "Zlib")
- set(_download "https://github.com/zlib-ng/zlib-ng")
- set(_supplier "Organization: zlib-ng contributors")
- set(_copyright "Copyright zlib-ng contributors")
- # Try to detect zlib version from CMake variable
- if(DEFINED ZLIB_VERSION_STRING)
- set(_version "${ZLIB_VERSION_STRING}")
- endif()
- elseif("${module_name}" STREQUAL "ITKLIBLBFGS")
- set(_license "MIT")
- set(_download "https://github.com/chokkan/liblbfgs")
- set(_supplier "Organization: Naoaki Okazaki")
- set(_copyright "Copyright Naoaki Okazaki")
- endif()
-
- set(${out_license} "${_license}" PARENT_SCOPE)
- set(${out_download} "${_download}" PARENT_SCOPE)
- set(${out_supplier} "${_supplier}" PARENT_SCOPE)
- set(${out_copyright} "${_copyright}" PARENT_SCOPE)
- set(${out_version} "${_version}" PARENT_SCOPE)
-endfunction()
-
#-----------------------------------------------------------------------------
# Main function: Generate the SPDX 2.3 SBOM JSON document.
#
@@ -370,24 +202,29 @@ function(itk_generate_sbom)
# Collect ThirdParty modules from enabled modules list
set(_thirdparty_spdx_ids "")
+ # Track custom license refs for hasExtractedLicensingInfo
+ set(_custom_license_ids "")
+ set(_custom_license_names "")
+ set(_custom_license_texts "")
foreach(_mod ${ITK_MODULES_ENABLED})
if(${_mod}_IS_TEST)
continue()
endif()
- # Check if this is a ThirdParty module by examining its source directory
- if(NOT DEFINED ${_mod}_SOURCE_DIR)
- continue()
- endif()
- string(FIND "${${_mod}_SOURCE_DIR}" "Modules/ThirdParty" _tp_pos)
- if(_tp_pos EQUAL -1)
+ # Only include modules that have SPDX metadata declared
+ set(_pkg_license "${ITK_MODULE_${_mod}_SPDX_LICENSE}")
+ if(NOT _pkg_license)
continue()
endif()
- # Get metadata for this ThirdParty module
- _itk_sbom_get_thirdparty_metadata("${_mod}"
- _pkg_license _pkg_download _pkg_supplier _pkg_copyright _pkg_version
- )
+ set(_pkg_download "${ITK_MODULE_${_mod}_SPDX_DOWNLOAD_LOCATION}")
+ set(_pkg_copyright "${ITK_MODULE_${_mod}_SPDX_COPYRIGHT}")
+ if(NOT _pkg_download)
+ set(_pkg_download "NOASSERTION")
+ endif()
+ if(NOT _pkg_copyright)
+ set(_pkg_copyright "NOASSERTION")
+ endif()
# Get description from module declaration and escape for JSON
set(_pkg_description "${ITK_MODULE_${_mod}_DESCRIPTION}")
@@ -395,6 +232,15 @@ function(itk_generate_sbom)
_itk_sbom_json_escape("${_pkg_description}" _pkg_description)
endif()
+ # Collect custom license references
+ set(_custom_text "${ITK_MODULE_${_mod}_SPDX_CUSTOM_LICENSE_TEXT}")
+ set(_custom_name "${ITK_MODULE_${_mod}_SPDX_CUSTOM_LICENSE_NAME}")
+ if(_custom_text AND _custom_name)
+ list(APPEND _custom_license_ids "${_pkg_license}")
+ list(APPEND _custom_license_names "${_custom_name}")
+ list(APPEND _custom_license_texts "${_custom_text}")
+ endif()
+
# Sanitize module name for SPDX ID
string(REGEX REPLACE "[^A-Za-z0-9-]" "-" _spdx_id "${_mod}")
list(APPEND _thirdparty_spdx_ids "SPDXRef-${_spdx_id}")
@@ -403,9 +249,9 @@ function(itk_generate_sbom)
string(APPEND _json " {\n")
string(APPEND _json " \"SPDXID\": \"SPDXRef-${_spdx_id}\",\n")
string(APPEND _json " \"name\": \"${_mod}\",\n")
- string(APPEND _json " \"versionInfo\": \"${_pkg_version}\",\n")
+ string(APPEND _json " \"versionInfo\": \"NOASSERTION\",\n")
string(APPEND _json " \"downloadLocation\": \"${_pkg_download}\",\n")
- string(APPEND _json " \"supplier\": \"${_pkg_supplier}\",\n")
+ string(APPEND _json " \"supplier\": \"NOASSERTION\",\n")
string(APPEND _json " \"licenseConcluded\": \"${_pkg_license}\",\n")
string(APPEND _json " \"licenseDeclared\": \"${_pkg_license}\",\n")
string(APPEND _json " \"copyrightText\": \"${_pkg_copyright}\",\n")
@@ -493,57 +339,34 @@ function(itk_generate_sbom)
string(APPEND _json " }")
endforeach()
- string(APPEND _json "\n ],\n")
+ string(APPEND _json "\n ]")
# --- hasExtractedLicensingInfo for custom LicenseRef identifiers ---
- string(APPEND _json " \"hasExtractedLicensingInfo\": [\n")
- string(APPEND _json " {\n")
- string(
- APPEND
- _json
- " \"licenseId\": \"LicenseRef-NIFTI-Public-Domain\",\n"
- )
- string(APPEND _json " \"name\": \"NIFTI Public Domain License\",\n")
- string(
- APPEND
- _json
- " \"extractedText\": \"This software is in the public domain. The NIFTI header and library are released into the public domain.\"\n"
- )
- string(APPEND _json " },\n")
- string(APPEND _json " {\n")
- string(
- APPEND
- _json
- " \"licenseId\": \"LicenseRef-NITRC-Public-Domain\",\n"
- )
- string(
- APPEND
- _json
- " \"name\": \"NITRC GIFTI Public Domain License\",\n"
- )
- string(
- APPEND
- _json
- " \"extractedText\": \"The GIFTI library is released into the public domain under the NITRC project.\"\n"
- )
- string(APPEND _json " },\n")
- string(APPEND _json " {\n")
- string(APPEND _json " \"licenseId\": \"LicenseRef-Netlib-SLATEC\",\n")
- string(
- APPEND
- _json
- " \"name\": \"Netlib SLATEC Public Domain License\",\n"
- )
- string(
- APPEND
- _json
- " \"extractedText\": \"The SLATEC Common Mathematical Library is issued by the U.S. Government and is in the public domain.\"\n"
- )
- string(APPEND _json " }\n")
- string(APPEND _json " ]\n")
+ list(LENGTH _custom_license_ids _num_custom)
+ if(_num_custom GREATER 0)
+ string(APPEND _json ",\n")
+ string(APPEND _json " \"hasExtractedLicensingInfo\": [\n")
+ set(_first_custom TRUE)
+ math(EXPR _last_idx "${_num_custom} - 1")
+ foreach(_idx RANGE ${_last_idx})
+ list(GET _custom_license_ids ${_idx} _lic_id)
+ list(GET _custom_license_names ${_idx} _lic_name)
+ list(GET _custom_license_texts ${_idx} _lic_text)
+ if(NOT _first_custom)
+ string(APPEND _json ",\n")
+ endif()
+ set(_first_custom FALSE)
+ string(APPEND _json " {\n")
+ string(APPEND _json " \"licenseId\": \"${_lic_id}\",\n")
+ string(APPEND _json " \"name\": \"${_lic_name}\",\n")
+ string(APPEND _json " \"extractedText\": \"${_lic_text}\"\n")
+ string(APPEND _json " }")
+ endforeach()
+ string(APPEND _json "\n ]")
+ endif()
# --- Close JSON document ---
- string(APPEND _json "}\n")
+ string(APPEND _json "\n}\n")
# Write SBOM to build directory
set(_sbom_file "${CMAKE_BINARY_DIR}/sbom.spdx.json")
diff --git a/Modules/ThirdParty/DCMTK/itk-module.cmake b/Modules/ThirdParty/DCMTK/itk-module.cmake
index 83cbf13f46f..17bd2e9aade 100644
--- a/Modules/ThirdParty/DCMTK/itk-module.cmake
+++ b/Modules/ThirdParty/DCMTK/itk-module.cmake
@@ -6,7 +6,17 @@ library suite."
)
if(ITK_USE_SYSTEM_DCMTK)
- itk_module(ITKDCMTK DESCRIPTION "${DOCUMENTATION}" EXCLUDE_FROM_DEFAULT)
+ itk_module(
+ ITKDCMTK
+ DESCRIPTION "${DOCUMENTATION}"
+ EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://dicom.offis.de/dcmtk"
+ SPDX_COPYRIGHT
+ "Copyright OFFIS e.V."
+ )
else()
itk_module(
ITKDCMTK
@@ -17,5 +27,11 @@ else()
ITKPNG
DESCRIPTION "${DOCUMENTATION}"
EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://dicom.offis.de/dcmtk"
+ SPDX_COPYRIGHT
+ "Copyright OFFIS e.V."
)
endif()
diff --git a/Modules/ThirdParty/DICOMParser/itk-module.cmake b/Modules/ThirdParty/DICOMParser/itk-module.cmake
index d452bc733d3..d517f5d72b5 100644
--- a/Modules/ThirdParty/DICOMParser/itk-module.cmake
+++ b/Modules/ThirdParty/DICOMParser/itk-module.cmake
@@ -6,4 +6,13 @@ DICOMParser is a small, lightweight C++ toolkit for reading DICOM format medical
image files."
)
-itk_module(ITKDICOMParser DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKDICOMParser
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/InsightSoftwareConsortium/ITK"
+ SPDX_COPYRIGHT
+ "Copyright Kitware Inc."
+)
diff --git a/Modules/ThirdParty/DoubleConversion/itk-module.cmake b/Modules/ThirdParty/DoubleConversion/itk-module.cmake
index 6fb81b78546..9c256d5c355 100644
--- a/Modules/ThirdParty/DoubleConversion/itk-module.cmake
+++ b/Modules/ThirdParty/DoubleConversion/itk-module.cmake
@@ -5,4 +5,13 @@ double-conversion library published by
Google."
)
-itk_module(ITKDoubleConversion DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKDoubleConversion
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/google/double-conversion"
+ SPDX_COPYRIGHT
+ "Copyright Google Inc."
+)
diff --git a/Modules/ThirdParty/Eigen3/itk-module.cmake b/Modules/ThirdParty/Eigen3/itk-module.cmake
index c2783bfa2f2..1d4c82661c6 100644
--- a/Modules/ThirdParty/Eigen3/itk-module.cmake
+++ b/Modules/ThirdParty/Eigen3/itk-module.cmake
@@ -9,4 +9,10 @@ itk_module(
DEPENDS
DESCRIPTION "${DOCUMENTATION}"
EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "MPL-2.0"
+ SPDX_DOWNLOAD_LOCATION
+ "https://eigen.tuxfamily.org"
+ SPDX_COPYRIGHT
+ "Copyright Eigen contributors"
)
diff --git a/Modules/ThirdParty/Expat/itk-module.cmake b/Modules/ThirdParty/Expat/itk-module.cmake
index c51714845eb..ccd21b5c63a 100644
--- a/Modules/ThirdParty/Expat/itk-module.cmake
+++ b/Modules/ThirdParty/Expat/itk-module.cmake
@@ -5,4 +5,13 @@ href=\"http://expat.sourceforge.net/\">Expat library.
Expat is an XML parser library written in C."
)
-itk_module(ITKExpat DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKExpat
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "MIT"
+ SPDX_DOWNLOAD_LOCATION
+ "https://libexpat.github.io"
+ SPDX_COPYRIGHT
+ "Copyright Expat development team"
+)
diff --git a/Modules/ThirdParty/GDCM/itk-module.cmake b/Modules/ThirdParty/GDCM/itk-module.cmake
index c50bb710e5d..ac028a5c61b 100644
--- a/Modules/ThirdParty/GDCM/itk-module.cmake
+++ b/Modules/ThirdParty/GDCM/itk-module.cmake
@@ -6,7 +6,17 @@ Grassroots DiCoM is a C++ library for DICOM medical files."
)
if(ITK_USE_SYSTEM_GDCM)
- itk_module(ITKGDCM DESCRIPTION "${DOCUMENTATION}" EXCLUDE_FROM_DEFAULT)
+ itk_module(
+ ITKGDCM
+ DESCRIPTION "${DOCUMENTATION}"
+ EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://gdcm.sourceforge.net"
+ SPDX_COPYRIGHT
+ "Copyright GDCM contributors"
+ )
else()
itk_module(
ITKGDCM
@@ -15,5 +25,11 @@ else()
ITKExpat
ITKOpenJPEG
DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://gdcm.sourceforge.net"
+ SPDX_COPYRIGHT
+ "Copyright GDCM contributors"
)
endif()
diff --git a/Modules/ThirdParty/GIFTI/itk-module.cmake b/Modules/ThirdParty/GIFTI/itk-module.cmake
index f13fc091ea5..84723025637 100644
--- a/Modules/ThirdParty/GIFTI/itk-module.cmake
+++ b/Modules/ThirdParty/GIFTI/itk-module.cmake
@@ -12,4 +12,14 @@ itk_module(
ITKExpat
ITKNIFTI
DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "LicenseRef-NITRC-Public-Domain"
+ SPDX_DOWNLOAD_LOCATION
+ "https://www.nitrc.org/projects/gifti"
+ SPDX_COPYRIGHT
+ "NOASSERTION"
+ SPDX_CUSTOM_LICENSE_NAME
+ "NITRC GIFTI Public Domain License"
+ SPDX_CUSTOM_LICENSE_TEXT
+ "The GIFTI library is released into the public domain under the NITRC project."
)
diff --git a/Modules/ThirdParty/GoogleTest/itk-module.cmake b/Modules/ThirdParty/GoogleTest/itk-module.cmake
index 7473d474488..37b672310e5 100644
--- a/Modules/ThirdParty/GoogleTest/itk-module.cmake
+++ b/Modules/ThirdParty/GoogleTest/itk-module.cmake
@@ -4,4 +4,14 @@ set(
Google's C++ test framework. This module provides the GTest::gtest and GTest::gtest_main targets in the build directory only, and are not installed."
)
-itk_module(ITKGoogleTest DEPENDS DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKGoogleTest
+ DEPENDS
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/google/googletest"
+ SPDX_COPYRIGHT
+ "Copyright Google Inc."
+)
diff --git a/Modules/ThirdParty/HDF5/itk-module.cmake b/Modules/ThirdParty/HDF5/itk-module.cmake
index 4e446253184..73ea064b93c 100644
--- a/Modules/ThirdParty/HDF5/itk-module.cmake
+++ b/Modules/ThirdParty/HDF5/itk-module.cmake
@@ -5,4 +5,15 @@ href=\"http://www.hdfgroup.org/HDF5/\">HDF5 library.
HDF5 is a data model, library, and file format for storing and managing data."
)
-itk_module(ITKHDF5 DEPENDS ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKHDF5
+ DEPENDS
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://www.hdfgroup.org/solutions/hdf5"
+ SPDX_COPYRIGHT
+ "Copyright The HDF Group"
+)
diff --git a/Modules/ThirdParty/JPEG/itk-module.cmake b/Modules/ThirdParty/JPEG/itk-module.cmake
index 4da1bfdcfca..c2defcbf44a 100644
--- a/Modules/ThirdParty/JPEG/itk-module.cmake
+++ b/Modules/ThirdParty/JPEG/itk-module.cmake
@@ -5,4 +5,13 @@ library published by the
Independent JPEG Group and libjpeg-turbo."
)
-itk_module(ITKJPEG DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKJPEG
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "IJG AND BSD-3-Clause AND Zlib"
+ SPDX_DOWNLOAD_LOCATION
+ "https://libjpeg-turbo.org"
+ SPDX_COPYRIGHT
+ "Copyright libjpeg-turbo contributors"
+)
diff --git a/Modules/ThirdParty/KWSys/itk-module.cmake b/Modules/ThirdParty/KWSys/itk-module.cmake
index 36ebf9e0ae9..c1f6defbecb 100644
--- a/Modules/ThirdParty/KWSys/itk-module.cmake
+++ b/Modules/ThirdParty/KWSys/itk-module.cmake
@@ -7,4 +7,13 @@ library is intended to be shared among many projects. For more information, see
Modules/ThirdParty/KWSys/src/README.kwsys."
)
-itk_module(ITKKWSys DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKKWSys
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://gitlab.kitware.com/utils/kwsys"
+ SPDX_COPYRIGHT
+ "Copyright Kitware Inc."
+)
diff --git a/Modules/ThirdParty/MINC/itk-module.cmake b/Modules/ThirdParty/MINC/itk-module.cmake
index 19eb4ba58b2..516c14d7d70 100644
--- a/Modules/ThirdParty/MINC/itk-module.cmake
+++ b/Modules/ThirdParty/MINC/itk-module.cmake
@@ -6,7 +6,17 @@ image file format library."
)
if(ITK_USE_SYSTEM_MINC)
- itk_module(ITKMINC DESCRIPTION "${DOCUMENTATION}" EXCLUDE_FROM_DEFAULT)
+ itk_module(
+ ITKMINC
+ DESCRIPTION "${DOCUMENTATION}"
+ EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "LGPL-2.1-only"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/BIC-MNI/libminc"
+ SPDX_COPYRIGHT
+ "Copyright McConnell Brain Imaging Centre"
+ )
else()
itk_module(
ITKMINC
@@ -16,5 +26,11 @@ else()
ITKZLIB
DESCRIPTION "${DOCUMENTATION}"
EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "LGPL-2.1-only"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/BIC-MNI/libminc"
+ SPDX_COPYRIGHT
+ "Copyright McConnell Brain Imaging Centre"
)
endif()
diff --git a/Modules/ThirdParty/MetaIO/itk-module.cmake b/Modules/ThirdParty/MetaIO/itk-module.cmake
index 3d9c955a10c..6c633d1704f 100644
--- a/Modules/ThirdParty/MetaIO/itk-module.cmake
+++ b/Modules/ThirdParty/MetaIO/itk-module.cmake
@@ -8,4 +8,15 @@ vessels, needles, etc.), blobs (for arbitrary shaped objects), cubes, spheres,
etc. The complete library is known as MetaIO."
)
-itk_module(ITKMetaIO DEPENDS ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKMetaIO
+ DEPENDS
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "Apache-2.0"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/Kitware/MetaIO"
+ SPDX_COPYRIGHT
+ "Copyright Kitware Inc."
+)
diff --git a/Modules/ThirdParty/NIFTI/itk-module.cmake b/Modules/ThirdParty/NIFTI/itk-module.cmake
index 43a6bde4e44..63c15891b9f 100644
--- a/Modules/ThirdParty/NIFTI/itk-module.cmake
+++ b/Modules/ThirdParty/NIFTI/itk-module.cmake
@@ -6,4 +6,19 @@ Neuroimaging Informatics Technology Initiative provides an Analyze-style MRI
file format."
)
-itk_module(ITKNIFTI DEPENDS ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKNIFTI
+ DEPENDS
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "LicenseRef-NIFTI-Public-Domain"
+ SPDX_DOWNLOAD_LOCATION
+ "https://nifti.nimh.nih.gov"
+ SPDX_COPYRIGHT
+ "NOASSERTION"
+ SPDX_CUSTOM_LICENSE_NAME
+ "NIFTI Public Domain License"
+ SPDX_CUSTOM_LICENSE_TEXT
+ "This software is in the public domain. The NIFTI header and library are released into the public domain."
+)
diff --git a/Modules/ThirdParty/Netlib/itk-module.cmake b/Modules/ThirdParty/Netlib/itk-module.cmake
index 411112d5b39..a626fd4e5d8 100644
--- a/Modules/ThirdParty/Netlib/itk-module.cmake
+++ b/Modules/ThirdParty/Netlib/itk-module.cmake
@@ -5,4 +5,19 @@ href=\"http://www.netlib.org/slatec/\">netlib slatec routines. They are
used by the probability distributions in ITK."
)
-itk_module(ITKNetlib DEPENDS ITKVNL DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKNetlib
+ DEPENDS
+ ITKVNL
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "LicenseRef-Netlib-SLATEC"
+ SPDX_DOWNLOAD_LOCATION
+ "https://www.netlib.org/slatec"
+ SPDX_COPYRIGHT
+ "NOASSERTION"
+ SPDX_CUSTOM_LICENSE_NAME
+ "Netlib SLATEC Public Domain License"
+ SPDX_CUSTOM_LICENSE_TEXT
+ "The SLATEC Common Mathematical Library is issued by the U.S. Government and is in the public domain."
+)
diff --git a/Modules/ThirdParty/NrrdIO/itk-module.cmake b/Modules/ThirdParty/NrrdIO/itk-module.cmake
index 91123ea8cd8..9fbadb50c36 100644
--- a/Modules/ThirdParty/NrrdIO/itk-module.cmake
+++ b/Modules/ThirdParty/NrrdIO/itk-module.cmake
@@ -4,4 +4,15 @@ set(
href=\"http://teem.sourceforge.net/nrrd/lib.html\">NRRD image file format."
)
-itk_module(ITKNrrdIO DEPENDS ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKNrrdIO
+ DEPENDS
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "LGPL-2.1-only"
+ SPDX_DOWNLOAD_LOCATION
+ "https://teem.sourceforge.net/nrrd"
+ SPDX_COPYRIGHT
+ "Copyright Teem contributors"
+)
diff --git a/Modules/ThirdParty/OpenJPEG/itk-module.cmake b/Modules/ThirdParty/OpenJPEG/itk-module.cmake
index 21129c30e8e..9f88f7c71a2 100644
--- a/Modules/ThirdParty/OpenJPEG/itk-module.cmake
+++ b/Modules/ThirdParty/OpenJPEG/itk-module.cmake
@@ -7,4 +7,14 @@ has been developed in order to promote the use of JPEG 2000, the new still-image
compression standard from the Joint Photographic Experts Group (JPEG)."
)
-itk_module(ITKOpenJPEG EXCLUDE_FROM_DEFAULT DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKOpenJPEG
+ EXCLUDE_FROM_DEFAULT
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-2-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://www.openjpeg.org"
+ SPDX_COPYRIGHT
+ "Copyright OpenJPEG contributors"
+)
diff --git a/Modules/ThirdParty/PNG/itk-module.cmake b/Modules/ThirdParty/PNG/itk-module.cmake
index ad742c3b0f0..c9994c1e202 100644
--- a/Modules/ThirdParty/PNG/itk-module.cmake
+++ b/Modules/ThirdParty/PNG/itk-module.cmake
@@ -5,4 +5,15 @@ href=\"http://www.libpng.org/pub/png/libpng.html/\">Portable Network Graphics
(PNG) image file format library."
)
-itk_module(ITKPNG DEPENDS ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKPNG
+ DEPENDS
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "Libpng-2.0"
+ SPDX_DOWNLOAD_LOCATION
+ "https://www.libpng.org/pub/png/libpng.html"
+ SPDX_COPYRIGHT
+ "Copyright libpng contributors"
+)
diff --git a/Modules/ThirdParty/TBB/itk-module.cmake b/Modules/ThirdParty/TBB/itk-module.cmake
index 63ebc3ad4b3..305b388fdda 100644
--- a/Modules/ThirdParty/TBB/itk-module.cmake
+++ b/Modules/ThirdParty/TBB/itk-module.cmake
@@ -7,4 +7,14 @@ TBB is Intel TBB threading library."
# ITKTBB module needs to be defined even if ITK_USE_TBB
# is OFF, otherwise ITK cannot compile.
-itk_module(ITKTBB DESCRIPTION "${DOCUMENTATION}" EXCLUDE_FROM_DEFAULT)
+itk_module(
+ ITKTBB
+ DESCRIPTION "${DOCUMENTATION}"
+ EXCLUDE_FROM_DEFAULT
+ SPDX_LICENSE
+ "Apache-2.0"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/oneapi-src/oneTBB"
+ SPDX_COPYRIGHT
+ "Copyright Intel Corporation"
+)
diff --git a/Modules/ThirdParty/TIFF/itk-module.cmake b/Modules/ThirdParty/TIFF/itk-module.cmake
index 5d005104093..0296cda2cfe 100644
--- a/Modules/ThirdParty/TIFF/itk-module.cmake
+++ b/Modules/ThirdParty/TIFF/itk-module.cmake
@@ -11,4 +11,10 @@ itk_module(
ITKZLIB
ITKJPEG
DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "libtiff"
+ SPDX_DOWNLOAD_LOCATION
+ "https://libtiff.maptools.org"
+ SPDX_COPYRIGHT
+ "Copyright libtiff contributors"
)
diff --git a/Modules/ThirdParty/VNL/itk-module.cmake b/Modules/ThirdParty/VNL/itk-module.cmake
index 957d4ccc0f4..ea9c4e5f07a 100644
--- a/Modules/ThirdParty/VNL/itk-module.cmake
+++ b/Modules/ThirdParty/VNL/itk-module.cmake
@@ -5,4 +5,13 @@ href=\"http://vxl.sourceforge.net\">VNL numeric library from the VXL vision
library suite."
)
-itk_module(ITKVNL DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKVNL
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "BSD-3-Clause"
+ SPDX_DOWNLOAD_LOCATION
+ "https://vxl.github.io"
+ SPDX_COPYRIGHT
+ "Copyright VXL contributors"
+)
diff --git a/Modules/ThirdParty/ZLIB/itk-module.cmake b/Modules/ThirdParty/ZLIB/itk-module.cmake
index 547b75167d6..cd09ec6f4ec 100644
--- a/Modules/ThirdParty/ZLIB/itk-module.cmake
+++ b/Modules/ThirdParty/ZLIB/itk-module.cmake
@@ -6,4 +6,13 @@ general purpose data compression library,
designed as a drop-in replacement for ZLIB."
)
-itk_module(ITKZLIB DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKZLIB
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "Zlib"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/zlib-ng/zlib-ng"
+ SPDX_COPYRIGHT
+ "Copyright zlib-ng contributors"
+)
diff --git a/Modules/ThirdParty/libLBFGS/itk-module.cmake b/Modules/ThirdParty/libLBFGS/itk-module.cmake
index 3ef2ce59ed8..1ce757eaca5 100644
--- a/Modules/ThirdParty/libLBFGS/itk-module.cmake
+++ b/Modules/ThirdParty/libLBFGS/itk-module.cmake
@@ -5,4 +5,13 @@ href=\"https://github.com/chokkan/liblbfgs\">libLBFGS library,
a C++ implementaiton of the LBFGS implementation in Netlib."
)
-itk_module(ITKLIBLBFGS DESCRIPTION "${DOCUMENTATION}")
+itk_module(
+ ITKLIBLBFGS
+ DESCRIPTION "${DOCUMENTATION}"
+ SPDX_LICENSE
+ "MIT"
+ SPDX_DOWNLOAD_LOCATION
+ "https://github.com/chokkan/liblbfgs"
+ SPDX_COPYRIGHT
+ "Copyright Naoaki Okazaki"
+)
From a7e8acae37cd847b04edbff564b819641a5e79e8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 14 Apr 2026 20:34:52 -0500
Subject: [PATCH 3/8] BUG: Fix JSON escaping in SBOM hasExtractedLicensingInfo
Custom license names and extracted texts were written directly
into the JSON output without escaping. Any text containing
quotes, backslashes, or newlines would produce invalid JSON.
Apply _itk_sbom_json_escape() to both the name and
extractedText fields before embedding.
---
CMake/ITKSBOMGeneration.cmake | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
index 041b763bc83..f59e13827b5 100644
--- a/CMake/ITKSBOMGeneration.cmake
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -356,10 +356,12 @@ function(itk_generate_sbom)
string(APPEND _json ",\n")
endif()
set(_first_custom FALSE)
+ _itk_sbom_json_escape("${_lic_name}" _lic_name_escaped)
+ _itk_sbom_json_escape("${_lic_text}" _lic_text_escaped)
string(APPEND _json " {\n")
string(APPEND _json " \"licenseId\": \"${_lic_id}\",\n")
- string(APPEND _json " \"name\": \"${_lic_name}\",\n")
- string(APPEND _json " \"extractedText\": \"${_lic_text}\"\n")
+ string(APPEND _json " \"name\": \"${_lic_name_escaped}\",\n")
+ string(APPEND _json " \"extractedText\": \"${_lic_text_escaped}\"\n")
string(APPEND _json " }")
endforeach()
string(APPEND _json "\n ]")
From d77ee3a5cbef73a58cc311ec03a53754acfe20f6 Mon Sep 17 00:00:00 2001
From: "Hans J. Johnson"
Date: Tue, 14 Apr 2026 20:35:10 -0500
Subject: [PATCH 4/8] ENH: Add SPDX_VERSION parameter with vendored dependency
versions
Add SPDX_VERSION to the itk_module() macro for declaring the
version of each vendored third-party dependency. Populate
versions for 11 modules extractable from source headers or
UpdateFromUpstream.sh tags:
Eigen3 (3.4.90), Expat (2.7.4), GoogleTest (1.17.0),
HDF5 (1.14.5), JPEG/libjpeg-turbo (3.0.4), MINC (2.4.06),
NIFTI (3.0.0), OpenJPEG (2.5.4), PNG (1.6.54),
TIFF (4.7.0), ZLIB (2.2.5)
Also fix Eigen3 license from "MPL-2.0" to "MPL-2.0 OR Apache-2.0"
(dual-licensed) and update NIFTI download URL to the current
GitHub repository.
---
CMake/ITKModuleMacros.cmake | 6 +++++-
CMake/ITKSBOMGeneration.cmake | 7 ++++++-
Modules/ThirdParty/Eigen3/itk-module.cmake | 6 ++++--
Modules/ThirdParty/Expat/itk-module.cmake | 2 ++
Modules/ThirdParty/GoogleTest/itk-module.cmake | 2 ++
Modules/ThirdParty/HDF5/itk-module.cmake | 2 ++
Modules/ThirdParty/JPEG/itk-module.cmake | 2 ++
Modules/ThirdParty/MINC/itk-module.cmake | 4 ++++
Modules/ThirdParty/NIFTI/itk-module.cmake | 4 +++-
Modules/ThirdParty/OpenJPEG/itk-module.cmake | 2 ++
Modules/ThirdParty/PNG/itk-module.cmake | 2 ++
Modules/ThirdParty/TIFF/itk-module.cmake | 2 ++
Modules/ThirdParty/ZLIB/itk-module.cmake | 2 ++
13 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/CMake/ITKModuleMacros.cmake b/CMake/ITKModuleMacros.cmake
index 4a933db26d8..a5e8735dc96 100644
--- a/CMake/ITKModuleMacros.cmake
+++ b/CMake/ITKModuleMacros.cmake
@@ -70,6 +70,7 @@ macro(itk_module _name)
set(ITK_MODULE_${itk-module}_EXCLUDE_FROM_DEFAULT 0)
set(ITK_MODULE_${itk-module}_ENABLE_SHARED 0)
set(ITK_MODULE_${itk-module}_SPDX_LICENSE "")
+ set(ITK_MODULE_${itk-module}_SPDX_VERSION "")
set(ITK_MODULE_${itk-module}_SPDX_DOWNLOAD_LOCATION "")
set(ITK_MODULE_${itk-module}_SPDX_COPYRIGHT "")
set(ITK_MODULE_${itk-module}_SPDX_CUSTOM_LICENSE_TEXT "")
@@ -79,7 +80,7 @@ macro(itk_module _name)
if(
"${arg}"
MATCHES
- "^((|COMPILE_|PRIVATE_|TEST_|)DEPENDS|DESCRIPTION|DEFAULT|FACTORY_NAMES|SPDX_LICENSE|SPDX_DOWNLOAD_LOCATION|SPDX_COPYRIGHT|SPDX_CUSTOM_LICENSE_TEXT|SPDX_CUSTOM_LICENSE_NAME)$"
+ "^((|COMPILE_|PRIVATE_|TEST_|)DEPENDS|DESCRIPTION|DEFAULT|FACTORY_NAMES|SPDX_LICENSE|SPDX_VERSION|SPDX_DOWNLOAD_LOCATION|SPDX_COPYRIGHT|SPDX_CUSTOM_LICENSE_TEXT|SPDX_CUSTOM_LICENSE_NAME)$"
)
set(_doing "${arg}")
elseif("${arg}" MATCHES "^EXCLUDE_FROM_DEFAULT$")
@@ -112,6 +113,9 @@ macro(itk_module _name)
elseif("${_doing}" MATCHES "^SPDX_LICENSE$")
set(_doing "")
set(ITK_MODULE_${itk-module}_SPDX_LICENSE "${arg}")
+ elseif("${_doing}" MATCHES "^SPDX_VERSION$")
+ set(_doing "")
+ set(ITK_MODULE_${itk-module}_SPDX_VERSION "${arg}")
elseif("${_doing}" MATCHES "^SPDX_DOWNLOAD_LOCATION$")
set(_doing "")
set(ITK_MODULE_${itk-module}_SPDX_DOWNLOAD_LOCATION "${arg}")
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
index f59e13827b5..01321241f76 100644
--- a/CMake/ITKSBOMGeneration.cmake
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -8,6 +8,7 @@
Per-module SPDX metadata is declared in each module's itk-module.cmake via
the itk_module() macro arguments:
SPDX_LICENSE - SPDX license identifier (e.g. "Apache-2.0")
+ SPDX_VERSION - Version of the vendored dependency
SPDX_DOWNLOAD_LOCATION - URL for the upstream source
SPDX_COPYRIGHT - Copyright text
SPDX_CUSTOM_LICENSE_TEXT - Extracted text for custom LicenseRef-* IDs
@@ -217,8 +218,12 @@ function(itk_generate_sbom)
continue()
endif()
+ set(_pkg_version "${ITK_MODULE_${_mod}_SPDX_VERSION}")
set(_pkg_download "${ITK_MODULE_${_mod}_SPDX_DOWNLOAD_LOCATION}")
set(_pkg_copyright "${ITK_MODULE_${_mod}_SPDX_COPYRIGHT}")
+ if(NOT _pkg_version)
+ set(_pkg_version "NOASSERTION")
+ endif()
if(NOT _pkg_download)
set(_pkg_download "NOASSERTION")
endif()
@@ -249,7 +254,7 @@ function(itk_generate_sbom)
string(APPEND _json " {\n")
string(APPEND _json " \"SPDXID\": \"SPDXRef-${_spdx_id}\",\n")
string(APPEND _json " \"name\": \"${_mod}\",\n")
- string(APPEND _json " \"versionInfo\": \"NOASSERTION\",\n")
+ string(APPEND _json " \"versionInfo\": \"${_pkg_version}\",\n")
string(APPEND _json " \"downloadLocation\": \"${_pkg_download}\",\n")
string(APPEND _json " \"supplier\": \"NOASSERTION\",\n")
string(APPEND _json " \"licenseConcluded\": \"${_pkg_license}\",\n")
diff --git a/Modules/ThirdParty/Eigen3/itk-module.cmake b/Modules/ThirdParty/Eigen3/itk-module.cmake
index 1d4c82661c6..21a646bd1cd 100644
--- a/Modules/ThirdParty/Eigen3/itk-module.cmake
+++ b/Modules/ThirdParty/Eigen3/itk-module.cmake
@@ -10,9 +10,11 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
EXCLUDE_FROM_DEFAULT
SPDX_LICENSE
- "MPL-2.0"
+ "MPL-2.0 OR Apache-2.0"
+ SPDX_VERSION
+ "3.4.90"
SPDX_DOWNLOAD_LOCATION
- "https://eigen.tuxfamily.org"
+ "https://gitlab.com/libeigen/eigen"
SPDX_COPYRIGHT
"Copyright Eigen contributors"
)
diff --git a/Modules/ThirdParty/Expat/itk-module.cmake b/Modules/ThirdParty/Expat/itk-module.cmake
index ccd21b5c63a..aba1f536b62 100644
--- a/Modules/ThirdParty/Expat/itk-module.cmake
+++ b/Modules/ThirdParty/Expat/itk-module.cmake
@@ -10,6 +10,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"MIT"
+ SPDX_VERSION
+ "2.7.4"
SPDX_DOWNLOAD_LOCATION
"https://libexpat.github.io"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/GoogleTest/itk-module.cmake b/Modules/ThirdParty/GoogleTest/itk-module.cmake
index 37b672310e5..0077baa4eae 100644
--- a/Modules/ThirdParty/GoogleTest/itk-module.cmake
+++ b/Modules/ThirdParty/GoogleTest/itk-module.cmake
@@ -10,6 +10,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"BSD-3-Clause"
+ SPDX_VERSION
+ "1.17.0"
SPDX_DOWNLOAD_LOCATION
"https://github.com/google/googletest"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/HDF5/itk-module.cmake b/Modules/ThirdParty/HDF5/itk-module.cmake
index 73ea064b93c..ab10c5ded45 100644
--- a/Modules/ThirdParty/HDF5/itk-module.cmake
+++ b/Modules/ThirdParty/HDF5/itk-module.cmake
@@ -12,6 +12,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"BSD-3-Clause"
+ SPDX_VERSION
+ "1.14.5"
SPDX_DOWNLOAD_LOCATION
"https://www.hdfgroup.org/solutions/hdf5"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/JPEG/itk-module.cmake b/Modules/ThirdParty/JPEG/itk-module.cmake
index c2defcbf44a..c0d033f0f89 100644
--- a/Modules/ThirdParty/JPEG/itk-module.cmake
+++ b/Modules/ThirdParty/JPEG/itk-module.cmake
@@ -10,6 +10,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"IJG AND BSD-3-Clause AND Zlib"
+ SPDX_VERSION
+ "3.0.4"
SPDX_DOWNLOAD_LOCATION
"https://libjpeg-turbo.org"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/MINC/itk-module.cmake b/Modules/ThirdParty/MINC/itk-module.cmake
index 516c14d7d70..4774b78701b 100644
--- a/Modules/ThirdParty/MINC/itk-module.cmake
+++ b/Modules/ThirdParty/MINC/itk-module.cmake
@@ -12,6 +12,8 @@ if(ITK_USE_SYSTEM_MINC)
EXCLUDE_FROM_DEFAULT
SPDX_LICENSE
"LGPL-2.1-only"
+ SPDX_VERSION
+ "2.4.06"
SPDX_DOWNLOAD_LOCATION
"https://github.com/BIC-MNI/libminc"
SPDX_COPYRIGHT
@@ -28,6 +30,8 @@ else()
EXCLUDE_FROM_DEFAULT
SPDX_LICENSE
"LGPL-2.1-only"
+ SPDX_VERSION
+ "2.4.06"
SPDX_DOWNLOAD_LOCATION
"https://github.com/BIC-MNI/libminc"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/NIFTI/itk-module.cmake b/Modules/ThirdParty/NIFTI/itk-module.cmake
index 63c15891b9f..41ccb7dcf1b 100644
--- a/Modules/ThirdParty/NIFTI/itk-module.cmake
+++ b/Modules/ThirdParty/NIFTI/itk-module.cmake
@@ -13,8 +13,10 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"LicenseRef-NIFTI-Public-Domain"
+ SPDX_VERSION
+ "3.0.0"
SPDX_DOWNLOAD_LOCATION
- "https://nifti.nimh.nih.gov"
+ "https://github.com/NIFTI-Imaging/nifti_clib"
SPDX_COPYRIGHT
"NOASSERTION"
SPDX_CUSTOM_LICENSE_NAME
diff --git a/Modules/ThirdParty/OpenJPEG/itk-module.cmake b/Modules/ThirdParty/OpenJPEG/itk-module.cmake
index 9f88f7c71a2..d0a16b0f17e 100644
--- a/Modules/ThirdParty/OpenJPEG/itk-module.cmake
+++ b/Modules/ThirdParty/OpenJPEG/itk-module.cmake
@@ -13,6 +13,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"BSD-2-Clause"
+ SPDX_VERSION
+ "2.5.4"
SPDX_DOWNLOAD_LOCATION
"https://www.openjpeg.org"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/PNG/itk-module.cmake b/Modules/ThirdParty/PNG/itk-module.cmake
index c9994c1e202..0e2c276c9f3 100644
--- a/Modules/ThirdParty/PNG/itk-module.cmake
+++ b/Modules/ThirdParty/PNG/itk-module.cmake
@@ -12,6 +12,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"Libpng-2.0"
+ SPDX_VERSION
+ "1.6.54"
SPDX_DOWNLOAD_LOCATION
"https://www.libpng.org/pub/png/libpng.html"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/TIFF/itk-module.cmake b/Modules/ThirdParty/TIFF/itk-module.cmake
index 0296cda2cfe..adffb740178 100644
--- a/Modules/ThirdParty/TIFF/itk-module.cmake
+++ b/Modules/ThirdParty/TIFF/itk-module.cmake
@@ -13,6 +13,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"libtiff"
+ SPDX_VERSION
+ "4.7.0"
SPDX_DOWNLOAD_LOCATION
"https://libtiff.maptools.org"
SPDX_COPYRIGHT
diff --git a/Modules/ThirdParty/ZLIB/itk-module.cmake b/Modules/ThirdParty/ZLIB/itk-module.cmake
index cd09ec6f4ec..4ce178fd719 100644
--- a/Modules/ThirdParty/ZLIB/itk-module.cmake
+++ b/Modules/ThirdParty/ZLIB/itk-module.cmake
@@ -11,6 +11,8 @@ itk_module(
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
"Zlib"
+ SPDX_VERSION
+ "2.2.5"
SPDX_DOWNLOAD_LOCATION
"https://github.com/zlib-ng/zlib-ng"
SPDX_COPYRIGHT
From 2fb9d9b6e1b2df471e7b4dedaabfa2c26db290ab Mon Sep 17 00:00:00 2001
From: "Hans J. Johnson"
Date: Tue, 14 Apr 2026 20:35:42 -0500
Subject: [PATCH 5/8] ENH: Add SBOM JSON validation CTest
Add ITKSBOMValidation test that validates the generated
sbom.spdx.json at test time: checks JSON syntax, required
SPDX 2.3 fields, at least one package, DESCRIBES relationship,
unique SPDX IDs, and extracted license entries.
Runs only when ITK_GENERATE_SBOM=ON and BUILD_TESTING=ON.
---
CMake/ITKSBOMValidation.cmake | 90 +++++++++++++++++++++++++++++++++++
CMakeLists.txt | 1 +
2 files changed, 91 insertions(+)
create mode 100644 CMake/ITKSBOMValidation.cmake
diff --git a/CMake/ITKSBOMValidation.cmake b/CMake/ITKSBOMValidation.cmake
new file mode 100644
index 00000000000..6024e939005
--- /dev/null
+++ b/CMake/ITKSBOMValidation.cmake
@@ -0,0 +1,90 @@
+#[=============================================================================[
+ ITKSBOMValidation.cmake - Validate the generated SPDX SBOM document
+
+ Adds a CTest test that validates the SBOM JSON file produced by
+ ITKSBOMGeneration.cmake. Checks:
+ 1. Valid JSON syntax
+ 2. Required SPDX 2.3 top-level fields present
+ 3. At least one package (ITK itself)
+ 4. DESCRIBES relationship present
+#]=============================================================================]
+
+if(NOT ITK_GENERATE_SBOM OR NOT BUILD_TESTING)
+ return()
+endif()
+
+set(_sbom_file "${CMAKE_BINARY_DIR}/sbom.spdx.json")
+if(NOT EXISTS "${_sbom_file}")
+ return()
+endif()
+
+add_test(
+ NAME ITKSBOMValidation
+ COMMAND
+ ${Python3_EXECUTABLE} -c
+ "
+import json, sys
+
+with open(sys.argv[1]) as f:
+ doc = json.load(f)
+
+errors = []
+
+# Required SPDX 2.3 fields
+for field in ['spdxVersion', 'dataLicense', 'SPDXID', 'name',
+ 'documentNamespace', 'creationInfo', 'packages',
+ 'relationships']:
+ if field not in doc:
+ errors.append(f'Missing required field: {field}')
+
+if doc.get('spdxVersion') != 'SPDX-2.3':
+ errors.append(f'Expected spdxVersion SPDX-2.3, got {doc.get(\"spdxVersion\")}')
+
+if doc.get('dataLicense') != 'CC0-1.0':
+ errors.append(f'Expected dataLicense CC0-1.0, got {doc.get(\"dataLicense\")}')
+
+# Must have at least ITK package
+packages = doc.get('packages', [])
+if len(packages) < 1:
+ errors.append('No packages found')
+
+itk_pkg = next((p for p in packages if p.get('name') == 'ITK'), None)
+if itk_pkg is None:
+ errors.append('ITK package not found')
+
+# Check DESCRIBES relationship exists
+rels = doc.get('relationships', [])
+describes = [r for r in rels if r.get('relationshipType') == 'DESCRIBES']
+if not describes:
+ errors.append('No DESCRIBES relationship found')
+
+# Validate package SPDX IDs are unique
+spdx_ids = [p.get('SPDXID') for p in packages]
+dupes = set(x for x in spdx_ids if spdx_ids.count(x) > 1)
+if dupes:
+ errors.append(f'Duplicate SPDX IDs: {dupes}')
+
+# Check hasExtractedLicensingInfo references are valid
+extracted = doc.get('hasExtractedLicensingInfo', [])
+for entry in extracted:
+ if 'licenseId' not in entry:
+ errors.append(f'Extracted license missing licenseId')
+ if 'extractedText' not in entry:
+ errors.append(f'Extracted license missing extractedText')
+
+if errors:
+ for e in errors:
+ print(f'SBOM ERROR: {e}', file=sys.stderr)
+ sys.exit(1)
+
+print(f'SBOM valid: {len(packages)} packages, {len(rels)} relationships, '
+ f'{len(extracted)} extracted licenses')
+"
+ "${_sbom_file}"
+)
+set_tests_properties(
+ ITKSBOMValidation
+ PROPERTIES
+ LABELS
+ "SBOM"
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7452f611271..46a1b2c0ed7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -757,6 +757,7 @@ include(ITKSBOMGeneration)
if(ITK_GENERATE_SBOM)
itk_generate_sbom()
endif()
+include(ITKSBOMValidation)
# Setup clang-tidy for code best-practices enforcement for C++11
include(ITKClangTidySetup)
From bc8d90bd8f7e3a885fea8f2ff5b7666e3646cca9 Mon Sep 17 00:00:00 2001
From: "Hans J. Johnson"
Date: Tue, 14 Apr 2026 20:36:00 -0500
Subject: [PATCH 6/8] ENH: Add SPDX version consistency test against
UpdateFromUpstream.sh
Add VerifySPDXVersions.py that cross-checks SPDX_VERSION declared
in each itk-module.cmake against the tag in UpdateFromUpstream.sh.
For modules with parseable version tags (v1.2.3, R_2_7_4,
hdf5_1.14.5, bare semver), the test verifies consistency. Modules
tracking master, commit SHAs, or custom ITK tags are skipped.
This catches the case where a vendored dependency is updated via
UpdateFromUpstream.sh but SPDX_VERSION is not bumped. Currently
validates 8 of 16 modules with UpdateFromUpstream.sh scripts.
---
CMake/ITKSBOMValidation.cmake | 17 +++
Utilities/Maintenance/VerifySPDXVersions.py | 141 ++++++++++++++++++++
2 files changed, 158 insertions(+)
create mode 100755 Utilities/Maintenance/VerifySPDXVersions.py
diff --git a/CMake/ITKSBOMValidation.cmake b/CMake/ITKSBOMValidation.cmake
index 6024e939005..138a54ff41e 100644
--- a/CMake/ITKSBOMValidation.cmake
+++ b/CMake/ITKSBOMValidation.cmake
@@ -88,3 +88,20 @@ set_tests_properties(
LABELS
"SBOM"
)
+
+# Verify SPDX_VERSION entries match UpdateFromUpstream.sh tags.
+# This catches the case where a vendored dependency is updated but
+# the SPDX_VERSION in itk-module.cmake is not bumped.
+add_test(
+ NAME ITKSBOMVersionConsistency
+ COMMAND
+ ${Python3_EXECUTABLE}
+ "${ITK_SOURCE_DIR}/Utilities/Maintenance/VerifySPDXVersions.py"
+ "${ITK_SOURCE_DIR}"
+)
+set_tests_properties(
+ ITKSBOMVersionConsistency
+ PROPERTIES
+ LABELS
+ "SBOM"
+)
diff --git a/Utilities/Maintenance/VerifySPDXVersions.py b/Utilities/Maintenance/VerifySPDXVersions.py
new file mode 100755
index 00000000000..26ab562ae79
--- /dev/null
+++ b/Utilities/Maintenance/VerifySPDXVersions.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python3
+"""Verify SPDX_VERSION in itk-module.cmake matches UpdateFromUpstream.sh tags.
+
+For each ThirdParty module that has both an UpdateFromUpstream.sh with a
+parseable version tag and an SPDX_VERSION in itk-module.cmake, verify
+they are consistent.
+
+Modules tracking 'master', commit SHAs, or custom ITK tags are skipped
+since their version cannot be derived from the tag alone.
+
+Exit code 0 if all checked modules match, 1 if any mismatch is found.
+"""
+
+import re
+import sys
+from pathlib import Path
+
+
+def extract_tag_from_upstream_script(script_path: Path) -> str | None:
+ """Extract the 'tag' value from UpdateFromUpstream.sh."""
+ text = script_path.read_text()
+ # Match: readonly tag="..." or tag="..." or readonly tag='...'
+ m = re.search(r"""(?:readonly\s+)?tag\s*=\s*['"]([^'"]+)['"]""", text)
+ return m.group(1) if m else None
+
+
+def normalize_version_from_tag(tag: str) -> str | None:
+ """Attempt to extract a semver-like version from a git tag.
+
+ Returns None if the tag is a SHA, 'master', or an unrecognizable format.
+ """
+ # Skip SHAs (40-hex or short)
+ if re.fullmatch(r"[0-9a-f]{7,40}", tag):
+ return None
+ # Skip 'master' or 'main'
+ if tag in ("master", "main"):
+ return None
+ # Skip custom ITK tags like 'for/itk-20260305-4c99fca'
+ if tag.startswith("for/"):
+ return None
+
+ # Try common patterns:
+ # v1.2.3 or V1.2.3
+ m = re.fullmatch(r"[vV]?(\d+\.\d+(?:\.\d+)?)", tag)
+ if m:
+ return m.group(1)
+
+ # hdf5_1.14.5
+ m = re.fullmatch(r"[a-zA-Z0-9]+[_-](\d+\.\d+(?:\.\d+)?)", tag)
+ if m:
+ return m.group(1)
+
+ # R_2_7_4 (Expat style)
+ m = re.fullmatch(r"R_(\d+)_(\d+)_(\d+)", tag)
+ if m:
+ return f"{m.group(1)}.{m.group(2)}.{m.group(3)}"
+
+ # Bare version like 2.2.5 or 3.0.4
+ m = re.fullmatch(r"(\d+\.\d+(?:\.\d+)?)", tag)
+ if m:
+ return m.group(1)
+
+ return None
+
+
+def extract_spdx_version(module_cmake: Path) -> str | None:
+ """Extract SPDX_VERSION value from itk-module.cmake."""
+ text = module_cmake.read_text()
+ m = re.search(r'SPDX_VERSION\s+"([^"]+)"', text)
+ return m.group(1) if m else None
+
+
+def main() -> int:
+ if len(sys.argv) > 1:
+ itk_source = Path(sys.argv[1])
+ else:
+ # Default: assume running from ITK source root
+ itk_source = Path(__file__).resolve().parents[2]
+
+ thirdparty_dir = itk_source / "Modules" / "ThirdParty"
+ if not thirdparty_dir.is_dir():
+ print(f"ERROR: {thirdparty_dir} not found", file=sys.stderr)
+ return 1
+
+ errors = []
+ checked = 0
+ skipped = 0
+
+ for module_dir in sorted(thirdparty_dir.iterdir()):
+ if not module_dir.is_dir():
+ continue
+
+ upstream_script = module_dir / "UpdateFromUpstream.sh"
+ module_cmake = module_dir / "itk-module.cmake"
+
+ if not upstream_script.exists() or not module_cmake.exists():
+ continue
+
+ tag = extract_tag_from_upstream_script(upstream_script)
+ if tag is None:
+ skipped += 1
+ continue
+
+ expected_version = normalize_version_from_tag(tag)
+ if expected_version is None:
+ skipped += 1
+ continue
+
+ declared_version = extract_spdx_version(module_cmake)
+ if declared_version is None:
+ errors.append(
+ f"{module_dir.name}: UpdateFromUpstream.sh tag='{tag}' "
+ f"implies version {expected_version}, but no SPDX_VERSION "
+ f"declared in itk-module.cmake"
+ )
+ checked += 1
+ continue
+
+ if declared_version != expected_version:
+ errors.append(
+ f"{module_dir.name}: SPDX_VERSION='{declared_version}' "
+ f"does not match UpdateFromUpstream.sh tag='{tag}' "
+ f"(expected '{expected_version}')"
+ )
+
+ checked += 1
+
+ print(f"Checked {checked} modules, skipped {skipped} " f"(master/SHA/custom tags)")
+
+ if errors:
+ print(f"\n{len(errors)} version mismatch(es):", file=sys.stderr)
+ for e in errors:
+ print(f" FAIL: {e}", file=sys.stderr)
+ return 1
+
+ print("All SPDX_VERSION entries match UpdateFromUpstream.sh tags.")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
From 6359e818a0ce46af447bdb605b72c8f1bdb2d77a Mon Sep 17 00:00:00 2001
From: "Hans J. Johnson"
Date: Tue, 14 Apr 2026 20:42:15 -0500
Subject: [PATCH 7/8] ENH: Warn when ThirdParty modules lack SPDX metadata
Add AUTHOR_WARNING during SBOM generation for enabled ThirdParty
modules that have no SPDX_LICENSE declared in their itk-module.cmake.
This encourages remote module and ThirdParty maintainers to add
SPDX metadata for supply chain compliance.
---
CMake/ITKSBOMGeneration.cmake | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
index 01321241f76..2e952afbf22 100644
--- a/CMake/ITKSBOMGeneration.cmake
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -215,6 +215,13 @@ function(itk_generate_sbom)
# Only include modules that have SPDX metadata declared
set(_pkg_license "${ITK_MODULE_${_mod}_SPDX_LICENSE}")
if(NOT _pkg_license)
+ if(${_mod}_THIRD_PARTY)
+ message(
+ AUTHOR_WARNING
+ "ThirdParty module ${_mod} has no SPDX_LICENSE in itk-module.cmake. "
+ "Please add SPDX metadata for SBOM compliance."
+ )
+ endif()
continue()
endif()
From 6d31421fb1fa9245c4797eb737656ebd9303d4f0 Mon Sep 17 00:00:00 2001
From: "Hans J. Johnson"
Date: Wed, 15 Apr 2026 14:54:55 -0500
Subject: [PATCH 8/8] BUG: Fix SBOM JSON escaping, Python3 guard, and SPDX
metadata issues
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Address all findings from greptile code review:
P1: Escape all user-supplied fields (version, download location,
license, copyright) through _itk_sbom_json_escape() before JSON
interpolation — both in itk_generate_sbom() and
itk_sbom_register_package(). Previously only description was escaped.
P1: Add Python3_EXECUTABLE guard in ITKSBOMValidation.cmake to skip
SBOM validation tests when Python3 is not available.
P2: Use foreach(... IN LISTS ...) instead of unquoted variable
expansion to prevent re-splitting on embedded semicolons.
P2: Fix PNG SPDX license identifier from "Libpng-2.0" to "Libpng"
(the valid SPDX license list entry for libpng 1.6.x).
P2: Expose licenseListVersion as ITK_SBOM_SPDX_LICENSE_LIST_VERSION
cache variable (default "3.25") instead of hardcoding "3.22".
---
CMake/ITKSBOMGeneration.cmake | 54 ++++++++++++++++++++-----
CMake/ITKSBOMValidation.cmake | 5 +++
Modules/ThirdParty/PNG/itk-module.cmake | 2 +-
3 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/CMake/ITKSBOMGeneration.cmake b/CMake/ITKSBOMGeneration.cmake
index 2e952afbf22..a26f4edb9ce 100644
--- a/CMake/ITKSBOMGeneration.cmake
+++ b/CMake/ITKSBOMGeneration.cmake
@@ -26,6 +26,13 @@ if(NOT ITK_GENERATE_SBOM)
return()
endif()
+set(
+ ITK_SBOM_SPDX_LICENSE_LIST_VERSION
+ "3.25"
+ CACHE STRING
+ "SPDX license list version recorded in the generated SBOM"
+)
+
#-----------------------------------------------------------------------------
# Allow remote modules to register SBOM package metadata.
#
@@ -90,24 +97,40 @@ function(itk_sbom_register_package)
# Sanitize the name for use as SPDX ID (only alphanumeric and -)
string(REGEX REPLACE "[^A-Za-z0-9-]" "-" _spdx_id "${_pkg_NAME}")
+ # Escape all user-supplied fields for JSON safety
+ _itk_sbom_json_escape("${_pkg_NAME}" _pkg_NAME_escaped)
+ _itk_sbom_json_escape("${_pkg_VERSION}" _pkg_VERSION_escaped)
+ _itk_sbom_json_escape("${_pkg_DOWNLOAD_LOCATION}" _pkg_DOWNLOAD_escaped)
+ _itk_sbom_json_escape("${_pkg_SUPPLIER}" _pkg_SUPPLIER_escaped)
+ _itk_sbom_json_escape("${_pkg_SPDX_LICENSE}" _pkg_LICENSE_escaped)
+ _itk_sbom_json_escape("${_pkg_COPYRIGHT}" _pkg_COPYRIGHT_escaped)
+
set(_entry "")
string(APPEND _entry " {\n")
string(APPEND _entry " \"SPDXID\": \"SPDXRef-${_spdx_id}\",\n")
- string(APPEND _entry " \"name\": \"${_pkg_NAME}\",\n")
- string(APPEND _entry " \"versionInfo\": \"${_pkg_VERSION}\",\n")
+ string(APPEND _entry " \"name\": \"${_pkg_NAME_escaped}\",\n")
+ string(APPEND _entry " \"versionInfo\": \"${_pkg_VERSION_escaped}\",\n")
+ string(
+ APPEND
+ _entry
+ " \"downloadLocation\": \"${_pkg_DOWNLOAD_escaped}\",\n"
+ )
+ string(APPEND _entry " \"supplier\": \"${_pkg_SUPPLIER_escaped}\",\n")
+ string(
+ APPEND
+ _entry
+ " \"licenseConcluded\": \"${_pkg_LICENSE_escaped}\",\n"
+ )
string(
APPEND
_entry
- " \"downloadLocation\": \"${_pkg_DOWNLOAD_LOCATION}\",\n"
+ " \"licenseDeclared\": \"${_pkg_LICENSE_escaped}\",\n"
)
- string(APPEND _entry " \"supplier\": \"${_pkg_SUPPLIER}\",\n")
string(
APPEND
_entry
- " \"licenseConcluded\": \"${_pkg_SPDX_LICENSE}\",\n"
+ " \"copyrightText\": \"${_pkg_COPYRIGHT_escaped}\",\n"
)
- string(APPEND _entry " \"licenseDeclared\": \"${_pkg_SPDX_LICENSE}\",\n")
- string(APPEND _entry " \"copyrightText\": \"${_pkg_COPYRIGHT}\",\n")
string(APPEND _entry " \"filesAnalyzed\": false\n")
string(APPEND _entry " }")
@@ -174,7 +197,11 @@ function(itk_generate_sbom)
string(APPEND _json " \"Tool: CMake-${CMAKE_VERSION}\",\n")
string(APPEND _json " \"Organization: NumFOCUS\"\n")
string(APPEND _json " ],\n")
- string(APPEND _json " \"licenseListVersion\": \"3.22\"\n")
+ string(
+ APPEND
+ _json
+ " \"licenseListVersion\": \"${ITK_SBOM_SPDX_LICENSE_LIST_VERSION}\"\n"
+ )
string(APPEND _json " },\n")
# --- packages array ---
@@ -238,7 +265,12 @@ function(itk_generate_sbom)
set(_pkg_copyright "NOASSERTION")
endif()
- # Get description from module declaration and escape for JSON
+ # Escape all user-supplied fields for JSON safety
+ _itk_sbom_json_escape("${_pkg_version}" _pkg_version)
+ _itk_sbom_json_escape("${_pkg_download}" _pkg_download)
+ _itk_sbom_json_escape("${_pkg_license}" _pkg_license)
+ _itk_sbom_json_escape("${_pkg_copyright}" _pkg_copyright)
+
set(_pkg_description "${ITK_MODULE_${_mod}_DESCRIPTION}")
if(_pkg_description)
_itk_sbom_json_escape("${_pkg_description}" _pkg_description)
@@ -314,7 +346,7 @@ function(itk_generate_sbom)
# Append extra packages registered by remote modules
get_property(_extra_packages GLOBAL PROPERTY ITK_SBOM_EXTRA_PACKAGES)
- foreach(_extra_pkg ${_extra_packages})
+ foreach(_extra_pkg IN LISTS _extra_packages)
string(APPEND _json ",\n${_extra_pkg}")
endforeach()
@@ -342,7 +374,7 @@ function(itk_generate_sbom)
# Extra packages registered by remote modules
get_property(_extra_spdx_ids GLOBAL PROPERTY ITK_SBOM_EXTRA_SPDX_IDS)
- foreach(_spdx_id ${_extra_spdx_ids})
+ foreach(_spdx_id IN LISTS _extra_spdx_ids)
string(APPEND _json ",\n")
string(APPEND _json " {\n")
string(APPEND _json " \"spdxElementId\": \"SPDXRef-ITK\",\n")
diff --git a/CMake/ITKSBOMValidation.cmake b/CMake/ITKSBOMValidation.cmake
index 138a54ff41e..2787c9462cf 100644
--- a/CMake/ITKSBOMValidation.cmake
+++ b/CMake/ITKSBOMValidation.cmake
@@ -18,6 +18,11 @@ if(NOT EXISTS "${_sbom_file}")
return()
endif()
+if(NOT Python3_EXECUTABLE)
+ message(WARNING "Python3 not found; skipping SBOM validation tests.")
+ return()
+endif()
+
add_test(
NAME ITKSBOMValidation
COMMAND
diff --git a/Modules/ThirdParty/PNG/itk-module.cmake b/Modules/ThirdParty/PNG/itk-module.cmake
index 0e2c276c9f3..65085256156 100644
--- a/Modules/ThirdParty/PNG/itk-module.cmake
+++ b/Modules/ThirdParty/PNG/itk-module.cmake
@@ -11,7 +11,7 @@ itk_module(
ITKZLIB
DESCRIPTION "${DOCUMENTATION}"
SPDX_LICENSE
- "Libpng-2.0"
+ "Libpng"
SPDX_VERSION
"1.6.54"
SPDX_DOWNLOAD_LOCATION