Merge topic 'sbom-fix-uninstalled-deps' into release-4.5

2d732c85de cmSbom: Export uninstalled libraries / references to private static deps

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12730
This commit is contained in:
Brad King authored and Kitware Robot committed 2026-11-02 10:13:20 -05:00
commit e9767d8f79
180 files changed
+2417 -1133

No files matched your search

+7 -13
View File
@@ -2,26 +2,19 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmBuildSbomBuilder.h"
#include <utility>
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSbomArguments.h"
cmBuildSbomBuilder::cmBuildSbomBuilder(cmSbomArguments args,
std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg)
: cmSbomBuilder(std::move(args), std::move(exportSets), lg)
cmBuildSbomBuilder::cmBuildSbomBuilder(
cmSbomArguments const& args, std::vector<cmExportSet*> const& exportSets)
: cmSbomBuilder(args, exportSets)
{
}
bool cmBuildSbomBuilder::Generate(std::ostream& os, std::string const& config)
{
if (!this->LocalGenerator) {
return false;
}
return this->GenerateForTargets(os, config,
cmGeneratorExpression::BuildInterface);
}
@@ -34,9 +27,10 @@ cmExportFileGenerator::ExportInfo cmBuildSbomBuilder::FindExportInfoFor(
->FindBuildExportInfo(target);
}
cmSbomBuilder::SbomInfo cmBuildSbomBuilder::FindSbomInfoFor(
std::vector<std::string> cmBuildSbomBuilder::FindSbomPackagesFor(
cmGeneratorTarget const* target) const
{
return target->GetLocalGenerator()->GetGlobalGenerator()->FindBuildSbomInfo(
target);
return target->GetLocalGenerator()
->GetGlobalGenerator()
->FindBuildSbomPackages(target);
}
+4 -5
View File
@@ -12,7 +12,6 @@
#include "cmSbomBuilder.h"
class cmExportSet;
class cmLocalGenerator;
class cmSbomArguments;
/** Build-tree SBOM (`export(SBOM ...)`). Covers the targets in the
@@ -20,14 +19,14 @@ class cmSbomArguments;
class cmBuildSbomBuilder final : public cmSbomBuilder
{
public:
cmBuildSbomBuilder(cmSbomArguments args,
std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg = nullptr);
cmBuildSbomBuilder(cmSbomArguments const& args,
std::vector<cmExportSet*> const& exportSets);
bool Generate(std::ostream& os, std::string const& config) override;
protected:
cmExportFileGenerator::ExportInfo FindExportInfoFor(
cmGeneratorTarget const* target) const override;
SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override;
std::vector<std::string> FindSbomPackagesFor(
cmGeneratorTarget const* target) const override;
};
+2 -1
View File
@@ -2,6 +2,7 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmBuildSbomGenerator.h"
#include "cmBuildSbomBuilder.h"
#include "cmGeneratedFileStream.h"
#include "cmStringAlgorithms.h"
@@ -15,5 +16,5 @@ bool cmBuildSbomGenerator::GenerateForBuild(std::string const& config)
cmGeneratedFileStream os(cmStrCat(this->OutputFile, '-',
config.empty() ? "noconfig" : config,
".spdx.json"));
return this->Builder->Generate(os, config);
return this->GenerateForBuild(os, config);
}
+8 -9
View File
@@ -10,28 +10,27 @@
#include <cm/memory>
#include "cmBuildSbomBuilder.h"
#include "cmSbomArguments.h"
class cmExportSet;
class cmGeneratorTarget;
class cmLocalGenerator;
class cmSbomArguments;
/** \class cmBuildSbomGenerator
* \brief Thin wrapper around cmBuildSbomBuilder for the build-tree SBOM case.
* \brief Generate build-tree SBOM files, one document per configuration.
*
* Stored on cmMakefile at configure time. At generate time,
* ComputeBuildFileGenerators() calls Compute(lg) to resolve export set
* targets and supply the local generator - mirroring
* cmExportBuildFileGenerator.
* targets for peer SBOM lookups before any documents are generated.
*/
class cmBuildSbomGenerator
{
public:
cmBuildSbomGenerator(cmSbomArguments args,
std::vector<cmExportSet*> exportSets,
cmBuildSbomGenerator(cmSbomArguments const& args,
std::vector<cmExportSet*> const& exportSets,
std::string outputFile)
: OutputFile(std::move(outputFile))
, Builder(cm::make_unique<cmBuildSbomBuilder>(std::move(args),
std::move(exportSets)))
, Builder(cm::make_unique<cmBuildSbomBuilder>(args, exportSets))
{
}
@@ -60,6 +59,6 @@ public:
bool GenerateForBuild(std::string const& config);
private:
std::string OutputFile;
std::string const OutputFile;
std::unique_ptr<cmBuildSbomBuilder> Builder;
};
+3 -2
View File
@@ -442,7 +442,7 @@ static bool HandleSbomMode(std::vector<std::string> const& args,
std::string const dir =
arguments.GetDefaultDestination(mf.GetCurrentBinaryDirectory());
std::string const fpath = cmStrCat(dir, '/', arguments.GetPackageName());
std::string fpath = cmStrCat(dir, '/', arguments.GetPackageName());
if (gg->IsBuildSbomFile(fpath)) {
status.SetError(cmStrCat("SBOM command already specified for the file "_s,
@@ -461,7 +461,8 @@ static bool HandleSbomMode(std::vector<std::string> const& args,
sets.push_back(*exportSet);
}
auto builder = cm::make_unique<cmBuildSbomGenerator>(arguments, sets, fpath);
auto builder =
cm::make_unique<cmBuildSbomGenerator>(arguments, sets, std::move(fpath));
cmBuildSbomGenerator* rawPtr = builder.get();
mf.AddBuildSbomGenerator(std::move(builder));
gg->AddBuildSbomGenerator(rawPtr);
+20 -13
View File
@@ -37,6 +37,7 @@
#include "cmCustomCommandTypes.h"
#include "cmDiagnostics.h"
#include "cmDuration.h"
#include "cmExecutionStatus.h"
#include "cmExperimental.h"
#include "cmExportBuildFileGenerator.h"
#include "cmExternalMakefileProjectGenerator.h"
@@ -361,30 +362,30 @@ cmExportFileGenerator::ExportInfo cmGlobalGenerator::FindInstallExportInfo(
}
#ifndef CMAKE_BOOTSTRAP
cmSbomBuilder::SbomInfo cmGlobalGenerator::FindBuildSbomInfo(
std::vector<std::string> cmGlobalGenerator::FindBuildSbomPackages(
cmGeneratorTarget const* target) const
{
cmSbomBuilder::SbomInfo info;
std::vector<std::string> packages;
for (cmBuildSbomGenerator const* g : this->BuildSbomGenerators) {
if (g->CoversTarget(target)) {
info.Packages.push_back(g->GetPackageName());
packages.push_back(g->GetPackageName());
}
}
std::sort(info.Packages.begin(), info.Packages.end());
return info;
std::sort(packages.begin(), packages.end());
return packages;
}
cmSbomBuilder::SbomInfo cmGlobalGenerator::FindInstallSbomInfo(
std::vector<std::string> cmGlobalGenerator::FindInstallSbomPackages(
cmGeneratorTarget const* target) const
{
cmSbomBuilder::SbomInfo info;
std::vector<std::string> packages;
for (cmInstallSbomGenerator const* g : this->InstallSbomGenerators) {
if (g->CoversTarget(target)) {
info.Packages.push_back(g->GetPackageName());
packages.push_back(g->GetPackageName());
}
}
std::sort(info.Packages.begin(), info.Packages.end());
return info;
std::sort(packages.begin(), packages.end());
return packages;
}
#endif
@@ -1834,7 +1835,7 @@ bool cmGlobalGenerator::Compute()
return false;
}
std::string projectName = this->LocalGenerators[0]->GetProjectName();
std::string const projectName = this->LocalGenerators[0]->GetProjectName();
for (auto& exportSet : this->ExportSets) {
bool isCovered =
std::any_of(this->InstallSbomGenerators.cbegin(),
@@ -1850,14 +1851,20 @@ bool cmGlobalGenerator::Compute()
args.Format = *sbomFormat;
args.ProjectName = projectName;
args.PackageName = exportSet.first;
cmExecutionStatus status(*this->Makefiles[0]);
if (!args.SetMetadataFromProject(status)) {
this->Makefiles[0]->IssueMessage(MessageType::FATAL_ERROR,
status.GetError());
return false;
}
std::string dest = args.GetDefaultDestination(
cm::InstallDirs::GetLibraryDirectory(this->Makefiles[0].get()));
auto installGen = cm::make_unique<cmInstallSbomGenerator>(
std::vector<cmExportSet*>{ &exportSet.second }, dest, "",
std::vector<cmExportSet*>{ &exportSet.second }, std::move(dest), "",
std::vector<std::string>(), "",
cmInstallGenerator::SelectMessageLevel(this->Makefiles[0].get()),
false, std::move(args),
false, args,
cmInstallGenerator::CaptureContext(this->Makefiles[0].get()));
cmInstallSbomGenerator const* rawPtr = installGen.get();
+3 -4
View File
@@ -28,7 +28,6 @@
#include "cmExportSet.h"
#include "cmInstallGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSbomBuilder.h"
#include "cmStateSnapshot.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -731,11 +730,11 @@ public:
cmGeneratorTarget const* target) const;
/** Scan all build-tree SBOMs and report which of them cover `target`. */
cmSbomBuilder::SbomInfo FindBuildSbomInfo(
std::vector<std::string> FindBuildSbomPackages(
cmGeneratorTarget const* target) const;
/** Same as FindBuildSbomInfo, but searches install-tree SBOMs. */
cmSbomBuilder::SbomInfo FindInstallSbomInfo(
/** Same as FindBuildSbomPackages, but searches install-tree SBOMs. */
std::vector<std::string> FindInstallSbomPackages(
cmGeneratorTarget const* target) const;
void AddBuildExportSet(cmExportBuildFileGenerator* gen);
void AddBuildExportExportSet(cmExportBuildFileGenerator* gen);
+2 -2
View File
@@ -2898,8 +2898,8 @@ bool HandleSbomMode(std::vector<std::string> const& args,
// Create the SBOM install generator.
auto sbomGen = cm::make_unique<cmInstallSbomGenerator>(
std::move(sets), dest, ica.GetPermissions(), ica.GetConfigurations(),
ica.GetComponent(), message, ica.GetExcludeFromAll(), std::move(arguments),
sets, std::move(dest), ica.GetPermissions(), ica.GetConfigurations(),
ica.GetComponent(), message, ica.GetExcludeFromAll(), arguments,
helper.CaptureContext());
cmInstallSbomGenerator const* rawPtr = sbomGen.get();
helper.Makefile->AddInstallGenerator(std::move(sbomGen));
+4 -11
View File
@@ -2,27 +2,20 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmInstallSbomBuilder.h"
#include <utility>
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSbomArguments.h"
cmInstallSbomBuilder::cmInstallSbomBuilder(
cmSbomArguments args, std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg)
: cmSbomBuilder(std::move(args), std::move(exportSets), lg)
cmSbomArguments const& args, std::vector<cmExportSet*> const& exportSets)
: cmSbomBuilder(args, exportSets)
{
}
bool cmInstallSbomBuilder::Generate(std::ostream& os,
std::string const& config)
{
if (!this->LocalGenerator) {
return false;
}
return this->GenerateForTargets(os, config,
cmGeneratorExpression::InstallInterface);
}
@@ -35,10 +28,10 @@ cmExportFileGenerator::ExportInfo cmInstallSbomBuilder::FindExportInfoFor(
->FindInstallExportInfo(target);
}
cmSbomBuilder::SbomInfo cmInstallSbomBuilder::FindSbomInfoFor(
std::vector<std::string> cmInstallSbomBuilder::FindSbomPackagesFor(
cmGeneratorTarget const* target) const
{
return target->GetLocalGenerator()
->GetGlobalGenerator()
->FindInstallSbomInfo(target);
->FindInstallSbomPackages(target);
}
+4 -5
View File
@@ -12,7 +12,6 @@
#include "cmSbomBuilder.h"
class cmExportSet;
class cmLocalGenerator;
class cmSbomArguments;
/** Install-tree SBOM (`install(SBOM ...)`). Covers the targets in the
@@ -20,14 +19,14 @@ class cmSbomArguments;
class cmInstallSbomBuilder final : public cmSbomBuilder
{
public:
cmInstallSbomBuilder(cmSbomArguments args,
std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg = nullptr);
cmInstallSbomBuilder(cmSbomArguments const& args,
std::vector<cmExportSet*> const& exportSets);
bool Generate(std::ostream& os, std::string const& config) override;
protected:
cmExportFileGenerator::ExportInfo FindExportInfoFor(
cmGeneratorTarget const* target) const override;
SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override;
std::vector<std::string> FindSbomPackagesFor(
cmGeneratorTarget const* target) const override;
};
+12 -9
View File
@@ -15,25 +15,24 @@
#include "cmInstallType.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSbomArguments.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
cmInstallSbomGenerator::cmInstallSbomGenerator(
std::vector<cmExportSet*> exportSets, std::string destination,
std::vector<cmExportSet*> const& exportSets, std::string destination,
std::string filePermissions, std::vector<std::string> const& configurations,
std::string component, MessageLevel message, bool excludeFromAll,
cmSbomArguments args, cmDiagnosticContext context)
cmSbomArguments const& args, cmDiagnosticContext context)
: cmInstallGenerator(std::move(destination), configurations,
std::move(component), message, excludeFromAll, false,
std::move(context))
, FilePermissions(std::move(filePermissions))
, SbomFileName(args.GetPackageName())
, SbomFilePath(cmStrCat(this->Destination, '/', this->SbomFileName))
, SbomFormat(args.GetFormat())
, Builder(cm::make_unique<cmInstallSbomBuilder>(std::move(args),
std::move(exportSets)))
, Builder(cm::make_unique<cmInstallSbomBuilder>(args, exportSets))
{
}
@@ -42,8 +41,7 @@ cmInstallSbomGenerator::~cmInstallSbomGenerator() = default;
bool cmInstallSbomGenerator::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
this->Builder->Compute(lg);
return true;
return this->Builder->Compute(lg);
}
bool cmInstallSbomGenerator::CoversTarget(
@@ -78,10 +76,15 @@ void cmInstallSbomGenerator::GenerateScript(std::ostream& os)
cmStrCat(tempDir, '/', this->SbomFileName, '-',
c.empty() ? "noconfig" : c, ".spdx.json");
cmGeneratedFileStream sbomStream(configName);
this->TempSbomFiles.emplace(c, configName);
if (!this->Builder->Generate(sbomStream, c)) {
break;
if (!cmSystemTools::GetErrorOccurredFlag()) {
this->LocalGenerator->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Could not write SBOM file: ", configName));
}
return;
}
this->TempSbomFiles.emplace(c, std::move(configName));
}
// Emit the cmake_install.cmake script to copy the file at install time.
+5 -7
View File
@@ -11,30 +11,29 @@
#include <vector>
#include "cmInstallGenerator.h"
#include "cmSbomArguments.h"
class cmDiagnosticContext;
class cmExportSet;
class cmGeneratorTarget;
class cmInstallSbomBuilder;
class cmLocalGenerator;
class cmSbomArguments;
/** \class cmInstallSbomGenerator
* \brief Generate installation rules for SBOM files.
*
* Thin cmInstallGenerator subclass that owns a cmInstallSbomBuilder.
* At generate time it writes the SBOM into a temporary file in the build tree;
* At generate time it builds each configuration's SBOM in a temporary file;
* the cmake_install.cmake script then copies that file to the install
* destination.
*/
class cmInstallSbomGenerator : public cmInstallGenerator
{
public:
cmInstallSbomGenerator(std::vector<cmExportSet*> exportSets,
cmInstallSbomGenerator(std::vector<cmExportSet*> const& exportSets,
std::string destination, std::string filePermissions,
std::vector<std::string> const& configurations,
std::string component, MessageLevel message,
bool excludeFromAll, cmSbomArguments args,
bool excludeFromAll, cmSbomArguments const& args,
cmDiagnosticContext context);
cmInstallSbomGenerator(cmInstallSbomGenerator const&) = delete;
~cmInstallSbomGenerator() override;
@@ -64,7 +63,6 @@ private:
std::string const FilePermissions;
std::string const SbomFileName;
std::string const SbomFilePath;
cmSbomArguments::SbomFormat SbomFormat;
cmLocalGenerator* LocalGenerator = nullptr;
cmLocalGenerator const* LocalGenerator = nullptr;
std::unique_ptr<cmInstallSbomBuilder> Builder;
};
+25 -36
View File
@@ -8,41 +8,21 @@
#include "cmGeneratorExpression.h"
#include "cmStringAlgorithms.h"
struct SbomFormatPattern
namespace {
struct SbomFormatEntry
{
cm::string_view Name;
cm::string_view Alias;
cmSbomArguments::SbomFormat Id;
cmSbomArguments::SbomFormat Format;
};
static SbomFormatPattern SbomPatterns[] = {
{ "spdx-3.0+json", "spdx", cmSbomArguments::SbomFormat::SPDX_3_0_JSON },
SbomFormatEntry const SbomFormats[] = {
{ "spdx"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON },
{ "spdx-3.0+json"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON },
{ "spdx-3.0.1+json"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON },
};
cmSbomArguments::SbomFormat ParseSbomFormat(std::string const& input)
{
if (input.empty()) {
return cmSbomArguments::SbomFormat::SPDX_3_0_JSON;
}
cm::string_view s(input);
for (auto const& p : SbomPatterns) {
if (s == p.Name || (!p.Alias.empty() && s == p.Alias)) {
return p.Id;
}
}
return cmSbomArguments::SbomFormat::NONE;
}
std::string GetSbomFileExtension(cmSbomArguments::SbomFormat id)
{
switch (id) {
case cmSbomArguments::SbomFormat::SPDX_3_0_JSON:
return ".spdx.json";
default:
return "";
}
}
} // namespace
template void cmSbomArguments::Bind<void>(cmArgumentParser<void>&,
cmSbomArguments*);
@@ -58,12 +38,10 @@ bool cmSbomArguments::Check(cmExecutionStatus& status) const
}
}
if (!this->Format.empty()) {
if (this->GetFormat() == SbomFormat::NONE) {
status.SetError(
cmStrCat(R"(SBOM given invalid format ")"_s, this->Format, R"(".)"_s));
return false;
}
if (this->GetFormat() == SbomFormat::NONE) {
status.SetError(
cmStrCat(R"(SBOM given invalid format ")"_s, this->Format, R"(".)"_s));
return false;
}
if (!this->License.empty()) {
@@ -91,6 +69,11 @@ std::string cmSbomArguments::GetPackageName() const
return this->PackageName;
}
std::string const& cmSbomArguments::GetDataLicense() const
{
return this->DataLicense.empty() ? this->License : this->DataLicense;
}
std::string cmSbomArguments::GetDefaultDestination(
std::string const& root) const
{
@@ -105,5 +88,11 @@ cmSbomArguments::SbomFormat cmSbomArguments::GetFormat() const
if (this->Format.empty()) {
return SbomFormat::SPDX_3_0_JSON;
}
return ParseSbomFormat(this->Format);
for (auto const& entry : SbomFormats) {
if (this->Format == entry.Name) {
return entry.Format;
}
}
return SbomFormat::NONE;
}
+3
View File
@@ -36,6 +36,9 @@ public:
bool Check(cmExecutionStatus& status) const override;
std::string GetNamespace() const;
std::string GetPackageName() const;
/** Explicit data license, or the license inherited from the selected
* project. */
std::string const& GetDataLicense() const;
std::string GetDefaultDestination(std::string const& root = {}) const;
SbomFormat GetFormat() const;
+311 -412
View File
@@ -2,9 +2,8 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmSbomBuilder.h"
#include <algorithm>
#include <array>
#include <functional>
#include <cstddef>
#include <initializer_list>
#include <map>
#include <memory>
#include <set>
@@ -14,23 +13,26 @@
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmArgumentParserTypes.h"
#include "cmDiagnostics.h"
#include "cmExportFileGenerator.h"
#include "cmExportSet.h"
#include "cmFindPackageStack.h"
#include "cmGenExContext.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmSbomArguments.h"
#include "cmSbomObject.h"
#include "cmSpdx.h"
#include "cmSpdxSerializer.h"
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
@@ -38,7 +40,7 @@
#include "cmTargetTypes.h"
#include "cmValue.h"
cmSpdxPackage::PurposeId GetPurpose(cm::TargetType type)
cmSpdxPackage::PurposeId GetPurpose(cm::TargetType const type)
{
switch (type) {
case cm::TargetType::EXECUTABLE:
@@ -58,51 +60,45 @@ cmSpdxPackage::PurposeId GetPurpose(cm::TargetType type)
}
}
cmSbomBuilder::cmSbomBuilder(cmSbomArguments args,
std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg)
: LocalGenerator(lg)
, ExportSets(std::move(exportSets))
, PackageName(std::move(args.PackageName))
, Namespace(cmStrCat(this->PackageName, "::"_s))
, PackageVersion(std::move(args.Version))
, PackageDescription(std::move(args.Description))
, PackageWebsite(std::move(args.Website))
, PackageUrl(std::move(args.PackageUrl))
, DataLicense(std::move(args.DataLicense))
, DefaultLicense(std::move(args.DefaultLicense))
cmSbomBuilder::cmSbomBuilder(cmSbomArguments const& args,
std::vector<cmExportSet*> const& exportSets)
: PackageName(args.GetPackageName())
, PackageVersion(args.Version)
, PackageDescription(args.Description)
, PackageWebsite(args.Website)
, PackageUrl(args.PackageUrl)
, DataLicense(args.GetDataLicense())
, DefaultLicense(args.DefaultLicense)
, NoProjectDefaults(args.NoProjectDefaults)
, PackageFormat(args.GetFormat())
, ExportSets(exportSets)
{
}
bool cmSbomBuilder::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
this->SbomTargets.clear();
for (cmExportSet* exportSet : this->ExportSets) {
if (!exportSet->Compute(lg)) {
return false;
}
}
this->SbomTargets = this->CollectTargets();
return true;
}
std::set<cmGeneratorTarget const*> cmSbomBuilder::CollectTargets() const
{
std::set<cmGeneratorTarget const*> targets;
for (cmExportSet* exportSet : this->ExportSets) {
for (cmExportSet const* exportSet : this->ExportSets) {
for (auto const& te : exportSet->GetTargetExports()) {
if (cmGeneratorTarget const* gt =
this->LocalGenerator->FindGeneratorTargetToUse(te->TargetName)) {
targets.emplace(gt);
}
targets.emplace(te->Target);
}
}
return targets;
}
void cmSbomBuilder::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
if (!lg) {
return;
}
for (cmExportSet* es : this->ExportSets) {
es->Compute(lg);
}
// Populate the cache now (rather than at Generate time) so peer SBOMs can
// query CoversTarget() during their own NoteLinkedTarget walks.
this->SbomTargets = this->CollectTargets();
}
bool cmSbomBuilder::CoversTarget(cmGeneratorTarget const* target) const
{
return cm::contains(this->SbomTargets, target);
@@ -110,56 +106,59 @@ bool cmSbomBuilder::CoversTarget(cmGeneratorTarget const* target) const
bool cmSbomBuilder::CoversExportSet(cmExportSet const* set) const
{
return std::find(this->ExportSets.cbegin(), this->ExportSets.cend(), set) !=
this->ExportSets.cend();
return cm::contains(this->ExportSets, set);
}
bool cmSbomBuilder::GenerateForTargets(
std::ostream& os, std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext)
cmGeneratorExpression::PreprocessContext const preprocessContext)
{
cmSbomDocument doc;
doc.Graph.reserve(512);
if (!this->LocalGenerator) {
return false;
}
cmSpdxCreationInfo const* ci =
insert_back(doc.Graph, this->GenerateCreationInfo());
cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci));
std::vector<TargetProperties> targetProps;
targetProps.reserve(this->SbomTargets.size());
this->Relationships.clear();
this->Pending.clear();
this->LocalPackages.clear();
this->LinkedPackages.clear();
this->LinkTargets.clear();
this->Requirements.clear();
this->CreationInfo = this->GenerateCreationInfo();
this->Project = this->GenerateSbom();
this->Pending.reserve(this->SbomTargets.size());
for (cmGeneratorTarget const* target : this->SbomTargets) {
ImportPropertyMap properties;
this->PopulateLinkLibrariesProperty(target, preprocessContext, properties);
this->PopulateInterfaceLinkLibrariesProperty(target, preprocessContext,
properties);
targetProps.push_back(TargetProperties{
insert_back(project->RootElements,
this->GenerateImportTarget(ci, target)),
target,
std::move(properties),
});
this->GetOrCreateLocalPackage(target);
}
bool status = true;
for (TargetProperties const& target : targetProps) {
status &=
this->GenerateProperties(doc, project, ci, target, targetProps, config);
// GenerateTarget can append to Pending while this loop is running.
// NOLINTNEXTLINE(modernize-loop-convert)
for (std::size_t i = 0; i < this->Pending.size(); ++i) {
auto const* const target = this->Pending[i];
status &= this->GenerateTarget(target, config, preprocessContext);
}
if (status) {
this->WriteSbom(doc, os);
}
return status;
return status && this->WriteSbom(os);
}
void cmSbomBuilder::WriteSbom(cmSbomDocument& doc, std::ostream& os) const
bool cmSbomBuilder::WriteSbom(std::ostream& os) const
{
cmSbomDocument document;
document.Graph.reserve(2 + this->Relationships.size());
document.Graph.emplace_back(this->CreationInfo);
document.Graph.emplace_back(this->Project);
for (auto const& relationship : this->Relationships) {
document.Graph.emplace_back(relationship);
}
switch (this->PackageFormat) {
case cmSbomArguments::SbomFormat::SPDX_3_0_JSON:
cmSpdxSerializer{}.WriteSbom(os, cmSbomObject(doc));
break;
return cmSpdxSerializer{}.WriteSbom(os,
cmSbomObject(std::move(document)));
case cmSbomArguments::SbomFormat::NONE:
break;
}
return false;
}
bool cmSbomBuilder::AddPackageInformation(
@@ -196,6 +195,7 @@ bool cmSbomBuilder::AddPackageInformation(
cmSpdxExternalRef externalRef;
externalRef.Locator = cmStrCat("cmake:find_package(", name, ')');
externalRef.ExternalRefType = "buildSystem";
artifact.ExternalRef.emplace_back(std::move(externalRef));
return true;
}
@@ -210,37 +210,38 @@ cmSpdxCreationInfo cmSbomBuilder::GenerateCreationInfo() const
return ci;
}
cmSpdxDocument cmSbomBuilder::GenerateSbom(cmSpdxCreationInfo const* ci) const
cmSpdxDocument cmSbomBuilder::GenerateSbom() const
{
cmSpdxDocument proj;
proj.Name = PackageName;
proj.SpdxId = cmStrCat("urn:", PackageName, "#SPDXDocument");
proj.Name = this->PackageName;
proj.SpdxId = cmStrCat("urn:", this->PackageName, "#SPDXDocument");
proj.ProfileConformance = { "core", "software" };
proj.CreationInfo = ci;
proj.CreationInfo = &this->CreationInfo;
if (!this->PackageDescription.empty()) {
proj.Description = this->PackageDescription;
}
if (!this->DataLicense.empty()) {
cmSpdxLicenseExpression license;
license.SpdxId = cmStrCat("urn:", PackageName, "#LicenseExpression");
license.CreationInfo = ci;
license.LicenseExpression = this->DataLicense;
proj.DataLicense = license;
cmSpdxLicenseExpression licenseObject;
licenseObject.SpdxId =
cmStrCat("urn:", this->PackageName, "#DataLicenseExpression");
licenseObject.CreationInfo = &this->CreationInfo;
licenseObject.LicenseExpression = this->DataLicense;
proj.DataLicense = std::move(licenseObject);
}
return proj;
}
cmSpdxPackage cmSbomBuilder::GenerateImportTarget(
cmSpdxCreationInfo const* ci, cmGeneratorTarget const* target) const
cmSpdxPackage cmSbomBuilder::GenerateLocalPackage(
cmGeneratorTarget const* target) const
{
cmSpdxPackage package;
package.SpdxId = cmStrCat("urn:", target->GetName(), "#Package");
package.Name = target->GetName();
package.PrimaryPurpose = GetPurpose(target->GetType());
package.CreationInfo = ci;
package.CreationInfo = &this->CreationInfo;
if (!this->PackageVersion.empty()) {
package.PackageVersion = this->PackageVersion;
@@ -257,259 +258,245 @@ cmSpdxPackage cmSbomBuilder::GenerateImportTarget(
return package;
}
bool cmSbomBuilder::GenerateLinkProperties(
cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci,
std::string const& libraries, TargetProperties const& current,
std::vector<TargetProperties> const& allTargets,
std::string const& config) const
cmSpdxPackage const* cmSbomBuilder::GetOrCreateLocalPackage(
cmGeneratorTarget const* target)
{
auto itProp = current.Properties.find(libraries);
if (itProp == current.Properties.end()) {
auto const it = this->LocalPackages.find(target);
if (it != this->LocalPackages.end()) {
return it->second;
}
auto& elements = cm::contains(this->SbomTargets, target)
? this->Project.RootElements
: this->Project.Elements;
auto const* package =
insert_back(elements, this->GenerateLocalPackage(target));
this->LocalPackages.emplace(target, package);
this->Pending.push_back(target);
return package;
}
cmSpdxPackage const* cmSbomBuilder::GetOrCreateLinkedPackage(
cmGeneratorTarget const* source, cmLinkItem const& item)
{
LinkInfo const* info = nullptr;
if (item.Target) {
auto link = this->LinkTargets.find(item.Target);
if (link == this->LinkTargets.end()) {
link =
this->LinkTargets
.emplace(item.Target, this->NoteLinkedTarget(source, item.Target))
.first;
}
info = &link->second;
}
if (info && info->Local) {
return this->GetOrCreateLocalPackage(item.Target);
}
// An empty package name identifies a dependency without provenance.
auto const& name = item.AsStr();
auto key = std::make_pair(info ? info->Package : std::string{},
info ? info->Component : name);
auto const it = this->LinkedPackages.find(key);
if (it != this->LinkedPackages.end()) {
return it->second;
}
std::string const packageName = info && !info->Package.empty()
? cmStrCat(info->Package, ':', info->Component)
: name;
cmSpdxPackage package;
package.Name = packageName;
package.SpdxId = cmStrCat("urn:", packageName, "#Package");
package.CreationInfo = &this->CreationInfo;
if (info) {
auto const requirement = this->Requirements.find(info->Package);
if (requirement != this->Requirements.end()) {
this->AddPackageInformation(package, info->Package, requirement->second);
}
}
auto const* result = insert_back(this->Project.Elements, std::move(package));
this->LinkedPackages.emplace(std::move(key), result);
if (item.Target) {
this->GenerateDeclaredLicense(
result, item.Target->GetSafeProperty("SPDX_LICENSE"));
}
return result;
}
bool cmSbomBuilder::EvaluateLinkProperty(
cmGeneratorTarget const* target, std::string const& property,
std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext,
std::vector<cmLinkItem>& dependencies) const
{
cmValue const input = target->GetProperty(property);
if (!input) {
return true;
}
auto const preprocessed =
cmGeneratorExpression::Preprocess(*input, preprocessContext);
if (preprocessed.empty()) {
return true;
}
cmGeneratorExpression ge(*current.Target->Makefile->GetCMakeInstance());
std::unique_ptr<cmCompiledGeneratorExpression> cge =
ge.Parse(itProp->second);
std::string evaluatedLibraries =
cge->Evaluate(current.Target->GetLocalGenerator(), config, current.Target);
if (cge->GetHadHeadSensitiveCondition()) {
current.Target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Property \"", libraries, "\" of target \"",
current.Target->GetName(),
"\" contains a generator expression that is not allowed for "
"SBOM generation."));
return false;
}
auto makeRel = [&](char const* id, char const* desc) {
cmSpdxRelationship r;
r.SpdxId = cmStrCat("urn:", id, "#Relationship");
r.RelationshipType = cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON;
r.Description = desc;
r.From = current.Package;
r.CreationInfo = ci;
return r;
};
auto linkLibraries = makeRel("Static", "Linked Libraries");
auto linkRequires = makeRel("Dynamic", "Required Runtime Libraries");
auto buildRequires = makeRel("Shared", "Required Build-Time Libraries");
auto addArtifact =
[&](std::string const& name) -> std::pair<bool, cmSpdxPackage const*> {
auto it = this->LinkTargets.find(name);
if (it != this->LinkTargets.end()) {
LinkInfo const& linkInfo = it->second;
if (linkInfo.Package.empty()) {
for (auto const& t : allTargets) {
if (t.Target->GetName() == linkInfo.Component) {
return { true, t.Package };
}
}
cmGeneratorExpression const ge(*target->Makefile->GetCMakeInstance());
auto const cge = ge.Parse(preprocessed);
cm::GenEx::Context const context(target->GetLocalGenerator(), config);
for (auto const usage :
{ cmGeneratorTarget::UseTo::Compile, cmGeneratorTarget::UseTo::Link }) {
cmGeneratorExpressionDAGChecker dagChecker{ target, property, nullptr,
nullptr, context };
if (usage == cmGeneratorTarget::UseTo::Compile) {
dagChecker.SetTransitivePropertiesOnly();
if (property != "LINK_LIBRARIES" ||
target->GetPolicyStatusCMP0131() == cmPolicies::NEW) {
dagChecker.SetTransitivePropertiesOnlyCMP0131();
}
std::string pkgName =
cmStrCat(linkInfo.Package, ':', linkInfo.Component);
cmSpdxPackage pkg;
pkg.Name = pkgName;
pkg.SpdxId = cmStrCat("urn:", pkgName, "#Package");
pkg.CreationInfo = ci;
if (!linkInfo.Package.empty()) {
auto const& pkgIt = this->Requirements.find(linkInfo.Package);
if (pkgIt != this->Requirements.end() &&
pkgIt->second.Components.count(linkInfo.Component) > 0) {
this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second);
}
}
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 };
}
cmList const items{ cge->Evaluate(context, &dagChecker, target) };
if (cge->GetHadHeadSensitiveCondition()) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Property \"", property, "\" of target \"", target->GetName(),
"\" contains a generator expression that is not allowed for "
"SBOM generation."));
return false;
}
cmSpdxPackage pkg;
pkg.SpdxId = cmStrCat("urn:", name, "#Package");
pkg.Name = name;
pkg.CreationInfo = ci;
return { false, insert_back(project->Elements, std::move(pkg)) };
};
cmList names{ evaluatedLibraries };
names.sort();
names.remove_duplicates();
for (std::string const& n : names) {
auto res = addArtifact(n);
if (!res.second) {
continue;
cmLocalGenerator const* scope = target->GetLocalGenerator();
auto feature = cmLinkItem::DEFAULT;
for (auto const& item : items) {
if (auto linkFeature = ParseLinkFeature(item)) {
feature = std::move(*linkFeature);
continue;
}
if ((cmHasPrefix(item, "<LINK_GROUP:") ||
cmHasPrefix(item, "</LINK_GROUP:")) &&
cmHasSuffix(item, '>')) {
continue;
}
if (item.empty() || target->IsLinkLookupScope(item, scope)) {
continue;
}
dependencies.emplace_back(target->ResolveLinkItem(
BT<std::string>(item, cmListFileBacktrace{}), scope, feature));
}
if (res.first) {
linkLibraries.To.push_back(res.second);
} else {
buildRequires.To.push_back(res.second);
}
}
if (!linkLibraries.To.empty()) {
insert_back(doc.Graph, std::move(linkLibraries));
}
if (!linkRequires.To.empty()) {
insert_back(doc.Graph, std::move(linkRequires));
}
if (!buildRequires.To.empty()) {
insert_back(doc.Graph, std::move(buildRequires));
}
return true;
}
bool cmSbomBuilder::GenerateProperties(
cmSbomDocument& doc, cmSpdxDocument* proj, cmSpdxCreationInfo const* ci,
TargetProperties const& current,
std::vector<TargetProperties> const& allTargets,
std::string const& config) const
bool cmSbomBuilder::GenerateTarget(
cmGeneratorTarget const* target, std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext)
{
bool status = true;
status &= this->GenerateLinkProperties(doc, proj, ci, "LINK_LIBRARIES",
current, allTargets, config);
status &= this->GenerateLinkProperties(
doc, proj, ci, "INTERFACE_LINK_LIBRARIES", current, allTargets, config);
status &= this->GenerateMetaProperties(doc, proj, ci, current);
auto const* package = this->LocalPackages.at(target);
std::vector<cmLinkItem> dependencies;
bool status = this->EvaluateLinkProperty(target, "LINK_LIBRARIES", config,
preprocessContext, dependencies);
status &=
this->EvaluateLinkProperty(target, "INTERFACE_LINK_LIBRARIES", config,
preprocessContext, dependencies);
cmSpdxRelationship staticLibraries;
cmSpdxRelationship sharedLibraries;
std::set<cmSpdxPackage const*> seen;
for (auto const& item : dependencies) {
auto const* dependency = this->GetOrCreateLinkedPackage(target, item);
if (!seen.insert(dependency).second) {
continue;
}
auto const type =
item.Target ? item.Target->GetType() : cm::TargetType::UNKNOWN_LIBRARY;
switch (type) {
case cm::TargetType::INTERFACE_LIBRARY:
case cm::TargetType::STATIC_LIBRARY:
staticLibraries.To.emplace_back(dependency);
break;
case cm::TargetType::SHARED_LIBRARY:
case cm::TargetType::MODULE_LIBRARY:
sharedLibraries.To.emplace_back(dependency);
break;
default:
break;
}
}
auto const addRelationship = [&](cmSpdxRelationship& relationship,
char const* id, char const* description) {
if (relationship.To.empty()) {
return;
}
relationship.SpdxId =
cmStrCat("urn:", *package->Name, '#', id, "Relationship");
relationship.RelationshipType = cmSpdxRelationship::DEPENDS_ON;
relationship.Description = description;
relationship.From = package;
relationship.CreationInfo = &this->CreationInfo;
this->Relationships.emplace_back(std::move(relationship));
};
addRelationship(staticLibraries, "Static", "Required Build-Time Libraries");
addRelationship(sharedLibraries, "Shared", "Required Run-Time Libraries");
cmValue license = target->GetProperty("SPDX_LICENSE");
if (!license && !this->DefaultLicense.empty()) {
license = cmValue(this->DefaultLicense);
}
if (!license && !this->NoProjectDefaults) {
cmMakefile const* mf = target->Target->GetMakefile();
std::string const projectName = mf->GetStateSnapshot().GetProjectName();
license = mf->GetDefinition(cmStrCat(projectName, "_SPDX_LICENSE"));
}
this->GenerateDeclaredLicense(package, *license);
return status;
}
bool cmSbomBuilder::GenerateMetaProperties(
cmSbomDocument& doc, cmSpdxDocument* /*project*/,
cmSpdxCreationInfo const* ci, TargetProperties const& current) const
void cmSbomBuilder::GenerateDeclaredLicense(cmSpdxPackage const* package,
std::string const& license)
{
std::string licenseExpr = this->DefaultLicense;
cmValue licenseExprProp = current.Target->GetProperty("SPDX_LICENSE");
if (licenseExprProp) {
licenseExpr = licenseExprProp;
if (license.empty()) {
return;
}
if (!licenseExpr.empty()) {
auto const& tgtName = current.Target->GetName();
cmSpdxLicenseExpression expression;
expression.SpdxId = cmStrCat("urn:", *package->Name, "#LicenseExpression");
expression.CreationInfo = &this->CreationInfo;
expression.LicenseExpression = license;
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;
cmSpdxRelationship relationship;
relationship.SpdxId =
cmStrCat("urn:", *package->Name, "#DeclaredLicenseRelationship");
relationship.CreationInfo = &this->CreationInfo;
relationship.RelationshipType = cmSpdxRelationship::HAS_DECLARED_LICENSE;
relationship.From = package;
relationship.To.emplace_back(std::move(expression));
this->Relationships.emplace_back(std::move(relationship));
}
bool cmSbomBuilder::PopulateLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
cmSbomBuilder::LinkInfo cmSbomBuilder::NoteLinkedTarget(
cmGeneratorTarget const* target, cmGeneratorTarget const* linkedTarget)
{
static std::array<std::string, 3> const linkIfaceProps = { {
"LINK_LIBRARIES",
"LINK_LIBRARIES_DIRECT",
"LINK_LIBRARIES_DIRECT_EXCLUDE",
} };
bool hadLINK_LIBRARIES = false;
for (std::string const& linkIfaceProp : linkIfaceProps) {
if (cmValue input = target->GetProperty(linkIfaceProp)) {
std::string prepro =
cmGeneratorExpression::Preprocess(*input, preprocessRule);
if (!prepro.empty()) {
this->ResolveTargetsInGeneratorExpressions(prepro, target);
properties[linkIfaceProp] = prepro;
hadLINK_LIBRARIES = true;
}
}
}
return hadLINK_LIBRARIES;
}
bool cmSbomBuilder::AddTargetNamespace(std::string& input,
cmGeneratorTarget const* target,
cmLocalGenerator const* lg)
{
cmGeneratorTarget::TargetOrString resolved =
target->ResolveTargetReference(input, lg);
cmGeneratorTarget* tgt = resolved.Target;
if (!tgt) {
input = resolved.String;
return false;
}
if (tgt->IsImported()) {
input = tgt->GetName();
return this->NoteLinkedTarget(target, input, tgt);
}
if (this->SbomTargets.find(tgt) != this->SbomTargets.end()) {
input = this->Namespace + tgt->GetExportName();
} else {
input = tgt->GetName();
}
return this->NoteLinkedTarget(target, input, tgt);
}
bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target,
std::string const& linkedName,
cmGeneratorTarget const* linkedTarget)
{
auto linkedLicense = linkedTarget->GetSafeProperty("SPDX_LICENSE");
auto const& linkedName = linkedTarget->GetName();
if (cm::contains(this->SbomTargets, linkedTarget)) {
this->LinkTargets.emplace(
linkedName,
LinkInfo{ "", linkedTarget->GetExportName(), linkedLicense });
return true;
return { "", linkedTarget->GetExportName(), true };
}
if (linkedTarget->IsImported()) {
using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
auto pkgInfo = [](cmTarget* t) -> Package {
cmFindPackageStack pkgStack = t->GetFindPackageStack();
auto pkgInfo = [](cmTarget const* t) -> Package {
cmFindPackageStack const& pkgStack = t->GetFindPackageStack();
if (!pkgStack.Empty()) {
return std::make_pair(pkgStack.Top().Name,
*pkgStack.Top().PackageInfo);
}
std::string const pkgName =
t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME");
std::string pkgName = t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME");
if (pkgName.empty()) {
return cm::nullopt;
}
cmPackageInformation package;
return std::make_pair(pkgName, package);
return std::make_pair(std::move(pkgName), cmPackageInformation{});
}(linkedTarget->Target);
if (!pkgInfo) {
@@ -518,28 +505,23 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target,
cmStrCat("Target \"", target->GetName(),
"\" references imported target \"", linkedName,
"\" which does not come from any known package."));
return false;
return { "", linkedName };
}
std::string const& pkgName = pkgInfo->first;
auto const& prefix = cmStrCat(pkgName, "::");
std::string component;
if (!cmHasPrefix(linkedName, prefix)) {
component = linkedName;
} else {
component = linkedName.substr(prefix.length());
}
this->LinkTargets.emplace(linkedName,
LinkInfo{ pkgName, component, linkedLicense });
auto const prefix = cmStrCat(pkgName, "::");
std::string component = cmHasPrefix(linkedName, prefix)
? linkedName.substr(prefix.length())
: linkedName;
LinkInfo info{ pkgName, component };
cmPackageInformation& req =
this->Requirements.insert(std::move(*pkgInfo)).first->second;
req.Components.emplace(std::move(component));
return true;
return info;
}
// Target belongs to another export from this build or install.
// The leaf class chooses which export map to consult.
auto const& exportInfo = this->FindExportInfoFor(linkedTarget);
// The leaf class selects build or install export provenance.
auto const exportInfo = this->FindExportInfoFor(linkedTarget);
if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
auto const& linkNamespace = *exportInfo.Namespaces.begin();
if (!cmHasSuffix(linkNamespace, "::")) {
@@ -550,44 +532,26 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target,
"\", whose export does not use the standard namespace "
"separator. The dependency will be recorded by its bare "
"target name without provenance."));
return false;
return { "", linkedName };
}
std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
std::string component = linkedTarget->GetExportName();
if (pkgName == this->GetPackageName()) {
this->LinkTargets.emplace(linkedName,
LinkInfo{ "", component, linkedLicense });
} else {
this->LinkTargets.emplace(linkedName,
LinkInfo{ pkgName, component, linkedLicense });
this->Requirements[pkgName].Components.emplace(std::move(component));
if (pkgName == this->PackageName) {
return { "", std::move(component), true };
}
return true;
this->Requirements[pkgName].Components.emplace(component);
return { std::move(pkgName), std::move(component) };
}
if (exportInfo.Sets.empty()) {
// install(export) provenance is unavailable. Fall back to SBOM-level
// attribution: any peer SBOM (of the same build/install mode) that
// covers this target lends its package name. install(export) wins
// when both are available; only reached here when install(export) is
// absent.
auto const& sbomInfo = this->FindSbomInfoFor(linkedTarget);
if (sbomInfo.Packages.empty()) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Target \"", target->GetName(), "\" references target \"",
linkedName,
"\" which has no install(EXPORT)/export(EXPORT) namespace "
"and is not covered by any SBOM. An SBOM cannot attribute "
"this dependency. Give \"",
linkedTarget->GetName(),
"\" an install(EXPORT)/export(EXPORT) with a NAMESPACE, or "
"include it in an install(SBOM)/export(SBOM)."));
return false;
// A peer SBOM supplies provenance when no export namespace is available.
auto const sbomPackages = this->FindSbomPackagesFor(linkedTarget);
if (sbomPackages.empty()) {
return { "", linkedName, true };
}
std::string const& pkgName = sbomInfo.Packages.front();
if (sbomInfo.Packages.size() > 1) {
auto const& pkgName = sbomPackages.front();
if (sbomPackages.size() > 1) {
target->Makefile->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
cmStrCat(
@@ -595,14 +559,12 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target,
linkedName,
"\" which has no install(EXPORT)/export(EXPORT) namespace and is "
"covered by multiple SBOMs: ",
cmJoin(sbomInfo.Packages, ", "), ". Attributing to \"", pkgName,
cmJoin(sbomPackages, ", "), ". Attributing to \"", pkgName,
"\" (first alphabetically)."));
}
std::string component = linkedTarget->GetExportName();
this->LinkTargets.emplace(linkedName,
LinkInfo{ pkgName, component, linkedLicense });
this->Requirements[pkgName].Components.emplace(std::move(component));
return true;
this->Requirements[pkgName].Components.emplace(component);
return { pkgName, std::move(component) };
}
target->Makefile->IssueMessage(
@@ -619,68 +581,5 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target,
"export set or with more than one namespace. Consider "
"consolidating the exports of the \"",
linkedTarget->GetName(), "\" target to a single export."));
return false;
}
void cmSbomBuilder::ResolveTargetsInGeneratorExpression(
std::string& input, cmGeneratorTarget const* target,
cmLocalGenerator const* lg)
{
auto err = cmResolveTargetsInGeneratorExpression(
input, [this, target, lg](std::string& name) {
return this->AddTargetNamespace(name, target, lg);
});
if (err) {
target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err);
}
}
void cmSbomBuilder::ResolveTargetsInGeneratorExpressions(
std::string& input, cmGeneratorTarget const* target)
{
cmLocalGenerator const* lg = target->GetLocalGenerator();
std::vector<std::string> parts;
cmGeneratorExpression::Split(input, parts);
std::string sep;
input.clear();
for (std::string& li : parts) {
if (target->IsLinkLookupScope(li, lg)) {
continue;
}
if (cmGeneratorExpression::Find(li) == std::string::npos) {
this->AddTargetNamespace(li, target, lg);
} else {
this->ResolveTargetsInGeneratorExpression(li, target, lg);
}
input = cmStrCat(std::move(input), sep, li);
sep = ";";
}
}
bool cmSbomBuilder::PopulateInterfaceLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
if (!target->IsLinkable()) {
return false;
}
static std::array<std::string, 3> const linkIfaceProps = {
{ "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT",
"INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" }
};
bool hadINTERFACE_LINK_LIBRARIES = false;
for (std::string const& linkIfaceProp : linkIfaceProps) {
if (cmValue input = target->GetProperty(linkIfaceProp)) {
std::string prepro =
cmGeneratorExpression::Preprocess(*input, preprocessRule);
if (!prepro.empty()) {
this->ResolveTargetsInGeneratorExpressions(prepro, target);
properties[linkIfaceProp] = prepro;
hadINTERFACE_LINK_LIBRARIES = true;
}
}
}
return hadINTERFACE_LINK_LIBRARIES;
return { "", linkedName };
}
+77 -121
View File
@@ -8,32 +8,31 @@
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "cmExportFileGenerator.h"
#include "cmFindPackageStack.h"
#include "cmGeneratorExpression.h"
#include "cmSbomArguments.h"
#include "cmSpdx.h"
class cmExportSet;
class cmGeneratorTarget;
class cmLocalGenerator;
struct cmSbomDocument;
struct cmSpdxDocument;
struct cmSpdxPackage;
struct cmSpdxCreationInfo;
class cmLinkItem;
/** \class cmSbomBuilder
* \brief Abstract base for SBOM document generators.
* \brief Builds SBOM documents for the targets in a set of exports.
*
* Concrete leaves (cmBuildSbomBuilder, cmInstallSbomBuilder) supply two
* pure virtuals:
* Concrete leaves (cmBuildSbomBuilder, cmInstallSbomBuilder) supply the
* mode-specific operations:
*
* - Generate(): selects the generator-expression preprocess context
* (BuildInterface vs InstallInterface) and delegates to the shared
* GenerateForTargets() body.
* - Generate(): selects the generator-expression preprocess context and
* delegates to GenerateForTargets().
* - FindExportInfoFor(): consults the mode's export map (build-tree vs
* install-tree) to resolve cross-export references.
* - FindSbomPackagesFor(): finds peer SBOMs that cover a dependency.
*
* All shared SPDX assembly, link-graph walking, and serialization lives
* here so that the leaves stay trivial.
@@ -41,41 +40,32 @@ struct cmSpdxCreationInfo;
class cmSbomBuilder
{
public:
cmSbomBuilder(cmSbomBuilder const&) = delete;
cmSbomBuilder& operator=(cmSbomBuilder const&) = delete;
virtual ~cmSbomBuilder() = default;
/** Compute phase: wire the local generator, run Compute() on the owned
* export sets, and populate the SbomTargets cache so peer SBOMs can
* query CoversTarget() before any Generate() runs. */
void Compute(cmLocalGenerator* lg);
/** Resolve export sets and collect explicit targets for peer SBOM lookups
* before any documents are generated. */
bool Compute(cmLocalGenerator* lg);
/** Produce the SBOM document on `os`. Implementations select their own
* target set (via CollectTargets) and preprocess context. */
/** Produce one configuration's document on `os`. */
virtual bool Generate(std::ostream& os, std::string const& config) = 0;
/** Identifier this SBOM publishes itself as (the SPDX document name and
* the namespace under which other SBOMs refer to its contents). */
std::string const& GetPackageName() const { return this->PackageName; }
/** True if `target` is one of the targets this SBOM directly describes. */
/** True if `target` is explicitly included through an export set. */
bool CoversTarget(cmGeneratorTarget const* target) const;
bool CoversExportSet(cmExportSet const* set) const;
void AddConfiguration(std::string const& config)
{
this->Configurations.push_back(config);
}
/** Names of peer SBOMs (same build/install mode) that cover a target.
* Used by NoteLinkedTarget to attribute a cross-reference when no
* install(export) namespace is available. Sorted alphabetically. */
struct SbomInfo
{
std::vector<std::string> Packages;
};
protected:
cmSbomBuilder(cmSbomArguments args, std::vector<cmExportSet*> exportSets,
cmLocalGenerator* lg);
cmSbomBuilder(cmSbomArguments const& args,
std::vector<cmExportSet*> const& exportSets);
std::set<cmGeneratorTarget const*> CollectTargets() const;
bool GenerateForTargets(
std::ostream& os, std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext);
/** Mode-specific: where does `target` appear in the project's exports?
* Build leaves consult cmGlobalGenerator::FindBuildExportInfo;
@@ -83,111 +73,77 @@ protected:
virtual cmExportFileGenerator::ExportInfo FindExportInfoFor(
cmGeneratorTarget const* target) const = 0;
/** Mode-specific: which peer SBOMs cover `target`?
* Build leaves consult cmGlobalGenerator::FindBuildSbomInfo;
* install leaves consult cmGlobalGenerator::FindInstallSbomInfo. */
virtual SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const = 0;
/** The set of targets the SBOM directly describes - derived from the
* associated export sets. */
std::set<cmGeneratorTarget const*> CollectTargets() const;
/** Generate an sbom for the targets in this->SbomTargets. Each leaf class
* calls this internally */
bool GenerateForTargets(
std::ostream& os, std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext);
using ImportPropertyMap = std::map<std::string, std::string>;
struct TargetProperties
{
cmSpdxPackage const* Package;
cmGeneratorTarget const* Target;
ImportPropertyMap Properties;
};
void WriteSbom(cmSbomDocument& doc, std::ostream& os) const;
cmSpdxCreationInfo GenerateCreationInfo() const;
cmSpdxDocument GenerateSbom(cmSpdxCreationInfo const* ci) const;
cmSpdxPackage GenerateImportTarget(cmSpdxCreationInfo const* ci,
cmGeneratorTarget const* target) const;
bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name,
cmPackageInformation const& package) const;
bool GenerateProperties(cmSbomDocument& doc, cmSpdxDocument* project,
cmSpdxCreationInfo const* ci,
TargetProperties const& current,
std::vector<TargetProperties> const& allTargets,
std::string const& config) const;
bool GenerateLinkProperties(cmSbomDocument& doc, cmSpdxDocument* project,
cmSpdxCreationInfo const* ci,
std::string const& libraries,
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,
cmGeneratorTarget const* linkedTarget);
bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
cmLocalGenerator const* lg);
void ResolveTargetsInGeneratorExpression(std::string& input,
cmGeneratorTarget const* target,
cmLocalGenerator const* lg);
void ResolveTargetsInGeneratorExpressions(std::string& input,
cmGeneratorTarget const* target);
bool PopulateInterfaceLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties);
bool PopulateLinkLibrariesProperty(cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext,
ImportPropertyMap& properties);
// Set at construction or via setters
cmLocalGenerator* LocalGenerator = nullptr;
// Inputs
std::vector<cmExportSet*> ExportSets;
/** Names of peer SBOMs in this mode that cover `target`, sorted
* alphabetically. Used when no export namespace is available. */
virtual std::vector<std::string> FindSbomPackagesFor(
cmGeneratorTarget const* target) const = 0;
private:
struct LinkInfo
{
LinkInfo(std::string package, std::string component, bool local = false)
: Package(std::move(package))
, Component(std::move(component))
, Local(local)
{
}
std::string Package;
std::string Component;
std::string License;
bool Local;
};
// Metadata
bool WriteSbom(std::ostream& os) const;
cmSpdxCreationInfo GenerateCreationInfo() const;
cmSpdxDocument GenerateSbom() const;
cmSpdxPackage GenerateLocalPackage(cmGeneratorTarget const* target) const;
bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name,
cmPackageInformation const& package) const;
cmSpdxPackage const* GetOrCreateLocalPackage(
cmGeneratorTarget const* target);
cmSpdxPackage const* GetOrCreateLinkedPackage(
cmGeneratorTarget const* source, cmLinkItem const& item);
bool EvaluateLinkProperty(
cmGeneratorTarget const* target, std::string const& property,
std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext,
std::vector<cmLinkItem>& dependencies) const;
bool GenerateTarget(
cmGeneratorTarget const* target, std::string const& config,
cmGeneratorExpression::PreprocessContext preprocessContext);
void GenerateDeclaredLicense(cmSpdxPackage const* package,
std::string const& license);
LinkInfo NoteLinkedTarget(cmGeneratorTarget const* target,
cmGeneratorTarget const* linkedTarget);
std::string const PackageName;
std::string const Namespace;
std::string const PackageVersion;
std::string const PackageDescription;
std::string const PackageWebsite;
std::string const PackageUrl;
std::string const DataLicense;
std::string const DefaultLicense;
bool const NoProjectDefaults;
cmSbomArguments::SbomFormat const PackageFormat;
std::vector<cmExportSet*> const ExportSets;
cmLocalGenerator const* LocalGenerator = nullptr;
// Derived from inputs at generate time
std::set<cmGeneratorTarget const*> SbomTargets;
// Accumulated during generation
std::map<std::string, LinkInfo> LinkTargets;
std::map<std::string, cmPackageInformation> Requirements;
std::vector<std::string> Configurations;
cmSpdxCreationInfo CreationInfo;
cmSpdxDocument Project;
std::vector<cmSpdxRelationship> Relationships;
// Targets for which license data has been generated
mutable std::set<std::string> GeneratedLinkLicenses;
std::vector<cmGeneratorTarget const*> Pending;
std::map<cmGeneratorTarget const*, cmSpdxPackage const*> LocalPackages;
std::map<std::pair<std::string, std::string>, cmSpdxPackage const*>
LinkedPackages;
std::map<cmGeneratorTarget const*, LinkInfo> LinkTargets;
std::map<std::string, cmPackageInformation> Requirements;
};
+5
View File
@@ -5,6 +5,7 @@
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include <cm/type_traits>
@@ -265,6 +266,10 @@ private:
template <typename T, typename U = cm::decay_t<T>>
U* insert_back(std::vector<cmSbomObject>& vec, T&& obj) noexcept
{
// Returned pointers must survive reallocation of the containing vector.
static_assert(
std::is_same<ObjectStorage::Model<U>, ObjectStorage::Heap<U>>::value,
"insert_back requires heap storage for stable addresses");
vec.emplace_back(std::forward<U>(obj));
return std::addressof(vec.back().CastUnchecked<U>());
}
+99 -24
View File
@@ -2,12 +2,14 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <cm/optional>
#include <cm3p/json/reader.h>
#include <cm3p/json/value.h>
#include "cmSbomObject.h"
@@ -50,34 +52,107 @@ bool testSerializeSpdxJson()
doc.Serialize(serializer);
auto value = serializer.GetJson();
ASSERT_TRUE(value.isObject());
Json::Value const& context = value["@context"];
ASSERT_EQUAL(context.asString(), contextUrl);
Json::Value const& graph = value["@graph"];
ASSERT_TRUE(graph.isArray());
Json::Value const& docValue = graph[0];
ASSERT_TRUE(docValue.isObject());
ASSERT_EQUAL(docValue["type"].asString(), "SpdxDocument");
ASSERT_EQUAL(docValue["spdxId"].asString(), "_:SPDXRef-Document");
auto const& creationInfo = docValue["creationInfo"];
ASSERT_EQUAL(creationInfo["@id"].asString(), "_:SPDXRef-CreationInfo");
ASSERT_EQUAL(creationInfo["type"].asString(), "CreationInfo");
auto const& package = docValue["rootElement"][0];
ASSERT_EQUAL(package["type"].asString(), "software_Package");
ASSERT_EQUAL(package["spdxId"].asString(), "_:SPDXRef-Package");
Json::Value expected;
expected["@context"] = contextUrl;
auto& document = expected["@graph"][0];
document["type"] = "SpdxDocument";
document["spdxId"] = "_:SPDXRef-Document";
auto& creation = document["creationInfo"];
creation["@id"] = "_:SPDXRef-CreationInfo";
creation["type"] = "CreationInfo";
creation["specVersion"] = "3.0.1";
auto& package = document["rootElement"][0];
package["type"] = "software_Package";
package["spdxId"] = "_:SPDXRef-Package";
package["name"] = "sample-package";
ASSERT_EQUAL(value, expected);
}
return true;
}
} // namespace
int testSpdxSerializer(int /*unused*/, char* /*unused*/[])
bool testSerializeAfterReallocation()
{
return runTests({ testSerializeSpdxJson });
cmSbomDocument doc;
cmSpdxCreationInfo creationInfo;
creationInfo.SpdxId = "_:creation-info";
doc.Graph.emplace_back(creationInfo);
cmSpdxDocument document;
document.SpdxId = "urn:document";
document.CreationInfo = &creationInfo;
auto* spdx = insert_back(doc.Graph, std::move(document));
auto const count = 128u;
for (auto i = 0u; i < count; ++i) {
std::string const suffix = std::to_string(i);
cmSpdxPackage rootValue;
rootValue.SpdxId = "urn:root-" + suffix;
rootValue.CreationInfo = &creationInfo;
auto const* root = insert_back(spdx->RootElements, std::move(rootValue));
cmSpdxPackage dependencyValue;
dependencyValue.SpdxId = "urn:dependency-" + suffix;
dependencyValue.CreationInfo = &creationInfo;
auto const* dependency =
insert_back(spdx->Elements, std::move(dependencyValue));
cmSpdxRelationship relationship;
relationship.SpdxId = "urn:relationship-" + suffix;
relationship.CreationInfo = &creationInfo;
relationship.RelationshipType = cmSpdxRelationship::DEPENDS_ON;
relationship.From = root;
relationship.To.emplace_back(dependency);
doc.Graph.emplace_back(std::move(relationship));
}
cmSbomObject moved(std::move(doc));
cmSbomObject copied(moved);
cmSpdxSerializer serializer;
std::ostringstream output;
ASSERT_TRUE(serializer.WriteSbom(output, copied));
auto const value = serializer.GetJson();
Json::Value expected;
expected["@context"] = "https://spdx.org/rdf/3.0.1/spdx-context.jsonld";
auto& graph = expected["@graph"];
graph[0]["@id"] = "_:creation-info";
graph[0]["type"] = "CreationInfo";
graph[1]["spdxId"] = "urn:document";
graph[1]["type"] = "SpdxDocument";
graph[1]["creationInfo"] = "_:creation-info";
for (auto i = 0u; i < count; ++i) {
std::string const rootId = "urn:root-" + std::to_string(i);
std::string const dependencyId = "urn:dependency-" + std::to_string(i);
auto& root = graph[1]["rootElement"][i];
root["spdxId"] = rootId;
root["creationInfo"] = "_:creation-info";
root["type"] = "software_Package";
auto& dependency = graph[1]["element"][i];
dependency["spdxId"] = dependencyId;
dependency["creationInfo"] = "_:creation-info";
dependency["type"] = "software_Package";
auto& relationship = graph[i + 2];
relationship["spdxId"] = "urn:relationship-" + std::to_string(i);
relationship["creationInfo"] = "_:creation-info";
relationship["from"] = rootId;
relationship["to"][0] = dependencyId;
relationship["type"] = "Relationship";
relationship["relationshipType"] = "dependsOn";
}
ASSERT_EQUAL(value, expected);
Json::Value written;
Json::CharReaderBuilder reader;
std::string errors;
std::istringstream input(output.str());
ASSERT_TRUE(Json::parseFromStream(reader, input, &written, &errors));
ASSERT_EQUAL(written, expected);
return true;
}
}
int testSpdxSerializer(int, char*[])
{
return runTests({ testSerializeSpdxJson, testSerializeAfterReallocation });
}
+1 -2
View File
@@ -1,7 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-Debug.spdx.json" DebugSbom)
file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-Release.spdx.json" ReleaseSbom)
else()
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/licenses-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense.cmake)
install(SBOM licenses EXPORTS licenses DEFAULT_LICENSE MIT DESTINATION .)
@@ -0,0 +1,2 @@
CMake Error at DuplicateSbom\.cmake:[0-9]+ \(install\):
install SBOM command already specified for the file mySbom\.
@@ -0,0 +1,4 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DuplicateSbom.cmake)
install(SBOM mySbom EXPORTS setA DESTINATION .)
install(SBOM mySbom EXPORTS setA DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback-install-check.cmake)
@@ -0,0 +1,4 @@
CMake Warning \(author\) in CMakeLists\.txt:
Target "libb" references target "liba", whose export does not use the
standard namespace separator\. The dependency will be recorded by its bare
target name without provenance\.
@@ -0,0 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback.cmake)
install(EXPORT setA DESTINATION lib/cmake/A)
install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B)
install(SBOM mySbom EXPORTS setB DESTINATION .)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/Config-check.cmake)
+2 -2
View File
@@ -1,11 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BarConfig.spdx.json" BarConfig)
file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BazConfig.spdx.json" BazConfig)
else()
file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BarConfig.spdx.json" BarConfig)
endif()
set(interface_dependency install_dependency)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex-check.cmake)
@@ -0,0 +1,37 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
file(READ "${RunCMake_TEST_INSTALL_DIR}/interface_targets-Debug.spdx.json" content)
set(expected [=[
{
"@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
"@graph": [
{
"@id": "_:Build#CreationInfo",
"type": "CreationInfo",
"comment": "This SBOM was generated from the CMakeLists.txt File",
"createdBy": ["https://gitlab.kitware.com/cmake/cmake"],
"specVersion": "3.0.1"
},
{
"spdxId": "urn:interface_targets#SPDXDocument",
"type": "SpdxDocument",
"name": "interface_targets",
"creationInfo": "_:Build#CreationInfo",
"profileConformance": ["core", "software"],
"rootElement": [{
"spdxId": "urn:interface#Package",
"type": "software_Package",
"name": "interface",
"creationInfo": "_:Build#CreationInfo",
"software_primaryPurpose": "library"
}]
}
]
}
]=])
expect_object("${content}" expected)
expect_array("${content}" 2 @graph)
expect_array("${content}" 1 @graph 1 rootElement)
string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element)
if(NOT error)
set(RunCMake_TEST_FAILED "Include directories introduced unexpected SBOM dependencies")
endif()
@@ -0,0 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/IgnoresInterfaceDirs.cmake)
install(SBOM interface_targets
EXPORTS interface_targets
DESTINATION .
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake)
@@ -0,0 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InstallExportPlusSbomSameSet.cmake)
install(EXPORT export_set_a NAMESPACE A:: DESTINATION lib/cmake/setA)
install(SBOM mySbom EXPORTS export_set_a DESTINATION .)
install(EXPORT export_set_b DESTINATION lib/cmake/setB)
@@ -1 +0,0 @@
# Invalid formats are rejected even when there are no export sets.
@@ -0,0 +1,8 @@
CMake Error in CMakeLists\.txt:
Target "libb" references target "liba" that is in an export set which is
exported multiple times with different namespaces: lib/cmake/A/setA\.cmake,
lib/cmake/OldA/setA\.cmake\.
An SBOM cannot attribute a dependency exported in more than one export set
or with more than one namespace\. Consider consolidating the exports of the
"liba" target to a single export\.
@@ -0,0 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiNamespaceAmbiguity.cmake)
install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A)
install(EXPORT setA NAMESPACE OldA:: DESTINATION lib/cmake/OldA)
install(SBOM mySbom EXPORTS setB DESTINATION .)
@@ -0,0 +1,7 @@
CMake Error in CMakeLists\.txt:
Target "libb" references target "liba" that is in multiple export sets:
lib/cmake/A1/setA1\.cmake, lib/cmake/A2/setA2\.cmake\.
An SBOM cannot attribute a dependency exported in more than one export set
or with more than one namespace\. Consider consolidating the exports of the
"liba" target to a single export\.
@@ -0,0 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetAmbiguity.cmake)
install(EXPORT setA1 NAMESPACE A1:: DESTINATION lib/cmake/A1)
install(EXPORT setA2 NAMESPACE A2:: DESTINATION lib/cmake/A2)
install(SBOM mySbom EXPORTS setB DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom-install-check.cmake)
@@ -0,0 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom.cmake)
install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A)
install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B)
install(SBOM mySbom EXPORTS setA setB DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/metadata-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata.cmake)
install(SBOM metadata EXPORTS metadata NO_PROJECT_METADATA DESTINATION .)
@@ -21,4 +21,54 @@ endforeach()
if(failures)
string(REPLACE ";" "\n " msg "${failures}")
set(RunCMake_TEST_FAILED "${msg}")
return()
endif()
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
set(sbom_names explicit_root_sbom explicit_subdir_sbom implicit_root implicit_subdir)
set(target_names libA libC libB libD)
set(sbom_paths
"${RunCMake_TEST_INSTALL_DIR}/explicit_root_sbom-Debug.spdx.json"
"${RunCMake_TEST_INSTALL_DIR}/explicit_subdir_sbom-Debug.spdx.json"
"${RunCMake_TEST_INSTALL_DIR}/lib/sbom/implicit_root/implicit_root-Debug.spdx.json"
"${RunCMake_TEST_INSTALL_DIR}/lib/sbom/implicit_subdir/implicit_subdir-Debug.spdx.json"
)
foreach(sbom target path IN ZIP_LISTS sbom_names target_names sbom_paths)
file(READ "${path}" content)
string(CONFIGURE [=[
{
"@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
"@graph": [
{
"@id": "_:Build#CreationInfo",
"type": "CreationInfo",
"comment": "This SBOM was generated from the CMakeLists.txt File",
"createdBy": ["https://gitlab.kitware.com/cmake/cmake"],
"specVersion": "3.0.1"
},
{
"spdxId": "urn:@sbom@#SPDXDocument",
"type": "SpdxDocument",
"name": "@sbom@",
"creationInfo": "_:Build#CreationInfo",
"profileConformance": ["core", "software"],
"rootElement": [{
"spdxId": "urn:@target@#Package",
"type": "software_Package",
"name": "@target@",
"creationInfo": "_:Build#CreationInfo",
"software_primaryPurpose": "library"
}]
}
]
}
]=] expected @ONLY)
expect_object("${content}" expected)
expect_array("${content}" 2 @graph)
expect_array("${content}" 1 @graph 1 rootElement)
string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element)
if(NOT error)
expect_array("${content}" 0 @graph 1 element)
endif()
endforeach()
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/foo/foo-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary-install-check.cmake)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary.cmake)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/foo/foo-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency-install-check.cmake)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency.cmake)
@@ -0,0 +1,4 @@
foreach(sbom IN ITEMS bundle selected unmatched)
file(READ "${RunCMake_TEST_INSTALL_DIR}/${sbom}-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense-install-check.cmake)
endforeach()
@@ -0,0 +1,17 @@
project(Selected VERSION 2.3.4 SPDX_LICENSE ISC
DESCRIPTION "Selected project" LANGUAGES NONE)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense.cmake)
install(
SBOM bundle
PROJECT bar
VERSION 1.3.4
EXPORTS bar_targets child_targets
DESTINATION .
)
install(SBOM selected PROJECT Selected
EXPORTS bar_targets child_targets DESTINATION .)
install(SBOM unmatched
EXPORTS bar_targets child_targets DESTINATION .)
@@ -1,2 +1,17 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/test_targets/test_targets-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake)
expect_value("${SPDX_DOCUMENT}" "Metadata Test Project" description)
expect_value("${SPDX_DOCUMENT}" BSD-3 dataLicense simplelicensing_licenseExpression)
expect_value("${SPDX_DOCUMENT}" "urn:test_targets#DataLicenseExpression" dataLicense spdxId)
set(PROJECT_METADATA_TEMPLATE [=[
{
"spdxId": "urn:@package@#Package",
"software_packageVersion": "1.2.0",
"software_homePage": "www.example.com"
}
]=])
foreach(package IN ITEMS test test_targets)
string(CONFIGURE "${PROJECT_METADATA_TEMPLATE}" PROJECT_METADATA_EXPECTED @ONLY)
expect_object("${SPDX_DOCUMENT}" PROJECT_METADATA_EXPECTED rootElement)
endforeach()
+2 -2
View File
@@ -1,6 +1,6 @@
project(test_project LANGUAGES NONE
VERSION 1.3
DESCRIPTION "An eloquent description"
HOMEPAGE_URL "www.example.com"
SPDX_LICENSE "BSD-3")
HOMEPAGE_URL www.example.com
SPDX_LICENSE BSD-3)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake)
@@ -1,6 +0,0 @@
CMake Error in CMakeLists\.txt:
Target "canine" references target "mammal" which has no
install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\.
An SBOM cannot attribute this dependency\. Give "mammal" an
install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an
install\(SBOM\)/export\(SBOM\)\.
@@ -1,2 +0,0 @@
project(test_project)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake)
@@ -0,0 +1,3 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/bar_sbom-Debug.spdx.json" BAR_CONTENT)
file(READ "${RunCMake_TEST_INSTALL_DIR}/foo_sbom-Debug.spdx.json" FOO_CONTENT)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements-install-check.cmake)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements.cmake)
+35 -11
View File
@@ -1,13 +1,13 @@
include(RunCMake)
set(common_test_options
"-DCMAKE_INSTALL_SBOM_FORMATS:STRING=spdx"
"-DCMAKE_INSTALL_LIBDIR=lib"
-DCMAKE_INSTALL_SBOM_FORMATS:STRING=spdx
-DCMAKE_INSTALL_LIBDIR=lib
)
function(run_cmake_error test)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_OPTIONS ${common_test_options})
set(RunCMake_TEST_OPTIONS ${common_test_options} ${ARGN})
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
endif()
@@ -17,7 +17,7 @@ endfunction()
function(run_cmake_install test)
set(extra_options ${ARGN})
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_INSTALL_DIR ${RunCMake_BINARY_DIR}/${test}-install)
set(RunCMake_TEST_INSTALL_DIR ${RunCMake_TEST_BINARY_DIR}/${test}-install)
set(RunCMake_TEST_OPTIONS ${common_test_options} ${extra_options})
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_INSTALL_DIR})
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
@@ -32,26 +32,50 @@ function(run_cmake_install test)
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug)
endfunction()
function(run_cmake_configure test)
function(run_cmake_configure test config other_config)
set(extra_options ${ARGN})
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_OPTIONS ${common_test_options} ${extra_options})
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS
"-DCMAKE_CONFIGURATION_TYPES=${config}\\;${other_config}")
else()
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config})
endif()
run_cmake(${test})
endfunction()
run_cmake_install(ProjectLicense)
run_cmake_install(ApplicationTarget)
run_cmake_install(InstallExportPlusSbomSameSet)
run_cmake_install(InterfaceTarget)
run_cmake_install(SharedTarget)
run_cmake_install(Requirements)
run_cmake_install(SbomNamespaceFallback)
run_cmake_install(MultiSetSingleSbom)
run_cmake_install(TargetInMultipleSets)
run_cmake_install(EmptyNamespaceFallback -Wauthor)
run_cmake_install(IgnoresInterfaceDirs)
run_cmake_install(MissingPackageNamespace)
run_cmake_install(ProjectMetadata)
run_cmake_install(DefaultLicense)
run_cmake_install(UnknownDependencies)
run_cmake_install(NoProjectMetadata)
run_cmake_install(UninstalledStaticLibrary)
run_cmake_install(PartialCoverage)
run_cmake_install(TransitiveDependencies)
run_cmake_install(PrivateStaticDependency)
run_cmake_install(PrivateBareLibrary)
run_cmake_error(ReferencesNonExportedTarget)
run_cmake_error(MultiNamespaceAmbiguity)
run_cmake_error(MultiSetAmbiguity)
run_cmake_error(DuplicateSbom)
run_cmake_configure(Config)
run_cmake_configure(ExplicitFormat -DCMAKE_INSTALL_SBOM_FORMATS=spdx-3.0+json)
run_cmake_configure(InvalidFormat -DCMAKE_INSTALL_SBOM_FORMATS=JSON)
run_cmake_configure(Genex)
run_cmake_configure(ForbiddenGenex)
run_cmake_configure(Config Debug Release)
run_cmake_configure(ExplicitFormat Debug Release -DCMAKE_INSTALL_SBOM_FORMATS=spdx-3.0+json)
run_cmake_configure(Genex BarConfig BazConfig)
run_cmake_error(InvalidFormat -DCMAKE_INSTALL_SBOM_FORMATS=JSON)
run_cmake_error(ForbiddenGenex)
@@ -0,0 +1,3 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/bar-Debug.spdx.json" BAR_CONTENT)
file(READ "${RunCMake_TEST_INSTALL_DIR}/foo-Debug.spdx.json" FOO_CONTENT)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback-install-check.cmake)
@@ -0,0 +1,4 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback.cmake)
install(SBOM foo EXPORTS foo DESTINATION .)
install(SBOM bar EXPORTS bar DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets.cmake)
install(SBOM mySbom EXPORTS setA1 setA2 DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/traversal/traversal-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies-install-check.cmake)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies.cmake)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/baz-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary.cmake)
install(SBOM baz EXPORTS baz FORMAT spdx DESTINATION .)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/unknown_dependencies/unknown_dependencies-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies-install-check.cmake)
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies.cmake)
@@ -3,5 +3,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget.cmake)
export(
SBOM application_targets
EXPORTS application_targets
FORMAT "spdx-3.0+json"
FORMAT spdx-3.0+json
)
+1 -2
View File
@@ -1,7 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" DebugSbom)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Release.spdx.json" ReleaseSbom)
else()
+1 -1
View File
@@ -2,5 +2,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Config.cmake)
export(SBOM foo
EXPORTS foo
FORMAT "spdx-3.0+json"
FORMAT spdx-3.0+json
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/licenses/licenses-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense.cmake)
export(SBOM licenses EXPORTS licenses DEFAULT_LICENSE MIT)
@@ -1,5 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ForbiddenGenex.cmake)
export(SBOM foo
EXPORTS foo
FORMAT "spdx-3.0+json"
FORMAT spdx-3.0+json
)
+2 -2
View File
@@ -1,11 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BarConfig.spdx.json" BarConfig)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BazConfig.spdx.json" BazConfig)
else()
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BarConfig.spdx.json" BarConfig)
endif()
set(interface_dependency build_dependency)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex-check.cmake)
+1 -1
View File
@@ -1,5 +1,5 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex.cmake)
export(SBOM foo
EXPORTS foo
FORMAT "spdx-3.0+json"
FORMAT spdx-3.0+json
)
@@ -0,0 +1,37 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/interface_targets/interface_targets-Debug.spdx.json" content)
set(expected [=[
{
"@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
"@graph": [
{
"@id": "_:Build#CreationInfo",
"type": "CreationInfo",
"comment": "This SBOM was generated from the CMakeLists.txt File",
"createdBy": ["https://gitlab.kitware.com/cmake/cmake"],
"specVersion": "3.0.1"
},
{
"spdxId": "urn:interface_targets#SPDXDocument",
"type": "SpdxDocument",
"name": "interface_targets",
"creationInfo": "_:Build#CreationInfo",
"profileConformance": ["core", "software"],
"rootElement": [{
"spdxId": "urn:interface#Package",
"type": "software_Package",
"name": "interface",
"creationInfo": "_:Build#CreationInfo",
"software_primaryPurpose": "library"
}]
}
]
}
]=])
expect_object("${content}" expected)
expect_array("${content}" 2 @graph)
expect_array("${content}" 1 @graph 1 rootElement)
string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element)
if(NOT error)
set(RunCMake_TEST_FAILED "Include directories introduced unexpected SBOM dependencies")
endif()
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/IgnoresInterfaceDirs.cmake)
export(SBOM interface_targets EXPORTS interface_targets)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/mySbom/mySbom-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/metadata/metadata-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata.cmake)
export(SBOM metadata EXPORTS metadata NO_PROJECT_METADATA)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary.cmake)
export(SBOM foo EXPORTS foo FORMAT spdx)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency.cmake)
export(SBOM foo EXPORTS foo FORMAT spdx)
@@ -0,0 +1,4 @@
foreach(sbom IN ITEMS bundle selected unmatched)
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/${sbom}/${sbom}-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense-install-check.cmake)
endforeach()
@@ -0,0 +1,13 @@
project(Selected VERSION 2.3.4 SPDX_LICENSE ISC
DESCRIPTION "Selected project" LANGUAGES NONE)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense.cmake)
export(SBOM bundle
PROJECT bar
VERSION 1.3.4
EXPORTS bar_targets child_targets)
export(SBOM selected PROJECT Selected
EXPORTS bar_targets child_targets)
export(SBOM unmatched
EXPORTS bar_targets child_targets)
@@ -1,3 +1,30 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/test_targets/test_targets-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadataExplicitAssertions.cmake)
set(DOCUMENT_METADATA_EXPECTED [=[
{
"description" : "An eloquent description",
"dataLicense" :
{
"creationInfo" : "_:Build#CreationInfo",
"simplelicensing_licenseExpression" : "BSD-3",
"spdxId" : "urn:test_targets#DataLicenseExpression",
"type" : "simplelicensing_LicenseExpression"
}
}
]=])
set(PACKAGE_METADATA_TEMPLATE [=[
{
"spdxId" : "urn:@package@#Package",
"software_packageVersion" : "1.3.4",
"software_homePage" : "www.example.com",
"software_downloadLocation" : "https://example.com/test_targets.tar.gz"
}
]=])
expect_object("${SPDX_DOCUMENT}" DOCUMENT_METADATA_EXPECTED)
foreach(package IN ITEMS test test_targets)
string(CONFIGURE "${PACKAGE_METADATA_TEMPLATE}" PACKAGE_METADATA_EXPECTED @ONLY)
expect_object("${SPDX_DOCUMENT}" PACKAGE_METADATA_EXPECTED rootElement)
endforeach()
@@ -2,10 +2,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake)
export(
SBOM test_targets
NO_PROJECT_METADATA
EXPORTS test_targets
DESCRIPTION "An eloquent description"
DATA_LICENSE "BSD-3"
HOMEPAGE_URL "www.example.com"
PACKAGE_URL "https://example.com/test_targets.tar.gz"
VERSION "1.3.4"
DATA_LICENSE BSD-3
HOMEPAGE_URL www.example.com
PACKAGE_URL https://example.com/test_targets.tar.gz
VERSION 1.3.4
)
@@ -1,6 +0,0 @@
CMake Error in CMakeLists\.txt:
Target "canine" references target "mammal" which has no
install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\.
An SBOM cannot attribute this dependency\. Give "mammal" an
install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an
install\(SBOM\)/export\(SBOM\)\.
@@ -1,3 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake)
export(SBOM dog EXPORTS dog)
+23 -7
View File
@@ -12,7 +12,7 @@ endfunction()
function(run_cmake_install test)
set(extra_options ${ARGN})
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_INSTALL_DIR ${RunCMake_BINARY_DIR}/${test}-install)
set(RunCMake_TEST_INSTALL_DIR ${RunCMake_TEST_BINARY_DIR}/install)
set(RunCMake_TEST_OPTIONS ${extra_options})
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_INSTALL_DIR})
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
@@ -27,13 +27,20 @@ function(run_cmake_install test)
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug)
endfunction()
function(run_cmake_configure test)
function(run_cmake_configure test config other_config)
set(extra_options ${ARGN})
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_OPTIONS ${extra_options})
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS
"-DCMAKE_CONFIGURATION_TYPES=${config}\\;${other_config}")
else()
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config})
endif()
run_cmake(${test})
endfunction()
run_cmake_install(ProjectLicense)
run_cmake_install(ApplicationTarget)
run_cmake_install(InstallExportPlusSbomSameSet)
run_cmake_install(InterfaceTarget)
@@ -43,16 +50,25 @@ run_cmake_install(SbomNamespaceFallback)
run_cmake_install(MultiSetSingleSbom)
run_cmake_install(TargetInMultipleSets)
run_cmake_install(EmptyNamespaceFallback -Wauthor)
run_cmake_install(SbomNamespaceAmbiguity -Wauthor)
run_cmake_install(IgnoresInterfaceDirs)
run_cmake_install(MissingPackageNamespace)
run_cmake_install(ProjectMetadata)
run_cmake_install(DefaultLicense)
run_cmake_install(UnknownDependencies)
run_cmake_install(NoProjectMetadata)
run_cmake_install(UninstalledStaticLibrary)
block()
set(RunCMake_TEST_TIMEOUT 30)
run_cmake_install(TransitiveDependencies)
endblock()
run_cmake_install(PrivateStaticDependency)
run_cmake_install(PrivateBareLibrary)
run_cmake_error(ReferencesNonExportedTarget)
run_cmake_error(MultiNamespaceAmbiguity)
run_cmake_error(MultiSetAmbiguity)
run_cmake_error(DuplicateSbom)
run_cmake_configure(Config)
run_cmake_configure(Genex)
run_cmake_configure(ForbiddenGenex)
run_cmake_configure(Config Debug Release)
run_cmake_configure(Genex BarConfig BazConfig)
run_cmake_error(ForbiddenGenex)
@@ -1,2 +0,0 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar_sbom/bar_sbom-Debug.spdx.json" BAR_CONTENT)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity-install-check.cmake)
@@ -1,4 +0,0 @@
CMake Warning \(author\) in CMakeLists\.txt:
Target "libc" references target "libb" which has no
install\(EXPORT\)/export\(EXPORT\) namespace and is covered by multiple SBOMs:
foo_sbom, foo_sbom2\. Attributing to "foo_sbom" \(first alphabetically\)\.
@@ -1,5 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity.cmake)
export(SBOM foo_sbom EXPORTS foo)
export(SBOM foo_sbom2 EXPORTS foo)
export(SBOM bar_sbom EXPORTS bar)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/traversal/traversal-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies.cmake)
export(SBOM traversal EXPORTS traversal FORMAT spdx)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/baz/baz-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary.cmake)
export(SBOM baz EXPORTS baz FORMAT spdx)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/unknown_dependencies/unknown_dependencies-Debug.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies-install-check.cmake)
Loaded 100 of 180 files, more files were not shown because too many files have changed in this diff. Show more