Merge topic 'sbom-export-generators'

8ec5a595cd cmSbom: Add documentation for `install(SBOM)` and `export(SBOM)`
83671f2d87 cmSbom: Add `export(SBOM)` build generators and unit tests
f2027a886b cmSbom: Add `install(SBOM)` generator and unit tests
dcadde3662 CPS: Refactor ForbidGeneratorExpressions into cmGeneratorExpression
bcb6f689a4 cmExportInstallFileGenerator: Allow overriding of CheckInterfaceDirs
6d6c37c90d cmPackageInfoArguments: Extract common values
8c6676b2ea cmFindPackage: Fix PACKAGE_VERSION not being set when rebuilding

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !11396
This commit is contained in:
Brad King
2025-12-18 10:13:32 -05:00
committed by Kitware Robot
88 changed files with 3062 additions and 204 deletions
+26
View File
@@ -162,6 +162,32 @@ converted to lower case.
See :command:`install(PACKAGE_INFO)` for a description of the other options.
Exporting Software Bill of Materials (SBOM) Documents
"""""""""""""""""""""""""""""""""""""""""""""""""""""
.. code-block:: cmake
export(EXPORT <export-name> SBOM <sbom-name>
[PROJECT <project-name>|NO_PROJECT_METADATA]
[DESTINATION <dir>]
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[LICENSE <license-string>]
[DESCRIPTION <description-string>]
[HOMEPAGE_URL <url-string>]
[PACKAGE_URL <url-string>]
[FORMAT <string>])
.. versionadded:: 4.3
.. note::
Experimental. Gated by ``CMAKE_EXPERIMENTAL_GENERATE_SBOM``.
Generates a software bill of materials (SBOM) document describing the targets
in the export ``<export-name>`` and their dependencies in the build tree
See :command:`install(SBOM)` for details about the supported SBOM formats and a
description of the other options.
Exporting Packages
^^^^^^^^^^^^^^^^^^
+84
View File
@@ -21,6 +21,7 @@ Synopsis
install(`EXPORT`_ <export-name> [...])
install(`PACKAGE_INFO`_ <package-name> [...])
install(`RUNTIME_DEPENDENCY_SET`_ <set-name> [...])
install(`SBOM`_ <sbom-name> [...])
Introduction
^^^^^^^^^^^^
@@ -1192,6 +1193,87 @@ Signatures
:command:`install_files`, and :command:`install_programs` commands
is not defined.
.. signature::
install(SBOM <sbom-name> [...])
.. versionadded:: 4.3
.. note::
Experimental. Gated by ``CMAKE_EXPERIMENTAL_GENERATE_SBOM``.
Installs a |SBOM| or "SBOM" which describes the project:
.. code-block:: cmake
install(SBOM <sbom-name> EXPORT <export-name>
[PROJECT <project-name>|NO_PROJECT_METADATA]
[DESTINATION <dir>]
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[LICENSE <license-string>]
[DESCRIPTION <description-string>]
[HOMEPAGE_URL <url-string>]
[PACKAGE_URL <url-string>]
[FORMAT <string>])
The ``SBOM`` form generates a |SBOM| or "SBOM" file for a given project
and installs it as part of the project installation. A |SBOM| is a
machine-readable description of the project's targets, linked libraries,
and related metadata, such as versions and license information. CMake
currently generates SBOM files using the |SPDX|_ 3.0 specification in
its JSON-LD representation, as selected by the ``FORMAT`` option, but
the interface is designed to allow additional SBOM formats or schema
versions to be supported in future CMake releases.
Target installations are associated with the export ``<export-name>``
using the ``EXPORT`` option of the :command:`install(TARGETS)` signature
documented above. If ``DESTINATION`` is not specified, a platform-specific
default is used.
Several options may be used to specify package metadata:
``VERSION <version>``
The package version, specified as a series of non-negative integer components,
i.e. <major>[.<minor>[.<patch>[.<tweak>]]]. See :command:`project(VERSION)` for
more information.
``FORMAT <string>``
The format in which the SBOM should be exported, which must be an expression of
the form ``<format>[-<version>][+<representation>]``. CMake currently supports
the JSON-LD serialization of |SPDX|_ v3.0.1 (``spdx`` or ``spdx-3.0.1+json``),
which is also the default if ``FORMAT`` is not specified.
``HOMEPAGE_URL <url-string>``
An informational canonical home URL for the project.
``PACKAGE_URL <url-string>``
An informational canonical package URL for the project.
``LICENSE <license-string>``
A |SPDX|_ (SPDX) `License Expression`_ that describes the license(s) of the
project as a whole, including documentation, resources, or other materials
distributed with the project, in addition to software artifacts. See the
SPDX `License List`_ for a list of commonly used licenses and their
identifiers.
The license of individual components is taken from the
:prop_tgt:`SPDX_LICENSE` property of their respective targets.
``DESCRIPTION <description-string>``
An informational description of the project. It is recommended that this
description is a relatively short string, usually no more than a few words.
By default, if the specified ``<package-name>`` matches the current CMake
:variable:`PROJECT_NAME`, sbom metadata will be inherited from the
project. The ``PROJECT <project-name>`` option may be used to specify a
different project from which to inherit metadata. If ``NO_PROJECT_METADATA``
is specified, automatic inheritance of sbom metadata will be disabled.
In any case, any metadata values specified in the ``install`` command will
take precedence.
Examples
^^^^^^^^
@@ -1323,3 +1405,5 @@ and by CPack. You can also invoke this script manually with
.. _License Expression: https://spdx.github.io/spdx-spec/v3.0.1/annexes/spdx-license-expressions/
.. _License List: https://spdx.org/licenses/
.. |SBOM| replace:: Software Bill of Material
+19
View File
@@ -139,3 +139,22 @@ you need to set the following environment variables:
* ``CTEST_USE_INSTRUMENTATION=1``
* ``CTEST_EXPERIMENTAL_INSTRUMENTATION=ec7aa2dc-b87f-45a3-8022-fe01c5f59984``
Software Bill Of Materials |SBOM|
=================================
In order to activate support for the :command:`install(SBOM)` command,
set
* variable ``CMAKE_EXPERIMENTAL_GENERATE_SBOM`` to
* value ``ca494ed3-b261-4205-a01f-603c95e4cae0``.
This UUID may change in future versions of CMake. Be sure to use the value
documented here by the source tree of the version of CMake with which you are
experimenting.
When activated, this experimental feature provides the following:
* The experimental ``install(SBOM)`` command is available to generate
a Software Bill of Materials or "SBOM" for the current project. See
:command:`install(SBOM)` for a complete overview of the command
+12
View File
@@ -204,6 +204,8 @@ add_library(
cmExportBuildFileGenerator.cxx
cmExportBuildPackageInfoGenerator.h
cmExportBuildPackageInfoGenerator.cxx
cmExportBuildSbomGenerator.h
cmExportBuildSbomGenerator.cxx
cmExportCMakeConfigGenerator.h
cmExportCMakeConfigGenerator.cxx
cmExportFileGenerator.h
@@ -216,10 +218,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
@@ -345,6 +351,8 @@ add_library(
cmInstallRuntimeDependencySet.cxx
cmInstallRuntimeDependencySetGenerator.h
cmInstallRuntimeDependencySetGenerator.cxx
cmInstallSbomExportGenerator.h
cmInstallSbomExportGenerator.cxx
cmInstallScriptGenerator.h
cmInstallScriptGenerator.cxx
cmInstallSubdirectoryGenerator.h
@@ -431,6 +439,8 @@ add_library(
cmProcessOutput.h
cmProcessTools.cxx
cmProcessTools.h
cmProjectInfoArguments.cxx
cmProjectInfoArguments.h
cmValue.cxx
cmValue.h
cmProperty.h
@@ -706,6 +716,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,
};
+86
View File
@@ -0,0 +1,86 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportBuildSbomGenerator.h"
#include <functional>
#include <utility>
#include <vector>
#include <cmext/string_view>
#include "cmGeneratorExpression.h"
#include "cmSbomArguments.h"
#include "cmSbomObject.h"
#include "cmSpdx.h"
#include "cmStringAlgorithms.h"
class cmGeneratorTarget;
cmExportBuildSbomGenerator::cmExportBuildSbomGenerator(cmSbomArguments args)
: cmExportSbomGenerator(args)
{
this->SetNamespace(cmStrCat(this->GetPackageName(), "::"_s));
}
bool cmExportBuildSbomGenerator::GenerateMainFile(std::ostream& os)
{
if (!this->CollectExports([&](cmGeneratorTarget const*) {})) {
return false;
}
cmSbomDocument doc;
doc.Graph.reserve(256);
cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom());
std::vector<TargetProperties> targets;
for (auto const& exp : this->Exports) {
cmGeneratorTarget const* target = exp.Target;
ImportPropertyMap properties;
this->PopulateInterfaceProperties(target, properties);
this->PopulateInterfaceLinkLibrariesProperty(
target, cmGeneratorExpression::BuildInterface, properties);
this->PopulateLinkLibrariesProperty(
target, cmGeneratorExpression::BuildInterface, properties);
targets.push_back(TargetProperties{
insert_back(project->RootElements, this->GenerateImportTarget(target)),
target, std::move(properties) });
}
for (auto const& target : targets) {
this->GenerateProperties(doc, project, target, targets);
}
this->WriteSbom(doc, os);
return true;
}
void cmExportBuildSbomGenerator::HandleMissingTarget(
std::string& /* link_libs */, cmGeneratorTarget const* /* depender */,
cmGeneratorTarget* /* dependee */)
{
}
std::string cmExportBuildSbomGenerator::GetCxxModulesDirectory() const
{
return {};
}
cm::string_view cmExportBuildSbomGenerator::GetImportPrefixWithSlash() const
{
return "";
}
std::string cmExportBuildSbomGenerator::GetCxxModuleFile(
std::string const& /*name*/) const
{
return {};
}
void cmExportBuildSbomGenerator::GenerateCxxModuleConfigInformation(
std::string const& /*name*/, std::ostream& /*os*/) const
{
// TODO
}
+41
View File
@@ -0,0 +1,41 @@
/* 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/string_view>
#include "cmExportBuildFileGenerator.h"
#include "cmExportSbomGenerator.h"
class cmSbomArguments;
class cmExportBuildSbomGenerator
: public cmExportBuildFileGenerator
, public cmExportSbomGenerator
{
public:
cmExportBuildSbomGenerator(cmSbomArguments args);
protected:
void HandleMissingTarget(std::string& link_libs,
cmGeneratorTarget const* depender,
cmGeneratorTarget* dependee) override;
bool GenerateMainFile(std::ostream& os) override;
void GenerateImportTargetsConfig(std::ostream&, std::string const&,
std::string const&) override
{
}
std::string GetCxxModulesDirectory() const override;
cm::string_view GetImportPrefixWithSlash() const override;
std::string GetCxxModuleFile(std::string const& /*name*/) const override;
void GenerateCxxModuleConfigInformation(std::string const& /*name*/,
std::ostream& /*os*/) const override;
};
+40 -1
View File
@@ -22,6 +22,7 @@
#include "cmExportBuildCMakeConfigGenerator.h"
#include "cmExportBuildFileGenerator.h"
#include "cmExportBuildPackageInfoGenerator.h"
#include "cmExportBuildSbomGenerator.h"
#include "cmExportSet.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
@@ -30,6 +31,7 @@
#include "cmPackageInfoArguments.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmSbomArguments.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSubcommandTable.h"
@@ -217,6 +219,7 @@ static bool HandleExportMode(std::vector<std::string> const& args,
ArgumentParser::NonEmpty<std::string> Filename;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
cm::optional<cmPackageInfoArguments> PackageInfo;
cm::optional<cmSbomArguments> Sbom;
bool ExportPackageDependencies = false;
};
@@ -243,16 +246,29 @@ static bool HandleExportMode(std::vector<std::string> const& args,
&ExportArguments::PackageInfo);
}
cmArgumentParser<cmSbomArguments> sbomParser;
cmSbomArguments::Bind(sbomParser);
if (cmExperimental::HasSupportEnabled(
status.GetMakefile(), cmExperimental::Feature::GenerateSbom)) {
parser.BindSubParser("SBOM"_s, sbomParser, &ExportArguments::Sbom);
}
std::vector<std::string> unknownArgs;
ExportArguments arguments = parser.Parse(args, &unknownArgs);
cmMakefile& mf = status.GetMakefile();
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
if (arguments.PackageInfo && arguments.Sbom) {
status.SetError("PACKAGE_INFO and SBOM are mutually exclusive.");
return false;
}
if (!arguments.Check(args[0], &unknownArgs, status)) {
cmPolicies::PolicyStatus const p =
status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0208);
if (arguments.PackageInfo || !unknownArgs.empty() ||
if (arguments.PackageInfo || arguments.Sbom || !unknownArgs.empty() ||
p == cmPolicies::NEW) {
return false;
}
@@ -279,11 +295,31 @@ static bool HandleExportMode(std::vector<std::string> const& args,
return false;
}
}
if (arguments.Sbom) {
if (arguments.Sbom->PackageName.empty()) {
status.SetError("SBOM missing required value.");
return false;
}
if (!arguments.Filename.empty()) {
status.SetError("SBOM and FILE are mutually exclusive.");
return false;
}
if (!arguments.Namespace.empty()) {
status.SetError("SBOM and NAMESPACE are mutually exclusive.");
return false;
}
if (!arguments.Sbom->Check(status) ||
!arguments.Sbom->SetMetadataFromProject(status)) {
return false;
}
}
std::string fname;
if (arguments.Filename.empty()) {
if (arguments.PackageInfo) {
fname = arguments.PackageInfo->GetPackageFileName();
} else if (arguments.Sbom) {
fname = arguments.Sbom->GetPackageFileName();
} else {
fname = arguments.ExportSetName + ".cmake";
}
@@ -338,6 +374,9 @@ static bool HandleExportMode(std::vector<std::string> const& args,
auto ebpg = cm::make_unique<cmExportBuildPackageInfoGenerator>(
*arguments.PackageInfo);
ebfg = std::move(ebpg);
} else if (arguments.Sbom) {
auto ebsg = cm::make_unique<cmExportBuildSbomGenerator>(*arguments.Sbom);
ebfg = std::move(ebsg);
} else {
auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>();
ebcg->SetNamespace(arguments.Namespace);
+4 -3
View File
@@ -135,6 +135,10 @@ protected:
ImportPropertyMap& properties,
std::set<std::string>& importedLocations);
virtual bool CheckInterfaceDirs(std::string const& prepro,
cmGeneratorTarget const* target,
std::string const& prop) const;
cmInstallExportGenerator* IEGen;
// The import file generated for each configuration.
@@ -145,9 +149,6 @@ protected:
std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles;
private:
bool CheckInterfaceDirs(std::string const& prepro,
cmGeneratorTarget const* target,
std::string const& prop) const;
void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target,
ImportPropertyMap& properties);
void PopulateCustomTransitiveInterfaceProperties(
+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 = {});
};
+11 -52
View File
@@ -2,7 +2,6 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportPackageInfoGenerator.h"
#include <cstddef>
#include <memory>
#include <set>
#include <utility>
@@ -295,51 +294,6 @@ bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
return result;
}
namespace {
bool ForbidGeneratorExpressions(
cmGeneratorTarget const* target, std::string const& propertyName,
std::string const& propertyValue, std::string& evaluatedValue,
std::map<std::string, std::vector<std::string>>& allowList)
{
size_t const allowedExpressions = allowList.size();
evaluatedValue = cmGeneratorExpression::Collect(propertyValue, allowList);
if (evaluatedValue != propertyValue &&
allowList.size() > allowedExpressions) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Property \"", propertyName, "\" of target \"",
target->GetName(),
"\" contains a generator expression. This is not allowed."));
return false;
}
// Forbid Nested Generator Expressions
for (auto const& genexp : allowList) {
for (auto const& value : genexp.second) {
if (value.find("$<") != std::string::npos) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat(
"$<", genexp.first, ":...> expression in \"", propertyName,
"\" of target \"", target->GetName(),
"\" contains a generator expression. This is not allowed."));
return false;
}
}
}
return true;
}
bool ForbidGeneratorExpressions(cmGeneratorTarget const* target,
std::string const& propertyName,
std::string const& propertyValue)
{
std::map<std::string, std::vector<std::string>> allowList;
std::string evaluatedValue;
return ForbidGeneratorExpressions(target, propertyName, propertyValue,
evaluatedValue, allowList);
}
}
bool cmExportPackageInfoGenerator::NoteLinkedTarget(
cmGeneratorTarget const* target, std::string const& linkedName,
cmGeneratorTarget const* linkedTarget)
@@ -446,8 +400,9 @@ void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
std::map<std::string, std::vector<std::string>> allowList = { { "LINK_ONLY",
{} } };
std::string interfaceLinkLibraries;
if (!ForbidGeneratorExpressions(target, iter->first, iter->second,
interfaceLinkLibraries, allowList)) {
if (!cmGeneratorExpression::ForbidGeneratorExpressions(
target, iter->first, iter->second, interfaceLinkLibraries,
allowList)) {
result = false;
return;
}
@@ -490,7 +445,8 @@ void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
return;
}
if (!ForbidGeneratorExpressions(target, iter->first, iter->second)) {
if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
iter->second)) {
result = false;
return;
}
@@ -519,7 +475,8 @@ void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
}
// TODO: Support language-specific defines.
if (!ForbidGeneratorExpressions(target, iter->first, iter->second)) {
if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
iter->second)) {
result = false;
return;
}
@@ -550,7 +507,8 @@ void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
return;
}
if (!ForbidGeneratorExpressions(target, prop, iter->second)) {
if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, prop,
iter->second)) {
result = false;
return;
}
@@ -571,7 +529,8 @@ void cmExportPackageInfoGenerator::GenerateProperty(
return;
}
if (!ForbidGeneratorExpressions(target, inName, iter->second)) {
if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, inName,
iter->second)) {
result = false;
return;
}
+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;
};
+6 -2
View File
@@ -1671,6 +1671,12 @@ bool cmFindPackageCommand::HandlePackageMode(
"fileFound is true but FileFound is empty!");
fileFound = false;
}
if (fileFound) {
this->CurrentPackageInfo->Directory =
cmSystemTools::GetFilenamePath(this->FileFound);
this->CurrentPackageInfo->Version = this->VersionFound;
}
}
std::string const foundVar = cmStrCat(this->Name, "_FOUND");
@@ -1975,8 +1981,6 @@ bool cmFindPackageCommand::FindConfig()
std::string init;
if (found) {
init = cmSystemTools::GetFilenamePath(this->FileFound);
this->CurrentPackageInfo->Directory = init;
this->CurrentPackageInfo->Version = this->VersionFound;
} else {
init = this->Variable + "-NOTFOUND";
}
+48
View File
@@ -4,6 +4,7 @@
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <memory>
#include <stack>
#include <utility>
@@ -18,8 +19,11 @@
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorExpressionLexer.h"
#include "cmGeneratorExpressionParser.h"
#include "cmGeneratorTarget.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmake.h"
@@ -414,6 +418,50 @@ std::string cmGeneratorExpression::Collect(
return extractAllGeneratorExpressions(input, &collected);
}
bool cmGeneratorExpression::ForbidGeneratorExpressions(
cmGeneratorTarget const* target, std::string const& propertyName,
std::string const& propertyValue)
{
std::map<std::string, std::vector<std::string>> allowList;
std::string evaluatedValue;
return ForbidGeneratorExpressions(target, propertyName, propertyValue,
evaluatedValue, allowList);
}
bool cmGeneratorExpression::ForbidGeneratorExpressions(
cmGeneratorTarget const* target, std::string const& propertyName,
std::string const& propertyValue, std::string& evaluatedValue,
std::map<std::string, std::vector<std::string>>& allowList)
{
size_t const initialAllowedGenExps = allowList.size();
evaluatedValue = Collect(propertyValue, allowList);
if (evaluatedValue != propertyValue &&
allowList.size() > initialAllowedGenExps) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Property \"", propertyName, "\" of target \"",
target->GetName(),
"\" contains a generator expression. This is not allowed."));
return false;
}
// Check for nested generator expressions (e.g., $<LINK_ONLY:$<...>>).
for (auto const& genexp : allowList) {
for (auto const& value : genexp.second) {
if (value.find("$<") != std::string::npos) {
target->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("$<", genexp.first, ":...> expression in \"", propertyName,
"\" of target \"", target->GetName(),
"\" contains a generator expression. This is not "
"allowed."));
return false;
}
}
}
return true;
}
cm::string_view::size_type cmGeneratorExpression::Find(cm::string_view input)
{
cm::string_view::size_type const openpos = input.find("$<");
+9
View File
@@ -58,6 +58,15 @@ public:
cmGeneratorTarget const* currentTarget = nullptr,
std::string const& language = std::string());
static bool ForbidGeneratorExpressions(
cmGeneratorTarget const* target, std::string const& propertyName,
std::string const& propertyValue, std::string& evaluatedValue,
std::map<std::string, std::vector<std::string>>& allowList);
static bool ForbidGeneratorExpressions(cmGeneratorTarget const* target,
std::string const& propertyName,
std::string const& propertyValue);
enum PreprocessContext
{
StripAllGeneratorExpressions,
+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"; }
};
+4 -98
View File
@@ -2,35 +2,20 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmPackageInfoArguments.h"
#include <utility>
#include <cm/string_view>
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
template void cmPackageInfoArguments::Bind<void>(cmArgumentParser<void>&,
cmPackageInfoArguments*);
namespace {
bool ArgWasSpecified(std::string const& value)
cm::string_view cmPackageInfoArguments::CommandName() const
{
return !value.empty();
return "PACKAGE_INFO"_s;
}
bool ArgWasSpecified(std::vector<std::string> const& value)
{
return !value.empty();
}
} // anonymous namespace
#define ENFORCE_REQUIRES(req, value, arg) \
do { \
if (ArgWasSpecified(value)) { \
@@ -60,9 +45,6 @@ bool cmPackageInfoArguments::Check(cmExecutionStatus& status) const
"DEFAULT_CONFIGURATIONS");
ENFORCE_EXCLUSIVE("APPENDIX", this->ProjectName, "PROJECT");
}
if (this->NoProjectDefaults) {
ENFORCE_EXCLUSIVE("PROJECT", this->ProjectName, "NO_PROJECT_METADATA");
}
// Check for options that require other options.
if (this->Version.empty()) {
@@ -70,67 +52,12 @@ bool cmPackageInfoArguments::Check(cmExecutionStatus& status) const
ENFORCE_REQUIRES("VERSION", this->VersionSchema, "VERSION_SCHEMA");
}
// Validate the package name.
if (!this->PackageName.empty()) {
if (!cmGeneratorExpression::IsValidTargetName(this->PackageName) ||
this->PackageName.find(':') != std::string::npos) {
status.SetError(
cmStrCat(R"(PACKAGE_INFO given invalid package name ")"_s,
this->PackageName, R"(".)"_s));
return false;
}
}
return true;
return cmProjectInfoArguments::Check(status);
}
#undef ENFORCE_REQUIRES
#undef ENFORCE_EXCLUSIVE
bool cmPackageInfoArguments::SetMetadataFromProject(cmExecutionStatus& status)
{
// Determine what project to use for inherited metadata.
if (!this->SetEffectiveProject(status)) {
return false;
}
if (this->ProjectName.empty()) {
// We are not inheriting from a project.
return true;
}
cmMakefile& mf = status.GetMakefile();
auto mapProjectValue = [&](std::string& arg, cm::string_view suffix) {
cmValue const& projectValue =
mf.GetDefinition(cmStrCat(this->ProjectName, '_', suffix));
if (projectValue) {
arg = *projectValue;
return true;
}
return false;
};
if (this->Version.empty()) {
if (mapProjectValue(this->Version, "VERSION"_s)) {
mapProjectValue(this->VersionCompat, "COMPAT_VERSION"_s);
}
}
if (this->License.empty()) {
mapProjectValue(this->License, "SPDX_LICENSE"_s);
}
if (this->Description.empty()) {
mapProjectValue(this->Description, "DESCRIPTION"_s);
}
if (this->Website.empty()) {
mapProjectValue(this->Website, "HOMEPAGE_URL"_s);
}
return true;
}
bool cmPackageInfoArguments::SetEffectiveProject(cmExecutionStatus& status)
{
if (!this->Appendix.empty()) {
@@ -138,28 +65,7 @@ bool cmPackageInfoArguments::SetEffectiveProject(cmExecutionStatus& status)
return true;
}
if (this->NoProjectDefaults) {
// User requested that metadata not be inherited.
return true;
}
cmMakefile& mf = status.GetMakefile();
if (!this->ProjectName.empty()) {
// User specified a project; make sure it exists.
if (!mf.GetStateSnapshot().CheckProjectName(this->ProjectName)) {
status.SetError(cmStrCat(R"(PROJECT given unknown project name ")"_s,
this->ProjectName, R"(".)"_s));
return false;
}
} else {
// No project was specified; check if the package name is also a project.
std::string project = mf.GetStateSnapshot().GetProjectName();
if (this->PackageName == project) {
this->ProjectName = std::move(project);
}
}
return true;
return cmProjectInfoArguments::SetEffectiveProject(status);
}
std::string cmPackageInfoArguments::GetNamespace() const
+23 -48
View File
@@ -7,13 +7,13 @@
#include <string>
#include <vector>
#include <cm/string_view>
#include <cm/type_traits>
#include <cmext/string_view>
#include "cmArgumentParser.h" // IWYU pragma: keep
#include "cmArgumentParserTypes.h"
class cmExecutionStatus;
#include "cmProjectInfoArguments.h"
/** \class cmPackageInfoArguments
* \brief Convey information about a package.
@@ -23,7 +23,7 @@ class cmExecutionStatus;
* container, and also provides utilities to obtain this metadata from commands
* which produce packages (i.e. export and install).
*/
class cmPackageInfoArguments
class cmPackageInfoArguments : public cmProjectInfoArguments
{
public:
template <typename T,
@@ -31,7 +31,8 @@ public:
std::is_base_of<cmPackageInfoArguments, T>::value>>
static void Bind(cmArgumentParser<T>& parser)
{
cmPackageInfoArguments::Bind(parser, nullptr);
cmPackageInfoArguments* const self = nullptr;
cmPackageInfoArguments::Bind(parser, self);
}
void Bind(cmArgumentParser<void>& parser)
@@ -43,76 +44,50 @@ public:
std::string GetPackageDirName() const;
std::string GetPackageFileName() const;
/// Ensure that no conflicting options were specified.
bool Check(cmExecutionStatus& status) const;
bool Check(cmExecutionStatus& status) const override;
/// Set metadata (not already specified) from either the specified project,
/// or from the project which matches the package name.
bool SetMetadataFromProject(cmExecutionStatus& status);
ArgumentParser::NonEmpty<std::string> PackageName;
ArgumentParser::NonEmpty<std::string> Appendix;
ArgumentParser::NonEmpty<std::string> Version;
ArgumentParser::NonEmpty<std::string> VersionCompat;
ArgumentParser::NonEmpty<std::string> VersionSchema;
ArgumentParser::NonEmpty<std::string> License;
ArgumentParser::NonEmpty<std::string> DefaultLicense;
ArgumentParser::NonEmpty<std::string> Description;
ArgumentParser::NonEmpty<std::string> Website;
ArgumentParser::NonEmpty<std::vector<std::string>> DefaultTargets;
ArgumentParser::NonEmpty<std::vector<std::string>> DefaultConfigs;
bool LowerCase = false;
ArgumentParser::NonEmpty<std::string> ProjectName;
bool NoProjectDefaults = false;
protected:
cm::string_view CommandName() const override;
private:
bool SetEffectiveProject(cmExecutionStatus& status);
bool SetEffectiveProject(cmExecutionStatus& status) override;
template <typename T>
static void Bind(cmArgumentParser<T>& parser, cmPackageInfoArguments* self)
{
Bind(self, parser, "PACKAGE_INFO"_s, &cmPackageInfoArguments::PackageName);
cmProjectInfoArguments* const base = self;
Bind(base, parser, "PACKAGE_INFO"_s, &cmProjectInfoArguments::PackageName);
Bind(self, parser, "LOWER_CASE_FILE"_s,
&cmPackageInfoArguments::LowerCase);
Bind(self, parser, "APPENDIX"_s, &cmPackageInfoArguments::Appendix);
Bind(self, parser, "VERSION"_s, &cmPackageInfoArguments::Version);
Bind(self, parser, "COMPAT_VERSION"_s,
&cmPackageInfoArguments::VersionCompat);
Bind(self, parser, "VERSION_SCHEMA"_s,
&cmPackageInfoArguments::VersionSchema);
Bind(base, parser, "COMPAT_VERSION"_s,
&cmProjectInfoArguments::VersionCompat);
Bind(base, parser, "VERSION_SCHEMA"_s,
&cmProjectInfoArguments::VersionSchema);
Bind(self, parser, "DEFAULT_TARGETS"_s,
&cmPackageInfoArguments::DefaultTargets);
Bind(self, parser, "DEFAULT_CONFIGURATIONS"_s,
&cmPackageInfoArguments::DefaultConfigs);
Bind(self, parser, "LICENSE"_s, &cmPackageInfoArguments::License);
Bind(self, parser, "DEFAULT_LICENSE"_s,
&cmPackageInfoArguments::DefaultLicense);
Bind(self, parser, "DESCRIPTION"_s, &cmPackageInfoArguments::Description);
Bind(self, parser, "HOMEPAGE_URL"_s, &cmPackageInfoArguments::Website);
Bind(self, parser, "PROJECT"_s, &cmPackageInfoArguments::ProjectName);
Bind(self, parser, "NO_PROJECT_METADATA"_s,
&cmPackageInfoArguments::NoProjectDefaults);
cmProjectInfoArguments::Bind(parser, self);
}
template <typename T, typename U,
typename = cm::enable_if_t<
std::is_base_of<cmPackageInfoArguments, T>::value>>
static void Bind(cmPackageInfoArguments*, cmArgumentParser<T>& parser,
cm::static_string_view name,
U cmPackageInfoArguments::*member)
using cmProjectInfoArguments::Bind;
static bool ArgWasSpecified(std::vector<std::string> const& value)
{
parser.Bind(name, member);
}
template <typename U>
static void Bind(cmPackageInfoArguments* self,
cmArgumentParser<void>& parser, cm::static_string_view name,
U cmPackageInfoArguments::*member)
{
parser.Bind(name, (self)->*member);
return !value.empty();
}
using cmProjectInfoArguments::ArgWasSpecified;
};
extern template void cmPackageInfoArguments::Bind<void>(
+129
View File
@@ -0,0 +1,129 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmProjectInfoArguments.h"
#include <utility>
#include <cm/string_view>
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmValue.h"
template void cmProjectInfoArguments::Bind<void>(cmArgumentParser<void>&,
cmProjectInfoArguments*);
#define ENFORCE_REQUIRES(req, value, arg) \
do { \
if (ArgWasSpecified(value)) { \
status.SetError(arg " requires " req "."); \
return false; \
} \
} while (false)
#define ENFORCE_EXCLUSIVE(arg1, value, arg2) \
do { \
if (ArgWasSpecified(value)) { \
status.SetError(arg1 " and " arg2 " are mutually exclusive."); \
return false; \
} \
} while (false)
cmProjectInfoArguments::cmProjectInfoArguments() = default;
bool cmProjectInfoArguments::Check(cmExecutionStatus& status) const
{
// Check for incompatible options.
if (this->NoProjectDefaults) {
ENFORCE_EXCLUSIVE("PROJECT", this->ProjectName, "NO_PROJECT_METADATA");
}
// Validate the package name.
if (!this->PackageName.empty()) {
if (!cmGeneratorExpression::IsValidTargetName(this->PackageName) ||
this->PackageName.find(':') != std::string::npos) {
status.SetError(cmStrCat(this->CommandName(),
" given invalid package name \""_s,
this->PackageName, "\"."_s));
return false;
}
}
return true;
}
#undef ENFORCE_REQUIRES
#undef ENFORCE_EXCLUSIVE
bool cmProjectInfoArguments::SetMetadataFromProject(cmExecutionStatus& status)
{
// Determine what project to use for inherited metadata.
if (!this->SetEffectiveProject(status)) {
return false;
}
if (this->ProjectName.empty()) {
// We are not inheriting from a project.
return true;
}
cmMakefile& mf = status.GetMakefile();
auto mapProjectValue = [&](std::string& arg, cm::string_view suffix) {
cmValue const& projectValue =
mf.GetDefinition(cmStrCat(this->ProjectName, '_', suffix));
if (projectValue) {
arg = *projectValue;
return true;
}
return false;
};
if (this->Version.empty()) {
if (mapProjectValue(this->Version, "VERSION"_s)) {
mapProjectValue(this->VersionCompat, "COMPAT_VERSION"_s);
}
}
if (this->License.empty()) {
mapProjectValue(this->License, "SPDX_LICENSE"_s);
}
if (this->Description.empty()) {
mapProjectValue(this->Description, "DESCRIPTION"_s);
}
if (this->Website.empty()) {
mapProjectValue(this->Website, "HOMEPAGE_URL"_s);
}
return true;
}
bool cmProjectInfoArguments::SetEffectiveProject(cmExecutionStatus& status)
{
if (this->NoProjectDefaults) {
// User requested that metadata not be inherited.
return true;
}
cmMakefile& mf = status.GetMakefile();
if (!this->ProjectName.empty()) {
// User specified a project; make sure it exists.
if (!mf.GetStateSnapshot().CheckProjectName(this->ProjectName)) {
status.SetError(cmStrCat(R"(PROJECT given unknown project name ")"_s,
this->ProjectName, R"(".)"_s));
return false;
}
} else {
// No project was specified; check if the package name is also a project.
std::string project = mf.GetStateSnapshot().GetProjectName();
if (this->PackageName == project) {
this->ProjectName = std::move(project);
}
}
return true;
}
+103
View File
@@ -0,0 +1,103 @@
/* 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"
class cmExecutionStatus;
/** \class cmProjectInfoArguments
* \brief Convey information about a project.
*
* This class encapsulates several attributes of project metadata;
* specifically, those which can be inherited from the project. It is used as
* the base class for classes specific to SBOM and PACKAGE_INFO exports.
*/
class cmProjectInfoArguments
{
public:
cmProjectInfoArguments();
cmProjectInfoArguments(cmProjectInfoArguments const&) = default;
cmProjectInfoArguments& operator=(cmProjectInfoArguments const&) = default;
cmProjectInfoArguments(cmProjectInfoArguments&&) = default;
cmProjectInfoArguments& operator=(cmProjectInfoArguments&&) = default;
virtual ~cmProjectInfoArguments() = default;
/// Ensure that no conflicting options were specified.
virtual bool Check(cmExecutionStatus& status) const;
/// Set metadata (not already specified) from either the specified project,
/// or from the project which matches the package name.
bool SetMetadataFromProject(cmExecutionStatus& status);
ArgumentParser::NonEmpty<std::string> PackageName;
ArgumentParser::NonEmpty<std::string> Version;
ArgumentParser::NonEmpty<std::string> VersionCompat;
ArgumentParser::NonEmpty<std::string> VersionSchema;
ArgumentParser::NonEmpty<std::string> License;
ArgumentParser::NonEmpty<std::string> Description;
ArgumentParser::NonEmpty<std::string> Website;
ArgumentParser::NonEmpty<std::string> ProjectName;
bool NoProjectDefaults = false;
protected:
virtual cm::string_view CommandName() const = 0;
virtual bool SetEffectiveProject(cmExecutionStatus& status);
template <typename T>
static void Bind(cmArgumentParser<T>& parser, cmProjectInfoArguments* self)
{
Bind(self, parser, "VERSION"_s, &cmProjectInfoArguments::Version);
Bind(self, parser, "LICENSE"_s, &cmProjectInfoArguments::License);
Bind(self, parser, "DESCRIPTION"_s, &cmProjectInfoArguments::Description);
Bind(self, parser, "HOMEPAGE_URL"_s, &cmProjectInfoArguments::Website);
Bind(self, parser, "PROJECT"_s, &cmProjectInfoArguments::ProjectName);
Bind(self, parser, "NO_PROJECT_METADATA"_s,
&cmProjectInfoArguments::NoProjectDefaults);
}
template <
typename Self, typename T, typename U,
typename =
cm::enable_if_t<std::is_base_of<cmProjectInfoArguments, Self>::value>,
typename =
cm::enable_if_t<std::is_base_of<cmProjectInfoArguments, T>::value>>
static void Bind(Self*, cmArgumentParser<T>& parser,
cm::static_string_view name, U Self::*member)
{
parser.Bind(name, member);
}
template <
typename Self, typename T, typename U,
typename =
cm::enable_if_t<std::is_base_of<cmProjectInfoArguments, Self>::value>,
typename =
cm::enable_if_t<std::is_base_of<cmProjectInfoArguments, T>::value>>
static void Bind(Self* self, cmArgumentParser<void>& parser,
cm::static_string_view name, U T::*member)
{
parser.Bind(name, (self)->*member);
}
static bool ArgWasSpecified(std::string const& value)
{
return !value.empty();
}
};
extern template void cmProjectInfoArguments::Bind<void>(
cmArgumentParser<void>&, cmProjectInfoArguments*);
+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*);
+2
View File
@@ -1389,8 +1389,10 @@ add_RunCMake_test(AutoExportDll
add_RunCMake_test(AndroidMK)
add_RunCMake_test(ExportPackageInfo)
add_RunCMake_test(ExportSbom)
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_BINARY_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)
export(
EXPORT application_targets
SBOM application_targets
FORMAT "spdx-3.0+json"
)
+3
View File
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.30)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_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)
export(
EXPORT interface_targets
SBOM interface_targets
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/interface_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)
export(
EXPORT test_targets
SBOM interface_targets
VERSION 1.0.2
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/test_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake)
@@ -0,0 +1,10 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake)
export(
EXPORT test_targets
SBOM test_targets
DESCRIPTION "An eloquent description"
LICENSE "BSD-3"
HOMEPAGE_URL "www.example.com"
VERSION "1.3.4"
)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_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)
export(EXPORT dog SBOM dog)
@@ -0,0 +1,3 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/bar.spdx.json" BAR_CONTENT)
file(READ "${RunCMake_TEST_BINARY_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)
export(EXPORT foo SBOM foo)
export(EXPORT bar SBOM bar)
@@ -0,0 +1,32 @@
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(MissingPackageNamespace)
run_cmake_install(ReferencesNonExportedTarget)
@@ -0,0 +1,2 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/shared_targets.spdx.json" content)
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SharedTarget-install-check.cmake)
@@ -0,0 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SharedTarget.cmake)
export(
EXPORT shared_targets
SBOM shared_targets
)
@@ -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)
{
}