mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Merge topic 'sbom-export-generators'
8ec5a595cdcmSbom: Add documentation for `install(SBOM)` and `export(SBOM)`83671f2d87cmSbom: Add `export(SBOM)` build generators and unit testsf2027a886bcmSbom: Add `install(SBOM)` generator and unit testsdcadde3662CPS: Refactor ForbidGeneratorExpressions into cmGeneratorExpressionbcb6f689a4cmExportInstallFileGenerator: Allow overriding of CheckInterfaceDirs6d6c37c90dcmPackageInfoArguments: Extract common values8c6676b2eacmFindPackage: 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:
@@ -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
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
MappedPackageInfo,
|
||||
ExportBuildDatabase,
|
||||
Instrumentation,
|
||||
GenerateSbom,
|
||||
|
||||
Sentinel,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = {});
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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("$<");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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"; }
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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*);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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*);
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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 .)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
int main(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
void foo(int a)
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user