Merge topic 'spdx_license'

b533f229b8 cmSbom: Generate SPDX data denoting project licenses

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !12133
diff --git a/Help/command/export.rst b/Help/command/export.rst
index 775282f..5b98ecf 100644
--- a/Help/command/export.rst
+++ b/Help/command/export.rst
@@ -178,7 +178,8 @@
          [FORMAT <string>]
          [PROJECT <project-name>|NO_PROJECT_METADATA]
          [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
-         [LICENSE <license-string>]
+         [DATA_LICENSE <license-string>]
+         [DEFAULT_LICENSE <license-string>]
          [DESCRIPTION <description-string>]
          [HOMEPAGE_URL <url-string>]
          [PACKAGE_URL <url-string>])
diff --git a/Help/command/install.rst b/Help/command/install.rst
index 75016b0..cb8db41 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -1217,7 +1217,8 @@
             [PROJECT <project-name>|NO_PROJECT_METADATA]
             [DESTINATION <dir>]
             [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
-            [LICENSE <license-string>]
+            [DATA_LICENSE <license-string>]
+            [DEFAULT_LICENSE <license-string>]
             [DESCRIPTION <description-string>]
             [HOMEPAGE_URL <url-string>]
             [PACKAGE_URL <url-string>]
@@ -1260,16 +1261,21 @@
 
     An informational canonical package URL for the project.
 
-  ``LICENSE <license-string>``
+  ``DATA_LICENSE <license-string>``
 
     A |SPDX|_ (SPDX) `License Expression`_ that describes the license(s) of the
-    project as a whole, including documentation, resources, or other materials
-    distributed with the project, in addition to software artifacts.  See the
-    SPDX `License List`_ for a list of commonly used licenses and their
-    identifiers.
+    SBOM data itself in the generated document.  See the SPDX `License List`_
+    for a list of commonly used licenses and their identifiers. In the SPDX
+    format, it corresponds to the `dataLicense`_ property.
 
     The license of individual components is taken from the
-    :prop_tgt:`SPDX_LICENSE` property of their respective targets.
+    :prop_tgt:`SPDX_LICENSE` property of their respective targets if set and
+    the ``DEFAULT_LICENSE`` otherwise.
+
+  ``DEFAULT_LICENSE <license-string>``
+
+    A |SPDX|_ (SPDX) `License Expression`_ that describes the license(s) of any
+    components which do not otherwise specify their license(s).
 
   ``DESCRIPTION <description-string>``
 
@@ -1420,5 +1426,6 @@
 
 .. _License Expression: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
 .. _License List: https://spdx.org/licenses/
+.. _dataLicense: https://spdx.github.io/spdx-spec/v3.0.1/model/Core/Properties/dataLicense/
 
 .. |SBOM| replace:: Software Bill of Material
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 60955f1..6a3b7f7 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -109,7 +109,7 @@
 set
 
 * variable ``CMAKE_EXPERIMENTAL_GENERATE_SBOM`` to
-* value ``2d856d6d-53e8-488b-a17f-d486d2cac317``.
+* value ``248471c2-d905-4c9e-81b5-b89cd27965e1``.
 
 This UUID may change in future versions of CMake.  Be sure to use the value
 documented here by the source tree of the version of CMake with which you are
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx
index 2d9232c..3d50bf1 100644
--- a/Source/cmExperimental.cxx
+++ b/Source/cmExperimental.cxx
@@ -55,7 +55,7 @@
     {},
     cmExperimental::TryCompileCondition::Never },
   { "GenerateSbom",
-    "2d856d6d-53e8-488b-a17f-d486d2cac317",
+    "248471c2-d905-4c9e-81b5-b89cd27965e1",
     "CMAKE_EXPERIMENTAL_GENERATE_SBOM",
     "CMake's support for generating software bill of materials (Sbom) "
     "information in SPDX format is experimental. It is meant only for "
diff --git a/Source/cmSbomArguments.cxx b/Source/cmSbomArguments.cxx
index 155aefd..b5c48fa 100644
--- a/Source/cmSbomArguments.cxx
+++ b/Source/cmSbomArguments.cxx
@@ -65,6 +65,14 @@
       return false;
     }
   }
+
+  if (!this->License.empty()) {
+    // Do not allow the license argument to be provided as SBOM only accepts a
+    // DEFAULT_LICENSE
+    status.SetError("SBOM given unknown argument: \"LICENSE\".");
+    return false;
+  }
+
   return true;
 }
 
diff --git a/Source/cmSbomArguments.h b/Source/cmSbomArguments.h
index ab62226..a45d52e 100644
--- a/Source/cmSbomArguments.h
+++ b/Source/cmSbomArguments.h
@@ -41,6 +41,8 @@
 
   ArgumentParser::NonEmpty<std::string> Format;
   ArgumentParser::NonEmpty<std::string> PackageUrl;
+  ArgumentParser::NonEmpty<std::string> DefaultLicense;
+  ArgumentParser::NonEmpty<std::string> DataLicense;
 
 protected:
   cm::string_view CommandName() const override;
@@ -55,6 +57,8 @@
     Bind(base, parser, "SBOM"_s, &cmProjectInfoArguments::PackageName);
     Bind(self, parser, "FORMAT"_s, &cmSbomArguments::Format);
     Bind(self, parser, "PACKAGE_URL"_s, &cmSbomArguments::PackageUrl);
+    Bind(self, parser, "DEFAULT_LICENSE"_s, &cmSbomArguments::DefaultLicense);
+    Bind(self, parser, "DATA_LICENSE"_s, &cmSbomArguments::DataLicense);
     cmProjectInfoArguments::Bind(parser, self);
   }
 };
diff --git a/Source/cmSbomBuilder.cxx b/Source/cmSbomBuilder.cxx
index 16397a2..aed9b15 100644
--- a/Source/cmSbomBuilder.cxx
+++ b/Source/cmSbomBuilder.cxx
@@ -70,7 +70,8 @@
   , PackageDescription(std::move(args.Description))
   , PackageWebsite(std::move(args.Website))
   , PackageUrl(std::move(args.PackageUrl))
-  , PackageLicense(std::move(args.License))
+  , DataLicense(std::move(args.DataLicense))
+  , DefaultLicense(std::move(args.DefaultLicense))
   , PackageFormat(args.GetFormat())
 {
 }
@@ -222,8 +223,12 @@
     proj.Description = this->PackageDescription;
   }
 
-  if (!this->PackageLicense.empty()) {
-    proj.DataLicense = this->PackageLicense;
+  if (!this->DataLicense.empty()) {
+    cmSpdxLicenseExpression license;
+    license.SpdxId = cmStrCat("urn:", PackageName, "#LicenseExpression");
+    license.CreationInfo = ci;
+    license.LicenseExpression = this->DataLicense;
+    proj.DataLicense = license;
   }
 
   return proj;
@@ -319,7 +324,31 @@
           this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second);
         }
       }
-      return { true, insert_back(project->Elements, std::move(pkg)) };
+
+      cmSpdxPackage const* pkgPtr =
+        insert_back(project->Elements, std::move(pkg));
+      if (!linkInfo.License.empty() &&
+          !cm::contains(this->GeneratedLinkLicenses, name)) {
+        this->GeneratedLinkLicenses.emplace(name);
+
+        cmSpdxLicenseExpression license;
+        license.SpdxId = cmStrCat("urn:", name, "#LicenseExpression");
+        license.CreationInfo = ci;
+        license.LicenseExpression = linkInfo.License;
+
+        cmSpdxRelationship relHasLicense;
+        relHasLicense.SpdxId =
+          cmStrCat("urn:", name, "#DeclaredLicenseRelationship");
+        relHasLicense.CreationInfo = ci;
+        relHasLicense.RelationshipType =
+          cmSpdxRelationship::HAS_DECLARED_LICENSE;
+        relHasLicense.From = pkgPtr;
+        relHasLicense.To.emplace_back(std::move(license));
+
+        insert_back(doc.Graph, std::move(relHasLicense));
+      }
+
+      return { true, pkgPtr };
     }
 
     cmSpdxPackage pkg;
@@ -368,10 +397,40 @@
                                          current, allTargets, config);
   status &= this->GenerateLinkProperties(
     doc, proj, ci, "INTERFACE_LINK_LIBRARIES", current, allTargets, config);
-
+  status &= this->GenerateMetaProperties(doc, proj, ci, current);
   return status;
 }
 
+bool cmSbomBuilder::GenerateMetaProperties(
+  cmSbomDocument& doc, cmSpdxDocument* /*project*/,
+  cmSpdxCreationInfo const* ci, TargetProperties const& current) const
+{
+  std::string licenseExpr = this->DefaultLicense;
+  cmValue licenseExprProp = current.Target->GetProperty("SPDX_LICENSE");
+  if (licenseExprProp) {
+    licenseExpr = licenseExprProp;
+  }
+  if (!licenseExpr.empty()) {
+    auto const& tgtName = current.Target->GetName();
+
+    cmSpdxLicenseExpression license;
+    license.SpdxId = cmStrCat("urn:", tgtName, "#LicenseExpression");
+    license.CreationInfo = ci;
+    license.LicenseExpression = std::move(licenseExpr);
+
+    cmSpdxRelationship relHasLicense;
+    relHasLicense.SpdxId =
+      cmStrCat("urn:", tgtName, "#DeclaredLicenseRelationship");
+    relHasLicense.CreationInfo = ci;
+    relHasLicense.RelationshipType = cmSpdxRelationship::HAS_DECLARED_LICENSE;
+    relHasLicense.From = current.Package;
+    relHasLicense.To.emplace_back(std::move(license));
+
+    insert_back(doc.Graph, std::move(relHasLicense));
+  }
+  return true;
+}
+
 bool cmSbomBuilder::PopulateLinkLibrariesProperty(
   cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
@@ -428,9 +487,12 @@
                                      std::string const& linkedName,
                                      cmGeneratorTarget const* linkedTarget)
 {
+  auto linkedLicense = linkedTarget->GetSafeProperty("SPDX_LICENSE");
+
   if (cm::contains(this->SbomTargets, linkedTarget)) {
-    this->LinkTargets.emplace(linkedName,
-                              LinkInfo{ "", linkedTarget->GetExportName() });
+    this->LinkTargets.emplace(
+      linkedName,
+      LinkInfo{ "", linkedTarget->GetExportName(), linkedLicense });
     return true;
   }
 
@@ -468,7 +530,8 @@
     } else {
       component = linkedName.substr(prefix.length());
     }
-    this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+    this->LinkTargets.emplace(linkedName,
+                              LinkInfo{ pkgName, component, linkedLicense });
     cmPackageInformation& req =
       this->Requirements.insert(std::move(*pkgInfo)).first->second;
     req.Components.emplace(std::move(component));
@@ -494,9 +557,11 @@
     std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
     std::string component = linkedTarget->GetExportName();
     if (pkgName == this->GetPackageName()) {
-      this->LinkTargets.emplace(linkedName, LinkInfo{ "", component });
+      this->LinkTargets.emplace(linkedName,
+                                LinkInfo{ "", component, linkedLicense });
     } else {
-      this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+      this->LinkTargets.emplace(linkedName,
+                                LinkInfo{ pkgName, component, linkedLicense });
       this->Requirements[pkgName].Components.emplace(std::move(component));
     }
     return true;
@@ -535,7 +600,8 @@
           "\" (first alphabetically)."));
     }
     std::string component = linkedTarget->GetExportName();
-    this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
+    this->LinkTargets.emplace(linkedName,
+                              LinkInfo{ pkgName, component, linkedLicense });
     this->Requirements[pkgName].Components.emplace(std::move(component));
     return true;
   }
diff --git a/Source/cmSbomBuilder.h b/Source/cmSbomBuilder.h
index 961e9db..f092e3f 100644
--- a/Source/cmSbomBuilder.h
+++ b/Source/cmSbomBuilder.h
@@ -129,6 +129,9 @@
                               TargetProperties const& current,
                               std::vector<TargetProperties> const& allTargets,
                               std::string const& config) const;
+  bool GenerateMetaProperties(cmSbomDocument& doc, cmSpdxDocument* project,
+                              cmSpdxCreationInfo const* ci,
+                              TargetProperties const& current) const;
 
   bool NoteLinkedTarget(cmGeneratorTarget const* target,
                         std::string const& linkedName,
@@ -163,6 +166,7 @@
   {
     std::string Package;
     std::string Component;
+    std::string License;
   };
 
   // Metadata
@@ -172,7 +176,8 @@
   std::string const PackageDescription;
   std::string const PackageWebsite;
   std::string const PackageUrl;
-  std::string const PackageLicense;
+  std::string const DataLicense;
+  std::string const DefaultLicense;
   cmSbomArguments::SbomFormat const PackageFormat;
 
   // Derived from inputs at generate time
@@ -182,4 +187,7 @@
   std::map<std::string, LinkInfo> LinkTargets;
   std::map<std::string, cmPackageInformation> Requirements;
   std::vector<std::string> Configurations;
+
+  // Targets for which license data has been generated
+  mutable std::set<std::string> GeneratedLinkLicenses;
 };
diff --git a/Source/cmSpdx.cxx b/Source/cmSpdx.cxx
index 870eec4..492c9a3 100644
--- a/Source/cmSpdx.cxx
+++ b/Source/cmSpdx.cxx
@@ -128,6 +128,8 @@
       return "contains";
     case cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON:
       return "dependsOn";
+    case cmSpdxRelationship::RelationshipTypeId::HAS_DECLARED_LICENSE:
+      return "hasDeclaredLicense";
     case cmSpdxRelationship::RelationshipTypeId::OTHER:
       return "other";
   }
@@ -497,6 +499,20 @@
   }
 }
 
+void cmSpdxAnyLicenseInfo::Serialize(cmSbomSerializer& serializer) const
+{
+  cmSpdxElement::Serialize(serializer);
+  serializer.AddString("type", "simplelicensing_AnyLicenseInfo");
+}
+
+void cmSpdxLicenseExpression::Serialize(cmSbomSerializer& serializer) const
+{
+  cmSpdxAnyLicenseInfo::Serialize(serializer);
+  serializer.AddString("type", "simplelicensing_LicenseExpression");
+  SerializeIfPresent(serializer, "simplelicensing_licenseExpression",
+                     LicenseExpression);
+}
+
 void cmSpdxDocument::Serialize(cmSbomSerializer& serializer) const
 {
   cmSpdxElementCollection::Serialize(serializer);
@@ -508,7 +524,9 @@
   if (NamespaceMap) {
     serializer.AddVisitable("namespaceMap", *NamespaceMap);
   }
-  SerializeIfPresent(serializer, "dataLicense", DataLicense);
+  if (DataLicense) {
+    serializer.AddVisitable("dataLicense", *DataLicense);
+  }
 }
 
 void cmSbomDocument::Serialize(cmSbomSerializer& serializer) const
diff --git a/Source/cmSpdx.h b/Source/cmSpdx.h
index adaa26a..f792e7a 100644
--- a/Source/cmSpdx.h
+++ b/Source/cmSpdx.h
@@ -159,6 +159,7 @@
     DESCRIBES,
     CONTAINS,
     DEPENDS_ON,
+    HAS_DECLARED_LICENSE,
     OTHER
   };
 
@@ -343,11 +344,23 @@
   void Serialize(cmSbomSerializer&) const override;
 };
 
+struct cmSpdxAnyLicenseInfo : cmSpdxElement
+{
+  void Serialize(cmSbomSerializer&) const override;
+};
+
+struct cmSpdxLicenseExpression final : cmSpdxAnyLicenseInfo
+{
+  cm::optional<std::string> LicenseExpression;
+
+  void Serialize(cmSbomSerializer&) const override;
+};
+
 struct cmSpdxDocument final : cmSpdxElementCollection
 {
   cm::optional<cmSbomObject> ExternalMap;
   cm::optional<cmSbomObject> NamespaceMap;
-  std::string DataLicense;
+  cm::optional<cmSbomObject> DataLicense;
 
   void Serialize(cmSbomSerializer& serializer) const override;
 };
diff --git a/Tests/CMakeLib/testSpdxSerializer.cxx b/Tests/CMakeLib/testSpdxSerializer.cxx
index 7a5f6be..95e6caa 100644
--- a/Tests/CMakeLib/testSpdxSerializer.cxx
+++ b/Tests/CMakeLib/testSpdxSerializer.cxx
@@ -28,7 +28,6 @@
 
   cmSpdxDocument spdxValue;
   spdxValue.SpdxId = "_:SPDXRef-Document";
-  spdxValue.DataLicense = "CC0-1.0";
   auto spdx = insert_back(doc.Graph, std::move(spdxValue));
 
   {
diff --git a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
index 78debd2..39bd58c 100644
--- a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
   "-DCMAKE_INSTALL_SBOM_FORMATS:STRING=JSON"
   "-DCMAKE_INSTALL_LIBDIR=lib"
 )
diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
index 02b5ce3..43678f2 100644
--- a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake
@@ -4,7 +4,7 @@
   SBOM test_targets
   EXPORTS test_targets
   DESCRIPTION "An eloquent description"
-  LICENSE "BSD-3"
+  DATA_LICENSE "BSD-3"
   HOMEPAGE_URL "www.example.com"
   PACKAGE_URL "https://example.com/test_targets.tar.gz"
   VERSION "1.3.4"
diff --git a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
index 4f1f0ef..41b8a45 100644
--- a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
 )
 
 function(run_cmake_error test)
diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
index daaf3b4..d1f99b7 100644
--- a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake
@@ -3,7 +3,7 @@
 install(
   SBOM test_targets
   DESCRIPTION "An eloquent description"
-  LICENSE "BSD-3"
+  DATA_LICENSE "BSD-3"
   HOMEPAGE_URL "www.example.com"
   PACKAGE_URL "https://example.com/test_targets.tar.gz"
   VERSION "1.3.4"
diff --git a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
index fd567a8..bc1bddc 100644
--- a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
+++ b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake
@@ -2,7 +2,7 @@
 
 set(common_test_options
   -Wno-author
-  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=2d856d6d-53e8-488b-a17f-d486d2cac317"
+  "-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=248471c2-d905-4c9e-81b5-b89cd27965e1"
 )
 
 function(run_cmake_error test)
diff --git a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
index 697eb70..83ffda6 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake
@@ -36,6 +36,25 @@
 }
 ]=])
 
+set(LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:test#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:test#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "license-for-test-target",
+      "spdxId" : "urn:test#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
+
 
 expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
 string(JSON CREATION_INFO GET "${content}" "@graph" "0")
@@ -44,3 +63,6 @@
 string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1")
 expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED)
 expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement")
+
+string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/ProjectMetadata.cmake b/Tests/RunCMake/Sbom/ProjectMetadata.cmake
index 02604fc..c56c0e2 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadata.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadata.cmake
@@ -10,6 +10,7 @@
   VERSION 1.2.0)
 
 add_library(test INTERFACE)
+set_target_properties(test PROPERTIES SPDX_LICENSE license-for-test-target)
 
 install(
   TARGETS test
diff --git a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
index 33fca83..6d3247c 100644
--- a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
+++ b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake
@@ -3,7 +3,13 @@
 set(DOCUMENT_METADATA_EXPECTED [=[
 {
   "description" : "An eloquent description",
-  "dataLicense" : "BSD-3"
+  "dataLicense" :
+  {
+    "creationInfo" : "_:Build#CreationInfo",
+    "simplelicensing_licenseExpression" : "BSD-3",
+    "spdxId" : "urn:test_targets#LicenseExpression",
+    "type" : "simplelicensing_LicenseExpression"
+  }
 }
 ]=])
 
diff --git a/Tests/RunCMake/Sbom/Requirements-install-check.cmake b/Tests/RunCMake/Sbom/Requirements-install-check.cmake
index 92f2010..5e5aa14 100644
--- a/Tests/RunCMake/Sbom/Requirements-install-check.cmake
+++ b/Tests/RunCMake/Sbom/Requirements-install-check.cmake
@@ -119,6 +119,24 @@
 }
 ]=])
 
+set(TEST_LIBA_LINKED_LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:test:liba#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:test::liba#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "license-for-test-liba",
+      "spdxId" : "urn:test::liba#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
 
 set(CREATION_INFO_EXPECTED [=[
 {
@@ -147,3 +165,7 @@
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement")
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element")
 expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element")
+
+# Check that the license target property imported from cmake config is reported
+string(JSON LICENSE_RELATIONSHIP GET "${BAR_CONTENT}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" TEST_LIBA_LINKED_LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
index 7114364..1c698e8 100644
--- a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
+++ b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake
@@ -67,6 +67,25 @@
 }
 ]=])
 
+set(LICENSE_EXPECTED [=[
+{
+  "creationInfo" : "_:Build#CreationInfo",
+  "from" : "urn:foo:foo#Package",
+  "relationshipType" : "hasDeclaredLicense",
+  "spdxId" : "urn:foo::foo#DeclaredLicenseRelationship",
+  "to" :
+  [
+    {
+      "creationInfo" : "_:Build#CreationInfo",
+      "simplelicensing_licenseExpression" : "BSD-3-Clause",
+      "spdxId" : "urn:foo::foo#LicenseExpression",
+      "type" : "simplelicensing_LicenseExpression"
+    }
+  ],
+  "type" : "Relationship"
+}
+]=])
+
 
 expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
 string(JSON CREATION_INFO GET "${content}" "@graph" "0")
@@ -76,5 +95,9 @@
 expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED)
 expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement")
 expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element")
-string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2")
+string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "3")
 expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED)
+
+# Check that default_license from the imported CPS package is reported
+string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2")
+expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED)
diff --git a/Tests/RunCMake/Sbom/cmake/test-config.cmake b/Tests/RunCMake/Sbom/cmake/test-config.cmake
index 3c46ea6..12cea73 100644
--- a/Tests/RunCMake/Sbom/cmake/test-config.cmake
+++ b/Tests/RunCMake/Sbom/cmake/test-config.cmake
@@ -1 +1,2 @@
 add_library(test::liba INTERFACE IMPORTED)
+set_target_properties(test::liba PROPERTIES SPDX_LICENSE license-for-test-liba)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index 9408443..b628597 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -95,7 +95,7 @@
 run_cmake_with_options(PACKAGE_INFO-AbsoluteDest-error -Werror=install-absolute-destination)
 run_cmake_with_options(SBOM-AbsoluteDest-warn
   -Winstall-absolute-destination
-  -DCMAKE_EXPERIMENTAL_GENERATE_SBOM=2d856d6d-53e8-488b-a17f-d486d2cac317)
+  -DCMAKE_EXPERIMENTAL_GENERATE_SBOM=248471c2-d905-4c9e-81b5-b89cd27965e1)
 run_cmake_with_options(IMPORTED_RUNTIME_ARTIFACTS-AbsoluteDest-warn -Winstall-absolute-destination)
 if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$")
   run_cmake_with_options(RUNTIME_DEPENDENCY_SET-AbsoluteDest-warn -Winstall-absolute-destination)