mirror of
https://github.com/Kitware/CMake.git
synced 2026-11-06 05:16:58 +00:00
cmSbom: Export uninstalled libraries / references to private static deps
install(SBOM) could crash for dependencies outside an export set and mishandle private dependencies expressed through LINK_ONLY. Resolve link expressions before target lookup, traverse unexported dependencies transitively, and isolate document state per configuration. Preserve target license precedence and align document metadata inheritance with CPS. Add regression coverage for these cases. Fixes: #28141 Fixes: #28210
This commit is contained in:
1 parent
739fe769fe
commit
2d732c85de
180 files changed
+2417
-1133
No files matched your search
@@ -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);
|
||||
}
|
||||
@@ -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,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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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,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>());
|
||||
}
|
||||
@@ -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,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 .)
|
||||
File renamed without changes.
@@ -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)
|
||||
@@ -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.
|
||||
File renamed without changes.
@@ -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 .)
|
||||
File renamed without changes.
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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,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)
|
||||
@@ -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
Reference in new issue
Block a user