#.rst: # ECMCheckOutboundLicense # ----------------------- # # Assert that source file licenses are compatible with a desired outbound license # of a compiled binary artifact (e.g., library, plugin or application). # # This module provides the ``ecm_check_outbound_license`` function that # generates unit tests for checking the compatibility of license statements. # The license statements in all tested files are required to be added by using # the SPDX marker ``SPDX-License-Identifier``. # # During the CMake configuration of the project, a temporary license bill of # materials (BOM) in SPDX format is generated by calling the REUSE tool # (see ). That BOM is parsed and license computations # based on an internal compatibility matrix are performed. # # Preconditions for using this module: # * All tested input source files must contain the SPDX-License-Identifier tag. # * Python3 must be available. # * The REUSE tool must be available, which generates the bill-of-materials # by running ``reuse spdx`` on the tested directory. # # When this module is included, a ``SKIP_LICENSE_TESTS`` option is added (default # OFF). Turning this option on skips the generation of license tests, which might # be convenient if licenses shall not be tested in all build configurations. # # :: # # ecm_check_outbound_license(LICENSES # FILES # [TEST_NAME ] # [WILL_FAIL]) # # This method adds a custom unit test to ensure the specified outbound license to be # compatible with the specified license headers. Note that a convenient way is to use # the CMake GLOB command of the FILE function. # # ``LICENSES`` : List of one or multiple outbound license regarding which the compatibility # of the source code files shall be tested. Currently, the following values # are supported (values are SPDX registry identifiers): # * MIT # * BSD-2-Clause # * BSD-3-Clause # * LGPL-2.0-only # * LGPL-2.1-only # * LGPL-3.0-only # * GPL-2.0-only # * GPL-3.0-only # # ``FILES:`` : List of source files that contain valid SPDX-License-Identifier markers. # The paths can be relative to the CMake file that generates the test case # or be absolute paths. # # ``TEST_NAME`` : Optional parameter that defines the name of the generated test case. # If no name is defined, the relative path to the test directory with appended # license name is used. Every test has ``licensecheck_`` as prefix. # # ``WILL_FAIL`` : Optional parameter that inverts the test result. This parameter is usually only # used for tests of the module. # # Since 5.75.0 #============================================================================= # SPDX-FileCopyrightText: 2020 Andreas Cord-Landwehr # SPDX-License-Identifier: BSD-3-Clause include(FeatureSummary) option(SKIP_LICENSE_TESTS "Skip outbound license tests" OFF) find_package(Python3) set_package_properties(Python3 PROPERTIES PURPOSE "Required to run tests of module ECMCheckOutboundLicense" TYPE OPTIONAL ) find_package(ReuseTool) set_package_properties(ReuseTool PROPERTIES PURPOSE "Required to run tests of module ECMCheckOutboundLicense" TYPE OPTIONAL ) if (NOT SKIP_LICENSE_TESTS AND NOT REUSETOOL_FOUND) add_feature_info(SPDX_LICENSE_TESTING FALSE "Automatic license testing based on SPDX definitions (requires reuse tool)") message(WARNING "Reuse tool not found, skipping test generation") else() add_feature_info(SPDX_LICENSE_TESTING TRUE "Automatic license testing based on SPDX definitions (requires reuse tool)") endif() set(SPDX_BOM_OUTPUT "${CMAKE_BINARY_DIR}/spdx.txt") # test fixture for generating SPDX bill of materials if(SKIP_LICENSE_TESTS OR NOT REUSETOOL_FOUND) message(STATUS "Skipping execution of outbound license tests.") else() add_test( NAME generate_spdx_bom COMMAND reuse spdx -o ${SPDX_BOM_OUTPUT} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) set_tests_properties(generate_spdx_bom PROPERTIES FIXTURES_SETUP SPDX_BOM) endif() function(ecm_check_outbound_license) if(SKIP_LICENSE_TESTS OR NOT REUSETOOL_FOUND) return() endif() set(_options WILL_FAIL) set(_oneValueArgs TEST_NAME) set(_multiValueArgs LICENSES FILES) cmake_parse_arguments(ARG "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN} ) if(NOT ARG_LICENSES) message(FATAL_ERROR "No LICENSES argument given to ecm_check_outbound_license") endif() if(NOT ARG_FILES) message(WARNING "No FILES argument given to ecm_check_outbound_license") return() endif() if(NOT ARG_TEST_NAME) # compute test name based on licensecheck__ if not name given string(REPLACE "${CMAKE_SOURCE_DIR}/" "" TEMP_TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}_${ARG_LICENSE}") string(MAKE_C_IDENTIFIER ${TEMP_TEST_NAME} ARG_TEST_NAME) endif() # generate file with list of relative file paths string(REPLACE "${CMAKE_BINARY_DIR}/" "" RELATIVE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR}) set(OUTPUT_FILE ${CMAKE_BINARY_DIR}/licensecheck_${ARG_TEST_NAME}.txt) message(STATUS "Generate test input file: ${OUTPUT_FILE}") file(REMOVE ${OUTPUT_FILE}) foreach(_file ${ARG_FILES}) # check script expects files to start with "./", which must be relative to CMAKE_SOURCE_DIR if (IS_ABSOLUTE ${_file}) string(REPLACE ${CMAKE_SOURCE_DIR} "." TEMPORARY_PATH ${_file}) file(APPEND ${OUTPUT_FILE} "${TEMPORARY_PATH}\n") else() file(APPEND ${OUTPUT_FILE} "./${RELATIVE_PREFIX_PATH}/${_file}\n") endif() endforeach() file(COPY ${ECM_MODULE_DIR}/check-outbound-license.py DESTINATION ${CMAKE_BINARY_DIR}) foreach(_license ${ARG_LICENSES}) string(MAKE_C_IDENTIFIER ${_license} LICENSE_ID) add_test( NAME licensecheck_${ARG_TEST_NAME}_${LICENSE_ID} COMMAND python3 ${CMAKE_BINARY_DIR}/check-outbound-license.py -l ${_license} -s ${SPDX_BOM_OUTPUT} -i ${OUTPUT_FILE} ) set_tests_properties(licensecheck_${ARG_TEST_NAME}_${LICENSE_ID} PROPERTIES FIXTURES_REQUIRED SPDX_BOM) set_tests_properties(licensecheck_${ARG_TEST_NAME}_${LICENSE_ID} PROPERTIES WILL_FAIL ${ARG_WILL_FAIL}) endforeach() endfunction()