diff --git a/.cmake-format b/.cmake-format index 3d7988c..66c566c 100644 --- a/.cmake-format +++ b/.cmake-format @@ -12,7 +12,11 @@ parse: sbom_generate: kwargs: OUTPUT: 1 + INPUT: '*' + COPYRIGHT: 1 LICENSE: 1 + NAMESPACE: 1 + PROJECT: 1 SUPPLIER: 1 SUPPLIER_URL: 1 sbom_file: @@ -59,6 +63,7 @@ parse: DOWNLOAD_LOCATION: 1 EXTERNAL: 1 RENAME: 1 + SUPPLIER: 1 format: line_width: 100 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2ccf58..aa9375c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,20 @@ jobs: - name: build run: dist/ubuntu/build.sh + build-macos: + runs-on: macos-latest + steps: + - name: checkout + uses: actions/checkout@v3 + - name: bootstrap + run: | + dist/macos/bootstrap.sh + - name: build + run: dist/macos/build.sh + # Dummy job that depends on all other build-* jobs. build-all-check: - needs: [build-ubuntu] + needs: [build-ubuntu, build-macos] runs-on: ubuntu-latest if: success() outputs: diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c32e08..81acaa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,3 +15,4 @@ include(sbom) reuse_lint() add_subdirectory(example) +add_subdirectory(test) diff --git a/README.rst b/README.rst index 760af85..da0d60a 100644 --- a/README.rst +++ b/README.rst @@ -147,6 +147,7 @@ Generate the header of the SBOM, based on a standard template where the given de sbom_generate( [OUTPUT ] + [INPUT ...] [COPYRIGHT ] [LICENSE ] [NAMESPACE ] @@ -158,6 +159,14 @@ Generate the header of the SBOM, based on a standard template where the given de ``OUTPUT`` Output filename. It should probably start with ``${CMAKE_INSTALL_PREFIX}``, as the file is generated during ``install``. + The variable ``SBOM_FILENAME`` is set to the full path. + +``INPUT`` + One or more file names, which are concatenated into the SBOM output file. + Variables and generator expressions are supported in these files. + Variables in the form ``@var@`` are replaced during config, ``${var}`` during install. + When omitted, a standard document/package SBOM is generated. + The other parameters can be referenced in the input files, prefixed with ``SBOM_GENERATE_``. ``COPYRIGHT`` Copyright information. @@ -177,24 +186,11 @@ Generate the header of the SBOM, based on a standard template where the given de ``SUPPLIER`` Supplier name. - It may be omitted when the variable ``SBOM_SUPPLIER`` is set. + It may be omitted when the variable ``SBOM_SUPPLIER`` is set or when any ``INPUT`` is given. ``SUPPLIER_URL`` Supplier home page. - It may be omitted when the variable ``SBOM_SUPPLIER_URL`` is set. - -Alternatively, you can specify your own template. - -.. code:: cmake - - sbom_generate( - [OUTPUT ] - INPUT ... - ) - -``INPUT`` - One or more file names, which are concatenated into the SBOM output file. - Variables and generator expressions are supported in these files. + It may be omitted when the variable ``SBOM_SUPPLIER_URL`` is set or when any ``INPUT`` is given. ``sbom_add`` ```````````` @@ -225,7 +221,7 @@ Add something to the SBOM. Refer to the `SPDX specification `_. ``SPDXID`` - The ID to use. + The ID to use for identifier generation. By default, generate a new one. Whether or not this is specified, the variable ``SBOM_LAST_SPDXID`` is set to just generated/used SPDXID, which could be used for later relationship definitions. @@ -258,7 +254,7 @@ Add something to the SBOM. sbom_add( PACKAGE - DOWNLOAD_LOCATION + [DOWNLOAD_LOCATION ] [EXTREF ...] [LICENSE ] [RELATIONSHIP ] @@ -325,7 +321,28 @@ Finalize the SBOM and verify its contents and/or format. .. code:: cmake - sbom_finalize() + sbom_finalize( + [NO_VERIFY | VERIFY] + ) + + sbom_finalize( + GRAPH + ) + +``NO_VERIFY`` + Do not run the verification against the generated SBOM. + By default, verification is only performed when python3 is found with the appropriate packages. + +``VERIFY`` + Always run the verification against the generated SBOM. + Make sure to install ``dist/common/requirements.txt`` in your python environment first. + +``GRAPH`` + Generate a dependency graph of the SBOM. + This implies ``VERIFY``. + It requires ``spdx-tools[graph_generation]`` python package to be installed first. + + License ------- diff --git a/cmake/sbom.cmake b/cmake/sbom.cmake index 8412efc..a90c296 100644 --- a/cmake/sbom.cmake +++ b/cmake/sbom.cmake @@ -9,12 +9,6 @@ endif() include(GNUInstallDirs) include(${CMAKE_CURRENT_LIST_DIR}/version.cmake) -find_package( - Python3 - COMPONENTS Interpreter - REQUIRED -) - # Common Platform Enumeration: https://nvd.nist.gov/products/cpe # # TODO: This detection can be improved. @@ -65,6 +59,9 @@ function(sbom_spdxid) cmake_parse_arguments( SBOM_SPDXID "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if(SBOM_SPDXID_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_SPDXID_UNPARSED_ARGUMENTS}") + endif() if("${SBOM_SPDXID_VARIABLE}" STREQUAL "") message(FATAL_ERROR "Missing VARIABLE") @@ -115,6 +112,9 @@ function(sbom_generate) cmake_parse_arguments( SBOM_GENERATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if(SBOM_GENERATE_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_GENERATE_UNPARSED_ARGUMENTS}") + endif() string(TIMESTAMP NOW_UTC UTC) @@ -147,12 +147,11 @@ function(sbom_generate) set(SBOM_GENERATE_COPYRIGHT "${NOW_YEAR} ${SBOM_GENERATE_SUPPLIER}") endif() - if("${SBOM_GENERATE_SUPPLIER}" STREQUAL "") - message(FATAL_ERROR "Specify a SUPPLIER, or set SBOM_SUPPLIER") - endif() - if("${SBOM_GENERATE_SUPPLIER_URL}" STREQUAL "") set(SBOM_GENERATE_SUPPLIER_URL "${SBOM_SUPPLIER_URL}") + if("${SBOM_GENERATE_SUPPLIER_URL}" STREQUAL "") + set(SBOM_GENERATE_SUPPLIER_URL "${PROJECT_HOMEPAGE_URL}") + endif() elseif("${SBOM_SUPPLIER_URL}" STREQUAL "") set(SBOM_SUPPLIER_URL "${SBOM_GENERATE_SUPPLIER_URL}" @@ -160,10 +159,6 @@ function(sbom_generate) ) endif() - if("${SBOM_GENERATE_SUPPLIER_URL}" STREQUAL "") - message(FATAL_ERROR "Specify a SUPPLIER_URL, or set SBOM_SUPPLIER_URL") - endif() - if("${SBOM_GENERATE_NAMESPACE}" STREQUAL "") set(SBOM_GENERATE_NAMESPACE "${SBOM_GENERATE_SUPPLIER_URL}/spdxdocs/${PROJECT_NAME}-${GIT_VERSION}" @@ -183,6 +178,14 @@ function(sbom_generate) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/sbom) if("${SBOM_GENERATE_INPUT}" STREQUAL "") + if("${SBOM_GENERATE_SUPPLIER}" STREQUAL "") + message(FATAL_ERROR "Specify a SUPPLIER, or set SBOM_SUPPLIER") + endif() + + if("${SBOM_GENERATE_SUPPLIER_URL}" STREQUAL "") + message(FATAL_ERROR "Specify a SUPPLIER_URL, or set SBOM_SUPPLIER_URL") + endif() + set(_f "${CMAKE_CURRENT_BINARY_DIR}/SPDXRef-DOCUMENT.spdx.in") get_filename_component(doc_name "${SBOM_GENERATE_OUTPUT}" NAME_WE) @@ -272,21 +275,87 @@ Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-${SBOM_GENERATE_PROJECT} install(CODE "set(SBOM_VERIFICATION_CODES)") - set_property(GLOBAL PROPERTY sbom_filename "${SBOM_GENERATE_OUTPUT}") + set_property(GLOBAL PROPERTY SBOM_FILENAME "${SBOM_GENERATE_OUTPUT}") + set(SBOM_FILENAME + "${SBOM_GENERATE_OUTPUT}" + PARENT_SCOPE + ) set_property(GLOBAL PROPERTY sbom_project "${SBOM_GENERATE_PROJECT}") set_property(GLOBAL PROPERTY sbom_spdxids 0) file(WRITE ${PROJECT_BINARY_DIR}/sbom/CMakeLists.txt "") endfunction() -# Verify the generated SBOM. Call after sbom_generate() and other SBOM populating commands. +# Find python. # -# Usage: sbom_finalize() +# Usage sbom_find_python([REQUIRED]) +macro(sbom_find_python) + if(Python3_EXECUTABLE) + set(Python3_FOUND TRUE) + elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) + find_package(Python3 COMPONENTS Interpreter ${ARGV}) + else() + if(WIN32) + find_program(Python3_EXECUTABLE NAMES python ${ARGV}) + else() + find_program(Python3_EXECUTABLE NAMES python3 ${ARGV}) + endif() + + if(Python3_EXECUTABLE) + set(Python3_FOUND TRUE) + else() + set(Python3_FOUND FALSE) + endif() + endif() + + if(Python3_FOUND) + if(NOT DEFINED SBOM_HAVE_PYTHON_DEPS) + execute_process( + COMMAND + ${Python3_EXECUTABLE} -c " +import reuse +import spdx_tools.spdx.clitools.pyspdxtools +import ntia_conformance_checker.main +" + RESULT_VARIABLE _res + ERROR_QUIET OUTPUT_QUIET + ) + + if("${_res}" STREQUAL "0") + set(SBOM_HAVE_PYTHON_DEPS + TRUE + CACHE INTERNAL "" + ) + else() + set(SBOM_HAVE_PYTHON_DEPS + FALSE + CACHE INTERNAL "" + ) + endif() + endif() + + if("${ARGN}" STREQUAL "REQUIRED" AND NOT SBOM_HAVE_PYTHON_DEPS) + message(FATAL_ERROR "Missing python packages") + endif() + endif() +endmacro() + +# Verify the generated SBOM. Call after sbom_generate() and other SBOM populating commands. function(sbom_finalize) - get_property(_sbom GLOBAL PROPERTY sbom_filename) + set(options NO_VERIFY VERIFY) + set(oneValueArgs GRAPH) + set(multiValueArgs) + cmake_parse_arguments( + SBOM_FINALIZE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + if(SBOM_FINALIZE_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_FINALIZE_UNPARSED_ARGUMENTS}") + endif() + + get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) get_property(_sbom_project GLOBAL PROPERTY sbom_project) - if("${_sbom}" STREQUAL "") + if("${_sbom_project}" STREQUAL "") message(FATAL_ERROR "Call sbom_generate() first") endif() @@ -299,38 +368,85 @@ function(sbom_finalize) file(WRITE \"${PROJECT_BINARY_DIR}/sbom/verification.txt\" \"\${SBOM_VERIFICATION_CODES}\") file(SHA1 \"${PROJECT_BINARY_DIR}/sbom/verification.txt\" SBOM_VERIFICATION_CODE) configure_file(\"${PROJECT_BINARY_DIR}/sbom/sbom.spdx.in\" \"${_sbom}\") - - message(STATUS \"Verifying: ${_sbom}\") - execute_process( - COMMAND ${Python3_EXECUTABLE} -m spdx_tools.spdx.clitools.pyspdxtools - -i \"${_sbom}\" - RESULT_VARIABLE _res - ) - if(NOT _res EQUAL 0) - message(FATAL_ERROR \"SBOM verification failed\") - endif() " ) + if(NOT "${SBOM_FINALIZE_GRAPH}" STREQUAL "") + set(SBOM_FINALIZE_NO_VERIFY FALSE) + set(SBOM_FINALIZE_VERIFY TRUE) + set(_graph --graph --outfile "${SBOM_FINALIZE_GRAPH}") + else() + set(_graph) + endif() + + if(SBOM_FINALIZE_NO_VERIFY) + set(SBOM_FINALIZE_VERIFY FALSE) + else() + if(SBOM_FINALIZE_VERIFY) + # Force verify. + set(_req REQUIRED) + else() + # Check if we can verify. + set(_req) + endif() + + sbom_find_python(${_req}) + + if(Python3_FOUND) + set(SBOM_FINALIZE_VERIFY TRUE) + endif() + endif() + + if(SBOM_FINALIZE_VERIFY) + file( + APPEND ${PROJECT_BINARY_DIR}/sbom/verify.cmake + " + message(STATUS \"Verifying: ${_sbom}\") + execute_process( + COMMAND ${Python3_EXECUTABLE} -m spdx_tools.spdx.clitools.pyspdxtools + -i \"${_sbom}\" ${_graph} + RESULT_VARIABLE _res + ) + if(NOT _res EQUAL 0) + message(FATAL_ERROR \"SBOM verification failed\") + endif() + + execute_process( + COMMAND ${Python3_EXECUTABLE} -m ntia_conformance_checker.main + --file \"${_sbom}\" + RESULT_VARIABLE _res + ) + if(NOT _res EQUAL 0) + message(FATAL_ERROR \"SBOM NTIA verification failed\") + endif() + " + ) + endif() + file(APPEND ${PROJECT_BINARY_DIR}/sbom/CMakeLists.txt "install(SCRIPT verify.cmake) " ) # Workaround for pre-CMP0082. add_subdirectory(${PROJECT_BINARY_DIR}/sbom ${PROJECT_BINARY_DIR}/sbom) + + # Mark finalized. + set(SBOM_FILENAME + "${_sbom}" + PARENT_SCOPE + ) + set_property(GLOBAL PROPERTY sbom_project "") endfunction() # Append a file to the SBOM. Use this after calling sbom_generate(). -# -# Usage: sbom_file(FILENAME FILETYPE [RELATIONSHIP ] [SPDXID ] -# [OPTIONAL]) -# -# The FILENAME must be relative to CMAKE_INSTALL_PREFIX. Generator expressions are supported. function(sbom_file) set(options OPTIONAL) set(oneValueArgs FILENAME FILETYPE RELATIONSHIP SPDXID) set(multiValueArgs) cmake_parse_arguments(SBOM_FILE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if(SBOM_FILE_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_FILE_UNPARSED_ARGUMENTS}") + endif() if("${SBOM_FILE_FILENAME}" STREQUAL "") message(FATAL_ERROR "Missing FILENAME argument") @@ -350,7 +466,7 @@ function(sbom_file) message(FATAL_ERROR "Missing FILETYPE argument") endif() - get_property(_sbom GLOBAL PROPERTY sbom_filename) + get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) get_property(_sbom_project GLOBAL PROPERTY sbom_project) if("${SBOM_FILE_RELATIONSHIP}" STREQUAL "") @@ -361,7 +477,7 @@ function(sbom_file) ) endif() - if("${_sbom}" STREQUAL "") + if("${_sbom_project}" STREQUAL "") message(FATAL_ERROR "Call sbom_generate() first") endif() @@ -399,10 +515,6 @@ Relationship: ${SBOM_FILE_RELATIONSHIP} endfunction() # Append a target output to the SBOM. Use this after calling sbom_generate(). -# -# Usage: sbom_target(TARGET [RELATIONSHIP ] [SPDXID ]) -# -# The target must be installed in the default location, according to GNUInstallDirs. function(sbom_target) set(options) set(oneValueArgs TARGET) @@ -420,6 +532,29 @@ function(sbom_target) sbom_file(FILENAME ${CMAKE_INSTALL_BINDIR}/$ FILETYPE BINARY ${SBOM_TARGET_UNPARSED_ARGUMENTS} ) + elseif("${_type}" STREQUAL "STATIC_LIBRARY") + sbom_file(FILENAME ${CMAKE_INSTALL_LIBDIR}/$ + FILETYPE BINARY ${SBOM_TARGET_UNPARSED_ARGUMENTS} + ) + elseif("${_type}" STREQUAL "SHARED_LIBRARY") + if(WIN32) + sbom_file( + FILENAME + ${CMAKE_INSTALL_BINDIR}/$ + FILETYPE BINARY ${SBOM_TARGET_UNPARSED_ARGUMENTS} + ) + sbom_file( + FILENAME + ${CMAKE_INSTALL_LIBDIR}/$ + FILETYPE BINARY ${SBOM_TARGET_UNPARSED_ARGUMENTS} + ) + else() + sbom_file( + FILENAME + ${CMAKE_INSTALL_LIBDIR}/$ + FILETYPE BINARY ${SBOM_TARGET_UNPARSED_ARGUMENTS} + ) + endif() else() message(FATAL_ERROR "Unsupported target type ${_type}") endif() @@ -438,6 +573,9 @@ function(sbom_directory) cmake_parse_arguments( SBOM_DIRECTORY "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if(SBOM_DIRECTORY_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_DIRECTORY_UNPARSED_ARGUMENTS}") + endif() if("${SBOM_DIRECTORY_DIRECTORY}" STREQUAL "") message(FATAL_ERROR "Missing DIRECTORY argument") @@ -451,7 +589,7 @@ function(sbom_directory) message(FATAL_ERROR "Missing FILETYPE argument") endif() - get_property(_sbom GLOBAL PROPERTY sbom_filename) + get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) get_property(_sbom_project GLOBAL PROPERTY sbom_project) if("${SBOM_DIRECTORY_RELATIONSHIP}" STREQUAL "") @@ -464,7 +602,7 @@ function(sbom_directory) ) endif() - if("${_sbom}" STREQUAL "") + if("${_sbom_project}" STREQUAL "") message(FATAL_ERROR "Call sbom_generate() first") endif() @@ -523,13 +661,16 @@ function(sbom_package) cmake_parse_arguments( SBOM_PACKAGE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if(SBOM_PACKAGE_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_PACKAGE_UNPARSED_ARGUMENTS}") + endif() if("${SBOM_PACKAGE_PACKAGE}" STREQUAL "") message(FATAL_ERROR "Missing PACKAGE") endif() if("${SBOM_PACKAGE_DOWNLOAD_LOCATION}" STREQUAL "") - message(FATAL_ERROR "Missing DOWNLOAD_LOCATION") + set(SBOM_PACKAGE_DOWNLOAD_LOCATION NOASSERTION) endif() sbom_spdxid( @@ -568,7 +709,7 @@ ExternalRef: ${_ref}" ) endforeach() - get_property(_sbom GLOBAL PROPERTY sbom_filename) + get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) get_property(_sbom_project GLOBAL PROPERTY sbom_project) if("${SBOM_PACKAGE_RELATIONSHIP}" STREQUAL "") @@ -581,7 +722,7 @@ ExternalRef: ${_ref}" ) endif() - if("${_sbom}" STREQUAL "") + if("${_sbom_project}" STREQUAL "") message(FATAL_ERROR "Call sbom_generate() first") endif() @@ -622,6 +763,9 @@ function(sbom_external) cmake_parse_arguments( SBOM_EXTERNAL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if(SBOM_EXTERNAL_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: ${SBOM_EXTERNAL_UNPARSED_ARGUMENTS}") + endif() if("${SBOM_EXTERNAL_EXTERNAL}" STREQUAL "") message(FATAL_ERROR "Missing EXTERNAL") @@ -647,13 +791,14 @@ function(sbom_external) PARENT_SCOPE ) - get_property(_sbom GLOBAL PROPERTY sbom_filename) - if("${_sbom}" STREQUAL "") + get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) + get_property(_sbom_project GLOBAL PROPERTY sbom_project) + + if("${_sbom_project}" STREQUAL "") message(FATAL_ERROR "Call sbom_generate() first") endif() get_filename_component(sbom_dir "${_sbom}" DIRECTORY) - get_property(_sbom_project GLOBAL PROPERTY sbom_project) if("${SBOM_EXTERNAL_RELATIONSHIP}" STREQUAL "") set(SBOM_EXTERNAL_RELATIONSHIP @@ -674,7 +819,7 @@ function(sbom_external) file(SHA1 \"${SBOM_EXTERNAL_FILENAME}\" ext_sha1) file(READ \"${SBOM_EXTERNAL_FILENAME}\" ext_content) if(\"${SBOM_EXTERNAL_RENAME}\" STREQUAL \"\") - get_filename_component(ext_name \"${SBOM_EXTERNAL_FILENAME}\" NAME_WE) + get_filename_component(ext_name \"${SBOM_EXTERNAL_FILENAME}\" NAME) file(WRITE \"${sbom_dir}/\${ext_name}\" \"\${ext_content}\") else() file(WRITE \"${sbom_dir}/${SBOM_EXTERNAL_RENAME}\" \"\${ext_content}\") @@ -729,11 +874,7 @@ endfunction() # installed (see dist/common/requirements.txt). function(reuse_lint) if(NOT TARGET ${PROJECT_NAME}-reuse-lint) - find_package( - Python3 - COMPONENTS Interpreter - REQUIRED - ) + sbom_find_python(REQUIRED) add_custom_target( ${PROJECT_NAME}-reuse-lint ALL @@ -748,11 +889,7 @@ endfunction() # packages installed (see dist/common/requirements.txt). function(reuse_spdx) if(NOT TARGET ${PROJECT_NAME}-reuse-spdx) - find_package( - Python3 - COMPONENTS Interpreter - REQUIRED - ) + sbom_find_python(REQUIRED) set(outfile "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-src.spdx") diff --git a/cmake/version.cmake b/cmake/version.cmake index 83a749c..06d4e89 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -244,10 +244,7 @@ function(version_generate) file(WRITE ${PROJECT_BINARY_DIR}/version.txt "${GIT_VERSION}") if(NOT TARGET ${PROJECT_NAME}-version) - add_library( - ${PROJECT_NAME}-version STATIC - "${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}_version.h" - ) + add_library(${PROJECT_NAME}-version INTERFACE) set_target_properties( ${PROJECT_NAME}-version @@ -258,8 +255,8 @@ function(version_generate) target_include_directories( ${PROJECT_NAME}-version - PUBLIC "$" - "$" + INTERFACE "$" + "$" ) endif() endfunction() diff --git a/dist/common/requirements.txt b/dist/common/requirements.txt index be21e58..c2a7264 100644 --- a/dist/common/requirements.txt +++ b/dist/common/requirements.txt @@ -5,3 +5,4 @@ reuse poetry-core; python_version < '3.9' # build dep for reuse spdx-tools +ntia-conformance-checker diff --git a/dist/macos/.gitignore b/dist/macos/.gitignore new file mode 100644 index 0000000..93e16d4 --- /dev/null +++ b/dist/macos/.gitignore @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: CC0-1.0 + +/build/ diff --git a/dist/macos/bootstrap.sh b/dist/macos/bootstrap.sh new file mode 100755 index 0000000..21e92d6 --- /dev/null +++ b/dist/macos/bootstrap.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +set -exuo pipefail + +function gotErr { + echo -e "\nError occurred, stopping\n" + exit 1 +} + +trap gotErr ERR + +function get_brew { + if ! which brew > /dev/null; then + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || gotErr + fi +} + +function do_install { + for c in "$@"; do + if ! which "${c}" > /dev/null; then + # Command not found. We need brew to install it. + get_brew || gotErr + + if ! brew ls --versions "${c}" > /dev/null; then + HOMEBREW_NO_AUTO_UPDATE=1 brew install "${c}" || gotErr + fi + fi + done +} + +do_install python3 git cmake diff --git a/dist/macos/build.sh b/dist/macos/build.sh new file mode 120000 index 0000000..03f3ae5 --- /dev/null +++ b/dist/macos/build.sh @@ -0,0 +1 @@ +../ubuntu/build.sh \ No newline at end of file diff --git a/dist/ubuntu/bootstrap.sh b/dist/ubuntu/bootstrap.sh index 46114bd..ab7da5e 100755 --- a/dist/ubuntu/bootstrap.sh +++ b/dist/ubuntu/bootstrap.sh @@ -7,4 +7,3 @@ set -exuo pipefail sudo apt-get install -y build-essential git-core g++ cmake python3 python3-venv - diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..9a0db1b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) + set(rmdir rm -rf) +else() + set(rmdir "remove_directory") +endif() + +add_custom_target(test ALL) + +sbom_find_python(REQUIRED) + +function(test name) + set(_dir "${CMAKE_CURRENT_BINARY_DIR}/${name}") + make_directory(${_dir}/src) + make_directory(${_dir}/build) + + set(TEST_PREAMBLE + "cmake_minimum_required(VERSION 3.5) +cmake_policy(VERSION 3.5) +project(${name})" + ) + + configure_file("${name}.cmake" "${_dir}/src/CMakeLists.txt" @ONLY) + + add_custom_target( + test-${name} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.cmake + COMMAND + ${CMAKE_COMMAND} "${_dir}/src" -G "${CMAKE_GENERATOR}" + "-DCMAKE_INSTALL_PREFIX=${_dir}/out" + "-DCMAKE_MODULE_PATH=${PROJECT_SOURCE_DIR}/cmake" + "-DPython3_EXECUTABLE=${Python3_EXECUTABLE}" + COMMAND ${CMAKE_COMMAND} --build . --target install + WORKING_DIRECTORY "${_dir}/build" + COMMENT "Testing ${name}" + VERBATIM + ) + + add_dependencies(test test-${name}) +endfunction() + +test(minimal) +test(minimal2) +test(full_doc) +test(input_doc) +test(file) +test(package) +test(dir) +test(target) +test(external) diff --git a/test/dir.cmake b/test/dir.cmake new file mode 100644 index 0000000..2bbf3aa --- /dev/null +++ b/test/dir.cmake @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(SUPPLIER Demcon SUPPLIER_URL https://demcon.com) + +install(FILES ${CMAKE_CURRENT_LIST_FILE} DESTINATION dir) +install(FILES ${CMAKE_CURRENT_LIST_FILE} DESTINATION dir RENAME file.txt) + +sbom_add(DIRECTORY dir FILETYPE OTHER) +sbom_add(DIRECTORY dir FILETYPE DOCUMENTATION) + +sbom_finalize() diff --git a/test/external.cmake b/test/external.cmake new file mode 100644 index 0000000..37beef9 --- /dev/null +++ b/test/external.cmake @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) +set(SBOM_SUPPLIER Demcon) +set(SBOM_SUPPLIER_URL https://demcon.com) + +make_directory(${CMAKE_CURRENT_BINARY_DIR}/other) +file( + WRITE ${CMAKE_CURRENT_BINARY_DIR}/other/CMakeLists.txt + " + project(other) + sbom_generate(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/other-sbom.spdx) + sbom_finalize() + " +) +add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/other ${CMAKE_CURRENT_BINARY_DIR}/other-build) + +# Last generated SBOM file. It's valid until the next sbom_generate(). +get_property(_sbom GLOBAL PROPERTY SBOM_FILENAME) + +sbom_generate() +sbom_add(EXTERNAL SPDXRef-other FILENAME "${_sbom}") + +sbom_external( + EXTERNAL SPDXRef-other + FILENAME "${_sbom}" + RELATIONSHIP "\@SBOM_LAST_SPDXID\@:SPDXRef-other VARIANT_OF ${SBOM_LAST_SPDXID}:SPDXRef-other" +) +sbom_finalize() diff --git a/test/file.cmake b/test/file.cmake new file mode 100644 index 0000000..c7e5390 --- /dev/null +++ b/test/file.cmake @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(SUPPLIER Demcon SUPPLIER_URL https://demcon.com) + +install(FILES ${CMAKE_CURRENT_LIST_FILE} DESTINATION .) + +# Does not exist before installing. +sbom_add(FILENAME CMakeLists.txt FILETYPE OTHER) + +# Twice the same file, should not conflict. +sbom_add(FILENAME CMakeLists.txt FILETYPE OTHER) + +# Once more, with specified SPDXID. +sbom_add(FILENAME CMakeLists.txt FILETYPE OTHER SPDXID SPDXRef-again) + +sbom_finalize() diff --git a/test/full_doc.cmake b/test/full_doc.cmake new file mode 100644 index 0000000..d6ef645 --- /dev/null +++ b/test/full_doc.cmake @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate( + OUTPUT "${CMAKE_INSTALL_PREFIX}/full-sbom.spdx" + COPYRIGHT "2023 me" + LICENSE "CC0-1.0" + NAMESPACE "https://test.com/spdxdoc/me" + PROJECT "test-full_doc" + SUPPLIER Demcon + SUPPLIER_URL https://demcon.com +) + +sbom_finalize() diff --git a/test/input_doc.cmake b/test/input_doc.cmake new file mode 100644 index 0000000..3366654 --- /dev/null +++ b/test/input_doc.cmake @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(INPUT @CMAKE_CURRENT_LIST_DIR@/input_doc.spdx.in COPYRIGHT "2023 me") + +sbom_finalize() diff --git a/test/input_doc.spdx.in b/test/input_doc.spdx.in new file mode 100644 index 0000000..600265c --- /dev/null +++ b/test/input_doc.spdx.in @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: CC0-1.0 + +SPDXVersion: SPDX-2.3 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: @PROJECT_NAME@ +DocumentNamespace: https://test.com/spdxdoc/@PROJECT_NAME@ +Creator: Organization: Demcon +Creator: Tool: cmake-sbom +Created: @NOW_UTC@ + +PackageName: @PROJECT_NAME@ +SPDXID: SPDXRef-package +PackageVersion: 1 +PackageSupplier: Organization: Demcon +PackageDownloadLocation: NOASSERTION +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION +PackageCopyrightText: @SBOM_GENERATE_COPYRIGHT@ +PackageHomePage: https://demcon.com +BuiltDate: @NOW_UTC@ +PackageVerificationCode: ${SBOM_VERIFICATION_CODE} +Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-package diff --git a/test/minimal.cmake b/test/minimal.cmake new file mode 100644 index 0000000..91d14fa --- /dev/null +++ b/test/minimal.cmake @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(SUPPLIER Demcon SUPPLIER_URL https://demcon.com) +sbom_finalize() diff --git a/test/minimal2.cmake b/test/minimal2.cmake new file mode 100644 index 0000000..761ed9f --- /dev/null +++ b/test/minimal2.cmake @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +set(SBOM_SUPPLIER Demcon) +set(SBOM_SUPPLIER_URL https://demcon.com) + +sbom_generate() +sbom_finalize() diff --git a/test/package.cmake b/test/package.cmake new file mode 100644 index 0000000..beffb1e --- /dev/null +++ b/test/package.cmake @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(SUPPLIER Demcon SUPPLIER_URL https://demcon.com) + +sbom_add(PACKAGE foo) +sbom_add(PACKAGE foo DOWNLOAD_LOCATION http://foo.bar/baz) +sbom_add( + PACKAGE bar + DOWNLOAD_LOCATION http://somwhere.com/bar + LICENSE CC0-1.0 + SUPPLIER "Person: me" + VERSION 0.1 +) + +sbom_finalize(VERIFY) diff --git a/test/target.cmake b/test/target.cmake new file mode 100644 index 0000000..19a6d79 --- /dev/null +++ b/test/target.cmake @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2023 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +@TEST_PREAMBLE@ + +include(sbom) + +sbom_generate(SUPPLIER Demcon SUPPLIER_URL https://demcon.com) + +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/foo.c "int main() {}") + +add_executable(foo ${CMAKE_CURRENT_BINARY_DIR}/foo.c) +install(TARGETS foo) +sbom_add(TARGET foo) + +add_library(libfoo STATIC ${CMAKE_CURRENT_BINARY_DIR}/foo.c) +install(TARGETS libfoo) +sbom_add(TARGET libfoo) + +add_library(libfoo2 SHARED ${CMAKE_CURRENT_BINARY_DIR}/foo.c) +install(TARGETS libfoo2) +sbom_add(TARGET libfoo2) + +# Headers are not included. You may want to add sbom_add(DIRECTORY include FILETYPE SOURCE). + +sbom_finalize()