mirror of
https://github.com/Kitware/CMake.git
synced 2026-11-06 05:16:58 +00:00
Merge topic 'sbom-fix-uninstalled-deps' into release-4.5
2d732c85de cmSbom: Export uninstalled libraries / references to private static deps
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12730
This commit is contained in:
commit
e9767d8f79
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