Pārlūkot izejas kodu

export: Import/export target property for license

Add import/export support and documentation for the SPDX_LICENSE
target property. A target's license can be specified by setting
this property to an SPDX license expression. CMake and CPS-format
export files generated with `export()` or `install()` will
retain the license information. CMake also imports the license
property for imported targets.

Closes: #26706
Daniel Tierney 5 mēneši atpakaļ
vecāks
revīzija
df3e246bc1

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -328,6 +328,7 @@ Properties on Targets
    /prop_tgt/LIBRARY_OUTPUT_DIRECTORY_CONFIG
    /prop_tgt/LIBRARY_OUTPUT_NAME
    /prop_tgt/LIBRARY_OUTPUT_NAME_CONFIG
+   /prop_tgt/SPDX_LICENSE
    /prop_tgt/LINK_DEPENDS
    /prop_tgt/LINK_DEPENDS_NO_SHARED
    /prop_tgt/LINK_DIRECTORIES

+ 14 - 0
Help/prop_tgt/SPDX_LICENSE.rst

@@ -0,0 +1,14 @@
+SPDX_LICENSE
+------------
+
+.. versionadded:: 4.1
+
+Specify the license of a target using a |SPDX|_ (SPDX) `License Expression`_.
+See the SPDX `License List`_ for a list of commonly used licenses and their
+identifiers.
+
+.. _SPDX: https://spdx.dev/
+.. |SPDX| replace:: System Package Data Exchange
+
+.. _License Expression: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
+.. _License List: https://spdx.org/licenses/

+ 3 - 0
Source/cmExportFileGenerator.cxx

@@ -117,6 +117,9 @@ bool cmExportFileGenerator::PopulateInterfaceProperties(
   this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE",
                                   target, properties);
 
+  this->PopulateInterfaceProperty("SPDX_LICENSE", target, preprocessRule,
+                                  properties);
+
   std::string errorMessage;
   if (!this->PopulateCxxModuleExportProperties(
         target, properties, preprocessRule, includesDestinationDirs,

+ 22 - 1
Source/cmExportPackageInfoGenerator.cxx

@@ -205,7 +205,10 @@ bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
   this->GenerateInterfaceListProperty(result, component, target, "includes",
                                       "INCLUDE_DIRECTORIES"_s, properties);
 
-  // TODO: description, license
+  this->GenerateProperty(result, component, target, "license", "SPDX_LICENSE",
+                         properties);
+
+  // TODO: description
 
   return result;
 }
@@ -464,6 +467,24 @@ void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
   }
 }
 
+void cmExportPackageInfoGenerator::GenerateProperty(
+  bool& result, Json::Value& component, cmGeneratorTarget const* target,
+  std::string const& outName, std::string const& inName,
+  ImportPropertyMap const& properties) const
+{
+  auto const& iter = properties.find(inName);
+  if (iter == properties.end()) {
+    return;
+  }
+
+  if (!ForbidGeneratorExpressions(target, inName, iter->second)) {
+    result = false;
+    return;
+  }
+
+  component[outName] = iter->second;
+}
+
 Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
   std::string const& suffix, ImportPropertyMap const& properties) const
 {

+ 5 - 0
Source/cmExportPackageInfoGenerator.h

@@ -101,6 +101,11 @@ private:
     std::string const& outName, cm::string_view inName,
     ImportPropertyMap const& properties) const;
 
+  void GenerateProperty(bool& result, Json::Value& component,
+                        cmGeneratorTarget const* target,
+                        std::string const& outName, std::string const& inName,
+                        ImportPropertyMap const& properties) const;
+
   std::string const PackageName;
   std::string const PackageVersion;
   std::string const PackageVersionCompat;

+ 24 - 10
Source/cmPackageInfoReader.cxx

@@ -564,10 +564,10 @@ std::string cmPackageInfoReader::ResolvePath(std::string path) const
   return path;
 }
 
-void cmPackageInfoReader::SetOptionalProperty(cmTarget* target,
-                                              cm::string_view property,
-                                              cm::string_view configuration,
-                                              Json::Value const& value) const
+void cmPackageInfoReader::SetImportProperty(cmTarget* target,
+                                            cm::string_view property,
+                                            cm::string_view configuration,
+                                            Json::Value const& value) const
 {
   if (!value.isNull()) {
     std::string fullprop;
@@ -582,6 +582,15 @@ void cmPackageInfoReader::SetOptionalProperty(cmTarget* target,
   }
 }
 
+void cmPackageInfoReader::SetMetaProperty(cmTarget* target,
+                                          cm::string_view property,
+                                          Json::Value const& value) const
+{
+  if (!value.isNull()) {
+    target->SetProperty(property.data(), value.asString());
+  }
+}
+
 void cmPackageInfoReader::SetTargetProperties(
   cmMakefile* makefile, cmTarget* target, Json::Value const& data,
   std::string const& package, cm::string_view configuration) const
@@ -630,14 +639,14 @@ void cmPackageInfoReader::SetTargetProperties(
                            });
 
   // Add link name/location(s).
-  this->SetOptionalProperty(target, "LOCATION"_s, configuration,
-                            data["location"]);
+  this->SetImportProperty(target, "LOCATION"_s, configuration,
+                          data["location"]);
 
-  this->SetOptionalProperty(target, "IMPLIB"_s, configuration,
-                            data["link_location"]);
+  this->SetImportProperty(target, "IMPLIB"_s, configuration,
+                          data["link_location"]);
 
-  this->SetOptionalProperty(target, "SONAME"_s, configuration,
-                            data["link_name"]);
+  this->SetImportProperty(target, "SONAME"_s, configuration,
+                          data["link_name"]);
 
   // Add link languages.
   for (std::string const& originalLang : ReadList(data, "link_languages")) {
@@ -659,6 +668,11 @@ void cmPackageInfoReader::SetTargetProperties(
       cmStrCat("$<LINK_ONLY:"_s, NormalizeTargetName(dep, package), '>');
     AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
   }
+
+  // Add other information.
+  if (configuration.empty()) {
+    this->SetMetaProperty(target, "SPDX_LICENSE"_s, data["license"]);
+  }
 }
 
 cmTarget* cmPackageInfoReader::AddLibraryComponent(

+ 5 - 3
Source/cmPackageInfoReader.h

@@ -94,9 +94,11 @@ private:
   void SetTargetProperties(cmMakefile* makefile, cmTarget* target,
                            Json::Value const& data, std::string const& package,
                            cm::string_view configuration) const;
-  void SetOptionalProperty(cmTarget* target, cm::string_view property,
-                           cm::string_view configuration,
-                           Json::Value const& value) const;
+  void SetImportProperty(cmTarget* target, cm::string_view property,
+                         cm::string_view configuration,
+                         Json::Value const& value) const;
+  void SetMetaProperty(cmTarget* target, cm::string_view property,
+                       Json::Value const& value) const;
 
   std::string ResolvePath(std::string path) const;
 

+ 1 - 0
Tests/RunCMake/ExportImport/RunCMakeTest.cmake

@@ -22,6 +22,7 @@ function(run_ExportImport_test case)
 endfunction()
 
 run_ExportImport_test(SharedDep)
+run_ExportImport_test(SpdxLicenseProperty)
 
 function(run_ExportImportBuildInstall_test case)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-export-build)

+ 6 - 0
Tests/RunCMake/ExportImport/SpdxLicenseProperty-export.cmake

@@ -0,0 +1,6 @@
+add_library(foo INTERFACE)
+set_property(TARGET foo PROPERTY SPDX_LICENSE "BSD-3-Clause")
+
+install(TARGETS foo EXPORT foo)
+install(EXPORT foo DESTINATION lib/cmake/foo)
+install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/cmake/foo)

+ 6 - 0
Tests/RunCMake/ExportImport/SpdxLicenseProperty-import.cmake

@@ -0,0 +1,6 @@
+find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH)
+get_property(SPDX_LICENSE TARGET foo PROPERTY SPDX_LICENSE)
+if(NOT SPDX_LICENSE STREQUAL "BSD-3-Clause")
+  message(FATAL_ERROR
+    "Expected SPDX_LICENSE property to be 'BSD-3-Clause' but got '${SPDX_LICENSE}'")
+endif()

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/InterfaceProperties-check.cmake

@@ -22,3 +22,4 @@ expect_array("${component}" 1 "link_flags")
 expect_value("${component}" "--needed" "link_flags" 0)
 expect_array("${component}" 1 "link_libraries")
 expect_value("${component}" "/usr/lib/libm.so" "link_libraries" 0)
+expect_value("${component}" "BSD-3-Clause" "license")

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/InterfaceProperties.cmake

@@ -10,6 +10,7 @@ target_include_directories(
 target_link_directories(foo INTERFACE /opt/foo/lib)
 target_link_options(foo INTERFACE --needed)
 target_link_libraries(foo INTERFACE /usr/lib/libm.so)
+set_property(TARGET foo PROPERTY SPDX_LICENSE "BSD-3-Clause")
 
 install(TARGETS foo EXPORT foo DESTINATION .)
 export(EXPORT foo PACKAGE_INFO foo)

+ 1 - 0
Tests/RunCMake/ExportPackageInfo/Minimal-check.cmake

@@ -16,3 +16,4 @@ expect_missing("${content}" "components" "foo" "link_features")
 expect_missing("${content}" "components" "foo" "link_flags")
 expect_missing("${content}" "components" "foo" "link_libraries")
 expect_missing("${content}" "components" "foo" "requires")
+expect_missing("${content}" "components" "foo" "license")

+ 1 - 0
Tests/RunCMake/InstallPackageInfo/InterfaceProperties-check.cmake

@@ -22,3 +22,4 @@ expect_array("${component}" 1 "link_flags")
 expect_value("${component}" "--needed" "link_flags" 0)
 expect_array("${component}" 1 "link_libraries")
 expect_value("${component}" "/usr/lib/libm.so" "link_libraries" 0)
+expect_value("${component}" "BSD-3-Clause" "license")

+ 1 - 0
Tests/RunCMake/InstallPackageInfo/InterfaceProperties.cmake

@@ -10,6 +10,7 @@ target_include_directories(
 target_link_directories(foo INTERFACE /opt/foo/lib)
 target_link_options(foo INTERFACE --needed)
 target_link_libraries(foo INTERFACE /usr/lib/libm.so)
+set_property(TARGET foo PROPERTY SPDX_LICENSE "BSD-3-Clause")
 
 install(TARGETS foo EXPORT foo DESTINATION .)
 install(PACKAGE_INFO foo DESTINATION cps EXPORT foo)

+ 1 - 0
Tests/RunCMake/InstallPackageInfo/Minimal-check.cmake

@@ -16,3 +16,4 @@ expect_missing("${content}" "components" "foo" "link_features")
 expect_missing("${content}" "components" "foo" "link_flags")
 expect_missing("${content}" "components" "foo" "link_libraries")
 expect_missing("${content}" "components" "foo" "requires")
+expect_missing("${content}" "components" "foo" "license")

+ 3 - 0
Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake

@@ -17,6 +17,9 @@ run_cmake(VersionLimit2)
 run_cmake(TransitiveVersion)
 run_cmake(CustomVersion)
 
+# Metadata Tests
+run_cmake(SupplementalAttributes)
+
 # Version-matching failure tests
 run_cmake(MissingVersion1)
 run_cmake(MissingVersion2)

+ 13 - 0
Tests/RunCMake/find_package-CPS/SupplementalAttributes.cmake

@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+find_package(SupplementalAttributesTest REQUIRED COMPONENTS Sample)
+
+get_target_property(license SupplementalAttributesTest::Sample "SPDX_LICENSE")
+if (NOT "${license}" STREQUAL "BSD-3-Clause")
+  message(SEND_ERROR "SupplementalAttributesTest wrong license ${license} !")
+endif()

+ 12 - 0
Tests/RunCMake/find_package-CPS/cps/supplementalattributestest.cps

@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "SupplementalAttributesTest",
+  "version": "1.0",
+  "cps_path": "@prefix@/cps",
+  "components": {
+    "Sample": {
+      "type": "interface",
+      "license": "BSD-3-Clause"
+    }
+  }
+}