cmSbom: Add install(SBOM) generator and unit tests

This commit is contained in:
Taylor Sasser
2025-12-17 14:33:34 -05:00
parent dcadde3662
commit f2027a886b
57 changed files with 2331 additions and 0 deletions
+8
View File
@@ -215,10 +215,14 @@ add_library(
cmExportInstallFileGenerator.cxx
cmExportInstallPackageInfoGenerator.h
cmExportInstallPackageInfoGenerator.cxx
cmExportInstallSbomGenerator.h
cmExportInstallSbomGenerator.cxx
cmExportPackageInfoGenerator.h
cmExportPackageInfoGenerator.cxx
cmExportTryCompileFileGenerator.h
cmExportTryCompileFileGenerator.cxx
cmExportSbomGenerator.h
cmExportSbomGenerator.cxx
cmExportSet.h
cmExportSet.cxx
cmExternalMakefileProjectGenerator.cxx
@@ -344,6 +348,8 @@ add_library(
cmInstallRuntimeDependencySet.cxx
cmInstallRuntimeDependencySetGenerator.h
cmInstallRuntimeDependencySetGenerator.cxx
cmInstallSbomExportGenerator.h
cmInstallSbomExportGenerator.cxx
cmInstallScriptGenerator.h
cmInstallScriptGenerator.cxx
cmInstallSubdirectoryGenerator.h
@@ -707,6 +713,8 @@ add_library(
cmRemoveDefinitionsCommand.h
cmReturnCommand.cxx
cmReturnCommand.h
cmSbomArguments.h
cmSbomArguments.cxx
cmSbomObject.h
cmSbomSerializer.h
cmSearchPath.cxx
+8
View File
@@ -80,6 +80,14 @@ cmExperimental::FeatureData const LookupTable[] = {
"is meant only for experimentation and feedback to CMake developers.",
{},
cmExperimental::TryCompileCondition::Never },
{ "GenerateSbom",
"ca494ed3-b261-4205-a01f-603c95e4cae0",
"CMAKE_EXPERIMENTAL_GENERATE_SBOM",
"CMake's support for generating software bill of materials (Sbom) "
"information in SPDX format is experimental. It is meant only for "
"experimentation and feedback to CMake developers.",
{},
cmExperimental::TryCompileCondition::Never },
};
static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) ==
static_cast<size_t>(cmExperimental::Feature::Sentinel),
+1
View File
@@ -24,6 +24,7 @@ public:
MappedPackageInfo,
ExportBuildDatabase,
Instrumentation,
GenerateSbom,
Sentinel,
};
+247
View File
@@ -0,0 +1,247 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportInstallSbomGenerator.h"
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <utility>
#include <vector>
#include <cmext/string_view>
#include "cmExportSet.h"
#include "cmFileSet.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmInstallExportGenerator.h"
#include "cmInstallFileSetGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmSbomArguments.h"
#include "cmSbomObject.h"
#include "cmSpdx.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTargetExport.h"
cmExportInstallSbomGenerator::cmExportInstallSbomGenerator(
cmInstallExportGenerator* iegen, cmSbomArguments args)
: cmExportSbomGenerator(std::move(args))
, cmExportInstallFileGenerator(iegen)
{
this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s));
}
std::string cmExportInstallSbomGenerator::GetConfigImportFileGlob() const
{
std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt);
return glob;
}
std::string const& cmExportInstallSbomGenerator::GetExportName() const
{
return this->GetPackageName();
}
cm::string_view cmExportInstallSbomGenerator::GetImportPrefixWithSlash() const
{
return "@prefix@/"_s;
}
bool cmExportInstallSbomGenerator::GenerateMainFile(std::ostream& os)
{
std::vector<cmTargetExport const*> allTargets;
{
auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); };
if (!this->CollectExports(visitor)) {
return false;
}
}
cmSbomDocument doc;
doc.Graph.reserve(256);
cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom());
std::vector<TargetProperties> targets;
targets.reserve(allTargets.size());
for (cmTargetExport const* te : allTargets) {
cmGeneratorTarget const* gt = te->Target;
ImportPropertyMap properties;
if (!this->PopulateInterfaceProperties(te, properties)) {
return false;
}
this->PopulateLinkLibrariesProperty(
gt, cmGeneratorExpression::InstallInterface, properties);
this->PopulateInterfaceLinkLibrariesProperty(
gt, cmGeneratorExpression::InstallInterface, properties);
targets.push_back(
TargetProperties{ insert_back(project->RootElements,
this->GenerateImportTarget(te->Target)),
te->Target, std::move(properties) });
}
for (auto const& target : targets) {
this->GenerateProperties(doc, project, target, targets);
}
this->WriteSbom(doc, os);
return true;
}
void cmExportInstallSbomGenerator::GenerateImportTargetsConfig(
std::ostream& os, std::string const& config, std::string const& suffix)
{
cmSbomDocument doc;
doc.Graph.reserve(256);
cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom());
std::vector<TargetProperties> targets;
std::string cfg = (config.empty() ? "noconfig" : config);
for (auto const& te : this->GetExportSet()->GetTargetExports()) {
ImportPropertyMap properties;
std::set<std::string> importedLocations;
if (this->GetExportTargetType(te.get()) !=
cmStateEnums::INTERFACE_LIBRARY) {
this->PopulateImportProperties(config, suffix, te.get(), properties,
importedLocations);
}
this->PopulateInterfaceProperties(te.get(), properties);
this->PopulateInterfaceLinkLibrariesProperty(
te->Target, cmGeneratorExpression::InstallInterface, properties);
this->PopulateLinkLibrariesProperty(
te->Target, cmGeneratorExpression::InstallInterface, properties);
targets.push_back(
TargetProperties{ insert_back(project->RootElements,
this->GenerateImportTarget(te->Target)),
te->Target, std::move(properties) });
}
for (auto const& target : targets) {
this->GenerateProperties(doc, project, target, targets);
}
this->WriteSbom(doc, os);
}
std::string cmExportInstallSbomGenerator::GenerateImportPrefix() const
{
std::string expDest = this->IEGen->GetDestination();
if (cmSystemTools::FileIsFullPath(expDest)) {
std::string const& installPrefix =
this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
"CMAKE_INSTALL_PREFIX");
if (cmHasPrefix(expDest, installPrefix)) {
auto n = installPrefix.length();
while (n < expDest.length() && expDest[n] == '/') {
++n;
}
expDest = expDest.substr(n);
} else {
this->ReportError(
cmStrCat("install(SBOM \"", this->GetExportName(),
"\" ...) specifies DESTINATION \"", expDest,
"\" which is not a subdirectory of the install prefix."));
return {};
}
}
if (expDest.empty()) {
return this->GetInstallPrefix();
}
return cmStrCat(this->GetImportPrefixWithSlash(), expDest);
}
void cmExportInstallSbomGenerator::HandleMissingTarget(
std::string& /* link_libs */, cmGeneratorTarget const* /* depender */,
cmGeneratorTarget* /* dependee */)
{
}
bool cmExportInstallSbomGenerator::CheckInterfaceDirs(
std::string const& /* prepro */, cmGeneratorTarget const* /* target */,
std::string const& /* prop */) const
{
return true;
}
std::string cmExportInstallSbomGenerator::InstallNameDir(
cmGeneratorTarget const* target, std::string const& config)
{
std::string install_name_dir;
cmMakefile* mf = target->Target->GetMakefile();
if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
install_name_dir =
target->GetInstallNameDirForInstallTree(config, "@prefix@");
}
return install_name_dir;
}
std::string cmExportInstallSbomGenerator::GetCxxModulesDirectory() const
{
return {};
}
void cmExportInstallSbomGenerator::GenerateCxxModuleConfigInformation(
std::string const&, std::ostream&) const
{
}
std::string cmExportInstallSbomGenerator::GetCxxModuleFile(
std::string const& /* name */) const
{
return {};
}
cm::optional<std::string> cmExportInstallSbomGenerator::GetFileSetDirectory(
cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet,
cm::optional<std::string> const& config)
{
cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
auto cge =
ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
std::string const unescapedDest =
cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
if (config && !isConfigDependent) {
return {};
}
std::string const& type = fileSet->GetType();
if (config && (type == "CXX_MODULES"_s)) {
cmMakefile* mf = gte->LocalGenerator->GetMakefile();
std::ostringstream e;
e << "The \"" << gte->GetName() << "\" target's interface file set \""
<< fileSet->GetName() << "\" of type \"" << type
<< "\" contains context-sensitive base file entries which is not "
"supported.";
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return {};
}
cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake(
unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
dest = cmStrCat("@prefix@/"_s, *dest);
}
return dest;
}
+68
View File
@@ -0,0 +1,68 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
#include <string>
#include <cm/optional>
#include <cm/string_view>
#include "cmExportInstallFileGenerator.h"
#include "cmExportSbomGenerator.h"
class cmFileSet;
class cmGeneratorTarget;
class cmInstallExportGenerator;
class cmSbomArguments;
class cmTargetExport;
class cmExportInstallSbomGenerator
: public cmExportSbomGenerator
, public cmExportInstallFileGenerator
{
public:
/** Construct with the export installer that will install the
files. */
cmExportInstallSbomGenerator(cmInstallExportGenerator* iegen,
cmSbomArguments arguments);
/** Compute the globbing expression used to load per-config import
files from the main file. */
std::string GetConfigImportFileGlob() const override;
protected:
std::string const& GetExportName() const override;
cm::string_view GetImportPrefixWithSlash() const override;
std::string GetCxxModuleFile(std::string const& name) const override;
void GenerateCxxModuleConfigInformation(std::string const&,
std::ostream& os) const override;
// Implement virtual methods from the superclass.
bool GenerateMainFile(std::ostream& os) override;
void GenerateImportTargetsConfig(std::ostream& os, std::string const& config,
std::string const& suffix) override;
void HandleMissingTarget(std::string& /* link_libs */,
cmGeneratorTarget const* /* depender */,
cmGeneratorTarget* /* dependee */) override;
bool CheckInterfaceDirs(std::string const& prepro,
cmGeneratorTarget const* target,
std::string const& prop) const override;
char GetConfigFileNameSeparator() const override { return '@'; }
std::string GenerateImportPrefix() const;
std::string InstallNameDir(cmGeneratorTarget const* target,
std::string const& config) override;
std::string GetCxxModulesDirectory() const override;
cm::optional<std::string> GetFileSetDirectory(
cmGeneratorTarget* gte, cmTargetExport const* te, cmFileSet* fileSet,
cm::optional<std::string> const& config = {});
};
+383
View File
@@ -0,0 +1,383 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportSbomGenerator.h"
#include <array>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <cm/optional>
#include <cmext/algorithm>
#include "cmArgumentParserTypes.h"
#include "cmFindPackageStack.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSbomArguments.h"
#include "cmSbomObject.h"
#include "cmSpdx.h"
#include "cmSpdxSerializer.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
cmSpdxPackage::PurposeId GetPurpose(cmStateEnums::TargetType type)
{
switch (type) {
case cmStateEnums::TargetType::EXECUTABLE:
return cmSpdxPackage::PurposeId::APPLICATION;
case cmStateEnums::TargetType::STATIC_LIBRARY:
case cmStateEnums::TargetType::SHARED_LIBRARY:
case cmStateEnums::TargetType::MODULE_LIBRARY:
case cmStateEnums::TargetType::OBJECT_LIBRARY:
case cmStateEnums::TargetType::INTERFACE_LIBRARY:
return cmSpdxPackage::PurposeId::LIBRARY;
case cmStateEnums::TargetType::UTILITY:
return cmSpdxPackage::PurposeId::SOURCE;
case cmStateEnums::TargetType::GLOBAL_TARGET:
case cmStateEnums::TargetType::UNKNOWN_LIBRARY:
default:
return cmSpdxPackage::PurposeId::ARCHIVE;
}
}
cmExportSbomGenerator::cmExportSbomGenerator(cmSbomArguments args)
: PackageName(std::move(args.PackageName))
, PackageVersion(std::move(args.Version))
, PackageDescription(std::move(args.Description))
, PackageWebsite(std::move(args.Website))
, PackageLicense(std::move(args.License))
, PackageFormat(args.GetFormat())
{
}
bool cmExportSbomGenerator::GenerateImportFile(std::ostream& os)
{
return this->GenerateMainFile(os);
}
void cmExportSbomGenerator::WriteSbom(cmSbomDocument& doc,
std::ostream& os) const
{
switch (this->PackageFormat) {
case cmSbomArguments::SbomFormat::SPDX_3_0_JSON:
cmSpdxSerializer{}.WriteSbom(os, cmSbomObject(doc));
break;
case cmSbomArguments::SbomFormat::NONE:
break;
}
}
bool cmExportSbomGenerator::AddPackageInformation(
cmSpdxPackage& artifact, std::string const& name,
cmPackageInformation const& package) const
{
if (name.empty()) {
return false;
}
cmSpdxOrganization org;
org.Name = name;
artifact.OriginatedBy.emplace_back(std::move(org));
if (package.Description) {
artifact.Description = *package.Description;
}
if (package.Version) {
artifact.PackageVersion = *package.Version;
}
if (package.PackageUrl) {
artifact.PackageUrl = *package.PackageUrl;
}
if (package.License) {
artifact.CopyrightText = *package.License;
}
artifact.BuiltTime = cmSystemTools::GetCurrentDateTime("%FT%TZ");
cmSpdxExternalRef externalRef;
externalRef.Locator = cmStrCat("cmake:find_package(", name, ")");
externalRef.ExternalRefType = "buildSystem";
return true;
}
cmSpdxDocument cmExportSbomGenerator::GenerateSbom() const
{
cmSpdxTool tool;
tool.SpdxId = "CMake#Agent";
tool.Name = "CMake";
cmSpdxCreationInfo ci;
ci.Created = cmSystemTools::GetCurrentDateTime("%FT%TZ");
ci.CreatedUsing = { tool };
ci.Comment = "This SBOM was generated from the CMakeLists.txt File";
cmSpdxDocument proj;
proj.Name = PackageName;
proj.SpdxId = cmStrCat(PackageName, "#SPDXDocument");
proj.ProfileConformance = { "core", "software" };
proj.CreationInfo = ci;
if (!this->PackageDescription.empty()) {
proj.Description = this->PackageDescription;
}
if (!this->PackageLicense.empty()) {
proj.DataLicense = this->PackageLicense;
}
return proj;
}
cmSpdxPackage cmExportSbomGenerator::GenerateImportTarget(
cmGeneratorTarget const* target) const
{
cmSpdxPackage package;
package.SpdxId = cmStrCat(target->GetName(), "#Package");
package.Name = target->GetName();
package.PrimaryPurpose = GetPurpose(target->GetType());
cmSpdxExternalRef buildSystem;
buildSystem.Locator = "CMake#Agent";
buildSystem.ExternalRefType = "buildSystem";
buildSystem.Comment = "Build System used for this target";
package.ExternalRef = { buildSystem };
if (!this->PackageVersion.empty()) {
package.PackageVersion = this->PackageVersion;
}
if (!this->PackageWebsite.empty()) {
package.Homepage = this->PackageWebsite;
}
if (!this->PackageUrl.empty()) {
package.DownloadLocation = this->PackageUrl;
}
return package;
}
void cmExportSbomGenerator::GenerateLinkProperties(
cmSbomDocument& doc, cmSpdxDocument* project, std::string const& libraries,
TargetProperties const& current,
std::vector<TargetProperties> const& allTargets) const
{
auto itProp = current.Properties.find(libraries);
if (itProp == current.Properties.end()) {
return;
}
std::map<std::string, std::vector<std::string>> allowList = { { "LINK_ONLY",
{} } };
std::string interfaceLinkLibraries;
if (!cmGeneratorExpression::ForbidGeneratorExpressions(
current.Target, itProp->first, itProp->second, interfaceLinkLibraries,
allowList)) {
return;
}
auto makeRel = [&](char const* desc) {
cmSpdxRelationship r;
r.RelationshipType = cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON;
r.Description = desc;
r.From = current.Package;
return r;
};
auto linkLibraries = makeRel("Linked Libraries");
auto linkRequires = makeRel("Required Runtime Libraries");
auto buildRequires = makeRel("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 };
}
}
}
std::string pkgName =
cmStrCat(linkInfo.Package, ":", linkInfo.Component);
cmSpdxPackage pkg;
pkg.Name = pkgName;
pkg.SpdxId = cmStrCat(pkgName, "#Package");
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);
}
}
return { true, insert_back(project->Elements, std::move(pkg)) };
}
cmSpdxPackage pkg;
pkg.SpdxId = cmStrCat(name, "#Package");
pkg.Name = name;
return { false, insert_back(project->Elements, std::move(pkg)) };
};
auto handleDependencies = [&](std::vector<std::string> const& names,
cmSpdxRelationship& internalDeps,
cmSpdxRelationship& externalDeps) {
for (auto const& n : names) {
auto res = addArtifact(n);
if (!res.second) {
continue;
}
if (res.first) {
internalDeps.To.push_back(res.second);
} else {
externalDeps.To.push_back(res.second);
}
}
};
handleDependencies(allowList["LINK_ONLY"], linkLibraries, linkRequires);
handleDependencies(cmList{ interfaceLinkLibraries }, linkLibraries,
buildRequires);
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));
}
}
bool cmExportSbomGenerator::GenerateProperties(
cmSbomDocument& doc, cmSpdxDocument* proj, TargetProperties const& current,
std::vector<TargetProperties> const& allTargets) const
{
this->GenerateLinkProperties(doc, proj, "LINK_LIBRARIES", current,
allTargets);
this->GenerateLinkProperties(doc, proj, "INTERFACE_LINK_LIBRARIES", current,
allTargets);
return true;
}
bool cmExportSbomGenerator::PopulateLinkLibrariesProperty(
cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext preprocessRule,
ImportPropertyMap& properties)
{
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,
ReplaceFreeTargets);
properties[linkIfaceProp] = prepro;
hadLINK_LIBRARIES = true;
}
}
}
return hadLINK_LIBRARIES;
}
bool cmExportSbomGenerator::NoteLinkedTarget(
cmGeneratorTarget const* target, std::string const& linkedName,
cmGeneratorTarget const* linkedTarget)
{
if (cm::contains(this->ExportedTargets, linkedTarget)) {
this->LinkTargets.emplace(linkedName,
LinkInfo{ "", linkedTarget->GetExportName() });
return true;
}
if (linkedTarget->IsImported()) {
using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
auto pkgInfo = [](cmTarget* t) -> Package {
cmFindPackageStack 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");
if (pkgName.empty()) {
return cm::nullopt;
}
cmPackageInformation package;
return std::make_pair(pkgName, package);
}(linkedTarget->Target);
if (!pkgInfo) {
target->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("Target \"", target->GetName(),
"\" references imported target \"", linkedName,
"\" which does not come from any known package."));
return false;
}
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 });
cmPackageInformation& req =
this->Requirements.insert(std::move(*pkgInfo)).first->second;
req.Components.emplace(std::move(component));
return true;
}
// Target belongs to another export from this build.
auto const& exportInfo = this->FindExportInfo(linkedTarget);
if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
auto const& linkNamespace = *exportInfo.Namespaces.begin();
if (!cmHasSuffix(linkNamespace, "::")) {
target->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("Target \"", target->GetName(), "\" references target \"",
linkedName,
"\", which does not use the standard namespace separator. "
"This is not allowed."));
}
std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
std::string component = linkedTarget->GetExportName();
if (pkgName == this->GetPackageName()) {
this->LinkTargets.emplace(linkedName, LinkInfo{ "", component });
} else {
this->LinkTargets.emplace(linkedName, LinkInfo{ pkgName, component });
this->Requirements[pkgName].Components.emplace(std::move(component));
}
return true;
}
// Target belongs to multiple namespaces or multiple export sets.
// cmExportFileGenerator::HandleMissingTarget should have complained about
// this already.
return false;
}
+83
View File
@@ -0,0 +1,83 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
#include <map>
#include <string>
#include <vector>
#include "cmExportFileGenerator.h"
#include "cmFindPackageStack.h"
#include "cmGeneratorExpression.h"
#include "cmSbomArguments.h"
class cmGeneratorTarget;
struct cmSbomDocument;
struct cmSpdxDocument;
struct cmSpdxPackage;
class cmExportSbomGenerator : virtual public cmExportFileGenerator
{
public:
cmExportSbomGenerator(cmSbomArguments args);
using cmExportFileGenerator::GenerateImportFile;
protected:
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;
cmSpdxDocument GenerateSbom() const;
cmSpdxPackage GenerateImportTarget(cmGeneratorTarget const* target) const;
std::string const& GetPackageName() const { return this->PackageName; }
bool GenerateImportFile(std::ostream& os) override;
bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name,
cmPackageInformation const& package) const;
bool GenerateProperties(
cmSbomDocument& doc, cmSpdxDocument* project,
TargetProperties const& current,
std::vector<TargetProperties> const& allTargets) const;
void GenerateLinkProperties(
cmSbomDocument& doc, cmSpdxDocument* project, std::string const& libraries,
TargetProperties const& current,
std::vector<TargetProperties> const& allTargets) const;
bool NoteLinkedTarget(cmGeneratorTarget const* target,
std::string const& linkedName,
cmGeneratorTarget const* linkedTarget) override;
bool PopulateLinkLibrariesProperty(cmGeneratorTarget const* target,
cmGeneratorExpression::PreprocessContext,
ImportPropertyMap& properties);
private:
struct LinkInfo
{
std::string Package;
std::string Component;
};
std::string const PackageName;
std::string const PackageVersion;
std::string const PackageDescription;
std::string const PackageWebsite;
std::string const PackageLicense;
std::string const PackageUrl;
cmSbomArguments::SbomFormat const PackageFormat;
std::map<std::string, LinkInfo> LinkTargets;
std::map<std::string, cmPackageInformation> Requirements;
};
+89
View File
@@ -39,6 +39,7 @@
#include "cmInstallPackageInfoExportGenerator.h"
#include "cmInstallRuntimeDependencySet.h"
#include "cmInstallRuntimeDependencySetGenerator.h"
#include "cmInstallSbomExportGenerator.h"
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
#include "cmList.h"
@@ -48,6 +49,7 @@
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmRuntimeDependencyArchive.h"
#include "cmSbomArguments.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSubcommandTable.h"
@@ -2513,6 +2515,92 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
return true;
}
bool HandleSbomMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
#ifndef CMAKE_BOOTSTRAP
if (!cmExperimental::HasSupportEnabled(
status.GetMakefile(), cmExperimental::Feature::GenerateSbom)) {
status.SetError("does not recognize sub-command SBOM");
return false;
}
Helper helper(status);
cmInstallCommandArguments ica(helper.DefaultComponentName, *helper.Makefile);
cmSbomArguments arguments;
ArgumentParser::NonEmpty<std::string> exportName;
ArgumentParser::NonEmpty<std::string> cxxModulesDirectory;
arguments.Bind(ica);
ica.Bind("EXPORT"_s, exportName);
// ica.Bind("CXX_MODULES_DIRECTORY"_s, cxxModulesDirectory); TODO?
std::vector<std::string> unknownArgs;
ica.Parse(args, &unknownArgs);
ArgumentParser::ParseResult result = ica.Parse(args, &unknownArgs);
if (!result.Check(args[0], &unknownArgs, status)) {
return false;
}
if (!ica.Finalize()) {
return false;
}
if (arguments.PackageName.empty()) {
// TODO: Fix our use of the parser to enforce this.
status.SetError(cmStrCat(args[0], " missing SBOM name."));
return false;
}
if (exportName.empty()) {
status.SetError(cmStrCat(args[0], " missing EXPORT."));
return false;
}
if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) {
return false;
}
// Get or construct the destination path.
std::string dest = ica.GetDestination();
if (dest.empty()) {
if (helper.Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Windows") {
dest = std::string{ "/sbom/"_s };
} else {
dest = cmStrCat(helper.GetLibraryDestination(nullptr), "/sbom/",
arguments.GetPackageDirName());
}
}
cmExportSet& exportSet =
helper.Makefile->GetGlobalGenerator()->GetExportSets()[exportName];
cmInstallGenerator::MessageLevel message =
cmInstallGenerator::SelectMessageLevel(helper.Makefile);
// Tell the global generator about any installation component names
// specified
helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
ica.GetComponent());
// Create the export install generator.
helper.Makefile->AddInstallGenerator(
cm::make_unique<cmInstallSbomExportGenerator>(
&exportSet, dest, ica.GetPermissions(), ica.GetConfigurations(),
ica.GetComponent(), message, ica.GetExcludeFromAll(),
std::move(arguments), std::move(cxxModulesDirectory),
helper.Makefile->GetBacktrace()));
return true;
#else
static_cast<void>(args);
status.SetError("SBOM not supported in bootstrap cmake");
return false;
#endif
}
bool Helper::MakeFilesFullPath(char const* modeName,
std::vector<std::string> const& relFiles,
std::vector<std::string>& absFiles)
@@ -2750,6 +2838,7 @@ bool cmInstallCommand(std::vector<std::string> const& args,
{ "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode },
{ "PACKAGE_INFO"_s, HandlePackageInfoMode },
{ "RUNTIME_DEPENDENCY_SET"_s, HandleRuntimeDependencySetMode },
{ "SBOM"_s, HandleSbomMode }
};
return subcommand(args[0], args, status);
+30
View File
@@ -0,0 +1,30 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmInstallSbomExportGenerator.h"
#include <utility>
#include <cm/memory>
#include "cmExportInstallFileGenerator.h"
#include "cmExportInstallSbomGenerator.h"
#include "cmListFileCache.h"
#include "cmSbomArguments.h"
class cmExportSet;
cmInstallSbomExportGenerator::cmInstallSbomExportGenerator(
cmExportSet* exportSet, std::string destination, std::string filePermissions,
std::vector<std::string> const& configurations, std::string component,
MessageLevel message, bool excludeFromAll, cmSbomArguments args,
std::string cxxModulesDirectory, cmListFileBacktrace backtrace)
: cmInstallExportGenerator(
exportSet, std::move(destination), std::move(filePermissions),
configurations, std::move(component), message, excludeFromAll,
args.GetPackageFileName(), args.GetNamespace(),
std::move(cxxModulesDirectory), std::move(backtrace))
{
this->EFGen = cm::make_unique<cmExportInstallSbomGenerator>(this, args);
}
cmInstallSbomExportGenerator::~cmInstallSbomExportGenerator() = default;
+31
View File
@@ -0,0 +1,31 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include <string>
#include <vector>
#include "cmInstallExportGenerator.h"
class cmExportSet;
class cmListFileBacktrace;
class cmSbomArguments;
class cmInstallSbomExportGenerator final : public cmInstallExportGenerator
{
public:
cmInstallSbomExportGenerator(cmExportSet* exportSet, std::string destination,
std::string filePermissions,
std::vector<std::string> const& configurations,
std::string component, MessageLevel message,
bool excludeFromAll, cmSbomArguments arguments,
std::string cxxModulesDirectory,
cmListFileBacktrace backtrace);
cmInstallSbomExportGenerator(cmInstallSbomExportGenerator const&) = delete;
~cmInstallSbomExportGenerator() override;
cmInstallSbomExportGenerator& operator=(
cmInstallSbomExportGenerator const&) = delete;
char const* InstallSubcommand() const override { return "SBOM"; }
};
+104
View File
@@ -0,0 +1,104 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmSbomArguments.h"
#include <algorithm>
#include <cctype>
#include <cm/string_view>
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmStringAlgorithms.h"
struct SbomFormatPattern
{
cm::string_view Name;
cm::string_view Alias;
cmSbomArguments::SbomFormat Id;
};
static SbomFormatPattern SbomPatterns[] = {
{ "spdx-3.0+json", "spdx", 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 "";
}
}
template void cmSbomArguments::Bind<void>(cmArgumentParser<void>&,
cmSbomArguments*);
bool cmSbomArguments::Check(cmExecutionStatus& status) const
{
if (!this->PackageName.empty()) {
if (!cmGeneratorExpression::IsValidTargetName(this->PackageName) ||
this->PackageName.find(':') != std::string::npos) {
status.SetError(cmStrCat(R"(SBOM given invalid package name ")"_s,
this->PackageName, R"(".)"_s));
return false;
}
}
if (!this->Format.empty()) {
if (this->GetFormat() == SbomFormat::NONE) {
status.SetError(
cmStrCat(R"(SBOM given invalid format ")"_s, this->Format, R"(".)"_s));
return false;
}
}
return true;
}
cm::string_view cmSbomArguments::CommandName() const
{
return "SBOM"_s;
}
std::string cmSbomArguments::GetNamespace() const
{
return cmStrCat(this->PackageName, "::"_s);
}
std::string cmSbomArguments::GetPackageDirName() const
{
return this->PackageName;
}
cmSbomArguments::SbomFormat cmSbomArguments::GetFormat() const
{
if (this->Format.empty()) {
return SbomFormat::SPDX_3_0_JSON;
}
return ParseSbomFormat(this->Format);
}
std::string cmSbomArguments::GetPackageFileName() const
{
std::string const pkgNameOnDisk = this->GetPackageDirName();
std::string format = GetSbomFileExtension(this->GetFormat());
std::transform(format.begin(), format.end(), format.begin(),
[](char const c) { return std::tolower(c); });
return cmStrCat(pkgNameOnDisk, format);
}
+61
View File
@@ -0,0 +1,61 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <string>
#include <cm/string_view>
#include <cm/type_traits>
#include <cmext/string_view>
#include "cmArgumentParser.h" // IWYU pragma: keep
#include "cmArgumentParserTypes.h"
#include "cmProjectInfoArguments.h"
class cmSbomArguments : public cmProjectInfoArguments
{
public:
enum class SbomFormat
{
SPDX_3_0_JSON,
NONE,
};
template <
typename T,
typename = cm::enable_if_t<std::is_base_of<cmSbomArguments, T>::value>>
static void Bind(cmArgumentParser<T>& parser)
{
cmSbomArguments* const self = nullptr;
cmSbomArguments::Bind(parser, self);
}
void Bind(cmArgumentParser<void>& parser) { Bind(parser, this); }
bool Check(cmExecutionStatus& status) const override;
std::string GetNamespace() const;
std::string GetPackageDirName() const;
std::string GetPackageFileName() const;
SbomFormat GetFormat() const;
ArgumentParser::NonEmpty<std::string> Format;
protected:
cm::string_view CommandName() const override;
private:
using cmProjectInfoArguments::Bind;
template <typename T>
static void Bind(cmArgumentParser<T>& parser, cmSbomArguments* self)
{
cmProjectInfoArguments* const base = self;
Bind(base, parser, "SBOM"_s, &cmProjectInfoArguments::PackageName);
Bind(self, parser, "FORMAT"_s, &cmSbomArguments::Format);
cmProjectInfoArguments::Bind(parser, self);
}
};
extern template void cmSbomArguments::Bind<void>(cmArgumentParser<void>&,
cmSbomArguments*);
+1
View File
@@ -1343,6 +1343,7 @@ add_RunCMake_test(AndroidMK)
add_RunCMake_test(ExportPackageInfo)
add_RunCMake_test(InstallPackageInfo)
add_RunCMake_test(InstallExportsAsPackageInfo)
add_RunCMake_test(InstallSbom)
if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN)
if(NOT "${CMAKE_GENERATOR}" MATCHES "Make|Ninja|Visual Studio 1[456]")
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/application_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget-install-check.cmake)
@@ -0,0 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget.cmake)
install(SBOM application_targets
EXPORT application_targets
FORMAT "spdx-3.0+json"
DESTINATION .
)
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.30)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1,19 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_library(interface INTERFACE)
target_include_directories(interface INTERFACE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
install(
TARGETS interface
EXPORT interface_targets
)
install(SBOM interface_targets
EXPORT interface_targets
DESTINATION .
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/interface_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InterfaceTarget-install-check.cmake)
@@ -0,0 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InterfaceTarget.cmake)
install(SBOM interface_targets
EXPORT interface_targets
DESTINATION .
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/test_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MissingPackageNamespace-install-check.cmake)
@@ -0,0 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MissingPackageNamespace.cmake)
install(SBOM test_targets
VERSION "1.0.2"
EXPORT test_targets
DESTINATION .
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/test_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake)
@@ -0,0 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake)
install(
SBOM test_targets
DESCRIPTION "An eloquent description"
LICENSE "BSD-3"
HOMEPAGE_URL "www.example.com"
VERSION "1.3.4"
EXPORT test_targets
DESTINATION .
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/dog.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget-install-check.cmake)
@@ -0,0 +1,3 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake)
install(SBOM dog EXPORT dog DESTINATION .)
@@ -0,0 +1,3 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/bar.spdx.json" BAR_CONTENT)
file(READ "${RunCMake_TEST_INSTALL_DIR}/foo.spdx.json" FOO_CONTENT)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements-install-check.cmake)
@@ -0,0 +1,4 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements.cmake)
install(SBOM foo EXPORT foo DESTINATION .)
install(SBOM bar EXPORT bar DESTINATION .)
@@ -0,0 +1,33 @@
include(RunCMake)
set(common_test_options
-Wno-dev
"-DCMAKE_EXPERIMENTAL_GENERATE_SBOM:STRING=ca494ed3-b261-4205-a01f-603c95e4cae0"
"-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
"-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
)
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_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)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=DEBUG)
endif()
run_cmake(${test})
set(RunCMake_TEST_NO_CLEAN TRUE)
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug)
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug)
endfunction()
run_cmake_install(ApplicationTarget)
run_cmake_install(InterfaceTarget)
run_cmake_install(SharedTarget)
run_cmake_install(Requirements)
run_cmake_install(IgnoresInterfaceDirs)
run_cmake_install(MissingPackageNamespace)
run_cmake_install(ReferencesNonExportedTarget)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_INSTALL_DIR}/shared_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SharedTarget-install-check.cmake)
@@ -0,0 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SharedTarget.cmake)
install(
SBOM shared_targets
EXPORT shared_targets
DESTINATION .
)
@@ -0,0 +1,93 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(ELEMENTS [=[
[
{
"@id" : "bar:bar#Package",
"name" : "bar:bar",
"type" : "software_Package"
}
]
]=])
set(SPDX_DOCUMENT [=[
{
"@id" : "application_targets#SPDXDocument",
"name": "application_targets",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(APPLICATION [=[
{
"@id" : "application#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "application",
"primaryPurpose" : "APPLICATION",
"type" : "software_Package"
}
]=])
set(DEPENDENCY [=[
{
"@id" : "bar:bar#Package",
"name" : "bar:bar",
"originatedBy" :
[
{
"name" : "bar",
"type" : "Organization"
}
],
"packageVersion" : "1.3.5",
"type" : "software_Package"
}
]=])
set(BUILD_LINKED_LIBRARIES [=[
{
"description" : "Linked Libraries",
"from" : "application#Package",
"relationshipType" : "DEPENDS_ON",
"to" :
[
"bar:bar#Package"
],
"type" : "Relationship"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" APPLICATION "rootElement" "0")
expect_object("${SPDX_DOCUMENT}" DEPENDENCY "element" "0")
string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "1")
expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES)
@@ -0,0 +1,19 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_executable(application ${CMAKE_CURRENT_LIST_DIR}/main.c)
find_package(
bar 1.3.4 REQUIRED
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(application PUBLIC bar::bar)
install(
TARGETS application
EXPORT application_targets
)
+223
View File
@@ -0,0 +1,223 @@
function(format_path out)
set(path_segments ${ARGN})
set(path_string "")
foreach(segment IN LISTS path_segments)
if(segment MATCHES "^[0-9]+$")
string(APPEND path_string "[${segment}]")
else()
if(path_string STREQUAL "")
string(APPEND path_string "${segment}")
else()
string(APPEND path_string ".${segment}")
endif()
endif()
endforeach()
if(path_string STREQUAL "")
set(path_string "<root>")
endif()
set(${out} "${path_string}" PARENT_SCOPE)
endfunction()
macro(fail_at entity actual expected)
format_path(formatted_path ${ARGN})
set(RunCMake_TEST_FAILED "Attribute '${formatted_path}' ${entity} '${actual}' does not match expected ${entity} '${expected}'")
endmacro()
macro(fail_array_subset expected_index)
format_path(formatted_path ${ARGN})
set(RunCMake_TEST_FAILED "Attribute '${formatted_path}' array element (subset match) had no match for expected index ${expected_index}")
endmacro()
macro(bubble_error)
set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
endmacro()
function(json_matches out actual_node expected_node display_path)
set(current_path ${display_path})
string(JSON actual_type TYPE "${actual_node}")
string(JSON expected_type TYPE "${expected_node}")
if (NOT actual_type STREQUAL expected_type)
fail_at("type" "${actual_type}" "${expected_type}" ${current_path})
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
if (expected_type STREQUAL "OBJECT")
string(JSON expected_length LENGTH "${expected_node}")
math(EXPR expected_last_index "${expected_length}-1")
foreach(key_index RANGE ${expected_last_index})
string(JSON key MEMBER "${expected_node}" "${key_index}")
string(JSON probe ERROR_VARIABLE error GET "${actual_node}" "${key}")
if (error STREQUAL "NOT_FOUND")
fail_at("object member presence" "<missing>" "${key}" ${current_path} "${key}")
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
string(JSON actual_child_type TYPE "${actual_node}" "${key}")
string(JSON expected_child_type TYPE "${expected_node}" "${key}")
if (NOT actual_child_type STREQUAL expected_child_type)
fail_at("type" "${actual_child_type}" "${expected_child_type}" ${current_path} "${key}")
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
if (expected_child_type STREQUAL "OBJECT" OR expected_child_type STREQUAL "ARRAY")
string(JSON actual_child GET "${actual_node}" "${key}")
string(JSON expected_child GET "${expected_node}" "${key}")
set(next_path ${current_path})
list(APPEND next_path "${key}")
json_matches(is_ok "${actual_child}" "${expected_child}" "${next_path}")
if (NOT is_ok)
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
else()
string(JSON actual_value GET "${actual_node}" "${key}")
string(JSON expected_value GET "${expected_node}" "${key}")
if (NOT "${actual_value}" STREQUAL "${expected_value}")
fail_at("value" "${actual_value}" "${expected_value}" ${current_path} "${key}")
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
endif()
endforeach()
set(${out} TRUE PARENT_SCOPE)
return()
elseif (expected_type STREQUAL "ARRAY")
string(JSON actual_length LENGTH "${actual_node}")
string(JSON expected_length LENGTH "${expected_node}")
if (actual_length LESS expected_length)
fail_at("array length (subset requirement)" "${actual_length}" ">= ${expected_length}" ${current_path})
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
math(EXPR expected_last_index "${expected_length}-1")
math(EXPR actual_last_index "${actual_length}-1")
foreach(expected_index RANGE ${expected_last_index})
set(is_matched FALSE)
set(best_error "")
set(best_error_set FALSE)
string(JSON expected_element_type TYPE "${expected_node}" "${expected_index}")
if (expected_element_type STREQUAL "OBJECT" OR expected_element_type STREQUAL "ARRAY")
string(JSON expected_element GET "${expected_node}" "${expected_index}")
foreach(actual_index RANGE ${actual_last_index})
string(JSON actual_element_type TYPE "${actual_node}" "${actual_index}")
if (NOT actual_element_type STREQUAL expected_element_type)
continue()
endif()
string(JSON actual_element GET "${actual_node}" "${actual_index}")
set(next_path ${current_path})
list(APPEND next_path "${actual_index}")
json_matches(one_ok "${actual_element}" "${expected_element}" "${next_path}")
if (one_ok)
set(is_matched TRUE)
break()
else()
if (NOT best_error_set)
set(best_error "${RunCMake_TEST_FAILED}")
set(best_error_set TRUE)
endif()
set(RunCMake_TEST_FAILED "")
endif()
endforeach()
else()
string(JSON expected_value GET "${expected_node}" "${expected_index}")
foreach(actual_index RANGE ${actual_last_index})
string(JSON actual_element_type TYPE "${actual_node}" "${actual_index}")
if (NOT actual_element_type STREQUAL expected_element_type)
continue()
endif()
string(JSON actual_value GET "${actual_node}" "${actual_index}")
if ("${actual_value}" STREQUAL "${expected_value}")
set(is_matched TRUE)
break()
endif()
endforeach()
if (NOT is_matched AND NOT best_error_set)
set(best_error "")
set(best_error_set TRUE)
endif()
endif()
if (NOT is_matched)
if (best_error_set AND NOT "${best_error}" STREQUAL "")
set(RunCMake_TEST_FAILED "${best_error}")
else()
fail_array_subset("${expected_index}" ${current_path})
endif()
bubble_error()
set(${out} FALSE PARENT_SCOPE)
return()
endif()
endforeach()
set(${out} TRUE PARENT_SCOPE)
return()
endif()
endfunction()
function(expect_value content expected)
string(JSON actual ERROR_VARIABLE error GET "${content}" ${ARGN})
if (error STREQUAL "NOT_FOUND")
list(JOIN ARGN "." path_name)
set(RunCMake_TEST_FAILED "Path '${path_name}' not found in JSON input" PARENT_SCOPE)
return()
endif()
if (NOT "${actual}" STREQUAL "${expected}")
fail_at("value" "${actual}" "${expected}" ${ARGN})
bubble_error()
endif()
endfunction()
function(expect_array content expected_length)
string(JSON value_type ERROR_VARIABLE error TYPE "${content}" ${ARGN})
if (error STREQUAL "NOT_FOUND")
list(JOIN ARGN "." path_name)
set(RunCMake_TEST_FAILED "Path '${path_name}' not found in JSON input" PARENT_SCOPE)
return()
endif()
if (NOT value_type STREQUAL "ARRAY")
fail_at("type" "${value_type}" "ARRAY" ${ARGN})
bubble_error()
return()
endif()
string(JSON actual_length LENGTH "${content}" ${ARGN})
if (NOT actual_length EQUAL "${expected_length}")
fail_at("length" "${actual_length}" "${expected_length}" ${ARGN})
bubble_error()
endif()
endfunction()
function(expect_object content expected_var)
string(JSON actual_node ERROR_VARIABLE error GET "${content}" ${ARGN})
if (error STREQUAL "NOT_FOUND")
list(JOIN ARGN "." path_name)
set(RunCMake_TEST_FAILED "Path '${path_name}' not found in JSON input" PARENT_SCOPE)
return()
endif()
set(expected_text "${${expected_var}}")
set(display_path ${ARGN})
json_matches(is_ok "${actual_node}" "${expected_text}" "${display_path}")
if (NOT is_ok)
bubble_error()
return()
endif()
endfunction()
@@ -0,0 +1,93 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(ELEMENTS [=[
[
{
"@id" : "bar:bar#Package",
"name" : "bar:bar",
"type" : "software_Package"
}
]
]=])
set(SPDX_DOCUMENT [=[
{
"@id" : "interface_targets#SPDXDocument",
"name": "interface_targets",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(INTERFACE [=[
{
"@id" : "interface#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "interface",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(DEPENDENCY [=[
{
"@id" : "bar:bar#Package",
"name" : "bar:bar",
"originatedBy" :
[
{
"name" : "bar",
"type" : "Organization"
}
],
"packageVersion" : "1.3.5",
"type" : "software_Package"
}
]=])
set(BUILD_LINKED_LIBRARIES [=[
{
"description" : "Linked Libraries",
"from" : "interface#Package",
"relationshipType" : "DEPENDS_ON",
"to" :
[
"bar:bar#Package"
],
"type" : "Relationship"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" INTERFACE "rootElement" "0")
expect_object("${SPDX_DOCUMENT}" DEPENDENCY "element" "0")
string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "1")
expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES)
+19
View File
@@ -0,0 +1,19 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_library(interface INTERFACE)
find_package(
bar 1.3.4 REQUIRED
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(interface INTERFACE bar::bar)
install(
TARGETS interface
EXPORT interface_targets
)
+2
View File
@@ -0,0 +1,2 @@
This is a sample file to verify the SBOM install files command.
It's not an actual license.
@@ -0,0 +1,93 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(ELEMENTS [=[
[
{
"@id" : "bar:bar#Package",
"name" : "bar:bar",
"type" : "software_Package"
}
]
]=])
set(SPDX_DOCUMENT [=[
{
"@id" : "test_targets#SPDXDocument",
"name": "test_targets",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(TEST [=[
{
"@id" : "test#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "test",
"primaryPurpose" : "APPLICATION",
"type" : "software_Package"
}
]=])
set(DEPENDENCY [=[
{
"@id" : "baz:baz#Package",
"name" : "baz:baz",
"originatedBy" :
[
{
"name" : "baz",
"type" : "Organization"
}
],
"packageVersion" : "1.8.5",
"type" : "software_Package"
}
]=])
set(BUILD_LINKED_LIBRARIES [=[
{
"description" : "Linked Libraries",
"from" : "test#Package",
"relationshipType" : "DEPENDS_ON",
"to" :
[
"baz:baz#Package"
],
"type" : "Relationship"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" TEST "rootElement" "0")
expect_object("${SPDX_DOCUMENT}" DEPENDENCY "element" "0")
string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "1")
expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES)
@@ -0,0 +1,21 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_executable(test
${CMAKE_CURRENT_LIST_DIR}/main.c
)
find_package(
baz REQUIRED
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(test PUBLIC baz)
install(
TARGETS test
EXPORT test_targets
)
@@ -0,0 +1,51 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(SPDX_DOCUMENT [=[
{
"@id" : "test_targets#SPDXDocument",
"name": "test_targets",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(TEST [=[
{
"@id" : "test#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "test",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" TEST "rootElement" "0")
+11
View File
@@ -0,0 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_library(test INTERFACE)
install(
TARGETS test
EXPORT test_targets
)
@@ -0,0 +1,74 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(SPDX_DOCUMENT [=[
{
"@id": "_:dog#SPDXDocument",
"name": "dog",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(CANINE [=[
{
"@id" : "canine#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "canine",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(DEPENDENCY [=[
{
"@id" : "mammal#Package",
"name" : "mammal",
"type" : "software_Package"
}
]=])
set(BUILD_LINK_LIBRARIES [=[
{
"description" : "Required Build-Time Libraries",
"from" : "canine#Package",
"relationshipType" : "DEPENDS_ON",
"to" :
[
"mammal#Package"
],
"type" : "Relationship"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" CANINE "rootElement" "0")
expect_object("${SPDX_DOCUMENT}" DEPENDENCY "element" "0")
string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "1")
expect_object("${LINKED_LIBRARIES}" BUILD_LINK_LIBRARIES)
@@ -0,0 +1,11 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_library(mammal INTERFACE)
add_library(canine INTERFACE)
target_link_libraries(canine INTERFACE mammal)
install(TARGETS canine EXPORT dog DESTINATION .)
install(SBOM dog EXPORT dog DESTINATION .)
@@ -0,0 +1,137 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(BAR_SPDX_DOCUMENT [=[
{
"@id" : "bar#SPDXDocument",
"name": "bar",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(FOO_SPDX_DOCUMENT [=[
{
"@id" : "foo#SPDXDocument",
"name": "foo",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(FOO_LIBB [=[
{
"@id" : "libb#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "libb",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(BAR_LIBC [=[
{
"@id" : "libc#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "libc",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(BAR_LIBD [=[
{
"@id" : "libd#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "libd",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(BAR_DEPENDENCY_TEST [=[
{
"@id" : "test:liba#Package",
"name" : "test:liba",
"originatedBy" :
[
{
"name" : "test",
"type" : "Organization"
}
],
"type" : "software_Package"
}
]=])
set(BAR_DEPENDENCY_FOO [=[
{
"@id" : "foo:libb#Package",
"name" : "foo:libb",
"originatedBy" :
[
{
"name" : "foo",
"type" : "Organization"
}
],
"type" : "software_Package"
}
]=])
expect_value("${FOO_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" "@graph" "0")
expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT)
expect_object("${FOO_SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement" "0")
expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "0")
expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT)
expect_object("${BAR_SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement" "0")
expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement" "1")
expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element" "0")
expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element" "1")
+25
View File
@@ -0,0 +1,25 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
find_package(
test REQUIRED CONFIG
NO_DEFAULT_PATH
PATHS ${CMAKE_CURRENT_LIST_DIR}
)
add_library(libb INTERFACE)
add_library(libc INTERFACE)
add_library(libd INTERFACE)
add_library(foo ALIAS libb)
add_library(bar ALIAS libc)
target_link_libraries(libd INTERFACE test::liba foo bar)
install(TARGETS libb EXPORT foo DESTINATION .)
install(SBOM foo EXPORT foo DESTINATION .)
install(TARGETS libc libd EXPORT bar DESTINATION .)
install(SBOM bar EXPORT bar DESTINATION .)
+10
View File
@@ -0,0 +1,10 @@
# Disable built-in search paths.
enable_language(C)
set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
@@ -0,0 +1,93 @@
include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake)
set(CREATION_INFO [=[
{
"comment" : "This SBOM was generated from the CMakeLists.txt File",
"createdUsing" :
[
{
"@id" : "CMake#Agent",
"name" : "CMake",
"type" : "Tool"
}
],
"type" : "CreationInfo"
}
]=])
set(ELEMENTS [=[
[
{
"@id" : "foo:foo#Package",
"name" : "foo:foo",
"type" : "software_Package"
}
]
]=])
set(SPDX_DOCUMENT [=[
{
"@id" : "shared_targets#SPDXDocument",
"name": "shared_targets",
"profileConformance": ["core", "software"],
"type": "SpdxDocument"
}
]=])
set(SHARED [=[
{
"@id" : "shared#Package",
"externalRef" :
[
{
"comment" : "Build System used for this target",
"externalRefType" : "buildSystem",
"locator" : "CMake#Agent",
"type" : "ExternalRef"
}
],
"name" : "shared",
"primaryPurpose" : "LIBRARY",
"type" : "software_Package"
}
]=])
set(DEPENDENCY [=[
{
"@id" : "foo:foo#Package",
"name" : "foo:foo",
"originatedBy" :
[
{
"name" : "foo",
"type" : "Organization"
}
],
"packageVersion" : "1.2.3",
"type" : "software_Package"
}
]=])
set(BUILD_LINKED_LIBRARIES [=[
{
"description" : "Linked Libraries",
"from" : "shared#Package",
"relationshipType" : "DEPENDS_ON",
"to" :
[
"foo:foo#Package"
],
"type" : "Relationship"
}
]=])
expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context")
string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "0")
expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT)
expect_object("${SPDX_DOCUMENT}" CREATION_INFO "creationInfo")
expect_object("${SPDX_DOCUMENT}" SHARED "rootElement" "0")
expect_object("${SPDX_DOCUMENT}" DEPENDENCY "element" "0")
string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "1")
expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES)
+14
View File
@@ -0,0 +1,14 @@
include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
add_library(shared SHARED ${CMAKE_CURRENT_LIST_DIR}/main.c)
find_package(foo PATHS ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(shared PUBLIC foo::foo)
install(
TARGETS shared
EXPORT shared_targets
)
@@ -0,0 +1,29 @@
set(PACKAGE_VERSION "1.3.5")
if (PACKAGE_FIND_VERSION_RANGE)
# Check for a version range
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_RANGE_MIN)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
if (PACKAGE_FIND_VERSION_RANGE_MAX)
if (PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_RANGE_MAX)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
endif ()
elseif (PACKAGE_FIND_VERSION)
# Check for a specific version or minimum version
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if (PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif ()
endif ()
endif ()
@@ -0,0 +1 @@
add_library(bar::bar INTERFACE IMPORTED)
@@ -0,0 +1,12 @@
{
"name": "bar",
"cps_path" : "@prefix@",
"cps_version" : "0.13.0",
"version_schema" : "simple",
"version": "1.3.5",
"description": "A description",
"license": "MIT",
"website": "https://https://gitlab.kitware.com/cmake/cmake",
"package_url": "https://github.com/Kitware/CMake/releases/download/v4.1.0/cmake-4.1.0.tar.gz",
"components" : {}
}
@@ -0,0 +1,29 @@
set(PACKAGE_VERSION "1.8.5")
if (PACKAGE_FIND_VERSION_RANGE)
# Check for a version range
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_RANGE_MIN)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
if (PACKAGE_FIND_VERSION_RANGE_MAX)
if (PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_RANGE_MAX)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
endif ()
endif ()
elseif (PACKAGE_FIND_VERSION)
# Check for a specific version or minimum version
if (PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else ()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if (PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif ()
endif ()
endif ()
@@ -0,0 +1 @@
add_library(baz INTERFACE IMPORTED)
@@ -0,0 +1,12 @@
{
"name": "baz",
"cps_path" : "@prefix@",
"cps_version" : "0.13.0",
"version_schema" : "simple",
"version": "1.8.5",
"description": "A description",
"license": "MIT",
"website": "https://example.com/baz",
"package_url": "https://example.com/baz/releases/download/v1.8.5/baz-1.8.5.tar.gz",
"components" : {}
}
@@ -0,0 +1 @@
add_library(test::liba INTERFACE IMPORTED)
+21
View File
@@ -0,0 +1,21 @@
{
"compat_version" : "1.2.0",
"components" :
{
"foo" :
{
"type" : "interface"
}
},
"configurations" : [ "release", "debug" ],
"cps_path" : "@prefix@/cps",
"cps_version" : "0.13.0",
"default_components" : [ "foo" ],
"default_license" : "BSD-3-Clause",
"description" : "Sample package",
"license" : "BSD-3-Clause AND CC-BY-SA-4.0",
"name" : "foo",
"version" : "1.2.3",
"version_schema" : "simple",
"website" : "https://www.example.com/package/foo"
}
+4
View File
@@ -0,0 +1,4 @@
int main(void)
{
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
void foo(int a)
{
}