diff --git a/Source/cmBuildSbomBuilder.cxx b/Source/cmBuildSbomBuilder.cxx index 24dbfa1e55..b7ff854165 100644 --- a/Source/cmBuildSbomBuilder.cxx +++ b/Source/cmBuildSbomBuilder.cxx @@ -2,26 +2,19 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmBuildSbomBuilder.h" -#include - #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" -#include "cmSbomArguments.h" -cmBuildSbomBuilder::cmBuildSbomBuilder(cmSbomArguments args, - std::vector exportSets, - cmLocalGenerator* lg) - : cmSbomBuilder(std::move(args), std::move(exportSets), lg) +cmBuildSbomBuilder::cmBuildSbomBuilder( + cmSbomArguments const& args, std::vector const& exportSets) + : cmSbomBuilder(args, exportSets) { } bool cmBuildSbomBuilder::Generate(std::ostream& os, std::string const& config) { - if (!this->LocalGenerator) { - return false; - } return this->GenerateForTargets(os, config, cmGeneratorExpression::BuildInterface); } @@ -34,9 +27,10 @@ cmExportFileGenerator::ExportInfo cmBuildSbomBuilder::FindExportInfoFor( ->FindBuildExportInfo(target); } -cmSbomBuilder::SbomInfo cmBuildSbomBuilder::FindSbomInfoFor( +std::vector cmBuildSbomBuilder::FindSbomPackagesFor( cmGeneratorTarget const* target) const { - return target->GetLocalGenerator()->GetGlobalGenerator()->FindBuildSbomInfo( - target); + return target->GetLocalGenerator() + ->GetGlobalGenerator() + ->FindBuildSbomPackages(target); } diff --git a/Source/cmBuildSbomBuilder.h b/Source/cmBuildSbomBuilder.h index c9cf430d7c..5bc2418b23 100644 --- a/Source/cmBuildSbomBuilder.h +++ b/Source/cmBuildSbomBuilder.h @@ -12,7 +12,6 @@ #include "cmSbomBuilder.h" class cmExportSet; -class cmLocalGenerator; class cmSbomArguments; /** Build-tree SBOM (`export(SBOM ...)`). Covers the targets in the @@ -20,14 +19,14 @@ class cmSbomArguments; class cmBuildSbomBuilder final : public cmSbomBuilder { public: - cmBuildSbomBuilder(cmSbomArguments args, - std::vector exportSets, - cmLocalGenerator* lg = nullptr); + cmBuildSbomBuilder(cmSbomArguments const& args, + std::vector const& exportSets); bool Generate(std::ostream& os, std::string const& config) override; protected: cmExportFileGenerator::ExportInfo FindExportInfoFor( cmGeneratorTarget const* target) const override; - SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override; + std::vector FindSbomPackagesFor( + cmGeneratorTarget const* target) const override; }; diff --git a/Source/cmBuildSbomGenerator.cxx b/Source/cmBuildSbomGenerator.cxx index 55e654c8d4..dcbe2a6d5e 100644 --- a/Source/cmBuildSbomGenerator.cxx +++ b/Source/cmBuildSbomGenerator.cxx @@ -2,6 +2,7 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmBuildSbomGenerator.h" +#include "cmBuildSbomBuilder.h" #include "cmGeneratedFileStream.h" #include "cmStringAlgorithms.h" @@ -15,5 +16,5 @@ bool cmBuildSbomGenerator::GenerateForBuild(std::string const& config) cmGeneratedFileStream os(cmStrCat(this->OutputFile, '-', config.empty() ? "noconfig" : config, ".spdx.json")); - return this->Builder->Generate(os, config); + return this->GenerateForBuild(os, config); } diff --git a/Source/cmBuildSbomGenerator.h b/Source/cmBuildSbomGenerator.h index 957d9c9a72..6f72b42b22 100644 --- a/Source/cmBuildSbomGenerator.h +++ b/Source/cmBuildSbomGenerator.h @@ -10,28 +10,27 @@ #include #include "cmBuildSbomBuilder.h" -#include "cmSbomArguments.h" + class cmExportSet; class cmGeneratorTarget; class cmLocalGenerator; +class cmSbomArguments; /** \class cmBuildSbomGenerator - * \brief Thin wrapper around cmBuildSbomBuilder for the build-tree SBOM case. + * \brief Generate build-tree SBOM files, one document per configuration. * * Stored on cmMakefile at configure time. At generate time, * ComputeBuildFileGenerators() calls Compute(lg) to resolve export set - * targets and supply the local generator - mirroring - * cmExportBuildFileGenerator. + * targets for peer SBOM lookups before any documents are generated. */ class cmBuildSbomGenerator { public: - cmBuildSbomGenerator(cmSbomArguments args, - std::vector exportSets, + cmBuildSbomGenerator(cmSbomArguments const& args, + std::vector const& exportSets, std::string outputFile) : OutputFile(std::move(outputFile)) - , Builder(cm::make_unique(std::move(args), - std::move(exportSets))) + , Builder(cm::make_unique(args, exportSets)) { } @@ -60,6 +59,6 @@ public: bool GenerateForBuild(std::string const& config); private: - std::string OutputFile; + std::string const OutputFile; std::unique_ptr Builder; }; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 568d9d9ddc..938b65de55 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -442,7 +442,7 @@ static bool HandleSbomMode(std::vector const& args, std::string const dir = arguments.GetDefaultDestination(mf.GetCurrentBinaryDirectory()); - std::string const fpath = cmStrCat(dir, '/', arguments.GetPackageName()); + std::string fpath = cmStrCat(dir, '/', arguments.GetPackageName()); if (gg->IsBuildSbomFile(fpath)) { status.SetError(cmStrCat("SBOM command already specified for the file "_s, @@ -461,7 +461,8 @@ static bool HandleSbomMode(std::vector const& args, sets.push_back(*exportSet); } - auto builder = cm::make_unique(arguments, sets, fpath); + auto builder = + cm::make_unique(arguments, sets, std::move(fpath)); cmBuildSbomGenerator* rawPtr = builder.get(); mf.AddBuildSbomGenerator(std::move(builder)); gg->AddBuildSbomGenerator(rawPtr); diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index dcba10eca2..b3c9103835 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -37,6 +37,7 @@ #include "cmCustomCommandTypes.h" #include "cmDiagnostics.h" #include "cmDuration.h" +#include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmExportBuildFileGenerator.h" #include "cmExternalMakefileProjectGenerator.h" @@ -361,30 +362,30 @@ cmExportFileGenerator::ExportInfo cmGlobalGenerator::FindInstallExportInfo( } #ifndef CMAKE_BOOTSTRAP -cmSbomBuilder::SbomInfo cmGlobalGenerator::FindBuildSbomInfo( +std::vector cmGlobalGenerator::FindBuildSbomPackages( cmGeneratorTarget const* target) const { - cmSbomBuilder::SbomInfo info; + std::vector packages; for (cmBuildSbomGenerator const* g : this->BuildSbomGenerators) { if (g->CoversTarget(target)) { - info.Packages.push_back(g->GetPackageName()); + packages.push_back(g->GetPackageName()); } } - std::sort(info.Packages.begin(), info.Packages.end()); - return info; + std::sort(packages.begin(), packages.end()); + return packages; } -cmSbomBuilder::SbomInfo cmGlobalGenerator::FindInstallSbomInfo( +std::vector cmGlobalGenerator::FindInstallSbomPackages( cmGeneratorTarget const* target) const { - cmSbomBuilder::SbomInfo info; + std::vector packages; for (cmInstallSbomGenerator const* g : this->InstallSbomGenerators) { if (g->CoversTarget(target)) { - info.Packages.push_back(g->GetPackageName()); + packages.push_back(g->GetPackageName()); } } - std::sort(info.Packages.begin(), info.Packages.end()); - return info; + std::sort(packages.begin(), packages.end()); + return packages; } #endif @@ -1834,7 +1835,7 @@ bool cmGlobalGenerator::Compute() return false; } - std::string projectName = this->LocalGenerators[0]->GetProjectName(); + std::string const projectName = this->LocalGenerators[0]->GetProjectName(); for (auto& exportSet : this->ExportSets) { bool isCovered = std::any_of(this->InstallSbomGenerators.cbegin(), @@ -1850,14 +1851,20 @@ bool cmGlobalGenerator::Compute() args.Format = *sbomFormat; args.ProjectName = projectName; args.PackageName = exportSet.first; + cmExecutionStatus status(*this->Makefiles[0]); + if (!args.SetMetadataFromProject(status)) { + this->Makefiles[0]->IssueMessage(MessageType::FATAL_ERROR, + status.GetError()); + return false; + } std::string dest = args.GetDefaultDestination( cm::InstallDirs::GetLibraryDirectory(this->Makefiles[0].get())); auto installGen = cm::make_unique( - std::vector{ &exportSet.second }, dest, "", + std::vector{ &exportSet.second }, std::move(dest), "", std::vector(), "", cmInstallGenerator::SelectMessageLevel(this->Makefiles[0].get()), - false, std::move(args), + false, args, cmInstallGenerator::CaptureContext(this->Makefiles[0].get())); cmInstallSbomGenerator const* rawPtr = installGen.get(); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 8f2f3cee8b..021a4e4646 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -28,7 +28,6 @@ #include "cmExportSet.h" #include "cmInstallGenerator.h" #include "cmLocalGenerator.h" -#include "cmSbomBuilder.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -731,11 +730,11 @@ public: cmGeneratorTarget const* target) const; /** Scan all build-tree SBOMs and report which of them cover `target`. */ - cmSbomBuilder::SbomInfo FindBuildSbomInfo( + std::vector FindBuildSbomPackages( cmGeneratorTarget const* target) const; - /** Same as FindBuildSbomInfo, but searches install-tree SBOMs. */ - cmSbomBuilder::SbomInfo FindInstallSbomInfo( + /** Same as FindBuildSbomPackages, but searches install-tree SBOMs. */ + std::vector FindInstallSbomPackages( cmGeneratorTarget const* target) const; void AddBuildExportSet(cmExportBuildFileGenerator* gen); void AddBuildExportExportSet(cmExportBuildFileGenerator* gen); diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 8c9495ef8e..5cb7cb222a 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2898,8 +2898,8 @@ bool HandleSbomMode(std::vector const& args, // Create the SBOM install generator. auto sbomGen = cm::make_unique( - std::move(sets), dest, ica.GetPermissions(), ica.GetConfigurations(), - ica.GetComponent(), message, ica.GetExcludeFromAll(), std::move(arguments), + sets, std::move(dest), ica.GetPermissions(), ica.GetConfigurations(), + ica.GetComponent(), message, ica.GetExcludeFromAll(), arguments, helper.CaptureContext()); cmInstallSbomGenerator const* rawPtr = sbomGen.get(); helper.Makefile->AddInstallGenerator(std::move(sbomGen)); diff --git a/Source/cmInstallSbomBuilder.cxx b/Source/cmInstallSbomBuilder.cxx index 00178c7dae..05afbd29c4 100644 --- a/Source/cmInstallSbomBuilder.cxx +++ b/Source/cmInstallSbomBuilder.cxx @@ -2,27 +2,20 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmInstallSbomBuilder.h" -#include - #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" -#include "cmSbomArguments.h" cmInstallSbomBuilder::cmInstallSbomBuilder( - cmSbomArguments args, std::vector exportSets, - cmLocalGenerator* lg) - : cmSbomBuilder(std::move(args), std::move(exportSets), lg) + cmSbomArguments const& args, std::vector const& exportSets) + : cmSbomBuilder(args, exportSets) { } bool cmInstallSbomBuilder::Generate(std::ostream& os, std::string const& config) { - if (!this->LocalGenerator) { - return false; - } return this->GenerateForTargets(os, config, cmGeneratorExpression::InstallInterface); } @@ -35,10 +28,10 @@ cmExportFileGenerator::ExportInfo cmInstallSbomBuilder::FindExportInfoFor( ->FindInstallExportInfo(target); } -cmSbomBuilder::SbomInfo cmInstallSbomBuilder::FindSbomInfoFor( +std::vector cmInstallSbomBuilder::FindSbomPackagesFor( cmGeneratorTarget const* target) const { return target->GetLocalGenerator() ->GetGlobalGenerator() - ->FindInstallSbomInfo(target); + ->FindInstallSbomPackages(target); } diff --git a/Source/cmInstallSbomBuilder.h b/Source/cmInstallSbomBuilder.h index 77f231d810..782a099b1c 100644 --- a/Source/cmInstallSbomBuilder.h +++ b/Source/cmInstallSbomBuilder.h @@ -12,7 +12,6 @@ #include "cmSbomBuilder.h" class cmExportSet; -class cmLocalGenerator; class cmSbomArguments; /** Install-tree SBOM (`install(SBOM ...)`). Covers the targets in the @@ -20,14 +19,14 @@ class cmSbomArguments; class cmInstallSbomBuilder final : public cmSbomBuilder { public: - cmInstallSbomBuilder(cmSbomArguments args, - std::vector exportSets, - cmLocalGenerator* lg = nullptr); + cmInstallSbomBuilder(cmSbomArguments const& args, + std::vector const& exportSets); bool Generate(std::ostream& os, std::string const& config) override; protected: cmExportFileGenerator::ExportInfo FindExportInfoFor( cmGeneratorTarget const* target) const override; - SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const override; + std::vector FindSbomPackagesFor( + cmGeneratorTarget const* target) const override; }; diff --git a/Source/cmInstallSbomGenerator.cxx b/Source/cmInstallSbomGenerator.cxx index c00b5d94ed..89f37ec76d 100644 --- a/Source/cmInstallSbomGenerator.cxx +++ b/Source/cmInstallSbomGenerator.cxx @@ -15,25 +15,24 @@ #include "cmInstallType.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmSbomArguments.h" #include "cmScriptGenerator.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" cmInstallSbomGenerator::cmInstallSbomGenerator( - std::vector exportSets, std::string destination, + std::vector const& exportSets, std::string destination, std::string filePermissions, std::vector const& configurations, std::string component, MessageLevel message, bool excludeFromAll, - cmSbomArguments args, cmDiagnosticContext context) + cmSbomArguments const& args, cmDiagnosticContext context) : cmInstallGenerator(std::move(destination), configurations, std::move(component), message, excludeFromAll, false, std::move(context)) , FilePermissions(std::move(filePermissions)) , SbomFileName(args.GetPackageName()) , SbomFilePath(cmStrCat(this->Destination, '/', this->SbomFileName)) - , SbomFormat(args.GetFormat()) - , Builder(cm::make_unique(std::move(args), - std::move(exportSets))) + , Builder(cm::make_unique(args, exportSets)) { } @@ -42,8 +41,7 @@ cmInstallSbomGenerator::~cmInstallSbomGenerator() = default; bool cmInstallSbomGenerator::Compute(cmLocalGenerator* lg) { this->LocalGenerator = lg; - this->Builder->Compute(lg); - return true; + return this->Builder->Compute(lg); } bool cmInstallSbomGenerator::CoversTarget( @@ -78,10 +76,15 @@ void cmInstallSbomGenerator::GenerateScript(std::ostream& os) cmStrCat(tempDir, '/', this->SbomFileName, '-', c.empty() ? "noconfig" : c, ".spdx.json"); cmGeneratedFileStream sbomStream(configName); - this->TempSbomFiles.emplace(c, configName); if (!this->Builder->Generate(sbomStream, c)) { - break; + if (!cmSystemTools::GetErrorOccurredFlag()) { + this->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Could not write SBOM file: ", configName)); + } + return; } + this->TempSbomFiles.emplace(c, std::move(configName)); } // Emit the cmake_install.cmake script to copy the file at install time. diff --git a/Source/cmInstallSbomGenerator.h b/Source/cmInstallSbomGenerator.h index 2c65c058c4..56aa90cdad 100644 --- a/Source/cmInstallSbomGenerator.h +++ b/Source/cmInstallSbomGenerator.h @@ -11,30 +11,29 @@ #include #include "cmInstallGenerator.h" -#include "cmSbomArguments.h" class cmDiagnosticContext; class cmExportSet; class cmGeneratorTarget; class cmInstallSbomBuilder; class cmLocalGenerator; +class cmSbomArguments; /** \class cmInstallSbomGenerator * \brief Generate installation rules for SBOM files. * - * Thin cmInstallGenerator subclass that owns a cmInstallSbomBuilder. - * At generate time it writes the SBOM into a temporary file in the build tree; + * At generate time it builds each configuration's SBOM in a temporary file; * the cmake_install.cmake script then copies that file to the install * destination. */ class cmInstallSbomGenerator : public cmInstallGenerator { public: - cmInstallSbomGenerator(std::vector exportSets, + cmInstallSbomGenerator(std::vector const& exportSets, std::string destination, std::string filePermissions, std::vector const& configurations, std::string component, MessageLevel message, - bool excludeFromAll, cmSbomArguments args, + bool excludeFromAll, cmSbomArguments const& args, cmDiagnosticContext context); cmInstallSbomGenerator(cmInstallSbomGenerator const&) = delete; ~cmInstallSbomGenerator() override; @@ -64,7 +63,6 @@ private: std::string const FilePermissions; std::string const SbomFileName; std::string const SbomFilePath; - cmSbomArguments::SbomFormat SbomFormat; - cmLocalGenerator* LocalGenerator = nullptr; + cmLocalGenerator const* LocalGenerator = nullptr; std::unique_ptr Builder; }; diff --git a/Source/cmSbomArguments.cxx b/Source/cmSbomArguments.cxx index fa7eb8757d..f43c17de7c 100644 --- a/Source/cmSbomArguments.cxx +++ b/Source/cmSbomArguments.cxx @@ -8,41 +8,21 @@ #include "cmGeneratorExpression.h" #include "cmStringAlgorithms.h" -struct SbomFormatPattern +namespace { + +struct SbomFormatEntry { cm::string_view Name; - cm::string_view Alias; - cmSbomArguments::SbomFormat Id; + cmSbomArguments::SbomFormat Format; }; -static SbomFormatPattern SbomPatterns[] = { - { "spdx-3.0+json", "spdx", cmSbomArguments::SbomFormat::SPDX_3_0_JSON }, +SbomFormatEntry const SbomFormats[] = { + { "spdx"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON }, + { "spdx-3.0+json"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON }, + { "spdx-3.0.1+json"_s, cmSbomArguments::SbomFormat::SPDX_3_0_JSON }, }; -cmSbomArguments::SbomFormat ParseSbomFormat(std::string const& input) -{ - if (input.empty()) { - return cmSbomArguments::SbomFormat::SPDX_3_0_JSON; - } - - cm::string_view s(input); - for (auto const& p : SbomPatterns) { - if (s == p.Name || (!p.Alias.empty() && s == p.Alias)) { - return p.Id; - } - } - return cmSbomArguments::SbomFormat::NONE; -} - -std::string GetSbomFileExtension(cmSbomArguments::SbomFormat id) -{ - switch (id) { - case cmSbomArguments::SbomFormat::SPDX_3_0_JSON: - return ".spdx.json"; - default: - return ""; - } -} +} // namespace template void cmSbomArguments::Bind(cmArgumentParser&, cmSbomArguments*); @@ -58,12 +38,10 @@ bool cmSbomArguments::Check(cmExecutionStatus& status) const } } - if (!this->Format.empty()) { - if (this->GetFormat() == SbomFormat::NONE) { - status.SetError( - cmStrCat(R"(SBOM given invalid format ")"_s, this->Format, R"(".)"_s)); - return false; - } + if (this->GetFormat() == SbomFormat::NONE) { + status.SetError( + cmStrCat(R"(SBOM given invalid format ")"_s, this->Format, R"(".)"_s)); + return false; } if (!this->License.empty()) { @@ -91,6 +69,11 @@ std::string cmSbomArguments::GetPackageName() const return this->PackageName; } +std::string const& cmSbomArguments::GetDataLicense() const +{ + return this->DataLicense.empty() ? this->License : this->DataLicense; +} + std::string cmSbomArguments::GetDefaultDestination( std::string const& root) const { @@ -105,5 +88,11 @@ cmSbomArguments::SbomFormat cmSbomArguments::GetFormat() const if (this->Format.empty()) { return SbomFormat::SPDX_3_0_JSON; } - return ParseSbomFormat(this->Format); + + for (auto const& entry : SbomFormats) { + if (this->Format == entry.Name) { + return entry.Format; + } + } + return SbomFormat::NONE; } diff --git a/Source/cmSbomArguments.h b/Source/cmSbomArguments.h index a45d52ed1d..b78a29cce3 100644 --- a/Source/cmSbomArguments.h +++ b/Source/cmSbomArguments.h @@ -36,6 +36,9 @@ public: bool Check(cmExecutionStatus& status) const override; std::string GetNamespace() const; std::string GetPackageName() const; + /** Explicit data license, or the license inherited from the selected + * project. */ + std::string const& GetDataLicense() const; std::string GetDefaultDestination(std::string const& root = {}) const; SbomFormat GetFormat() const; diff --git a/Source/cmSbomBuilder.cxx b/Source/cmSbomBuilder.cxx index fe15ad846b..07c470c061 100644 --- a/Source/cmSbomBuilder.cxx +++ b/Source/cmSbomBuilder.cxx @@ -2,9 +2,8 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include "cmSbomBuilder.h" -#include -#include -#include +#include +#include #include #include #include @@ -14,23 +13,26 @@ #include #include -#include #include "cmArgumentParserTypes.h" #include "cmDiagnostics.h" -#include "cmExportFileGenerator.h" #include "cmExportSet.h" #include "cmFindPackageStack.h" +#include "cmGenExContext.h" #include "cmGeneratorExpression.h" +#include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorTarget.h" +#include "cmLinkItem.h" #include "cmList.h" -#include "cmLocalGenerator.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmSbomArguments.h" #include "cmSbomObject.h" #include "cmSpdx.h" #include "cmSpdxSerializer.h" +#include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -38,7 +40,7 @@ #include "cmTargetTypes.h" #include "cmValue.h" -cmSpdxPackage::PurposeId GetPurpose(cm::TargetType type) +cmSpdxPackage::PurposeId GetPurpose(cm::TargetType const type) { switch (type) { case cm::TargetType::EXECUTABLE: @@ -58,51 +60,45 @@ cmSpdxPackage::PurposeId GetPurpose(cm::TargetType type) } } -cmSbomBuilder::cmSbomBuilder(cmSbomArguments args, - std::vector exportSets, - cmLocalGenerator* lg) - : LocalGenerator(lg) - , ExportSets(std::move(exportSets)) - , PackageName(std::move(args.PackageName)) - , Namespace(cmStrCat(this->PackageName, "::"_s)) - , PackageVersion(std::move(args.Version)) - , PackageDescription(std::move(args.Description)) - , PackageWebsite(std::move(args.Website)) - , PackageUrl(std::move(args.PackageUrl)) - , DataLicense(std::move(args.DataLicense)) - , DefaultLicense(std::move(args.DefaultLicense)) +cmSbomBuilder::cmSbomBuilder(cmSbomArguments const& args, + std::vector const& exportSets) + : PackageName(args.GetPackageName()) + , PackageVersion(args.Version) + , PackageDescription(args.Description) + , PackageWebsite(args.Website) + , PackageUrl(args.PackageUrl) + , DataLicense(args.GetDataLicense()) + , DefaultLicense(args.DefaultLicense) + , NoProjectDefaults(args.NoProjectDefaults) , PackageFormat(args.GetFormat()) + , ExportSets(exportSets) { } +bool cmSbomBuilder::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + this->SbomTargets.clear(); + for (cmExportSet* exportSet : this->ExportSets) { + if (!exportSet->Compute(lg)) { + return false; + } + } + this->SbomTargets = this->CollectTargets(); + return true; +} + std::set cmSbomBuilder::CollectTargets() const { std::set targets; - for (cmExportSet* exportSet : this->ExportSets) { + for (cmExportSet const* exportSet : this->ExportSets) { for (auto const& te : exportSet->GetTargetExports()) { - if (cmGeneratorTarget const* gt = - this->LocalGenerator->FindGeneratorTargetToUse(te->TargetName)) { - targets.emplace(gt); - } + targets.emplace(te->Target); } } return targets; } -void cmSbomBuilder::Compute(cmLocalGenerator* lg) -{ - this->LocalGenerator = lg; - if (!lg) { - return; - } - for (cmExportSet* es : this->ExportSets) { - es->Compute(lg); - } - // Populate the cache now (rather than at Generate time) so peer SBOMs can - // query CoversTarget() during their own NoteLinkedTarget walks. - this->SbomTargets = this->CollectTargets(); -} - bool cmSbomBuilder::CoversTarget(cmGeneratorTarget const* target) const { return cm::contains(this->SbomTargets, target); @@ -110,56 +106,59 @@ bool cmSbomBuilder::CoversTarget(cmGeneratorTarget const* target) const bool cmSbomBuilder::CoversExportSet(cmExportSet const* set) const { - return std::find(this->ExportSets.cbegin(), this->ExportSets.cend(), set) != - this->ExportSets.cend(); + return cm::contains(this->ExportSets, set); } bool cmSbomBuilder::GenerateForTargets( std::ostream& os, std::string const& config, - cmGeneratorExpression::PreprocessContext preprocessContext) + cmGeneratorExpression::PreprocessContext const preprocessContext) { - cmSbomDocument doc; - doc.Graph.reserve(512); + if (!this->LocalGenerator) { + return false; + } - cmSpdxCreationInfo const* ci = - insert_back(doc.Graph, this->GenerateCreationInfo()); - cmSpdxDocument* project = insert_back(doc.Graph, this->GenerateSbom(ci)); - std::vector targetProps; - targetProps.reserve(this->SbomTargets.size()); + this->Relationships.clear(); + this->Pending.clear(); + this->LocalPackages.clear(); + this->LinkedPackages.clear(); + this->LinkTargets.clear(); + this->Requirements.clear(); + this->CreationInfo = this->GenerateCreationInfo(); + this->Project = this->GenerateSbom(); + this->Pending.reserve(this->SbomTargets.size()); for (cmGeneratorTarget const* target : this->SbomTargets) { - ImportPropertyMap properties; - this->PopulateLinkLibrariesProperty(target, preprocessContext, properties); - this->PopulateInterfaceLinkLibrariesProperty(target, preprocessContext, - properties); - targetProps.push_back(TargetProperties{ - insert_back(project->RootElements, - this->GenerateImportTarget(ci, target)), - target, - std::move(properties), - }); + this->GetOrCreateLocalPackage(target); } bool status = true; - for (TargetProperties const& target : targetProps) { - status &= - this->GenerateProperties(doc, project, ci, target, targetProps, config); + // GenerateTarget can append to Pending while this loop is running. + // NOLINTNEXTLINE(modernize-loop-convert) + for (std::size_t i = 0; i < this->Pending.size(); ++i) { + auto const* const target = this->Pending[i]; + status &= this->GenerateTarget(target, config, preprocessContext); } - if (status) { - this->WriteSbom(doc, os); - } - return status; + return status && this->WriteSbom(os); } -void cmSbomBuilder::WriteSbom(cmSbomDocument& doc, std::ostream& os) const +bool cmSbomBuilder::WriteSbom(std::ostream& os) const { + cmSbomDocument document; + document.Graph.reserve(2 + this->Relationships.size()); + document.Graph.emplace_back(this->CreationInfo); + document.Graph.emplace_back(this->Project); + for (auto const& relationship : this->Relationships) { + document.Graph.emplace_back(relationship); + } + switch (this->PackageFormat) { case cmSbomArguments::SbomFormat::SPDX_3_0_JSON: - cmSpdxSerializer{}.WriteSbom(os, cmSbomObject(doc)); - break; + return cmSpdxSerializer{}.WriteSbom(os, + cmSbomObject(std::move(document))); case cmSbomArguments::SbomFormat::NONE: break; } + return false; } bool cmSbomBuilder::AddPackageInformation( @@ -196,6 +195,7 @@ bool cmSbomBuilder::AddPackageInformation( cmSpdxExternalRef externalRef; externalRef.Locator = cmStrCat("cmake:find_package(", name, ')'); externalRef.ExternalRefType = "buildSystem"; + artifact.ExternalRef.emplace_back(std::move(externalRef)); return true; } @@ -210,37 +210,38 @@ cmSpdxCreationInfo cmSbomBuilder::GenerateCreationInfo() const return ci; } -cmSpdxDocument cmSbomBuilder::GenerateSbom(cmSpdxCreationInfo const* ci) const +cmSpdxDocument cmSbomBuilder::GenerateSbom() const { cmSpdxDocument proj; - proj.Name = PackageName; - proj.SpdxId = cmStrCat("urn:", PackageName, "#SPDXDocument"); + proj.Name = this->PackageName; + proj.SpdxId = cmStrCat("urn:", this->PackageName, "#SPDXDocument"); proj.ProfileConformance = { "core", "software" }; - proj.CreationInfo = ci; + proj.CreationInfo = &this->CreationInfo; if (!this->PackageDescription.empty()) { proj.Description = this->PackageDescription; } if (!this->DataLicense.empty()) { - cmSpdxLicenseExpression license; - license.SpdxId = cmStrCat("urn:", PackageName, "#LicenseExpression"); - license.CreationInfo = ci; - license.LicenseExpression = this->DataLicense; - proj.DataLicense = license; + cmSpdxLicenseExpression licenseObject; + licenseObject.SpdxId = + cmStrCat("urn:", this->PackageName, "#DataLicenseExpression"); + licenseObject.CreationInfo = &this->CreationInfo; + licenseObject.LicenseExpression = this->DataLicense; + proj.DataLicense = std::move(licenseObject); } return proj; } -cmSpdxPackage cmSbomBuilder::GenerateImportTarget( - cmSpdxCreationInfo const* ci, cmGeneratorTarget const* target) const +cmSpdxPackage cmSbomBuilder::GenerateLocalPackage( + cmGeneratorTarget const* target) const { cmSpdxPackage package; package.SpdxId = cmStrCat("urn:", target->GetName(), "#Package"); package.Name = target->GetName(); package.PrimaryPurpose = GetPurpose(target->GetType()); - package.CreationInfo = ci; + package.CreationInfo = &this->CreationInfo; if (!this->PackageVersion.empty()) { package.PackageVersion = this->PackageVersion; @@ -257,259 +258,245 @@ cmSpdxPackage cmSbomBuilder::GenerateImportTarget( return package; } -bool cmSbomBuilder::GenerateLinkProperties( - cmSbomDocument& doc, cmSpdxDocument* project, cmSpdxCreationInfo const* ci, - std::string const& libraries, TargetProperties const& current, - std::vector const& allTargets, - std::string const& config) const +cmSpdxPackage const* cmSbomBuilder::GetOrCreateLocalPackage( + cmGeneratorTarget const* target) { - auto itProp = current.Properties.find(libraries); - if (itProp == current.Properties.end()) { + auto const it = this->LocalPackages.find(target); + if (it != this->LocalPackages.end()) { + return it->second; + } + + auto& elements = cm::contains(this->SbomTargets, target) + ? this->Project.RootElements + : this->Project.Elements; + auto const* package = + insert_back(elements, this->GenerateLocalPackage(target)); + this->LocalPackages.emplace(target, package); + this->Pending.push_back(target); + return package; +} + +cmSpdxPackage const* cmSbomBuilder::GetOrCreateLinkedPackage( + cmGeneratorTarget const* source, cmLinkItem const& item) +{ + LinkInfo const* info = nullptr; + if (item.Target) { + auto link = this->LinkTargets.find(item.Target); + if (link == this->LinkTargets.end()) { + link = + this->LinkTargets + .emplace(item.Target, this->NoteLinkedTarget(source, item.Target)) + .first; + } + info = &link->second; + } + if (info && info->Local) { + return this->GetOrCreateLocalPackage(item.Target); + } + + // An empty package name identifies a dependency without provenance. + auto const& name = item.AsStr(); + auto key = std::make_pair(info ? info->Package : std::string{}, + info ? info->Component : name); + auto const it = this->LinkedPackages.find(key); + if (it != this->LinkedPackages.end()) { + return it->second; + } + + std::string const packageName = info && !info->Package.empty() + ? cmStrCat(info->Package, ':', info->Component) + : name; + cmSpdxPackage package; + package.Name = packageName; + package.SpdxId = cmStrCat("urn:", packageName, "#Package"); + package.CreationInfo = &this->CreationInfo; + if (info) { + auto const requirement = this->Requirements.find(info->Package); + if (requirement != this->Requirements.end()) { + this->AddPackageInformation(package, info->Package, requirement->second); + } + } + + auto const* result = insert_back(this->Project.Elements, std::move(package)); + this->LinkedPackages.emplace(std::move(key), result); + if (item.Target) { + this->GenerateDeclaredLicense( + result, item.Target->GetSafeProperty("SPDX_LICENSE")); + } + return result; +} + +bool cmSbomBuilder::EvaluateLinkProperty( + cmGeneratorTarget const* target, std::string const& property, + std::string const& config, + cmGeneratorExpression::PreprocessContext preprocessContext, + std::vector& dependencies) const +{ + cmValue const input = target->GetProperty(property); + if (!input) { + return true; + } + auto const preprocessed = + cmGeneratorExpression::Preprocess(*input, preprocessContext); + if (preprocessed.empty()) { return true; } - cmGeneratorExpression ge(*current.Target->Makefile->GetCMakeInstance()); - std::unique_ptr cge = - ge.Parse(itProp->second); - std::string evaluatedLibraries = - cge->Evaluate(current.Target->GetLocalGenerator(), config, current.Target); - - if (cge->GetHadHeadSensitiveCondition()) { - current.Target->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Property \"", libraries, "\" of target \"", - current.Target->GetName(), - "\" contains a generator expression that is not allowed for " - "SBOM generation.")); - return false; - } - - auto makeRel = [&](char const* id, char const* desc) { - cmSpdxRelationship r; - r.SpdxId = cmStrCat("urn:", id, "#Relationship"); - r.RelationshipType = cmSpdxRelationship::RelationshipTypeId::DEPENDS_ON; - r.Description = desc; - r.From = current.Package; - r.CreationInfo = ci; - return r; - }; - - auto linkLibraries = makeRel("Static", "Linked Libraries"); - auto linkRequires = makeRel("Dynamic", "Required Runtime Libraries"); - auto buildRequires = makeRel("Shared", "Required Build-Time Libraries"); - - auto addArtifact = - [&](std::string const& name) -> std::pair { - auto it = this->LinkTargets.find(name); - if (it != this->LinkTargets.end()) { - LinkInfo const& linkInfo = it->second; - if (linkInfo.Package.empty()) { - for (auto const& t : allTargets) { - if (t.Target->GetName() == linkInfo.Component) { - return { true, t.Package }; - } - } + cmGeneratorExpression const ge(*target->Makefile->GetCMakeInstance()); + auto const cge = ge.Parse(preprocessed); + cm::GenEx::Context const context(target->GetLocalGenerator(), config); + for (auto const usage : + { cmGeneratorTarget::UseTo::Compile, cmGeneratorTarget::UseTo::Link }) { + cmGeneratorExpressionDAGChecker dagChecker{ target, property, nullptr, + nullptr, context }; + if (usage == cmGeneratorTarget::UseTo::Compile) { + dagChecker.SetTransitivePropertiesOnly(); + if (property != "LINK_LIBRARIES" || + target->GetPolicyStatusCMP0131() == cmPolicies::NEW) { + dagChecker.SetTransitivePropertiesOnlyCMP0131(); } - std::string pkgName = - cmStrCat(linkInfo.Package, ':', linkInfo.Component); - cmSpdxPackage pkg; - pkg.Name = pkgName; - pkg.SpdxId = cmStrCat("urn:", pkgName, "#Package"); - pkg.CreationInfo = ci; - if (!linkInfo.Package.empty()) { - auto const& pkgIt = this->Requirements.find(linkInfo.Package); - if (pkgIt != this->Requirements.end() && - pkgIt->second.Components.count(linkInfo.Component) > 0) { - this->AddPackageInformation(pkg, pkgIt->first, pkgIt->second); - } - } - - cmSpdxPackage const* pkgPtr = - insert_back(project->Elements, std::move(pkg)); - if (!linkInfo.License.empty() && - !cm::contains(this->GeneratedLinkLicenses, name)) { - this->GeneratedLinkLicenses.emplace(name); - - cmSpdxLicenseExpression license; - license.SpdxId = cmStrCat("urn:", name, "#LicenseExpression"); - license.CreationInfo = ci; - license.LicenseExpression = linkInfo.License; - - cmSpdxRelationship relHasLicense; - relHasLicense.SpdxId = - cmStrCat("urn:", name, "#DeclaredLicenseRelationship"); - relHasLicense.CreationInfo = ci; - relHasLicense.RelationshipType = - cmSpdxRelationship::HAS_DECLARED_LICENSE; - relHasLicense.From = pkgPtr; - relHasLicense.To.emplace_back(std::move(license)); - - insert_back(doc.Graph, std::move(relHasLicense)); - } - - return { true, pkgPtr }; + } + cmList const items{ cge->Evaluate(context, &dagChecker, target) }; + if (cge->GetHadHeadSensitiveCondition()) { + target->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Property \"", property, "\" of target \"", target->GetName(), + "\" contains a generator expression that is not allowed for " + "SBOM generation.")); + return false; } - cmSpdxPackage pkg; - pkg.SpdxId = cmStrCat("urn:", name, "#Package"); - pkg.Name = name; - pkg.CreationInfo = ci; - return { false, insert_back(project->Elements, std::move(pkg)) }; - }; - - cmList names{ evaluatedLibraries }; - names.sort(); - names.remove_duplicates(); - for (std::string const& n : names) { - auto res = addArtifact(n); - if (!res.second) { - continue; + cmLocalGenerator const* scope = target->GetLocalGenerator(); + auto feature = cmLinkItem::DEFAULT; + for (auto const& item : items) { + if (auto linkFeature = ParseLinkFeature(item)) { + feature = std::move(*linkFeature); + continue; + } + if ((cmHasPrefix(item, "')) { + continue; + } + if (item.empty() || target->IsLinkLookupScope(item, scope)) { + continue; + } + dependencies.emplace_back(target->ResolveLinkItem( + BT(item, cmListFileBacktrace{}), scope, feature)); } - - if (res.first) { - linkLibraries.To.push_back(res.second); - } else { - buildRequires.To.push_back(res.second); - } - } - - if (!linkLibraries.To.empty()) { - insert_back(doc.Graph, std::move(linkLibraries)); - } - if (!linkRequires.To.empty()) { - insert_back(doc.Graph, std::move(linkRequires)); - } - if (!buildRequires.To.empty()) { - insert_back(doc.Graph, std::move(buildRequires)); } return true; } -bool cmSbomBuilder::GenerateProperties( - cmSbomDocument& doc, cmSpdxDocument* proj, cmSpdxCreationInfo const* ci, - TargetProperties const& current, - std::vector const& allTargets, - std::string const& config) const +bool cmSbomBuilder::GenerateTarget( + cmGeneratorTarget const* target, std::string const& config, + cmGeneratorExpression::PreprocessContext preprocessContext) { - bool status = true; - status &= this->GenerateLinkProperties(doc, proj, ci, "LINK_LIBRARIES", - current, allTargets, config); - status &= this->GenerateLinkProperties( - doc, proj, ci, "INTERFACE_LINK_LIBRARIES", current, allTargets, config); - status &= this->GenerateMetaProperties(doc, proj, ci, current); + auto const* package = this->LocalPackages.at(target); + std::vector dependencies; + bool status = this->EvaluateLinkProperty(target, "LINK_LIBRARIES", config, + preprocessContext, dependencies); + status &= + this->EvaluateLinkProperty(target, "INTERFACE_LINK_LIBRARIES", config, + preprocessContext, dependencies); + + cmSpdxRelationship staticLibraries; + cmSpdxRelationship sharedLibraries; + std::set seen; + for (auto const& item : dependencies) { + auto const* dependency = this->GetOrCreateLinkedPackage(target, item); + if (!seen.insert(dependency).second) { + continue; + } + auto const type = + item.Target ? item.Target->GetType() : cm::TargetType::UNKNOWN_LIBRARY; + switch (type) { + case cm::TargetType::INTERFACE_LIBRARY: + case cm::TargetType::STATIC_LIBRARY: + staticLibraries.To.emplace_back(dependency); + break; + case cm::TargetType::SHARED_LIBRARY: + case cm::TargetType::MODULE_LIBRARY: + sharedLibraries.To.emplace_back(dependency); + break; + default: + break; + } + } + + auto const addRelationship = [&](cmSpdxRelationship& relationship, + char const* id, char const* description) { + if (relationship.To.empty()) { + return; + } + relationship.SpdxId = + cmStrCat("urn:", *package->Name, '#', id, "Relationship"); + relationship.RelationshipType = cmSpdxRelationship::DEPENDS_ON; + relationship.Description = description; + relationship.From = package; + relationship.CreationInfo = &this->CreationInfo; + this->Relationships.emplace_back(std::move(relationship)); + }; + + addRelationship(staticLibraries, "Static", "Required Build-Time Libraries"); + addRelationship(sharedLibraries, "Shared", "Required Run-Time Libraries"); + + cmValue license = target->GetProperty("SPDX_LICENSE"); + if (!license && !this->DefaultLicense.empty()) { + license = cmValue(this->DefaultLicense); + } + if (!license && !this->NoProjectDefaults) { + cmMakefile const* mf = target->Target->GetMakefile(); + std::string const projectName = mf->GetStateSnapshot().GetProjectName(); + license = mf->GetDefinition(cmStrCat(projectName, "_SPDX_LICENSE")); + } + + this->GenerateDeclaredLicense(package, *license); return status; } -bool cmSbomBuilder::GenerateMetaProperties( - cmSbomDocument& doc, cmSpdxDocument* /*project*/, - cmSpdxCreationInfo const* ci, TargetProperties const& current) const +void cmSbomBuilder::GenerateDeclaredLicense(cmSpdxPackage const* package, + std::string const& license) { - std::string licenseExpr = this->DefaultLicense; - cmValue licenseExprProp = current.Target->GetProperty("SPDX_LICENSE"); - if (licenseExprProp) { - licenseExpr = licenseExprProp; + if (license.empty()) { + return; } - if (!licenseExpr.empty()) { - auto const& tgtName = current.Target->GetName(); + cmSpdxLicenseExpression expression; + expression.SpdxId = cmStrCat("urn:", *package->Name, "#LicenseExpression"); + expression.CreationInfo = &this->CreationInfo; + expression.LicenseExpression = license; - cmSpdxLicenseExpression license; - license.SpdxId = cmStrCat("urn:", tgtName, "#LicenseExpression"); - license.CreationInfo = ci; - license.LicenseExpression = std::move(licenseExpr); - - cmSpdxRelationship relHasLicense; - relHasLicense.SpdxId = - cmStrCat("urn:", tgtName, "#DeclaredLicenseRelationship"); - relHasLicense.CreationInfo = ci; - relHasLicense.RelationshipType = cmSpdxRelationship::HAS_DECLARED_LICENSE; - relHasLicense.From = current.Package; - relHasLicense.To.emplace_back(std::move(license)); - - insert_back(doc.Graph, std::move(relHasLicense)); - } - return true; + cmSpdxRelationship relationship; + relationship.SpdxId = + cmStrCat("urn:", *package->Name, "#DeclaredLicenseRelationship"); + relationship.CreationInfo = &this->CreationInfo; + relationship.RelationshipType = cmSpdxRelationship::HAS_DECLARED_LICENSE; + relationship.From = package; + relationship.To.emplace_back(std::move(expression)); + this->Relationships.emplace_back(std::move(relationship)); } -bool cmSbomBuilder::PopulateLinkLibrariesProperty( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) +cmSbomBuilder::LinkInfo cmSbomBuilder::NoteLinkedTarget( + cmGeneratorTarget const* target, cmGeneratorTarget const* linkedTarget) { - static std::array const linkIfaceProps = { { - "LINK_LIBRARIES", - "LINK_LIBRARIES_DIRECT", - "LINK_LIBRARIES_DIRECT_EXCLUDE", - } }; - bool hadLINK_LIBRARIES = false; - for (std::string const& linkIfaceProp : linkIfaceProps) { - if (cmValue input = target->GetProperty(linkIfaceProp)) { - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target); - properties[linkIfaceProp] = prepro; - hadLINK_LIBRARIES = true; - } - } - } - return hadLINK_LIBRARIES; -} - -bool cmSbomBuilder::AddTargetNamespace(std::string& input, - cmGeneratorTarget const* target, - cmLocalGenerator const* lg) -{ - cmGeneratorTarget::TargetOrString resolved = - target->ResolveTargetReference(input, lg); - - cmGeneratorTarget* tgt = resolved.Target; - if (!tgt) { - input = resolved.String; - return false; - } - - if (tgt->IsImported()) { - input = tgt->GetName(); - return this->NoteLinkedTarget(target, input, tgt); - } - - if (this->SbomTargets.find(tgt) != this->SbomTargets.end()) { - input = this->Namespace + tgt->GetExportName(); - } else { - input = tgt->GetName(); - } - - return this->NoteLinkedTarget(target, input, tgt); -} - -bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, - std::string const& linkedName, - cmGeneratorTarget const* linkedTarget) -{ - auto linkedLicense = linkedTarget->GetSafeProperty("SPDX_LICENSE"); - + auto const& linkedName = linkedTarget->GetName(); if (cm::contains(this->SbomTargets, linkedTarget)) { - this->LinkTargets.emplace( - linkedName, - LinkInfo{ "", linkedTarget->GetExportName(), linkedLicense }); - return true; + return { "", linkedTarget->GetExportName(), true }; } if (linkedTarget->IsImported()) { using Package = cm::optional>; - auto pkgInfo = [](cmTarget* t) -> Package { - cmFindPackageStack pkgStack = t->GetFindPackageStack(); + auto pkgInfo = [](cmTarget const* t) -> Package { + cmFindPackageStack const& pkgStack = t->GetFindPackageStack(); if (!pkgStack.Empty()) { return std::make_pair(pkgStack.Top().Name, *pkgStack.Top().PackageInfo); } - std::string const pkgName = - t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME"); + std::string pkgName = t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME"); if (pkgName.empty()) { return cm::nullopt; } - cmPackageInformation package; - return std::make_pair(pkgName, package); + return std::make_pair(std::move(pkgName), cmPackageInformation{}); }(linkedTarget->Target); if (!pkgInfo) { @@ -518,28 +505,23 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, cmStrCat("Target \"", target->GetName(), "\" references imported target \"", linkedName, "\" which does not come from any known package.")); - return false; + return { "", linkedName }; } std::string const& pkgName = pkgInfo->first; - auto const& prefix = cmStrCat(pkgName, "::"); - std::string component; - if (!cmHasPrefix(linkedName, prefix)) { - component = linkedName; - } else { - component = linkedName.substr(prefix.length()); - } - this->LinkTargets.emplace(linkedName, - LinkInfo{ pkgName, component, linkedLicense }); + auto const prefix = cmStrCat(pkgName, "::"); + std::string component = cmHasPrefix(linkedName, prefix) + ? linkedName.substr(prefix.length()) + : linkedName; + LinkInfo info{ pkgName, component }; cmPackageInformation& req = this->Requirements.insert(std::move(*pkgInfo)).first->second; req.Components.emplace(std::move(component)); - return true; + return info; } - // Target belongs to another export from this build or install. - // The leaf class chooses which export map to consult. - auto const& exportInfo = this->FindExportInfoFor(linkedTarget); + // The leaf class selects build or install export provenance. + auto const exportInfo = this->FindExportInfoFor(linkedTarget); if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) { auto const& linkNamespace = *exportInfo.Namespaces.begin(); if (!cmHasSuffix(linkNamespace, "::")) { @@ -550,44 +532,26 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, "\", whose export does not use the standard namespace " "separator. The dependency will be recorded by its bare " "target name without provenance.")); - return false; + return { "", linkedName }; } std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 }; std::string component = linkedTarget->GetExportName(); - if (pkgName == this->GetPackageName()) { - this->LinkTargets.emplace(linkedName, - LinkInfo{ "", component, linkedLicense }); - } else { - this->LinkTargets.emplace(linkedName, - LinkInfo{ pkgName, component, linkedLicense }); - this->Requirements[pkgName].Components.emplace(std::move(component)); + if (pkgName == this->PackageName) { + return { "", std::move(component), true }; } - return true; + this->Requirements[pkgName].Components.emplace(component); + return { std::move(pkgName), std::move(component) }; } if (exportInfo.Sets.empty()) { - // install(export) provenance is unavailable. Fall back to SBOM-level - // attribution: any peer SBOM (of the same build/install mode) that - // covers this target lends its package name. install(export) wins - // when both are available; only reached here when install(export) is - // absent. - auto const& sbomInfo = this->FindSbomInfoFor(linkedTarget); - if (sbomInfo.Packages.empty()) { - target->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - cmStrCat("Target \"", target->GetName(), "\" references target \"", - linkedName, - "\" which has no install(EXPORT)/export(EXPORT) namespace " - "and is not covered by any SBOM. An SBOM cannot attribute " - "this dependency. Give \"", - linkedTarget->GetName(), - "\" an install(EXPORT)/export(EXPORT) with a NAMESPACE, or " - "include it in an install(SBOM)/export(SBOM).")); - return false; + // A peer SBOM supplies provenance when no export namespace is available. + auto const sbomPackages = this->FindSbomPackagesFor(linkedTarget); + if (sbomPackages.empty()) { + return { "", linkedName, true }; } - std::string const& pkgName = sbomInfo.Packages.front(); - if (sbomInfo.Packages.size() > 1) { + auto const& pkgName = sbomPackages.front(); + if (sbomPackages.size() > 1) { target->Makefile->IssueDiagnostic( cmDiagnostics::CMD_AUTHOR, cmStrCat( @@ -595,14 +559,12 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, linkedName, "\" which has no install(EXPORT)/export(EXPORT) namespace and is " "covered by multiple SBOMs: ", - cmJoin(sbomInfo.Packages, ", "), ". Attributing to \"", pkgName, + cmJoin(sbomPackages, ", "), ". Attributing to \"", pkgName, "\" (first alphabetically).")); } std::string component = linkedTarget->GetExportName(); - this->LinkTargets.emplace(linkedName, - LinkInfo{ pkgName, component, linkedLicense }); - this->Requirements[pkgName].Components.emplace(std::move(component)); - return true; + this->Requirements[pkgName].Components.emplace(component); + return { pkgName, std::move(component) }; } target->Makefile->IssueMessage( @@ -619,68 +581,5 @@ bool cmSbomBuilder::NoteLinkedTarget(cmGeneratorTarget const* target, "export set or with more than one namespace. Consider " "consolidating the exports of the \"", linkedTarget->GetName(), "\" target to a single export.")); - return false; -} - -void cmSbomBuilder::ResolveTargetsInGeneratorExpression( - std::string& input, cmGeneratorTarget const* target, - cmLocalGenerator const* lg) -{ - auto err = cmResolveTargetsInGeneratorExpression( - input, [this, target, lg](std::string& name) { - return this->AddTargetNamespace(name, target, lg); - }); - if (err) { - target->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR, *err); - } -} - -void cmSbomBuilder::ResolveTargetsInGeneratorExpressions( - std::string& input, cmGeneratorTarget const* target) -{ - cmLocalGenerator const* lg = target->GetLocalGenerator(); - std::vector parts; - cmGeneratorExpression::Split(input, parts); - - std::string sep; - input.clear(); - for (std::string& li : parts) { - if (target->IsLinkLookupScope(li, lg)) { - continue; - } - if (cmGeneratorExpression::Find(li) == std::string::npos) { - this->AddTargetNamespace(li, target, lg); - } else { - this->ResolveTargetsInGeneratorExpression(li, target, lg); - } - input = cmStrCat(std::move(input), sep, li); - sep = ";"; - } -} - -bool cmSbomBuilder::PopulateInterfaceLinkLibrariesProperty( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties) -{ - if (!target->IsLinkable()) { - return false; - } - static std::array const linkIfaceProps = { - { "INTERFACE_LINK_LIBRARIES", "INTERFACE_LINK_LIBRARIES_DIRECT", - "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE" } - }; - bool hadINTERFACE_LINK_LIBRARIES = false; - for (std::string const& linkIfaceProp : linkIfaceProps) { - if (cmValue input = target->GetProperty(linkIfaceProp)) { - std::string prepro = - cmGeneratorExpression::Preprocess(*input, preprocessRule); - if (!prepro.empty()) { - this->ResolveTargetsInGeneratorExpressions(prepro, target); - properties[linkIfaceProp] = prepro; - hadINTERFACE_LINK_LIBRARIES = true; - } - } - } - return hadINTERFACE_LINK_LIBRARIES; + return { "", linkedName }; } diff --git a/Source/cmSbomBuilder.h b/Source/cmSbomBuilder.h index 0a29699e83..798effd246 100644 --- a/Source/cmSbomBuilder.h +++ b/Source/cmSbomBuilder.h @@ -8,32 +8,31 @@ #include #include #include +#include #include #include "cmExportFileGenerator.h" #include "cmFindPackageStack.h" #include "cmGeneratorExpression.h" #include "cmSbomArguments.h" +#include "cmSpdx.h" class cmExportSet; class cmGeneratorTarget; class cmLocalGenerator; -struct cmSbomDocument; -struct cmSpdxDocument; -struct cmSpdxPackage; -struct cmSpdxCreationInfo; +class cmLinkItem; /** \class cmSbomBuilder - * \brief Abstract base for SBOM document generators. + * \brief Builds SBOM documents for the targets in a set of exports. * - * Concrete leaves (cmBuildSbomBuilder, cmInstallSbomBuilder) supply two - * pure virtuals: + * Concrete leaves (cmBuildSbomBuilder, cmInstallSbomBuilder) supply the + * mode-specific operations: * - * - Generate(): selects the generator-expression preprocess context - * (BuildInterface vs InstallInterface) and delegates to the shared - * GenerateForTargets() body. + * - Generate(): selects the generator-expression preprocess context and + * delegates to GenerateForTargets(). * - FindExportInfoFor(): consults the mode's export map (build-tree vs * install-tree) to resolve cross-export references. + * - FindSbomPackagesFor(): finds peer SBOMs that cover a dependency. * * All shared SPDX assembly, link-graph walking, and serialization lives * here so that the leaves stay trivial. @@ -41,41 +40,32 @@ struct cmSpdxCreationInfo; class cmSbomBuilder { public: + cmSbomBuilder(cmSbomBuilder const&) = delete; + cmSbomBuilder& operator=(cmSbomBuilder const&) = delete; virtual ~cmSbomBuilder() = default; - /** Compute phase: wire the local generator, run Compute() on the owned - * export sets, and populate the SbomTargets cache so peer SBOMs can - * query CoversTarget() before any Generate() runs. */ - void Compute(cmLocalGenerator* lg); + /** Resolve export sets and collect explicit targets for peer SBOM lookups + * before any documents are generated. */ + bool Compute(cmLocalGenerator* lg); - /** Produce the SBOM document on `os`. Implementations select their own - * target set (via CollectTargets) and preprocess context. */ + /** Produce one configuration's document on `os`. */ virtual bool Generate(std::ostream& os, std::string const& config) = 0; - /** Identifier this SBOM publishes itself as (the SPDX document name and - * the namespace under which other SBOMs refer to its contents). */ std::string const& GetPackageName() const { return this->PackageName; } - /** True if `target` is one of the targets this SBOM directly describes. */ + /** True if `target` is explicitly included through an export set. */ bool CoversTarget(cmGeneratorTarget const* target) const; bool CoversExportSet(cmExportSet const* set) const; - void AddConfiguration(std::string const& config) - { - this->Configurations.push_back(config); - } - - /** Names of peer SBOMs (same build/install mode) that cover a target. - * Used by NoteLinkedTarget to attribute a cross-reference when no - * install(export) namespace is available. Sorted alphabetically. */ - struct SbomInfo - { - std::vector Packages; - }; - protected: - cmSbomBuilder(cmSbomArguments args, std::vector exportSets, - cmLocalGenerator* lg); + cmSbomBuilder(cmSbomArguments const& args, + std::vector const& exportSets); + + std::set CollectTargets() const; + + bool GenerateForTargets( + std::ostream& os, std::string const& config, + cmGeneratorExpression::PreprocessContext preprocessContext); /** Mode-specific: where does `target` appear in the project's exports? * Build leaves consult cmGlobalGenerator::FindBuildExportInfo; @@ -83,111 +73,77 @@ protected: virtual cmExportFileGenerator::ExportInfo FindExportInfoFor( cmGeneratorTarget const* target) const = 0; - /** Mode-specific: which peer SBOMs cover `target`? - * Build leaves consult cmGlobalGenerator::FindBuildSbomInfo; - * install leaves consult cmGlobalGenerator::FindInstallSbomInfo. */ - virtual SbomInfo FindSbomInfoFor(cmGeneratorTarget const* target) const = 0; - - /** The set of targets the SBOM directly describes - derived from the - * associated export sets. */ - std::set CollectTargets() const; - - /** Generate an sbom for the targets in this->SbomTargets. Each leaf class - * calls this internally */ - bool GenerateForTargets( - std::ostream& os, std::string const& config, - cmGeneratorExpression::PreprocessContext preprocessContext); - - using ImportPropertyMap = std::map; - - struct TargetProperties - { - cmSpdxPackage const* Package; - cmGeneratorTarget const* Target; - ImportPropertyMap Properties; - }; - - void WriteSbom(cmSbomDocument& doc, std::ostream& os) const; - - cmSpdxCreationInfo GenerateCreationInfo() const; - cmSpdxDocument GenerateSbom(cmSpdxCreationInfo const* ci) const; - cmSpdxPackage GenerateImportTarget(cmSpdxCreationInfo const* ci, - cmGeneratorTarget const* target) const; - - bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name, - cmPackageInformation const& package) const; - - bool GenerateProperties(cmSbomDocument& doc, cmSpdxDocument* project, - cmSpdxCreationInfo const* ci, - TargetProperties const& current, - std::vector const& allTargets, - std::string const& config) const; - - bool GenerateLinkProperties(cmSbomDocument& doc, cmSpdxDocument* project, - cmSpdxCreationInfo const* ci, - std::string const& libraries, - TargetProperties const& current, - std::vector const& allTargets, - std::string const& config) const; - bool GenerateMetaProperties(cmSbomDocument& doc, cmSpdxDocument* project, - cmSpdxCreationInfo const* ci, - TargetProperties const& current) const; - - bool NoteLinkedTarget(cmGeneratorTarget const* target, - std::string const& linkedName, - cmGeneratorTarget const* linkedTarget); - - bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target, - cmLocalGenerator const* lg); - - void ResolveTargetsInGeneratorExpression(std::string& input, - cmGeneratorTarget const* target, - cmLocalGenerator const* lg); - void ResolveTargetsInGeneratorExpressions(std::string& input, - cmGeneratorTarget const* target); - - bool PopulateInterfaceLinkLibrariesProperty( - cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext preprocessRule, - ImportPropertyMap& properties); - - bool PopulateLinkLibrariesProperty(cmGeneratorTarget const* target, - cmGeneratorExpression::PreprocessContext, - ImportPropertyMap& properties); - - // Set at construction or via setters - cmLocalGenerator* LocalGenerator = nullptr; - - // Inputs - std::vector ExportSets; + /** Names of peer SBOMs in this mode that cover `target`, sorted + * alphabetically. Used when no export namespace is available. */ + virtual std::vector FindSbomPackagesFor( + cmGeneratorTarget const* target) const = 0; private: struct LinkInfo { + LinkInfo(std::string package, std::string component, bool local = false) + : Package(std::move(package)) + , Component(std::move(component)) + , Local(local) + { + } + std::string Package; std::string Component; - std::string License; + bool Local; }; - // Metadata + bool WriteSbom(std::ostream& os) const; + + cmSpdxCreationInfo GenerateCreationInfo() const; + cmSpdxDocument GenerateSbom() const; + cmSpdxPackage GenerateLocalPackage(cmGeneratorTarget const* target) const; + + bool AddPackageInformation(cmSpdxPackage& artifact, std::string const& name, + cmPackageInformation const& package) const; + + cmSpdxPackage const* GetOrCreateLocalPackage( + cmGeneratorTarget const* target); + cmSpdxPackage const* GetOrCreateLinkedPackage( + cmGeneratorTarget const* source, cmLinkItem const& item); + + bool EvaluateLinkProperty( + cmGeneratorTarget const* target, std::string const& property, + std::string const& config, + cmGeneratorExpression::PreprocessContext preprocessContext, + std::vector& dependencies) const; + + bool GenerateTarget( + cmGeneratorTarget const* target, std::string const& config, + cmGeneratorExpression::PreprocessContext preprocessContext); + void GenerateDeclaredLicense(cmSpdxPackage const* package, + std::string const& license); + + LinkInfo NoteLinkedTarget(cmGeneratorTarget const* target, + cmGeneratorTarget const* linkedTarget); + std::string const PackageName; - std::string const Namespace; std::string const PackageVersion; std::string const PackageDescription; std::string const PackageWebsite; std::string const PackageUrl; std::string const DataLicense; std::string const DefaultLicense; + bool const NoProjectDefaults; cmSbomArguments::SbomFormat const PackageFormat; + std::vector const ExportSets; + cmLocalGenerator const* LocalGenerator = nullptr; - // Derived from inputs at generate time std::set SbomTargets; - // Accumulated during generation - std::map LinkTargets; - std::map Requirements; - std::vector Configurations; + cmSpdxCreationInfo CreationInfo; + cmSpdxDocument Project; + std::vector Relationships; - // Targets for which license data has been generated - mutable std::set GeneratedLinkLicenses; + std::vector Pending; + std::map LocalPackages; + std::map, cmSpdxPackage const*> + LinkedPackages; + std::map LinkTargets; + std::map Requirements; }; diff --git a/Source/cmSbomObject.h b/Source/cmSbomObject.h index 3f42cfbd50..fd6c9050cf 100644 --- a/Source/cmSbomObject.h +++ b/Source/cmSbomObject.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -265,6 +266,10 @@ private: template > U* insert_back(std::vector& vec, T&& obj) noexcept { + // Returned pointers must survive reallocation of the containing vector. + static_assert( + std::is_same, ObjectStorage::Heap>::value, + "insert_back requires heap storage for stable addresses"); vec.emplace_back(std::forward(obj)); return std::addressof(vec.back().CastUnchecked()); } diff --git a/Tests/CMakeLib/testSpdxSerializer.cxx b/Tests/CMakeLib/testSpdxSerializer.cxx index 95e6caa2cc..3fe304514c 100644 --- a/Tests/CMakeLib/testSpdxSerializer.cxx +++ b/Tests/CMakeLib/testSpdxSerializer.cxx @@ -2,12 +2,14 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file LICENSE.rst or https://cmake.org/licensing for details. */ +#include #include #include #include #include +#include #include #include "cmSbomObject.h" @@ -50,34 +52,107 @@ bool testSerializeSpdxJson() doc.Serialize(serializer); auto value = serializer.GetJson(); - ASSERT_TRUE(value.isObject()); - - Json::Value const& context = value["@context"]; - ASSERT_EQUAL(context.asString(), contextUrl); - - Json::Value const& graph = value["@graph"]; - ASSERT_TRUE(graph.isArray()); - - Json::Value const& docValue = graph[0]; - ASSERT_TRUE(docValue.isObject()); - ASSERT_EQUAL(docValue["type"].asString(), "SpdxDocument"); - ASSERT_EQUAL(docValue["spdxId"].asString(), "_:SPDXRef-Document"); - - auto const& creationInfo = docValue["creationInfo"]; - ASSERT_EQUAL(creationInfo["@id"].asString(), "_:SPDXRef-CreationInfo"); - ASSERT_EQUAL(creationInfo["type"].asString(), "CreationInfo"); - - auto const& package = docValue["rootElement"][0]; - ASSERT_EQUAL(package["type"].asString(), "software_Package"); - ASSERT_EQUAL(package["spdxId"].asString(), "_:SPDXRef-Package"); + Json::Value expected; + expected["@context"] = contextUrl; + auto& document = expected["@graph"][0]; + document["type"] = "SpdxDocument"; + document["spdxId"] = "_:SPDXRef-Document"; + auto& creation = document["creationInfo"]; + creation["@id"] = "_:SPDXRef-CreationInfo"; + creation["type"] = "CreationInfo"; + creation["specVersion"] = "3.0.1"; + auto& package = document["rootElement"][0]; + package["type"] = "software_Package"; + package["spdxId"] = "_:SPDXRef-Package"; + package["name"] = "sample-package"; + ASSERT_EQUAL(value, expected); } return true; } -} // namespace - -int testSpdxSerializer(int /*unused*/, char* /*unused*/[]) +bool testSerializeAfterReallocation() { - return runTests({ testSerializeSpdxJson }); + cmSbomDocument doc; + cmSpdxCreationInfo creationInfo; + creationInfo.SpdxId = "_:creation-info"; + doc.Graph.emplace_back(creationInfo); + + cmSpdxDocument document; + document.SpdxId = "urn:document"; + document.CreationInfo = &creationInfo; + auto* spdx = insert_back(doc.Graph, std::move(document)); + + auto const count = 128u; + for (auto i = 0u; i < count; ++i) { + std::string const suffix = std::to_string(i); + cmSpdxPackage rootValue; + rootValue.SpdxId = "urn:root-" + suffix; + rootValue.CreationInfo = &creationInfo; + auto const* root = insert_back(spdx->RootElements, std::move(rootValue)); + + cmSpdxPackage dependencyValue; + dependencyValue.SpdxId = "urn:dependency-" + suffix; + dependencyValue.CreationInfo = &creationInfo; + auto const* dependency = + insert_back(spdx->Elements, std::move(dependencyValue)); + + cmSpdxRelationship relationship; + relationship.SpdxId = "urn:relationship-" + suffix; + relationship.CreationInfo = &creationInfo; + relationship.RelationshipType = cmSpdxRelationship::DEPENDS_ON; + relationship.From = root; + relationship.To.emplace_back(dependency); + doc.Graph.emplace_back(std::move(relationship)); + } + + cmSbomObject moved(std::move(doc)); + cmSbomObject copied(moved); + cmSpdxSerializer serializer; + std::ostringstream output; + ASSERT_TRUE(serializer.WriteSbom(output, copied)); + auto const value = serializer.GetJson(); + + Json::Value expected; + expected["@context"] = "https://spdx.org/rdf/3.0.1/spdx-context.jsonld"; + auto& graph = expected["@graph"]; + graph[0]["@id"] = "_:creation-info"; + graph[0]["type"] = "CreationInfo"; + graph[1]["spdxId"] = "urn:document"; + graph[1]["type"] = "SpdxDocument"; + graph[1]["creationInfo"] = "_:creation-info"; + for (auto i = 0u; i < count; ++i) { + std::string const rootId = "urn:root-" + std::to_string(i); + std::string const dependencyId = "urn:dependency-" + std::to_string(i); + auto& root = graph[1]["rootElement"][i]; + root["spdxId"] = rootId; + root["creationInfo"] = "_:creation-info"; + root["type"] = "software_Package"; + auto& dependency = graph[1]["element"][i]; + dependency["spdxId"] = dependencyId; + dependency["creationInfo"] = "_:creation-info"; + dependency["type"] = "software_Package"; + auto& relationship = graph[i + 2]; + relationship["spdxId"] = "urn:relationship-" + std::to_string(i); + relationship["creationInfo"] = "_:creation-info"; + relationship["from"] = rootId; + relationship["to"][0] = dependencyId; + relationship["type"] = "Relationship"; + relationship["relationshipType"] = "dependsOn"; + } + ASSERT_EQUAL(value, expected); + Json::Value written; + Json::CharReaderBuilder reader; + std::string errors; + std::istringstream input(output.str()); + ASSERT_TRUE(Json::parseFromStream(reader, input, &written, &errors)); + ASSERT_EQUAL(written, expected); + return true; +} + +} + +int testSpdxSerializer(int, char*[]) +{ + return runTests({ testSerializeSpdxJson, testSerializeAfterReallocation }); } diff --git a/Tests/RunCMake/EnvSbom/Config-check.cmake b/Tests/RunCMake/EnvSbom/Config-check.cmake index d5127a69c9..1371111779 100644 --- a/Tests/RunCMake/EnvSbom/Config-check.cmake +++ b/Tests/RunCMake/EnvSbom/Config-check.cmake @@ -1,7 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-Debug.spdx.json" DebugSbom) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-Release.spdx.json" ReleaseSbom) else() diff --git a/Tests/RunCMake/EnvSbom/DefaultLicense-install-check.cmake b/Tests/RunCMake/EnvSbom/DefaultLicense-install-check.cmake new file mode 100644 index 0000000000..07d0a1b743 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/DefaultLicense-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/licenses-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/DefaultLicense.cmake b/Tests/RunCMake/EnvSbom/DefaultLicense.cmake new file mode 100644 index 0000000000..a0031f2734 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/DefaultLicense.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense.cmake) + +install(SBOM licenses EXPORTS licenses DEFAULT_LICENSE MIT DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-result.txt b/Tests/RunCMake/EnvSbom/DuplicateSbom-result.txt similarity index 100% rename from Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-result.txt rename to Tests/RunCMake/EnvSbom/DuplicateSbom-result.txt diff --git a/Tests/RunCMake/EnvSbom/DuplicateSbom-stderr.txt b/Tests/RunCMake/EnvSbom/DuplicateSbom-stderr.txt new file mode 100644 index 0000000000..1f481d5361 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/DuplicateSbom-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at DuplicateSbom\.cmake:[0-9]+ \(install\): + install SBOM command already specified for the file mySbom\. diff --git a/Tests/RunCMake/EnvSbom/DuplicateSbom.cmake b/Tests/RunCMake/EnvSbom/DuplicateSbom.cmake new file mode 100644 index 0000000000..2959dd59a0 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/DuplicateSbom.cmake @@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DuplicateSbom.cmake) + +install(SBOM mySbom EXPORTS setA DESTINATION .) +install(SBOM mySbom EXPORTS setA DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-install-check.cmake b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-install-check.cmake new file mode 100644 index 0000000000..6fe86b33cf --- /dev/null +++ b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-stderr.txt b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-stderr.txt new file mode 100644 index 0000000000..c6d0d96b86 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback-stderr.txt @@ -0,0 +1,4 @@ +CMake Warning \(author\) in CMakeLists\.txt: + Target "libb" references target "liba", whose export does not use the + standard namespace separator\. The dependency will be recorded by its bare + target name without provenance\. diff --git a/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback.cmake b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback.cmake new file mode 100644 index 0000000000..d6d5d575ca --- /dev/null +++ b/Tests/RunCMake/EnvSbom/EmptyNamespaceFallback.cmake @@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/EmptyNamespaceFallback.cmake) + +install(EXPORT setA DESTINATION lib/cmake/A) +install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B) +install(SBOM mySbom EXPORTS setB DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/ExplicitFormat-check.cmake b/Tests/RunCMake/EnvSbom/ExplicitFormat-check.cmake new file mode 100644 index 0000000000..9c584211dc --- /dev/null +++ b/Tests/RunCMake/EnvSbom/ExplicitFormat-check.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Config-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/Genex-check.cmake b/Tests/RunCMake/EnvSbom/Genex-check.cmake index 38c1ddf6d9..8ecfabb15d 100644 --- a/Tests/RunCMake/EnvSbom/Genex-check.cmake +++ b/Tests/RunCMake/EnvSbom/Genex-check.cmake @@ -1,11 +1,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BarConfig.spdx.json" BarConfig) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BazConfig.spdx.json" BazConfig) else() file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/800147c20378e0c5769bafa56e83195d/foo-BarConfig.spdx.json" BarConfig) endif() +set(interface_dependency install_dependency) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs-install-check.cmake b/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs-install-check.cmake new file mode 100644 index 0000000000..edd1da2c65 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs-install-check.cmake @@ -0,0 +1,37 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) +file(READ "${RunCMake_TEST_INSTALL_DIR}/interface_targets-Debug.spdx.json" content) +set(expected [=[ +{ + "@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld", + "@graph": [ + { + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" + }, + { + "spdxId": "urn:interface_targets#SPDXDocument", + "type": "SpdxDocument", + "name": "interface_targets", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"], + "rootElement": [{ + "spdxId": "urn:interface#Package", + "type": "software_Package", + "name": "interface", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + }] + } + ] +} +]=]) +expect_object("${content}" expected) +expect_array("${content}" 2 @graph) +expect_array("${content}" 1 @graph 1 rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element) +if(NOT error) + set(RunCMake_TEST_FAILED "Include directories introduced unexpected SBOM dependencies") +endif() diff --git a/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs.cmake b/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs.cmake new file mode 100644 index 0000000000..bec1ae9139 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/IgnoresInterfaceDirs.cmake @@ -0,0 +1,6 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/IgnoresInterfaceDirs.cmake) + +install(SBOM interface_targets + EXPORTS interface_targets + DESTINATION . +) diff --git a/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet-install-check.cmake b/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet-install-check.cmake new file mode 100644 index 0000000000..063c386f29 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet.cmake b/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet.cmake new file mode 100644 index 0000000000..d07a369660 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/InstallExportPlusSbomSameSet.cmake @@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/InstallExportPlusSbomSameSet.cmake) + +install(EXPORT export_set_a NAMESPACE A:: DESTINATION lib/cmake/setA) +install(SBOM mySbom EXPORTS export_set_a DESTINATION .) +install(EXPORT export_set_b DESTINATION lib/cmake/setB) diff --git a/Tests/RunCMake/EnvSbom/InvalidFormat.cmake b/Tests/RunCMake/EnvSbom/InvalidFormat.cmake index 673750acd8..e69de29bb2 100644 --- a/Tests/RunCMake/EnvSbom/InvalidFormat.cmake +++ b/Tests/RunCMake/EnvSbom/InvalidFormat.cmake @@ -1 +0,0 @@ -# Invalid formats are rejected even when there are no export sets. diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-result.txt b/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity-result.txt similarity index 100% rename from Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-result.txt rename to Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity-result.txt diff --git a/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity-stderr.txt new file mode 100644 index 0000000000..739ec831d1 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity-stderr.txt @@ -0,0 +1,8 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in an export set which is + exported multiple times with different namespaces: lib/cmake/A/setA\.cmake, + lib/cmake/OldA/setA\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\. diff --git a/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity.cmake b/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity.cmake new file mode 100644 index 0000000000..b634a663c3 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiNamespaceAmbiguity.cmake @@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiNamespaceAmbiguity.cmake) + +install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A) +install(EXPORT setA NAMESPACE OldA:: DESTINATION lib/cmake/OldA) +install(SBOM mySbom EXPORTS setB DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-result.txt b/Tests/RunCMake/EnvSbom/MultiSetAmbiguity-result.txt similarity index 100% rename from Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-result.txt rename to Tests/RunCMake/EnvSbom/MultiSetAmbiguity-result.txt diff --git a/Tests/RunCMake/EnvSbom/MultiSetAmbiguity-stderr.txt b/Tests/RunCMake/EnvSbom/MultiSetAmbiguity-stderr.txt new file mode 100644 index 0000000000..45acf127cf --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiSetAmbiguity-stderr.txt @@ -0,0 +1,7 @@ +CMake Error in CMakeLists\.txt: + Target "libb" references target "liba" that is in multiple export sets: + lib/cmake/A1/setA1\.cmake, lib/cmake/A2/setA2\.cmake\. + + An SBOM cannot attribute a dependency exported in more than one export set + or with more than one namespace\. Consider consolidating the exports of the + "liba" target to a single export\. diff --git a/Tests/RunCMake/EnvSbom/MultiSetAmbiguity.cmake b/Tests/RunCMake/EnvSbom/MultiSetAmbiguity.cmake new file mode 100644 index 0000000000..a434374919 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiSetAmbiguity.cmake @@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetAmbiguity.cmake) + +install(EXPORT setA1 NAMESPACE A1:: DESTINATION lib/cmake/A1) +install(EXPORT setA2 NAMESPACE A2:: DESTINATION lib/cmake/A2) +install(SBOM mySbom EXPORTS setB DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/MultiSetSingleSbom-install-check.cmake b/Tests/RunCMake/EnvSbom/MultiSetSingleSbom-install-check.cmake new file mode 100644 index 0000000000..47cf3ca3ad --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiSetSingleSbom-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/MultiSetSingleSbom.cmake b/Tests/RunCMake/EnvSbom/MultiSetSingleSbom.cmake new file mode 100644 index 0000000000..ac55d2f1ad --- /dev/null +++ b/Tests/RunCMake/EnvSbom/MultiSetSingleSbom.cmake @@ -0,0 +1,5 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MultiSetSingleSbom.cmake) + +install(EXPORT setA NAMESPACE A:: DESTINATION lib/cmake/A) +install(EXPORT setB NAMESPACE B:: DESTINATION lib/cmake/B) +install(SBOM mySbom EXPORTS setA setB DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/NoProjectMetadata-install-check.cmake b/Tests/RunCMake/EnvSbom/NoProjectMetadata-install-check.cmake new file mode 100644 index 0000000000..acac579b69 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/NoProjectMetadata-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/metadata-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/NoProjectMetadata.cmake b/Tests/RunCMake/EnvSbom/NoProjectMetadata.cmake new file mode 100644 index 0000000000..0bfa822c5d --- /dev/null +++ b/Tests/RunCMake/EnvSbom/NoProjectMetadata.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata.cmake) + +install(SBOM metadata EXPORTS metadata NO_PROJECT_METADATA DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake b/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake index 19892cac5e..84cd65852d 100644 --- a/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake +++ b/Tests/RunCMake/EnvSbom/PartialCoverage-install-check.cmake @@ -21,4 +21,54 @@ endforeach() if(failures) string(REPLACE ";" "\n " msg "${failures}") set(RunCMake_TEST_FAILED "${msg}") + return() endif() + +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) + +set(sbom_names explicit_root_sbom explicit_subdir_sbom implicit_root implicit_subdir) +set(target_names libA libC libB libD) +set(sbom_paths + "${RunCMake_TEST_INSTALL_DIR}/explicit_root_sbom-Debug.spdx.json" + "${RunCMake_TEST_INSTALL_DIR}/explicit_subdir_sbom-Debug.spdx.json" + "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/implicit_root/implicit_root-Debug.spdx.json" + "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/implicit_subdir/implicit_subdir-Debug.spdx.json" +) +foreach(sbom target path IN ZIP_LISTS sbom_names target_names sbom_paths) + file(READ "${path}" content) + string(CONFIGURE [=[ + { + "@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld", + "@graph": [ + { + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" + }, + { + "spdxId": "urn:@sbom@#SPDXDocument", + "type": "SpdxDocument", + "name": "@sbom@", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"], + "rootElement": [{ + "spdxId": "urn:@target@#Package", + "type": "software_Package", + "name": "@target@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + }] + } + ] + } + ]=] expected @ONLY) + expect_object("${content}" expected) + expect_array("${content}" 2 @graph) + expect_array("${content}" 1 @graph 1 rootElement) + string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element) + if(NOT error) + expect_array("${content}" 0 @graph 1 element) + endif() +endforeach() diff --git a/Tests/RunCMake/EnvSbom/PrivateBareLibrary-install-check.cmake b/Tests/RunCMake/EnvSbom/PrivateBareLibrary-install-check.cmake new file mode 100644 index 0000000000..7e3039faab --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PrivateBareLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/foo/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/PrivateBareLibrary.cmake b/Tests/RunCMake/EnvSbom/PrivateBareLibrary.cmake new file mode 100644 index 0000000000..17a4a7b568 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PrivateBareLibrary.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary.cmake) diff --git a/Tests/RunCMake/EnvSbom/PrivateStaticDependency-install-check.cmake b/Tests/RunCMake/EnvSbom/PrivateStaticDependency-install-check.cmake new file mode 100644 index 0000000000..1d78d2f452 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PrivateStaticDependency-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/foo/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/PrivateStaticDependency.cmake b/Tests/RunCMake/EnvSbom/PrivateStaticDependency.cmake new file mode 100644 index 0000000000..22955919cb --- /dev/null +++ b/Tests/RunCMake/EnvSbom/PrivateStaticDependency.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency.cmake) diff --git a/Tests/RunCMake/EnvSbom/ProjectLicense-install-check.cmake b/Tests/RunCMake/EnvSbom/ProjectLicense-install-check.cmake new file mode 100644 index 0000000000..2d02fedf34 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/ProjectLicense-install-check.cmake @@ -0,0 +1,4 @@ +foreach(sbom IN ITEMS bundle selected unmatched) + file(READ "${RunCMake_TEST_INSTALL_DIR}/${sbom}-Debug.spdx.json" content) + include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense-install-check.cmake) +endforeach() diff --git a/Tests/RunCMake/EnvSbom/ProjectLicense.cmake b/Tests/RunCMake/EnvSbom/ProjectLicense.cmake new file mode 100644 index 0000000000..8727f1a85d --- /dev/null +++ b/Tests/RunCMake/EnvSbom/ProjectLicense.cmake @@ -0,0 +1,17 @@ +project(Selected VERSION 2.3.4 SPDX_LICENSE ISC + DESCRIPTION "Selected project" LANGUAGES NONE) + +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense.cmake) + +install( + SBOM bundle + PROJECT bar + VERSION 1.3.4 + EXPORTS bar_targets child_targets + DESTINATION . +) + +install(SBOM selected PROJECT Selected + EXPORTS bar_targets child_targets DESTINATION .) +install(SBOM unmatched + EXPORTS bar_targets child_targets DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/EnvSbom/ProjectMetadata-install-check.cmake index ae99a64f4b..080de4f645 100644 --- a/Tests/RunCMake/EnvSbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/EnvSbom/ProjectMetadata-install-check.cmake @@ -1,2 +1,17 @@ file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/test_targets/test_targets-Debug.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake) + +expect_value("${SPDX_DOCUMENT}" "Metadata Test Project" description) +expect_value("${SPDX_DOCUMENT}" BSD-3 dataLicense simplelicensing_licenseExpression) +expect_value("${SPDX_DOCUMENT}" "urn:test_targets#DataLicenseExpression" dataLicense spdxId) +set(PROJECT_METADATA_TEMPLATE [=[ +{ + "spdxId": "urn:@package@#Package", + "software_packageVersion": "1.2.0", + "software_homePage": "www.example.com" +} +]=]) +foreach(package IN ITEMS test test_targets) + string(CONFIGURE "${PROJECT_METADATA_TEMPLATE}" PROJECT_METADATA_EXPECTED @ONLY) + expect_object("${SPDX_DOCUMENT}" PROJECT_METADATA_EXPECTED rootElement) +endforeach() diff --git a/Tests/RunCMake/EnvSbom/ProjectMetadata.cmake b/Tests/RunCMake/EnvSbom/ProjectMetadata.cmake index 0b07078f9c..9601c8e136 100644 --- a/Tests/RunCMake/EnvSbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/EnvSbom/ProjectMetadata.cmake @@ -1,6 +1,6 @@ project(test_project LANGUAGES NONE VERSION 1.3 DESCRIPTION "An eloquent description" - HOMEPAGE_URL "www.example.com" - SPDX_LICENSE "BSD-3") + HOMEPAGE_URL www.example.com + SPDX_LICENSE BSD-3) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake) diff --git a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt deleted file mode 100644 index 3dfbdb65da..0000000000 --- a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error in CMakeLists\.txt: - Target "canine" references target "mammal" which has no - install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. - An SBOM cannot attribute this dependency\. Give "mammal" an - install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an - install\(SBOM\)/export\(SBOM\)\. diff --git a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget.cmake deleted file mode 100644 index 7c30010f4a..0000000000 --- a/Tests/RunCMake/EnvSbom/ReferencesNonExportedTarget.cmake +++ /dev/null @@ -1,2 +0,0 @@ -project(test_project) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake) diff --git a/Tests/RunCMake/EnvSbom/Requirements-install-check.cmake b/Tests/RunCMake/EnvSbom/Requirements-install-check.cmake new file mode 100644 index 0000000000..09f9a2a868 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/Requirements-install-check.cmake @@ -0,0 +1,3 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/bar_sbom-Debug.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo_sbom-Debug.spdx.json" FOO_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/Requirements.cmake b/Tests/RunCMake/EnvSbom/Requirements.cmake new file mode 100644 index 0000000000..350fd858d1 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/Requirements.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Requirements.cmake) diff --git a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake index aeae4ae14e..0d37a1f7d1 100644 --- a/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/EnvSbom/RunCMakeTest.cmake @@ -1,13 +1,13 @@ include(RunCMake) set(common_test_options - "-DCMAKE_INSTALL_SBOM_FORMATS:STRING=spdx" - "-DCMAKE_INSTALL_LIBDIR=lib" + -DCMAKE_INSTALL_SBOM_FORMATS:STRING=spdx + -DCMAKE_INSTALL_LIBDIR=lib ) function(run_cmake_error test) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) - set(RunCMake_TEST_OPTIONS ${common_test_options}) + set(RunCMake_TEST_OPTIONS ${common_test_options} ${ARGN}) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug) endif() @@ -17,7 +17,7 @@ endfunction() function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) - set(RunCMake_TEST_INSTALL_DIR ${RunCMake_BINARY_DIR}/${test}-install) + set(RunCMake_TEST_INSTALL_DIR ${RunCMake_TEST_BINARY_DIR}/${test}-install) set(RunCMake_TEST_OPTIONS ${common_test_options} ${extra_options}) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_INSTALL_DIR}) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) @@ -32,26 +32,50 @@ function(run_cmake_install test) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() -function(run_cmake_configure test) +function(run_cmake_configure test config other_config) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_OPTIONS ${common_test_options} ${extra_options}) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS + "-DCMAKE_CONFIGURATION_TYPES=${config}\\;${other_config}") + else() + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config}) + endif() run_cmake(${test}) endfunction() +run_cmake_install(ProjectLicense) run_cmake_install(ApplicationTarget) +run_cmake_install(InstallExportPlusSbomSameSet) run_cmake_install(InterfaceTarget) run_cmake_install(SharedTarget) +run_cmake_install(Requirements) +run_cmake_install(SbomNamespaceFallback) +run_cmake_install(MultiSetSingleSbom) +run_cmake_install(TargetInMultipleSets) +run_cmake_install(EmptyNamespaceFallback -Wauthor) +run_cmake_install(IgnoresInterfaceDirs) run_cmake_install(MissingPackageNamespace) run_cmake_install(ProjectMetadata) +run_cmake_install(DefaultLicense) +run_cmake_install(UnknownDependencies) +run_cmake_install(NoProjectMetadata) +run_cmake_install(UninstalledStaticLibrary) run_cmake_install(PartialCoverage) +run_cmake_install(TransitiveDependencies) +run_cmake_install(PrivateStaticDependency) +run_cmake_install(PrivateBareLibrary) -run_cmake_error(ReferencesNonExportedTarget) +run_cmake_error(MultiNamespaceAmbiguity) +run_cmake_error(MultiSetAmbiguity) +run_cmake_error(DuplicateSbom) -run_cmake_configure(Config) -run_cmake_configure(ExplicitFormat -DCMAKE_INSTALL_SBOM_FORMATS=spdx-3.0+json) -run_cmake_configure(InvalidFormat -DCMAKE_INSTALL_SBOM_FORMATS=JSON) -run_cmake_configure(Genex) -run_cmake_configure(ForbiddenGenex) +run_cmake_configure(Config Debug Release) +run_cmake_configure(ExplicitFormat Debug Release -DCMAKE_INSTALL_SBOM_FORMATS=spdx-3.0+json) +run_cmake_configure(Genex BarConfig BazConfig) + +run_cmake_error(InvalidFormat -DCMAKE_INSTALL_SBOM_FORMATS=JSON) +run_cmake_error(ForbiddenGenex) diff --git a/Tests/RunCMake/EnvSbom/SbomNamespaceFallback-install-check.cmake b/Tests/RunCMake/EnvSbom/SbomNamespaceFallback-install-check.cmake new file mode 100644 index 0000000000..ee60380f74 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/SbomNamespaceFallback-install-check.cmake @@ -0,0 +1,3 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/bar-Debug.spdx.json" BAR_CONTENT) +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo-Debug.spdx.json" FOO_CONTENT) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/SbomNamespaceFallback.cmake b/Tests/RunCMake/EnvSbom/SbomNamespaceFallback.cmake new file mode 100644 index 0000000000..132e9c9d61 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/SbomNamespaceFallback.cmake @@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceFallback.cmake) + +install(SBOM foo EXPORTS foo DESTINATION .) +install(SBOM bar EXPORTS bar DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/TargetInMultipleSets-install-check.cmake b/Tests/RunCMake/EnvSbom/TargetInMultipleSets-install-check.cmake new file mode 100644 index 0000000000..063c386f29 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/TargetInMultipleSets-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/TargetInMultipleSets.cmake b/Tests/RunCMake/EnvSbom/TargetInMultipleSets.cmake new file mode 100644 index 0000000000..20ced6a928 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/TargetInMultipleSets.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets.cmake) + +install(SBOM mySbom EXPORTS setA1 setA2 DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/TransitiveDependencies-install-check.cmake b/Tests/RunCMake/EnvSbom/TransitiveDependencies-install-check.cmake new file mode 100644 index 0000000000..b0c689d336 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/TransitiveDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/traversal/traversal-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/TransitiveDependencies.cmake b/Tests/RunCMake/EnvSbom/TransitiveDependencies.cmake new file mode 100644 index 0000000000..7484dffa23 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/TransitiveDependencies.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies.cmake) diff --git a/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary-install-check.cmake b/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary-install-check.cmake new file mode 100644 index 0000000000..d697b04dd5 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/baz-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary.cmake b/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary.cmake new file mode 100644 index 0000000000..bec4002e79 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/UninstalledStaticLibrary.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary.cmake) + +install(SBOM baz EXPORTS baz FORMAT spdx DESTINATION .) diff --git a/Tests/RunCMake/EnvSbom/UnknownDependencies-install-check.cmake b/Tests/RunCMake/EnvSbom/UnknownDependencies-install-check.cmake new file mode 100644 index 0000000000..b1cfdbc6c0 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/UnknownDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/lib/sbom/unknown_dependencies/unknown_dependencies-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies-install-check.cmake) diff --git a/Tests/RunCMake/EnvSbom/UnknownDependencies.cmake b/Tests/RunCMake/EnvSbom/UnknownDependencies.cmake new file mode 100644 index 0000000000..4e711e0e16 --- /dev/null +++ b/Tests/RunCMake/EnvSbom/UnknownDependencies.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies.cmake) diff --git a/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake b/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake index fafc73c02b..f6207c2779 100644 --- a/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake +++ b/Tests/RunCMake/ExportSbom/ApplicationTarget.cmake @@ -3,5 +3,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget.cmake) export( SBOM application_targets EXPORTS application_targets - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json ) diff --git a/Tests/RunCMake/ExportSbom/Config-check.cmake b/Tests/RunCMake/ExportSbom/Config-check.cmake index 61f1b06b84..3182aac7e6 100644 --- a/Tests/RunCMake/ExportSbom/Config-check.cmake +++ b/Tests/RunCMake/ExportSbom/Config-check.cmake @@ -1,7 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" DebugSbom) file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Release.spdx.json" ReleaseSbom) else() diff --git a/Tests/RunCMake/ExportSbom/Config.cmake b/Tests/RunCMake/ExportSbom/Config.cmake index 8d107134bb..be78feb8a3 100644 --- a/Tests/RunCMake/ExportSbom/Config.cmake +++ b/Tests/RunCMake/ExportSbom/Config.cmake @@ -2,5 +2,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Config.cmake) export(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json ) diff --git a/Tests/RunCMake/ExportSbom/DefaultLicense-install-check.cmake b/Tests/RunCMake/ExportSbom/DefaultLicense-install-check.cmake new file mode 100644 index 0000000000..51a0a7f142 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/DefaultLicense-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/licenses/licenses-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/DefaultLicense.cmake b/Tests/RunCMake/ExportSbom/DefaultLicense.cmake new file mode 100644 index 0000000000..bb341c6649 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/DefaultLicense.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense.cmake) + +export(SBOM licenses EXPORTS licenses DEFAULT_LICENSE MIT) diff --git a/Tests/RunCMake/ExportSbom/ForbiddenGenex.cmake b/Tests/RunCMake/ExportSbom/ForbiddenGenex.cmake index 2b6f6a5071..960eb08501 100644 --- a/Tests/RunCMake/ExportSbom/ForbiddenGenex.cmake +++ b/Tests/RunCMake/ExportSbom/ForbiddenGenex.cmake @@ -1,5 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ForbiddenGenex.cmake) export(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json ) diff --git a/Tests/RunCMake/ExportSbom/Genex-check.cmake b/Tests/RunCMake/ExportSbom/Genex-check.cmake index 9ae850ed16..5dac8b69f7 100644 --- a/Tests/RunCMake/ExportSbom/Genex-check.cmake +++ b/Tests/RunCMake/ExportSbom/Genex-check.cmake @@ -1,11 +1,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BarConfig.spdx.json" BarConfig) file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BazConfig.spdx.json" BazConfig) else() file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-BarConfig.spdx.json" BarConfig) endif() +set(interface_dependency build_dependency) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/Genex.cmake b/Tests/RunCMake/ExportSbom/Genex.cmake index fadbf197a9..e21aa9db94 100644 --- a/Tests/RunCMake/ExportSbom/Genex.cmake +++ b/Tests/RunCMake/ExportSbom/Genex.cmake @@ -1,5 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex.cmake) export(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json ) diff --git a/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs-install-check.cmake b/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs-install-check.cmake new file mode 100644 index 0000000000..ec5c53de3a --- /dev/null +++ b/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs-install-check.cmake @@ -0,0 +1,37 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/interface_targets/interface_targets-Debug.spdx.json" content) +set(expected [=[ +{ + "@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld", + "@graph": [ + { + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" + }, + { + "spdxId": "urn:interface_targets#SPDXDocument", + "type": "SpdxDocument", + "name": "interface_targets", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"], + "rootElement": [{ + "spdxId": "urn:interface#Package", + "type": "software_Package", + "name": "interface", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + }] + } + ] +} +]=]) +expect_object("${content}" expected) +expect_array("${content}" 2 @graph) +expect_array("${content}" 1 @graph 1 rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element) +if(NOT error) + set(RunCMake_TEST_FAILED "Include directories introduced unexpected SBOM dependencies") +endif() diff --git a/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs.cmake b/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs.cmake new file mode 100644 index 0000000000..8a7f98a0c3 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/IgnoresInterfaceDirs.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/IgnoresInterfaceDirs.cmake) + +export(SBOM interface_targets EXPORTS interface_targets) diff --git a/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet-install-check.cmake b/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet-install-check.cmake new file mode 100644 index 0000000000..5a79552389 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/InstallExportPlusSbomSameSet-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/mySbom/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/NoProjectMetadata-install-check.cmake b/Tests/RunCMake/ExportSbom/NoProjectMetadata-install-check.cmake new file mode 100644 index 0000000000..225b819c42 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/NoProjectMetadata-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/metadata/metadata-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/NoProjectMetadata.cmake b/Tests/RunCMake/ExportSbom/NoProjectMetadata.cmake new file mode 100644 index 0000000000..81a57cd891 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/NoProjectMetadata.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata.cmake) + +export(SBOM metadata EXPORTS metadata NO_PROJECT_METADATA) diff --git a/Tests/RunCMake/ExportSbom/PrivateBareLibrary-install-check.cmake b/Tests/RunCMake/ExportSbom/PrivateBareLibrary-install-check.cmake new file mode 100644 index 0000000000..c9458d7217 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/PrivateBareLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/PrivateBareLibrary.cmake b/Tests/RunCMake/ExportSbom/PrivateBareLibrary.cmake new file mode 100644 index 0000000000..6c86292930 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/PrivateBareLibrary.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary.cmake) + +export(SBOM foo EXPORTS foo FORMAT spdx) diff --git a/Tests/RunCMake/ExportSbom/PrivateStaticDependency-install-check.cmake b/Tests/RunCMake/ExportSbom/PrivateStaticDependency-install-check.cmake new file mode 100644 index 0000000000..9aa1b1778e --- /dev/null +++ b/Tests/RunCMake/ExportSbom/PrivateStaticDependency-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/foo/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/PrivateStaticDependency.cmake b/Tests/RunCMake/ExportSbom/PrivateStaticDependency.cmake new file mode 100644 index 0000000000..8afccbce7a --- /dev/null +++ b/Tests/RunCMake/ExportSbom/PrivateStaticDependency.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency.cmake) + +export(SBOM foo EXPORTS foo FORMAT spdx) diff --git a/Tests/RunCMake/ExportSbom/ProjectLicense-install-check.cmake b/Tests/RunCMake/ExportSbom/ProjectLicense-install-check.cmake new file mode 100644 index 0000000000..707bbffe9f --- /dev/null +++ b/Tests/RunCMake/ExportSbom/ProjectLicense-install-check.cmake @@ -0,0 +1,4 @@ +foreach(sbom IN ITEMS bundle selected unmatched) + file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/${sbom}/${sbom}-Debug.spdx.json" content) + include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense-install-check.cmake) +endforeach() diff --git a/Tests/RunCMake/ExportSbom/ProjectLicense.cmake b/Tests/RunCMake/ExportSbom/ProjectLicense.cmake new file mode 100644 index 0000000000..2836823022 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/ProjectLicense.cmake @@ -0,0 +1,13 @@ +project(Selected VERSION 2.3.4 SPDX_LICENSE ISC + DESCRIPTION "Selected project" LANGUAGES NONE) + +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense.cmake) + +export(SBOM bundle + PROJECT bar + VERSION 1.3.4 + EXPORTS bar_targets child_targets) +export(SBOM selected PROJECT Selected + EXPORTS bar_targets child_targets) +export(SBOM unmatched + EXPORTS bar_targets child_targets) diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake index 6797643b43..4c1e9a1d9a 100644 --- a/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/ExportSbom/ProjectMetadata-install-check.cmake @@ -1,3 +1,30 @@ file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/test_targets/test_targets-Debug.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadataExplicitAssertions.cmake) + +set(DOCUMENT_METADATA_EXPECTED [=[ +{ + "description" : "An eloquent description", + "dataLicense" : + { + "creationInfo" : "_:Build#CreationInfo", + "simplelicensing_licenseExpression" : "BSD-3", + "spdxId" : "urn:test_targets#DataLicenseExpression", + "type" : "simplelicensing_LicenseExpression" + } +} +]=]) + +set(PACKAGE_METADATA_TEMPLATE [=[ +{ + "spdxId" : "urn:@package@#Package", + "software_packageVersion" : "1.3.4", + "software_homePage" : "www.example.com", + "software_downloadLocation" : "https://example.com/test_targets.tar.gz" +} +]=]) + +expect_object("${SPDX_DOCUMENT}" DOCUMENT_METADATA_EXPECTED) +foreach(package IN ITEMS test test_targets) + string(CONFIGURE "${PACKAGE_METADATA_TEMPLATE}" PACKAGE_METADATA_EXPECTED @ONLY) + expect_object("${SPDX_DOCUMENT}" PACKAGE_METADATA_EXPECTED rootElement) +endforeach() diff --git a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake index 43678f2cd6..fdc5c34bca 100644 --- a/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/ExportSbom/ProjectMetadata.cmake @@ -2,10 +2,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake) export( SBOM test_targets + NO_PROJECT_METADATA EXPORTS test_targets DESCRIPTION "An eloquent description" - DATA_LICENSE "BSD-3" - HOMEPAGE_URL "www.example.com" - PACKAGE_URL "https://example.com/test_targets.tar.gz" - VERSION "1.3.4" + DATA_LICENSE BSD-3 + HOMEPAGE_URL www.example.com + PACKAGE_URL https://example.com/test_targets.tar.gz + VERSION 1.3.4 ) diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt deleted file mode 100644 index 3dfbdb65da..0000000000 --- a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error in CMakeLists\.txt: - Target "canine" references target "mammal" which has no - install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. - An SBOM cannot attribute this dependency\. Give "mammal" an - install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an - install\(SBOM\)/export\(SBOM\)\. diff --git a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake deleted file mode 100644 index 9c06ac846f..0000000000 --- a/Tests/RunCMake/ExportSbom/ReferencesNonExportedTarget.cmake +++ /dev/null @@ -1,3 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake) - -export(SBOM dog EXPORTS dog) diff --git a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake index 8ceb479821..7859a1e6c3 100644 --- a/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/ExportSbom/RunCMakeTest.cmake @@ -12,7 +12,7 @@ endfunction() function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) - set(RunCMake_TEST_INSTALL_DIR ${RunCMake_BINARY_DIR}/${test}-install) + set(RunCMake_TEST_INSTALL_DIR ${RunCMake_TEST_BINARY_DIR}/install) set(RunCMake_TEST_OPTIONS ${extra_options}) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_INSTALL_DIR}) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) @@ -27,13 +27,20 @@ function(run_cmake_install test) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() -function(run_cmake_configure test) +function(run_cmake_configure test config other_config) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_OPTIONS ${extra_options}) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS + "-DCMAKE_CONFIGURATION_TYPES=${config}\\;${other_config}") + else() + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config}) + endif() run_cmake(${test}) endfunction() +run_cmake_install(ProjectLicense) run_cmake_install(ApplicationTarget) run_cmake_install(InstallExportPlusSbomSameSet) run_cmake_install(InterfaceTarget) @@ -43,16 +50,25 @@ run_cmake_install(SbomNamespaceFallback) run_cmake_install(MultiSetSingleSbom) run_cmake_install(TargetInMultipleSets) run_cmake_install(EmptyNamespaceFallback -Wauthor) -run_cmake_install(SbomNamespaceAmbiguity -Wauthor) +run_cmake_install(IgnoresInterfaceDirs) run_cmake_install(MissingPackageNamespace) run_cmake_install(ProjectMetadata) +run_cmake_install(DefaultLicense) +run_cmake_install(UnknownDependencies) +run_cmake_install(NoProjectMetadata) +run_cmake_install(UninstalledStaticLibrary) +block() + set(RunCMake_TEST_TIMEOUT 30) + run_cmake_install(TransitiveDependencies) +endblock() +run_cmake_install(PrivateStaticDependency) +run_cmake_install(PrivateBareLibrary) -run_cmake_error(ReferencesNonExportedTarget) run_cmake_error(MultiNamespaceAmbiguity) run_cmake_error(MultiSetAmbiguity) run_cmake_error(DuplicateSbom) -run_cmake_configure(Config) -run_cmake_configure(Genex) -run_cmake_configure(ForbiddenGenex) +run_cmake_configure(Config Debug Release) +run_cmake_configure(Genex BarConfig BazConfig) +run_cmake_error(ForbiddenGenex) diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake deleted file mode 100644 index 79684b23b5..0000000000 --- a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-install-check.cmake +++ /dev/null @@ -1,2 +0,0 @@ -file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/bar_sbom/bar_sbom-Debug.spdx.json" BAR_CONTENT) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt deleted file mode 100644 index ea4234ff52..0000000000 --- a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity-stderr.txt +++ /dev/null @@ -1,4 +0,0 @@ -CMake Warning \(author\) in CMakeLists\.txt: - Target "libc" references target "libb" which has no - install\(EXPORT\)/export\(EXPORT\) namespace and is covered by multiple SBOMs: - foo_sbom, foo_sbom2\. Attributing to "foo_sbom" \(first alphabetically\)\. diff --git a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake deleted file mode 100644 index a667aca5b9..0000000000 --- a/Tests/RunCMake/ExportSbom/SbomNamespaceAmbiguity.cmake +++ /dev/null @@ -1,5 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity.cmake) - -export(SBOM foo_sbom EXPORTS foo) -export(SBOM foo_sbom2 EXPORTS foo) -export(SBOM bar_sbom EXPORTS bar) diff --git a/Tests/RunCMake/ExportSbom/TransitiveDependencies-install-check.cmake b/Tests/RunCMake/ExportSbom/TransitiveDependencies-install-check.cmake new file mode 100644 index 0000000000..3fe9bed036 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/TransitiveDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/traversal/traversal-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/TransitiveDependencies.cmake b/Tests/RunCMake/ExportSbom/TransitiveDependencies.cmake new file mode 100644 index 0000000000..aeca033905 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/TransitiveDependencies.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies.cmake) + +export(SBOM traversal EXPORTS traversal FORMAT spdx) diff --git a/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary-install-check.cmake b/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary-install-check.cmake new file mode 100644 index 0000000000..5503a21419 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/baz/baz-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary.cmake b/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary.cmake new file mode 100644 index 0000000000..7a30911ef7 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/UninstalledStaticLibrary.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary.cmake) + +export(SBOM baz EXPORTS baz FORMAT spdx) diff --git a/Tests/RunCMake/ExportSbom/UnknownDependencies-install-check.cmake b/Tests/RunCMake/ExportSbom/UnknownDependencies-install-check.cmake new file mode 100644 index 0000000000..69e7f0ab79 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/UnknownDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_BINARY_DIR}/sbom/unknown_dependencies/unknown_dependencies-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies-install-check.cmake) diff --git a/Tests/RunCMake/ExportSbom/UnknownDependencies.cmake b/Tests/RunCMake/ExportSbom/UnknownDependencies.cmake new file mode 100644 index 0000000000..e40f31bce4 --- /dev/null +++ b/Tests/RunCMake/ExportSbom/UnknownDependencies.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies.cmake) + +export(SBOM unknown_dependencies EXPORTS unknown_dependencies) diff --git a/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake b/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake index 60c73ac690..3dae415ee1 100644 --- a/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake +++ b/Tests/RunCMake/InstallSbom/ApplicationTarget.cmake @@ -2,6 +2,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ApplicationTarget.cmake) install(SBOM application_targets EXPORTS application_targets - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/Config-check.cmake b/Tests/RunCMake/InstallSbom/Config-check.cmake index 634960bf0c..bd2c33fded 100644 --- a/Tests/RunCMake/InstallSbom/Config-check.cmake +++ b/Tests/RunCMake/InstallSbom/Config-check.cmake @@ -1,7 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/5058f1af8388633f609cadb75a75dc9d/foo-Debug.spdx.json" DebugSbom) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/5058f1af8388633f609cadb75a75dc9d/foo-Release.spdx.json" ReleaseSbom) else() diff --git a/Tests/RunCMake/InstallSbom/Config.cmake b/Tests/RunCMake/InstallSbom/Config.cmake index 71f076ab65..1685dab267 100644 --- a/Tests/RunCMake/InstallSbom/Config.cmake +++ b/Tests/RunCMake/InstallSbom/Config.cmake @@ -1,6 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Config.cmake) install(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/DefaultLicense-install-check.cmake b/Tests/RunCMake/InstallSbom/DefaultLicense-install-check.cmake new file mode 100644 index 0000000000..07d0a1b743 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/DefaultLicense-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/licenses-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/DefaultLicense.cmake b/Tests/RunCMake/InstallSbom/DefaultLicense.cmake new file mode 100644 index 0000000000..a0031f2734 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/DefaultLicense.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/DefaultLicense.cmake) + +install(SBOM licenses EXPORTS licenses DEFAULT_LICENSE MIT DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/ForbiddenGenex.cmake b/Tests/RunCMake/InstallSbom/ForbiddenGenex.cmake index 536d43e53d..a3a44cc207 100644 --- a/Tests/RunCMake/InstallSbom/ForbiddenGenex.cmake +++ b/Tests/RunCMake/InstallSbom/ForbiddenGenex.cmake @@ -1,6 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ForbiddenGenex.cmake) install(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/Genex-check.cmake b/Tests/RunCMake/InstallSbom/Genex-check.cmake index d03682b9d7..59664f3fcd 100644 --- a/Tests/RunCMake/InstallSbom/Genex-check.cmake +++ b/Tests/RunCMake/InstallSbom/Genex-check.cmake @@ -1,11 +1,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/5058f1af8388633f609cadb75a75dc9d/foo-BarConfig.spdx.json" BarConfig) file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/5058f1af8388633f609cadb75a75dc9d/foo-BazConfig.spdx.json" BazConfig) else() file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/sbom/5058f1af8388633f609cadb75a75dc9d/foo-BarConfig.spdx.json" BarConfig) endif() +set(interface_dependency install_dependency) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/Genex.cmake b/Tests/RunCMake/InstallSbom/Genex.cmake index e07deb9d92..e4d85bffb5 100644 --- a/Tests/RunCMake/InstallSbom/Genex.cmake +++ b/Tests/RunCMake/InstallSbom/Genex.cmake @@ -1,6 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Genex.cmake) install(SBOM foo EXPORTS foo - FORMAT "spdx-3.0+json" + FORMAT spdx-3.0+json DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs-install-check.cmake b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs-install-check.cmake new file mode 100644 index 0000000000..edd1da2c65 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs-install-check.cmake @@ -0,0 +1,37 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/Assertions.cmake) +file(READ "${RunCMake_TEST_INSTALL_DIR}/interface_targets-Debug.spdx.json" content) +set(expected [=[ +{ + "@context": "https://spdx.org/rdf/3.0.1/spdx-context.jsonld", + "@graph": [ + { + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" + }, + { + "spdxId": "urn:interface_targets#SPDXDocument", + "type": "SpdxDocument", + "name": "interface_targets", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"], + "rootElement": [{ + "spdxId": "urn:interface#Package", + "type": "software_Package", + "name": "interface", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + }] + } + ] +} +]=]) +expect_object("${content}" expected) +expect_array("${content}" 2 @graph) +expect_array("${content}" 1 @graph 1 rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${content}" @graph 1 element) +if(NOT error) + set(RunCMake_TEST_FAILED "Include directories introduced unexpected SBOM dependencies") +endif() diff --git a/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake index 1f5a640efc..bec1ae9139 100644 --- a/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake +++ b/Tests/RunCMake/InstallSbom/IgnoresInterfaceDirs.cmake @@ -1,17 +1,4 @@ -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 -) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/IgnoresInterfaceDirs.cmake) install(SBOM interface_targets EXPORTS interface_targets diff --git a/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet-install-check.cmake b/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet-install-check.cmake new file mode 100644 index 0000000000..063c386f29 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/InstallExportPlusSbomSameSet-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/mySbom-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TargetInMultipleSets-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake b/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake index 2e3cf4ec0b..e8e86c0d0f 100644 --- a/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake +++ b/Tests/RunCMake/InstallSbom/MissingPackageNamespace.cmake @@ -1,7 +1,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/MissingPackageNamespace.cmake) install(SBOM test_targets - VERSION "1.0.2" + VERSION 1.0.2 EXPORTS test_targets DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/NoProjectMetadata-install-check.cmake b/Tests/RunCMake/InstallSbom/NoProjectMetadata-install-check.cmake new file mode 100644 index 0000000000..acac579b69 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/NoProjectMetadata-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/metadata-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/NoProjectMetadata.cmake b/Tests/RunCMake/InstallSbom/NoProjectMetadata.cmake new file mode 100644 index 0000000000..0bfa822c5d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/NoProjectMetadata.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/NoProjectMetadata.cmake) + +install(SBOM metadata EXPORTS metadata NO_PROJECT_METADATA DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/PrivateBareLibrary-install-check.cmake b/Tests/RunCMake/InstallSbom/PrivateBareLibrary-install-check.cmake new file mode 100644 index 0000000000..019143e007 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/PrivateBareLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/PrivateBareLibrary.cmake b/Tests/RunCMake/InstallSbom/PrivateBareLibrary.cmake new file mode 100644 index 0000000000..7864df3bd6 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/PrivateBareLibrary.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateBareLibrary.cmake) + +install(SBOM foo EXPORTS foo FORMAT spdx DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/PrivateStaticDependency-install-check.cmake b/Tests/RunCMake/InstallSbom/PrivateStaticDependency-install-check.cmake new file mode 100644 index 0000000000..e09cbb738d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/PrivateStaticDependency-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/foo-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/PrivateStaticDependency.cmake b/Tests/RunCMake/InstallSbom/PrivateStaticDependency.cmake new file mode 100644 index 0000000000..de2a6e6ee3 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/PrivateStaticDependency.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/PrivateStaticDependency.cmake) + +install(SBOM foo EXPORTS foo FORMAT spdx DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/ProjectLicense-install-check.cmake b/Tests/RunCMake/InstallSbom/ProjectLicense-install-check.cmake new file mode 100644 index 0000000000..2d02fedf34 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/ProjectLicense-install-check.cmake @@ -0,0 +1,4 @@ +foreach(sbom IN ITEMS bundle selected unmatched) + file(READ "${RunCMake_TEST_INSTALL_DIR}/${sbom}-Debug.spdx.json" content) + include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense-install-check.cmake) +endforeach() diff --git a/Tests/RunCMake/InstallSbom/ProjectLicense.cmake b/Tests/RunCMake/InstallSbom/ProjectLicense.cmake new file mode 100644 index 0000000000..8727f1a85d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/ProjectLicense.cmake @@ -0,0 +1,17 @@ +project(Selected VERSION 2.3.4 SPDX_LICENSE ISC + DESCRIPTION "Selected project" LANGUAGES NONE) + +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectLicense.cmake) + +install( + SBOM bundle + PROJECT bar + VERSION 1.3.4 + EXPORTS bar_targets child_targets + DESTINATION . +) + +install(SBOM selected PROJECT Selected + EXPORTS bar_targets child_targets DESTINATION .) +install(SBOM unmatched + EXPORTS bar_targets child_targets DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake index 7308b84c3d..89d1db8154 100644 --- a/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/InstallSbom/ProjectMetadata-install-check.cmake @@ -1,3 +1,30 @@ file(READ "${RunCMake_TEST_INSTALL_DIR}/test_targets-Debug.spdx.json" content) include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata-install-check.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadataExplicitAssertions.cmake) + +set(DOCUMENT_METADATA_EXPECTED [=[ +{ + "description" : "An eloquent description", + "dataLicense" : + { + "creationInfo" : "_:Build#CreationInfo", + "simplelicensing_licenseExpression" : "BSD-3", + "spdxId" : "urn:test_targets#DataLicenseExpression", + "type" : "simplelicensing_LicenseExpression" + } +} +]=]) + +set(PACKAGE_METADATA_TEMPLATE [=[ +{ + "spdxId" : "urn:@package@#Package", + "software_packageVersion" : "1.3.4", + "software_homePage" : "www.example.com", + "software_downloadLocation" : "https://example.com/test_targets.tar.gz" +} +]=]) + +expect_object("${SPDX_DOCUMENT}" DOCUMENT_METADATA_EXPECTED) +foreach(package IN ITEMS test test_targets) + string(CONFIGURE "${PACKAGE_METADATA_TEMPLATE}" PACKAGE_METADATA_EXPECTED @ONLY) + expect_object("${SPDX_DOCUMENT}" PACKAGE_METADATA_EXPECTED rootElement) +endforeach() diff --git a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake index d1f99b7b69..532ea2922a 100644 --- a/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/InstallSbom/ProjectMetadata.cmake @@ -2,11 +2,12 @@ include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ProjectMetadata.cmake) install( SBOM test_targets + NO_PROJECT_METADATA DESCRIPTION "An eloquent description" - DATA_LICENSE "BSD-3" - HOMEPAGE_URL "www.example.com" - PACKAGE_URL "https://example.com/test_targets.tar.gz" - VERSION "1.3.4" + DATA_LICENSE BSD-3 + HOMEPAGE_URL www.example.com + PACKAGE_URL https://example.com/test_targets.tar.gz + VERSION 1.3.4 EXPORTS test_targets DESTINATION . ) diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt deleted file mode 100644 index 3dfbdb65da..0000000000 --- a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error in CMakeLists\.txt: - Target "canine" references target "mammal" which has no - install\(EXPORT\)/export\(EXPORT\) namespace and is not covered by any SBOM\. - An SBOM cannot attribute this dependency\. Give "mammal" an - install\(EXPORT\)/export\(EXPORT\) with a NAMESPACE, or include it in an - install\(SBOM\)/export\(SBOM\)\. diff --git a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake deleted file mode 100644 index 561529c27f..0000000000 --- a/Tests/RunCMake/InstallSbom/ReferencesNonExportedTarget.cmake +++ /dev/null @@ -1,3 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/ReferencesNonExportedTarget.cmake) - -install(SBOM dog EXPORTS dog DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake index e5e116923a..7859a1e6c3 100644 --- a/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake +++ b/Tests/RunCMake/InstallSbom/RunCMakeTest.cmake @@ -12,7 +12,7 @@ endfunction() function(run_cmake_install test) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) - set(RunCMake_TEST_INSTALL_DIR ${RunCMake_BINARY_DIR}/${test}-install) + set(RunCMake_TEST_INSTALL_DIR ${RunCMake_TEST_BINARY_DIR}/install) set(RunCMake_TEST_OPTIONS ${extra_options}) list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_INSTALL_DIR}) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) @@ -27,29 +27,48 @@ function(run_cmake_install test) run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Debug) endfunction() -function(run_cmake_configure test) +function(run_cmake_configure test config other_config) set(extra_options ${ARGN}) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build) set(RunCMake_TEST_OPTIONS ${extra_options}) + if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND RunCMake_TEST_OPTIONS + "-DCMAKE_CONFIGURATION_TYPES=${config}\\;${other_config}") + else() + list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config}) + endif() run_cmake(${test}) endfunction() +run_cmake_install(ProjectLicense) run_cmake_install(ApplicationTarget) run_cmake_install(InstallExportPlusSbomSameSet) run_cmake_install(InterfaceTarget) +run_cmake_install(SharedTarget) +run_cmake_install(Requirements) +run_cmake_install(SbomNamespaceFallback) +run_cmake_install(MultiSetSingleSbom) run_cmake_install(TargetInMultipleSets) run_cmake_install(EmptyNamespaceFallback -Wauthor) -run_cmake_install(SbomNamespaceAmbiguity -Wauthor) run_cmake_install(IgnoresInterfaceDirs) run_cmake_install(MissingPackageNamespace) run_cmake_install(ProjectMetadata) +run_cmake_install(DefaultLicense) +run_cmake_install(UnknownDependencies) +run_cmake_install(NoProjectMetadata) +run_cmake_install(UninstalledStaticLibrary) +block() + set(RunCMake_TEST_TIMEOUT 30) + run_cmake_install(TransitiveDependencies) +endblock() +run_cmake_install(PrivateStaticDependency) +run_cmake_install(PrivateBareLibrary) -run_cmake_error(ReferencesNonExportedTarget) run_cmake_error(MultiNamespaceAmbiguity) run_cmake_error(MultiSetAmbiguity) run_cmake_error(DuplicateSbom) -run_cmake_configure(Config) -run_cmake_configure(Genex) -run_cmake_configure(ForbiddenGenex) +run_cmake_configure(Config Debug Release) +run_cmake_configure(Genex BarConfig BazConfig) +run_cmake_error(ForbiddenGenex) diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake deleted file mode 100644 index 580a84e240..0000000000 --- a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-install-check.cmake +++ /dev/null @@ -1,2 +0,0 @@ -file(READ "${RunCMake_TEST_INSTALL_DIR}/bar_sbom-Debug.spdx.json" BAR_CONTENT) -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt deleted file mode 100644 index ea4234ff52..0000000000 --- a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity-stderr.txt +++ /dev/null @@ -1,4 +0,0 @@ -CMake Warning \(author\) in CMakeLists\.txt: - Target "libc" references target "libb" which has no - install\(EXPORT\)/export\(EXPORT\) namespace and is covered by multiple SBOMs: - foo_sbom, foo_sbom2\. Attributing to "foo_sbom" \(first alphabetically\)\. diff --git a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake deleted file mode 100644 index 564979dc48..0000000000 --- a/Tests/RunCMake/InstallSbom/SbomNamespaceAmbiguity.cmake +++ /dev/null @@ -1,5 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/SbomNamespaceAmbiguity.cmake) - -install(SBOM foo_sbom EXPORTS foo DESTINATION .) -install(SBOM foo_sbom2 EXPORTS foo DESTINATION .) -install(SBOM bar_sbom EXPORTS bar DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/TransitiveDependencies-install-check.cmake b/Tests/RunCMake/InstallSbom/TransitiveDependencies-install-check.cmake new file mode 100644 index 0000000000..ef69afbe3d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/TransitiveDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/traversal-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/TransitiveDependencies.cmake b/Tests/RunCMake/InstallSbom/TransitiveDependencies.cmake new file mode 100644 index 0000000000..7ef4ab7a67 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/TransitiveDependencies.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/TransitiveDependencies.cmake) + +install(SBOM traversal EXPORTS traversal FORMAT spdx DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary-install-check.cmake b/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary-install-check.cmake new file mode 100644 index 0000000000..d697b04dd5 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/baz-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary.cmake b/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary.cmake new file mode 100644 index 0000000000..bec4002e79 --- /dev/null +++ b/Tests/RunCMake/InstallSbom/UninstalledStaticLibrary.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UninstalledStaticLibrary.cmake) + +install(SBOM baz EXPORTS baz FORMAT spdx DESTINATION .) diff --git a/Tests/RunCMake/InstallSbom/UnknownDependencies-install-check.cmake b/Tests/RunCMake/InstallSbom/UnknownDependencies-install-check.cmake new file mode 100644 index 0000000000..6e9855bf6d --- /dev/null +++ b/Tests/RunCMake/InstallSbom/UnknownDependencies-install-check.cmake @@ -0,0 +1,2 @@ +file(READ "${RunCMake_TEST_INSTALL_DIR}/unknown_dependencies-Debug.spdx.json" content) +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies-install-check.cmake) diff --git a/Tests/RunCMake/InstallSbom/UnknownDependencies.cmake b/Tests/RunCMake/InstallSbom/UnknownDependencies.cmake new file mode 100644 index 0000000000..bd6f2b180b --- /dev/null +++ b/Tests/RunCMake/InstallSbom/UnknownDependencies.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../Sbom/UnknownDependencies.cmake) + +install(SBOM unknown_dependencies EXPORTS unknown_dependencies DESTINATION .) diff --git a/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake b/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake index 0b63fe0925..2a38051969 100644 --- a/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/ApplicationTarget-install-check.cmake @@ -55,10 +55,10 @@ set(DEPENDENCY_EXPECTED [=[ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", + "description" : "Required Build-Time Libraries", "from" : "urn:application#Package", "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", + "spdxId" : "urn:application#StaticRelationship", "to" : [ "urn:bar:bar#Package" @@ -68,13 +68,17 @@ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ ]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) +string(JSON LINKED_LIBRARIES GET "${content}" @graph 2) expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED) +expect_array("${LINKED_LIBRARIES}" 1 to) +expect_array("${content}" 3 @graph) diff --git a/Tests/RunCMake/Sbom/Assertions.cmake b/Tests/RunCMake/Sbom/Assertions.cmake index 86ff8720cb..1e9d258350 100644 --- a/Tests/RunCMake/Sbom/Assertions.cmake +++ b/Tests/RunCMake/Sbom/Assertions.cmake @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) function(_json_format_path out_var) - set(path_str "") + set(path_str ) set(segments ${ARGN}) if(segments) set(path_str "") @@ -40,7 +40,7 @@ function(expect_object actual_json expected_var) if(path_segments) string(JSON actual_node ERROR_VARIABLE err GET "${actual_json}" ${path_segments}) if(err) - _json_fail_at("Path Not Found" "" "${path_segments}" "") + _json_fail_at("Path Not Found" "${path_segments}" "") return() endif() else() @@ -69,7 +69,7 @@ function(expect_value actual_json expected_val) string(JSON actual_val ERROR_VARIABLE err GET "${actual_json}" ${path_segments}) if(err) - _json_fail_at("Path Not Found" "" "${path_segments}" "") + _json_fail_at("Path Not Found" "${path_segments}" "") return() endif() @@ -83,12 +83,12 @@ function(expect_array actual_json expected_len) string(JSON actual_type ERROR_VARIABLE err TYPE "${actual_json}" ${path_segments}) if(err) - _json_fail_at("Path Not Found" "" "${path_segments}" "") + _json_fail_at("Path Not Found" "${path_segments}" "") return() endif() if(NOT actual_type STREQUAL "ARRAY") - _json_fail_at("Type Mismatch" "${actual_type}" "ARRAY" "${path_segments}") + _json_fail_at("Type Mismatch" "${actual_type}" ARRAY "${path_segments}") return() endif() diff --git a/Tests/RunCMake/Sbom/Config-check.cmake b/Tests/RunCMake/Sbom/Config-check.cmake index f347ff0879..7d6d23cccb 100644 --- a/Tests/RunCMake/Sbom/Config-check.cmake +++ b/Tests/RunCMake/Sbom/Config-check.cmake @@ -1,15 +1,136 @@ include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) -function(check_config VAR_NAME1) - if(NOT "${${VAR_NAME1}}" STREQUAL "") - set(RunCMake_TEST_FAILED "${ERROR_MSG}" PARENT_SCOPE) - endif() -endfunction() +set(creation_expected [=[ +{ + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" +} +]=]) +set(document_expected [=[ +{ + "spdxId": "urn:foo#SPDXDocument", + "type": "SpdxDocument", + "name": "foo", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"] +} +]=]) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) - check_config(DebugSbom) - check_config(ReleaseSbom) -else() - check_config(DebugSbom) +set(configs Debug) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND configs Release) endif() +foreach(config IN LISTS configs) + set(content "${${config}Sbom}") + if(config STREQUAL "Debug") + set(dependency debug_dependency) + else() + set(dependency release_dependency) + endif() + + expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) + expect_array("${content}" 7 @graph) + expect_object("${content}" creation_expected @graph 0) + expect_object("${content}" document_expected @graph 1) + string(JSON document GET "${content}" @graph 1) + expect_array("${document}" 1 rootElement) + expect_array("${document}" 2 element) + + set(root_expected [=[ + { + "spdxId": "urn:foo#Package", + "type": "software_Package", + "name": "foo", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=]) + expect_object("${document}" root_expected rootElement) + string(CONFIGURE [=[ + { + "spdxId": "urn:@dependency@#Package", + "type": "software_Package", + "name": "@dependency@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=] dependency_expected @ONLY) + expect_object("${document}" dependency_expected element) + set(common_expected [=[ + { + "spdxId": "urn:Common:Common#Package", + "type": "software_Package", + "name": "Common:Common", + "creationInfo": "_:Build#CreationInfo", + "originatedBy": [{ + "spdxId": "urn:Common#Organization", + "type": "Organization", + "name": "Common", + "creationInfo": "_:Build#CreationInfo" + }], + "externalRef": [{ + "type": "ExternalRef", + "externalRefType": "buildSystem", + "locator": "cmake:find_package(Common)" + }] + } + ]=]) + expect_object("${document}" common_expected element) + + string(CONFIGURE [=[ + { + "spdxId": "urn:foo#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:foo#Package", + "relationshipType": "dependsOn", + "to": ["urn:Common:Common#Package", "urn:@dependency@#Package"] + } + ]=] root_links_expected @ONLY) + + expect_object("${content}" root_links_expected @graph) + string(CONFIGURE [=[ + { + "spdxId": "urn:@dependency@#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:@dependency@#Package", + "relationshipType": "dependsOn", + "to": ["urn:Common:Common#Package"] + } + ]=] dependency_links_expected @ONLY) + expect_object("${content}" dependency_links_expected @graph) + + foreach(package IN ITEMS foo "${dependency}" Common:Common) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#DeclaredLicenseRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "from": "urn:@package@#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{ + "spdxId": "urn:@package@#LicenseExpression", + "type": "simplelicensing_LicenseExpression", + "creationInfo": "_:Build#CreationInfo", + "simplelicensing_licenseExpression": "MIT" + }] + } + ]=] license_expected @ONLY) + expect_object("${content}" license_expected @graph) + endforeach() + + foreach(index RANGE 2 6) + string(JSON id GET "${content}" @graph ${index} spdxId) + if(id STREQUAL "urn:foo#StaticRelationship") + expect_array("${content}" 2 @graph ${index} to) + else() + expect_array("${content}" 1 @graph ${index} to) + endif() + endforeach() +endforeach() diff --git a/Tests/RunCMake/Sbom/Config.cmake b/Tests/RunCMake/Sbom/Config.cmake index 06799d25d0..f70004dd14 100644 --- a/Tests/RunCMake/Sbom/Config.cmake +++ b/Tests/RunCMake/Sbom/Config.cmake @@ -1,12 +1,21 @@ -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) - set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "" FORCE) - endif() -endif() - project(Config C) add_library(foo ${CMAKE_CURRENT_LIST_DIR}/test.c) + +add_library(debug_dependency INTERFACE) +add_library(release_dependency INTERFACE) +add_library(Common::Common INTERFACE IMPORTED) +set_target_properties(Common::Common PROPERTIES + EXPORT_FIND_PACKAGE_NAME Common + SPDX_LICENSE MIT +) +set_target_properties(foo debug_dependency release_dependency PROPERTIES + SPDX_LICENSE MIT +) +target_link_libraries(debug_dependency INTERFACE Common::Common) +target_link_libraries(release_dependency INTERFACE Common::Common) +target_link_libraries(foo PRIVATE + $,debug_dependency,release_dependency> + Common::Common +) + install(TARGETS foo EXPORT foo DESTINATION .) diff --git a/Tests/RunCMake/Sbom/DefaultLicense-install-check.cmake b/Tests/RunCMake/Sbom/DefaultLicense-install-check.cmake new file mode 100644 index 0000000000..64eae94637 --- /dev/null +++ b/Tests/RunCMake/Sbom/DefaultLicense-install-check.cmake @@ -0,0 +1,23 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +string(JSON document GET "${content}" @graph 1) +expect_array("${document}" 3 rootElement) +expect_array("${content}" 4 @graph) + +set(explicit_expected [=[ +{ + "from": "urn:explicit_license#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{"simplelicensing_licenseExpression": "Apache-2.0"}] +} +]=]) +expect_object("${content}" explicit_expected @graph) + +set(default_expected [=[ +{ + "from": "urn:default_license#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{"simplelicensing_licenseExpression": "MIT"}] +} +]=]) +expect_object("${content}" default_expected @graph) diff --git a/Tests/RunCMake/Sbom/DefaultLicense.cmake b/Tests/RunCMake/Sbom/DefaultLicense.cmake new file mode 100644 index 0000000000..26c16ac0cd --- /dev/null +++ b/Tests/RunCMake/Sbom/DefaultLicense.cmake @@ -0,0 +1,10 @@ +project(DefaultLicense SPDX_LICENSE GPL-3.0-only LANGUAGES NONE) + +add_library(explicit_license INTERFACE) +set_property(TARGET explicit_license PROPERTY SPDX_LICENSE Apache-2.0) +add_library(default_license INTERFACE) +add_library(empty_license INTERFACE) +set_property(TARGET empty_license PROPERTY SPDX_LICENSE "") + +install(TARGETS explicit_license default_license empty_license + EXPORT licenses DESTINATION .) diff --git a/Tests/RunCMake/Sbom/Empty-install-check.cmake b/Tests/RunCMake/Sbom/Empty-install-check.cmake deleted file mode 100644 index ffabf80bf2..0000000000 --- a/Tests/RunCMake/Sbom/Empty-install-check.cmake +++ /dev/null @@ -1,7 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) - -set(out_dir "${RunCMake_BINARY_DIR}/EmptyConfig-build") - -file(READ "${out_dir}/foo.cps" content) - -expect_object("${content}" "components" "foo" "configurations" "noconfig") diff --git a/Tests/RunCMake/Sbom/Empty.cmake b/Tests/RunCMake/Sbom/Empty.cmake deleted file mode 100644 index ef1923b126..0000000000 --- a/Tests/RunCMake/Sbom/Empty.cmake +++ /dev/null @@ -1,9 +0,0 @@ -project(EmptyConfig CXX) - -set(CMAKE_BUILD_TYPE "" CACHE STRING "" FORCE) -set(CMAKE_CONFIGURATION_TYPES "" CACHE STRING "" FORCE) - -add_library(foo foo.cxx) - -install(TARGETS foo EXPORT foo) -install(SBOM foo EXPORT foo) diff --git a/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake b/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake index dd5a6b1613..5f7ff61ec8 100644 --- a/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake +++ b/Tests/RunCMake/Sbom/EmptyNamespaceFallback-install-check.cmake @@ -30,7 +30,7 @@ set(LIBB_BUILD_REQUIRES_EXPECTED [=[ "description" : "Required Build-Time Libraries", "from" : "urn:libb#Package", "relationshipType" : "dependsOn", - "spdxId" : "urn:Shared#Relationship", + "spdxId" : "urn:libb#StaticRelationship", "to" : [ "urn:liba#Package" @@ -39,10 +39,13 @@ set(LIBB_BUILD_REQUIRES_EXPECTED [=[ } ]=]) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_array("${SPDX_DOCUMENT}" 1 "rootElement") -expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" LIBA_BARE_EXPECTED "element") -string(JSON BUILD_REQUIRES GET "${content}" "@graph" "2") +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" LIBA_BARE_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 element) +string(JSON BUILD_REQUIRES GET "${content}" @graph 2) expect_object("${BUILD_REQUIRES}" LIBB_BUILD_REQUIRES_EXPECTED) +expect_array("${BUILD_REQUIRES}" 1 to) +expect_array("${content}" 3 @graph) diff --git a/Tests/RunCMake/Sbom/Genex-check.cmake b/Tests/RunCMake/Sbom/Genex-check.cmake index 4e584ce7aa..e07dc61fd6 100644 --- a/Tests/RunCMake/Sbom/Genex-check.cmake +++ b/Tests/RunCMake/Sbom/Genex-check.cmake @@ -1,111 +1,194 @@ include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) -set(CREATION_INFO_EXPECTED [=[ +set(creation_expected [=[ { "@id": "_:Build#CreationInfo", + "type": "CreationInfo", "comment": "This SBOM was generated from the CMakeLists.txt File", - "createdBy": - [ - "https://gitlab.kitware.com/cmake/cmake" - ], - "specVersion": "3.0.1", - "type": "CreationInfo" + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" } ]=]) - -set(SPDX_DOCUMENT_EXPECTED [=[ +set(document_expected [=[ { - "spdxId" : "urn:interface_targets#SPDXDocument", - "name": "interface_targets", - "profileConformance" : - [ - "core", - "software" - ], - "creationInfo" : "_:Build#CreationInfo", - "type" : "SpdxDocument" + "spdxId": "urn:foo#SPDXDocument", + "type": "SpdxDocument", + "name": "foo", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"] } ]=]) -set(APPLICATION_EXPECTED [=[ -{ - "spdxId" : "urn:interface#Package", - "name" : "interface", - "software_primaryPurpose" : "library", - "type" : "software_Package" -} -]=]) - -set(DEPENDENCY_EXPECTED [=[ -{ - "spdxId" : "urn:bar:bar#Package", - "name" : "bar:bar", - "originatedBy" : - [ - { - "name" : "bar", - "type" : "Organization" - } - ], - "software_packageVersion" : "1.3.5", - "type" : "software_Package" -} -]=]) - -set(BAR_LINKED_LIBRARIES_EXPECTED [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", - "from" : "urn:interface#Package", - "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", - "to" : - [ - "urn:bar:bar#Package" - ], - "type" : "Relationship" -} -]=]) - -set(BAZ_LINKED_LIBRARIES_EXPECTED [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "description" : "Required Build-Time Libraries", - "from" : "urn:foo#Package", - "relationshipType" : "dependsOn", - "spdxId" : "urn:Shared#Relationship", - "to" : - [ - "urn:baz#Package" - ], - "type" : "Relationship" -} -]=]) - - -function(check_config VAR_NAME LIBS) - set(content "${${VAR_NAME}}") - if("${content}" STREQUAL "") - set(RunCMake_TEST_FAILED "Error: Variable '${VAR_NAME}' is empty or not defined." PARENT_SCOPE) - return() +set(configs BarConfig) +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + list(APPEND configs BazConfig) +endif() +foreach(config IN LISTS configs) + set(content "${${config}}") + if(config STREQUAL "BarConfig") + set(external_dependency bar:bar) + set(origin bar) + set(version 1.3.5) + else() + set(external_dependency baz:baz) + set(origin baz) + set(version 1.8.5) endif() - expect_value("${VAR_NAME}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") - string(JSON CREATION_INFO GET "${content}" "@graph" "0") - expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) + expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) + expect_array("${content}" 18 @graph) + expect_object("${content}" creation_expected @graph 0) + expect_object("${content}" document_expected @graph 1) + string(JSON document GET "${content}" @graph 1) + expect_array("${document}" 10 rootElement) + expect_array("${document}" 6 element) - string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") - expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) - expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") - expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") - string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") - expect_object("${LINKED_LIBRARIES}" ${LIBS}) -endfunction() + foreach(package IN ITEMS foo dep1 dep2 runtime_dep + plain_STATIC whole_STATIC group_STATIC + plain_SHARED whole_SHARED group_SHARED) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#Package", + "type": "software_Package", + "name": "@package@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=] package_expected @ONLY) + expect_object("${document}" package_expected rootElement) + endforeach() + foreach(package IN ITEMS resolved_dependency compile_dependency compile_leaf + "${interface_dependency}") + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#Package", + "type": "software_Package", + "name": "@package@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=] package_expected @ONLY) + expect_object("${document}" package_expected element) + endforeach() + string(CONFIGURE [=[ + { + "spdxId": "urn:@external_dependency@#Package", + "type": "software_Package", + "name": "@external_dependency@", + "creationInfo": "_:Build#CreationInfo", + "software_packageVersion": "@version@", + "originatedBy": [{ + "spdxId": "urn:@origin@#Organization", + "type": "Organization", + "name": "@origin@", + "creationInfo": "_:Build#CreationInfo" + }], + "externalRef": [{ + "type": "ExternalRef", + "externalRefType": "buildSystem", + "locator": "cmake:find_package(@origin@)" + }] + } + ]=] external_expected @ONLY) + expect_object("${document}" external_expected element) + set(bare_expected [=[ + { + "spdxId": "urn:bare_library#Package", + "type": "software_Package", + "name": "bare_library", + "creationInfo": "_:Build#CreationInfo" + } + ]=]) + expect_object("${document}" bare_expected element) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) - check_config(BarConfig BAR_LINKED_LIBRARIES_EXPECTED) - check_config(BazConfig BAZ_LINKED_LIBRARIES_EXPECTED) -else() - check_config(BarConfig BAR_LINKED_LIBRARIES_EXPECTED) -endif() + string(CONFIGURE [=[ + { + "spdxId": "urn:foo#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:foo#Package", + "relationshipType": "dependsOn", + "to": [ + "urn:@external_dependency@#Package", + "urn:resolved_dependency#Package", + "urn:@interface_dependency@#Package", + "urn:compile_dependency#Package" + ] + } + ]=] root_links_expected @ONLY) + expect_object("${content}" root_links_expected @graph) + foreach(type IN ITEMS STATIC SHARED) + foreach(feature IN ITEMS plain whole group) + set(package "${feature}_${type}") + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:@package@#Package", + "relationshipType": "dependsOn", + "to": ["urn:dep1#Package", "urn:dep2#Package"] + } + ]=] static_links_expected @ONLY) + expect_object("${content}" static_links_expected @graph) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#SharedRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Run-Time Libraries", + "from": "urn:@package@#Package", + "relationshipType": "dependsOn", + "to": ["urn:runtime_dep#Package"] + } + ]=] shared_links_expected @ONLY) + expect_object("${content}" shared_links_expected @graph) + endforeach() + endforeach() + + set(compile_links_expected [=[ + { + "spdxId": "urn:compile_dependency#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:compile_dependency#Package", + "relationshipType": "dependsOn", + "to": ["urn:compile_leaf#Package"] + } + ]=]) + expect_object("${content}" compile_links_expected @graph) + set(licensed_packages compile_dependency compile_leaf) + set(licenses MIT Zlib) + foreach(package license IN ZIP_LISTS licensed_packages licenses) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#DeclaredLicenseRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "from": "urn:@package@#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{ + "spdxId": "urn:@package@#LicenseExpression", + "type": "simplelicensing_LicenseExpression", + "creationInfo": "_:Build#CreationInfo", + "simplelicensing_licenseExpression": "@license@" + }] + } + ]=] license_expected @ONLY) + expect_object("${content}" license_expected @graph) + endforeach() + + foreach(index RANGE 2 17) + string(JSON id GET "${content}" @graph ${index} spdxId) + if(id STREQUAL "urn:foo#StaticRelationship") + expect_array("${content}" 4 @graph ${index} to) + elseif(id MATCHES "^urn:(plain|whole|group)_(STATIC|SHARED)#StaticRelationship$") + expect_array("${content}" 2 @graph ${index} to) + else() + expect_array("${content}" 1 @graph ${index} to) + endif() + endforeach() +endforeach() diff --git a/Tests/RunCMake/Sbom/Genex.cmake b/Tests/RunCMake/Sbom/Genex.cmake index baf525183a..00d16509ba 100644 --- a/Tests/RunCMake/Sbom/Genex.cmake +++ b/Tests/RunCMake/Sbom/Genex.cmake @@ -1,12 +1,3 @@ -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) - set(CMAKE_CONFIGURATION_TYPES "BarConfig;BazConfig" CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "BarConfig" CACHE STRING "" FORCE) - endif() -endif() - project(Genex C) add_library(foo INTERFACE) @@ -25,4 +16,45 @@ find_package( target_link_libraries(foo INTERFACE $<$:bar::bar>) target_link_libraries(foo INTERFACE $<$:baz>) -install(TARGETS foo EXPORT foo) +add_library(resolved_dependency INTERFACE) +add_library(resolved_alias ALIAS resolved_dependency) +add_library(build_dependency INTERFACE) +add_library(install_dependency INTERFACE) +add_library(compile_dependency INTERFACE) +add_library(compile_leaf INTERFACE) +target_link_libraries(compile_dependency INTERFACE compile_leaf) +set_property(TARGET compile_dependency PROPERTY SPDX_LICENSE MIT) +set_property(TARGET compile_leaf PROPERTY SPDX_LICENSE Zlib) +target_link_libraries(foo INTERFACE + $,$,discarded_library> + $ + $> + $> + $ + $,bare_library,discarded_library> +) + +set(CMAKE_C_LINK_LIBRARY_USING_WHOLE_ARCHIVE ) +set(CMAKE_C_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE) +set(CMAKE_C_LINK_GROUP_USING_RESCAN "" "") +set(CMAKE_C_LINK_GROUP_USING_RESCAN_SUPPORTED TRUE) +add_library(dep1 STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +add_library(dep2 STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +add_library(runtime_dep SHARED ${CMAKE_CURRENT_LIST_DIR}/test.c) +install(TARGETS foo dep1 dep2 runtime_dep EXPORT foo) +foreach(type IN ITEMS STATIC SHARED) + foreach(feature IN ITEMS plain whole group) + add_library(${feature}_${type} ${type} ${CMAKE_CURRENT_LIST_DIR}/test.c) + if(feature STREQUAL "plain") + target_link_libraries(${feature}_${type} PRIVATE dep1 dep2) + elseif(feature STREQUAL "whole") + target_link_libraries(${feature}_${type} PRIVATE + $) + else() + target_link_libraries(${feature}_${type} PRIVATE + $) + endif() + target_link_libraries(${feature}_${type} PRIVATE runtime_dep bare_library) + install(TARGETS ${feature}_${type} EXPORT foo) + endforeach() +endforeach() diff --git a/Tests/RunCMake/Sbom/IgnoresInterfaceDirs.cmake b/Tests/RunCMake/Sbom/IgnoresInterfaceDirs.cmake new file mode 100644 index 0000000000..5e6d86217f --- /dev/null +++ b/Tests/RunCMake/Sbom/IgnoresInterfaceDirs.cmake @@ -0,0 +1,10 @@ +include(${CMAKE_CURRENT_LIST_DIR}/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) diff --git a/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake b/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake index 1268fbe8d1..002e0e888a 100644 --- a/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake +++ b/Tests/RunCMake/Sbom/InstallExportPlusSbomSameSet.cmake @@ -1,8 +1,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) include(GNUInstallDirs) -# Guards against the issue described here: -# https://gitlab.kitware.com/cmake/cmake/-/work_items/27721 add_library(liba INTERFACE) add_library(libb INTERFACE) diff --git a/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake b/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake index 7033cf5073..2d3d420031 100644 --- a/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/InterfaceTarget-install-check.cmake @@ -55,10 +55,10 @@ set(DEPENDENCY_EXPECTED [=[ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", + "description" : "Required Build-Time Libraries", "from" : "urn:interface#Package", "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", + "spdxId" : "urn:interface#StaticRelationship", "to" : [ "urn:bar:bar#Package" @@ -68,13 +68,17 @@ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ ]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) +string(JSON LINKED_LIBRARIES GET "${content}" @graph 2) expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED) +expect_array("${LINKED_LIBRARIES}" 1 to) +expect_array("${content}" 3 @graph) diff --git a/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake b/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake index b5864dce91..5f58c00a4b 100644 --- a/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake +++ b/Tests/RunCMake/Sbom/MissingPackageNamespace-install-check.cmake @@ -55,10 +55,10 @@ set(DEPENDENCY_EXPECTED [=[ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", + "description" : "Required Build-Time Libraries", "from" : "urn:test#Package", "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", + "spdxId" : "urn:test#StaticRelationship", "to" : [ "urn:baz:baz#Package" @@ -68,13 +68,17 @@ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ ]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) +string(JSON LINKED_LIBRARIES GET "${content}" @graph 2) expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED) +expect_array("${LINKED_LIBRARIES}" 1 to) +expect_array("${content}" 3 @graph) diff --git a/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake b/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake index 50ac5a52b1..9120636129 100644 --- a/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake +++ b/Tests/RunCMake/Sbom/MultiSetSingleSbom-install-check.cmake @@ -28,7 +28,9 @@ set(LIBB_EXPECTED [=[ set(LIBB_LINKED_LIBRARIES_EXPECTED [=[ { - "description" : "Linked Libraries", + "creationInfo": "_:Build#CreationInfo", + "spdxId": "urn:libb#StaticRelationship", + "description" : "Required Build-Time Libraries", "from" : "urn:libb#Package", "relationshipType" : "dependsOn", "to" : @@ -39,11 +41,17 @@ set(LIBB_LINKED_LIBRARIES_EXPECTED [=[ } ]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${content}" 3 @graph) +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_array("${SPDX_DOCUMENT}" 2 "rootElement") -expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED "rootElement") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "2") +expect_array("${SPDX_DOCUMENT}" 2 rootElement) +expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" LIBB_EXPECTED rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${SPDX_DOCUMENT}" element) +if(NOT error) + expect_array("${SPDX_DOCUMENT}" 0 element) +endif() +string(JSON LINKED_LIBRARIES GET "${content}" @graph 2) expect_object("${LINKED_LIBRARIES}" LIBB_LINKED_LIBRARIES_EXPECTED) +expect_array("${LINKED_LIBRARIES}" 1 to) diff --git a/Tests/RunCMake/Sbom/NoProjectMetadata-install-check.cmake b/Tests/RunCMake/Sbom/NoProjectMetadata-install-check.cmake new file mode 100644 index 0000000000..6d20965942 --- /dev/null +++ b/Tests/RunCMake/Sbom/NoProjectMetadata-install-check.cmake @@ -0,0 +1,56 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +string(JSON document GET "${content}" @graph 1) +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${document}" 2 rootElement) +expect_array("${content}" 3 @graph) +foreach(field IN ITEMS description dataLicense) + string(JSON unused ERROR_VARIABLE error TYPE "${document}" "${field}") + if(NOT error) + set(RunCMake_TEST_FAILED "NO_PROJECT_METADATA inherited document ${field}") + endif() +endforeach() + +set(packages explicit_license no_license) +foreach(package IN LISTS packages) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#Package", + "type": "software_Package", + "name": "@package@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=] package_expected @ONLY) + expect_object("${document}" package_expected rootElement) +endforeach() +foreach(index RANGE 0 1) + foreach(field IN ITEMS software_packageVersion software_homePage) + string(JSON unused ERROR_VARIABLE error TYPE "${document}" rootElement ${index} "${field}") + if(NOT error) + set(RunCMake_TEST_FAILED "NO_PROJECT_METADATA inherited target ${field}") + endif() + endforeach() +endforeach() +string(JSON unused ERROR_VARIABLE error TYPE "${document}" element) +if(NOT error) + expect_array("${document}" 0 element) +endif() + +set(explicit_expected [=[ +{ + "spdxId": "urn:explicit_license#DeclaredLicenseRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "from": "urn:explicit_license#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{ + "spdxId": "urn:explicit_license#LicenseExpression", + "type": "simplelicensing_LicenseExpression", + "creationInfo": "_:Build#CreationInfo", + "simplelicensing_licenseExpression": "Apache-2.0" + }] +} +]=]) +expect_object("${content}" explicit_expected @graph) +expect_array("${content}" 1 @graph 2 to) diff --git a/Tests/RunCMake/Sbom/NoProjectMetadata.cmake b/Tests/RunCMake/Sbom/NoProjectMetadata.cmake new file mode 100644 index 0000000000..7df5f31d78 --- /dev/null +++ b/Tests/RunCMake/Sbom/NoProjectMetadata.cmake @@ -0,0 +1,9 @@ +project(metadata VERSION 1.2.3 SPDX_LICENSE GPL-3.0-only + DESCRIPTION "Project description" HOMEPAGE_URL https://project.example + LANGUAGES NONE) + +add_library(explicit_license INTERFACE) +set_property(TARGET explicit_license PROPERTY SPDX_LICENSE Apache-2.0) +add_library(no_license INTERFACE) + +install(TARGETS explicit_license no_license EXPORT metadata DESTINATION .) diff --git a/Tests/RunCMake/Sbom/PrivateBareLibrary-install-check.cmake b/Tests/RunCMake/Sbom/PrivateBareLibrary-install-check.cmake new file mode 100644 index 0000000000..d3fad9b44a --- /dev/null +++ b/Tests/RunCMake/Sbom/PrivateBareLibrary-install-check.cmake @@ -0,0 +1,52 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(CREATION_INFO_EXPECTED [=[ +{ + "@id": "_:Build#CreationInfo", + "type": "CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": ["https://gitlab.kitware.com/cmake/cmake"], + "specVersion": "3.0.1" +} +]=]) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "spdxId": "urn:foo#SPDXDocument", + "type": "SpdxDocument", + "name": "foo", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"] +} +]=]) + +set(FOO_EXPECTED [=[ +{ + "spdxId": "urn:foo#Package", + "type": "software_Package", + "name": "foo", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" +} +]=]) + +set(DEPENDENCY_EXPECTED [=[ +{ + "spdxId": "urn:m#Package", + "type": "software_Package", + "name": "m", + "creationInfo": "_:Build#CreationInfo" +} +]=]) + +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) +expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) + +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +expect_object("${SPDX_DOCUMENT}" FOO_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) +expect_array("${content}" 2 @graph) diff --git a/Tests/RunCMake/Sbom/PrivateBareLibrary.cmake b/Tests/RunCMake/Sbom/PrivateBareLibrary.cmake new file mode 100644 index 0000000000..54f5b27d8e --- /dev/null +++ b/Tests/RunCMake/Sbom/PrivateBareLibrary.cmake @@ -0,0 +1,6 @@ +project(PrivateBareLibrary C) + +add_library(foo STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +target_link_libraries(foo PRIVATE m) + +install(TARGETS foo EXPORT foo DESTINATION .) diff --git a/Tests/RunCMake/Sbom/PrivateStaticDependency-install-check.cmake b/Tests/RunCMake/Sbom/PrivateStaticDependency-install-check.cmake new file mode 100644 index 0000000000..36ee43d4f9 --- /dev/null +++ b/Tests/RunCMake/Sbom/PrivateStaticDependency-install-check.cmake @@ -0,0 +1,95 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(CREATION_INFO_EXPECTED [=[ +{ + "@id": "_:Build#CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": [ + "https://gitlab.kitware.com/cmake/cmake" + ], + "specVersion": "3.0.1", + "type": "CreationInfo" +} +]=]) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "foo", + "profileConformance": [ + "core", + "software" + ], + "spdxId": "urn:foo#SPDXDocument", + "type": "SpdxDocument" +} +]=]) + +set(NUMBER_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "foo", + "software_primaryPurpose": "library", + "spdxId": "urn:foo#Package", + "type": "software_Package" +} +]=]) + +set(DEPENDENCY_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "bar:bar", + "originatedBy": [ + { + "creationInfo": "_:Build#CreationInfo", + "name": "bar", + "spdxId": "urn:bar#Organization", + "type": "Organization" + } + ], + "software_packageVersion": "1.3.5", + "spdxId": "urn:bar:bar#Package", + "type": "software_Package" +} +]=]) + +set(SIBLING_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "sibling", + "software_primaryPurpose": "library", + "spdxId": "urn:sibling#Package", + "type": "software_Package" +} +]=]) + +set(LINKED_LIBRARIES_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:foo#Package", + "relationshipType": "dependsOn", + "spdxId": "urn:foo#StaticRelationship", + "to": [ + "urn:bar:bar#Package", + "urn:sibling#Package" + ], + "type": "Relationship" +} +]=]) + +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) +expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) + +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +expect_object("${SPDX_DOCUMENT}" NUMBER_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" SIBLING_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 2 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) + +expect_object("${content}" LINKED_LIBRARIES_EXPECTED @graph) +expect_array("${content}" 3 @graph) +expect_array("${content}" 2 @graph 2 to) diff --git a/Tests/RunCMake/Sbom/PrivateStaticDependency.cmake b/Tests/RunCMake/Sbom/PrivateStaticDependency.cmake new file mode 100644 index 0000000000..c2a9eadbce --- /dev/null +++ b/Tests/RunCMake/Sbom/PrivateStaticDependency.cmake @@ -0,0 +1,12 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) + +find_package(bar REQUIRED CONFIG + NO_DEFAULT_PATH + PATHS ${CMAKE_CURRENT_LIST_DIR} +) + +add_library(foo STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +add_library(sibling STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +target_link_libraries(foo PRIVATE sibling $) + +install(TARGETS foo sibling EXPORT foo DESTINATION .) diff --git a/Tests/RunCMake/Sbom/ProjectLicense-install-check.cmake b/Tests/RunCMake/Sbom/ProjectLicense-install-check.cmake new file mode 100644 index 0000000000..589fe7921d --- /dev/null +++ b/Tests/RunCMake/Sbom/ProjectLicense-install-check.cmake @@ -0,0 +1,43 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(bar_expected [=[{"spdxId":"urn:bar#Package"}]=]) +set(child_expected [=[{"spdxId":"urn:child#Package"}]=]) + +set(license_expected [=[ +{ + "from": "urn:bar#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{"simplelicensing_licenseExpression": "MIT"}] +} +]=]) + +set(child_license_expected [=[ +{ + "from": "urn:child#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{"simplelicensing_licenseExpression": "Apache-2.0"}] +} +]=]) + +string(JSON document GET "${content}" @graph 1) +if(sbom STREQUAL "bundle") + expect_value("${document}" "Description from project variables" description) + expect_value("${document}" MIT dataLicense simplelicensing_licenseExpression) +elseif(sbom STREQUAL "selected") + expect_value("${document}" "Selected project" description) + expect_value("${document}" ISC dataLicense simplelicensing_licenseExpression) +else() + foreach(field IN ITEMS description dataLicense) + string(JSON unused ERROR_VARIABLE error TYPE "${document}" "${field}") + if(NOT error) + set(RunCMake_TEST_FAILED "An unmatched SBOM inherited document ${field}") + endif() + endforeach() +endif() + +expect_array("${document}" 2 rootElement) +expect_object("${document}" bar_expected rootElement) +expect_object("${document}" child_expected rootElement) +expect_object("${content}" license_expected @graph) +expect_object("${content}" child_license_expected @graph) +expect_array("${content}" 4 @graph) diff --git a/Tests/RunCMake/Sbom/ProjectLicense.cmake b/Tests/RunCMake/Sbom/ProjectLicense.cmake new file mode 100644 index 0000000000..7174148c63 --- /dev/null +++ b/Tests/RunCMake/Sbom/ProjectLicense.cmake @@ -0,0 +1,17 @@ +project(bar SPDX_LICENSE BSD-3) + +set(bar_SPDX_LICENSE MIT) +set(bar_DESCRIPTION "Description from project variables") +set(PROJECT_NAME overridden-parent-name) +set(PROJECT_SPDX_LICENSE GPL-3.0-only) +set(PROJECT_DESCRIPTION "Generic description must not be used") + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/ProjectLicenseSubdir + ${CMAKE_CURRENT_BINARY_DIR}/licensed-subdir) + +add_library(bar INTERFACE) +install( + TARGETS bar + EXPORT bar_targets + DESTINATION . +) diff --git a/Tests/RunCMake/Sbom/ProjectLicenseSubdir/CMakeLists.txt b/Tests/RunCMake/Sbom/ProjectLicenseSubdir/CMakeLists.txt new file mode 100644 index 0000000000..d22d8769e4 --- /dev/null +++ b/Tests/RunCMake/Sbom/ProjectLicenseSubdir/CMakeLists.txt @@ -0,0 +1,11 @@ +project(child SPDX_LICENSE Apache-2.0 LANGUAGES NONE) + +set(PROJECT_NAME overridden-child-name) +set(PROJECT_SPDX_LICENSE GPL-3.0-only) + +add_library(child INTERFACE) +install( + TARGETS child + EXPORT child_targets + DESTINATION . +) diff --git a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake index 83ffda697e..fea6cf9a08 100644 --- a/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake +++ b/Tests/RunCMake/Sbom/ProjectMetadata-install-check.cmake @@ -36,6 +36,15 @@ set(APPLICATION_EXPECTED [=[ } ]=]) +set(SECOND_TARGET_EXPECTED [=[ +{ + "spdxId": "urn:test_targets#Package", + "name": "test_targets", + "software_primaryPurpose": "library", + "type": "software_Package" +} +]=]) + set(LICENSE_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", @@ -55,14 +64,35 @@ set(LICENSE_EXPECTED [=[ } ]=]) +set(SECOND_LICENSE_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "from": "urn:test_targets#Package", + "relationshipType": "hasDeclaredLicense", + "spdxId": "urn:test_targets#DeclaredLicenseRelationship", + "to": [{ + "creationInfo": "_:Build#CreationInfo", + "simplelicensing_licenseExpression": "Apache-2.0", + "spdxId": "urn:test_targets#LicenseExpression", + "type": "simplelicensing_LicenseExpression" + }], + "type": "Relationship" +} +]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") + +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" SECOND_TARGET_EXPECTED rootElement) +expect_array("${SPDX_DOCUMENT}" 2 rootElement) -string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2") -expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED) +expect_object("${content}" LICENSE_EXPECTED @graph) +expect_object("${content}" SECOND_LICENSE_EXPECTED @graph) +expect_array("${content}" 4 @graph) +expect_array("${content}" 1 @graph 2 to) +expect_array("${content}" 1 @graph 3 to) diff --git a/Tests/RunCMake/Sbom/ProjectMetadata.cmake b/Tests/RunCMake/Sbom/ProjectMetadata.cmake index c56c0e2a0a..d61948c80b 100644 --- a/Tests/RunCMake/Sbom/ProjectMetadata.cmake +++ b/Tests/RunCMake/Sbom/ProjectMetadata.cmake @@ -5,15 +5,18 @@ include(GNUInstallDirs) project(test LANGUAGES C DESCRIPTION "Metadata Test Project" - SPDX_LICENSE "BSD-3" - HOMEPAGE_URL "www.example.com" + SPDX_LICENSE BSD-3 + HOMEPAGE_URL www.example.com VERSION 1.2.0) add_library(test INTERFACE) set_target_properties(test PROPERTIES SPDX_LICENSE license-for-test-target) +add_library(test_targets INTERFACE) +set_target_properties(test_targets PROPERTIES SPDX_LICENSE Apache-2.0) + install( - TARGETS test + TARGETS test test_targets EXPORT test_targets DESTINATION . ) diff --git a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake b/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake deleted file mode 100644 index 6d3247c904..0000000000 --- a/Tests/RunCMake/Sbom/ProjectMetadataExplicitAssertions.cmake +++ /dev/null @@ -1,27 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) - -set(DOCUMENT_METADATA_EXPECTED [=[ -{ - "description" : "An eloquent description", - "dataLicense" : - { - "creationInfo" : "_:Build#CreationInfo", - "simplelicensing_licenseExpression" : "BSD-3", - "spdxId" : "urn:test_targets#LicenseExpression", - "type" : "simplelicensing_LicenseExpression" - } -} -]=]) - -set(PACKAGE_METADATA_EXPECTED [=[ -{ - "spdxId" : "urn:test#Package", - "software_packageVersion" : "1.3.4", - "software_homePage" : "www.example.com", - "software_downloadLocation" : "https://example.com/test_targets.tar.gz" -} -]=]) - -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") -expect_object("${SPDX_DOCUMENT}" DOCUMENT_METADATA_EXPECTED) -expect_object("${SPDX_DOCUMENT}" PACKAGE_METADATA_EXPECTED "rootElement") diff --git a/Tests/RunCMake/Sbom/ReferencesNonExportedTarget.cmake b/Tests/RunCMake/Sbom/ReferencesNonExportedTarget.cmake deleted file mode 100644 index 4c1cbf216f..0000000000 --- a/Tests/RunCMake/Sbom/ReferencesNonExportedTarget.cmake +++ /dev/null @@ -1,10 +0,0 @@ -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 .) diff --git a/Tests/RunCMake/Sbom/Requirements-install-check.cmake b/Tests/RunCMake/Sbom/Requirements-install-check.cmake index 5e5aa14833..97721bb9c8 100644 --- a/Tests/RunCMake/Sbom/Requirements-install-check.cmake +++ b/Tests/RunCMake/Sbom/Requirements-install-check.cmake @@ -124,13 +124,13 @@ set(TEST_LIBA_LINKED_LICENSE_EXPECTED [=[ "creationInfo" : "_:Build#CreationInfo", "from" : "urn:test:liba#Package", "relationshipType" : "hasDeclaredLicense", - "spdxId" : "urn:test::liba#DeclaredLicenseRelationship", + "spdxId" : "urn:test:liba#DeclaredLicenseRelationship", "to" : [ { "creationInfo" : "_:Build#CreationInfo", "simplelicensing_licenseExpression" : "license-for-test-liba", - "spdxId" : "urn:test::liba#LicenseExpression", + "spdxId" : "urn:test:liba#LicenseExpression", "type" : "simplelicensing_LicenseExpression" } ], @@ -151,21 +151,47 @@ set(CREATION_INFO_EXPECTED [=[ } ]=]) -expect_value("${FOO_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON FOO_CREATION_INFO GET "${FOO_CONTENT}" "@graph" "0") -string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" "@graph" "1") +expect_value("${FOO_CONTENT}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${FOO_CONTENT}" 2 @graph) +string(JSON FOO_CREATION_INFO GET "${FOO_CONTENT}" @graph 0) +expect_object("${FOO_CREATION_INFO}" CREATION_INFO_EXPECTED) +string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" @graph 1) expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT_EXPECTED) -expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement") +expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB rootElement) +expect_array("${FOO_SPDX_DOCUMENT}" 1 rootElement) -expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON BAR_CREATION_INFO GET "${BAR_CONTENT}" "@graph" "0") -string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") +expect_value("${BAR_CONTENT}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${BAR_CONTENT}" 4 @graph) +string(JSON BAR_CREATION_INFO GET "${BAR_CONTENT}" @graph 0) +expect_object("${BAR_CREATION_INFO}" CREATION_INFO_EXPECTED) +string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" @graph 1) expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD "rootElement") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST "element") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC rootElement) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBD rootElement) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_TEST element) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO element) +expect_array("${BAR_SPDX_DOCUMENT}" 2 rootElement) +expect_array("${BAR_SPDX_DOCUMENT}" 2 element) -# Check that the license target property imported from cmake config is reported -string(JSON LICENSE_RELATIONSHIP GET "${BAR_CONTENT}" "@graph" "2") -expect_object("${LICENSE_RELATIONSHIP}" TEST_LIBA_LINKED_LICENSE_EXPECTED) +expect_object("${BAR_CONTENT}" TEST_LIBA_LINKED_LICENSE_EXPECTED @graph) + +set(BAR_LINKED_LIBRARIES_EXPECTED [=[ +{ + "spdxId": "urn:libd#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:libd#Package", + "relationshipType": "dependsOn", + "to": ["urn:libc#Package", "urn:foo:libb#Package", "urn:test:liba#Package"] +} +]=]) +expect_object("${BAR_CONTENT}" BAR_LINKED_LIBRARIES_EXPECTED @graph) +foreach(index RANGE 2 3) + string(JSON relationship_type GET "${BAR_CONTENT}" @graph ${index} relationshipType) + if(relationship_type STREQUAL "dependsOn") + expect_array("${BAR_CONTENT}" 3 @graph ${index} to) + else() + expect_array("${BAR_CONTENT}" 1 @graph ${index} to) + endif() +endforeach() diff --git a/Tests/RunCMake/Sbom/SameNamedImportedTargets/CMakeLists.txt b/Tests/RunCMake/Sbom/SameNamedImportedTargets/CMakeLists.txt new file mode 100644 index 0000000000..65ecf91e40 --- /dev/null +++ b/Tests/RunCMake/Sbom/SameNamedImportedTargets/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(Scoped::dep INTERFACE IMPORTED) +set_target_properties(Scoped::dep PROPERTIES + EXPORT_FIND_PACKAGE_NAME "${dependency_package}" + SPDX_LICENSE "${dependency_license}" +) + +add_library(scoped_${dependency_package} INTERFACE) +target_link_libraries(scoped_${dependency_package} INTERFACE Scoped::dep) + +target_link_libraries(root INTERFACE Scoped::dep) +install(TARGETS scoped_${dependency_package} + EXPORT unknown_dependencies DESTINATION .) diff --git a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake deleted file mode 100644 index 47d12bca2f..0000000000 --- a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity-install-check.cmake +++ /dev/null @@ -1,49 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) - -set(BAR_SPDX_DOCUMENT_EXPECTED [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "name" : "bar_sbom", - "profileConformance" : - [ - "core", - "software" - ], - "spdxId" : "urn:bar_sbom#SPDXDocument", - "type" : "SpdxDocument" -} -]=]) - -set(BAR_LIBC [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "name" : "libc", - "software_primaryPurpose" : "library", - "spdxId" : "urn:libc#Package", - "type" : "software_Package" -} -]=]) - -set(BAR_DEPENDENCY_FOO [=[ -{ - "creationInfo" : "_:Build#CreationInfo", - "name" : "foo_sbom:libb", - "originatedBy" : - [ - { - "creationInfo" : "_:Build#CreationInfo", - "name" : "foo_sbom", - "spdxId" : "urn:foo_sbom#Organization", - "type" : "Organization" - } - ], - "spdxId" : "urn:foo_sbom:libb#Package", - "type" : "software_Package" -} -]=]) - -expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element") diff --git a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake b/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake deleted file mode 100644 index ba1be45615..0000000000 --- a/Tests/RunCMake/Sbom/SbomNamespaceAmbiguity.cmake +++ /dev/null @@ -1,9 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) - -add_library(libb INTERFACE) -add_library(libc INTERFACE) - -target_link_libraries(libc INTERFACE libb) - -install(TARGETS libb EXPORT foo DESTINATION .) -install(TARGETS libc EXPORT bar DESTINATION .) diff --git a/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake b/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake index d80856cb72..8a68baddde 100644 --- a/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake +++ b/Tests/RunCMake/Sbom/SbomNamespaceFallback-install-check.cmake @@ -66,13 +66,36 @@ set(BAR_DEPENDENCY_FOO [=[ } ]=]) -expect_value("${FOO_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" "@graph" "1") +expect_value("${FOO_CONTENT}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${FOO_CONTENT}" 2 @graph) +string(JSON FOO_SPDX_DOCUMENT GET "${FOO_CONTENT}" @graph 1) expect_object("${FOO_SPDX_DOCUMENT}" FOO_SPDX_DOCUMENT_EXPECTED) -expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB "rootElement") +expect_object("${FOO_SPDX_DOCUMENT}" FOO_LIBB rootElement) +expect_array("${FOO_SPDX_DOCUMENT}" 1 rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${FOO_SPDX_DOCUMENT}" element) +if(NOT error) + expect_array("${FOO_SPDX_DOCUMENT}" 0 element) +endif() -expect_value("${BAR_CONTENT}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" "@graph" "1") +expect_value("${BAR_CONTENT}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${BAR_CONTENT}" 3 @graph) +string(JSON BAR_SPDX_DOCUMENT GET "${BAR_CONTENT}" @graph 1) expect_object("${BAR_SPDX_DOCUMENT}" BAR_SPDX_DOCUMENT_EXPECTED) -expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC "rootElement") -expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO "element") +expect_object("${BAR_SPDX_DOCUMENT}" BAR_LIBC rootElement) +expect_object("${BAR_SPDX_DOCUMENT}" BAR_DEPENDENCY_FOO element) +expect_array("${BAR_SPDX_DOCUMENT}" 1 rootElement) +expect_array("${BAR_SPDX_DOCUMENT}" 1 element) + +set(BAR_LINKED_LIBRARIES_EXPECTED [=[ +{ + "spdxId": "urn:libc#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:libc#Package", + "relationshipType": "dependsOn", + "to": ["urn:foo:libb#Package"] +} +]=]) +expect_object("${BAR_CONTENT}" BAR_LINKED_LIBRARIES_EXPECTED @graph) +expect_array("${BAR_CONTENT}" 1 @graph 2 to) diff --git a/Tests/RunCMake/Sbom/Setup.cmake b/Tests/RunCMake/Sbom/Setup.cmake index c8cb2986c5..6ca3930c4f 100644 --- a/Tests/RunCMake/Sbom/Setup.cmake +++ b/Tests/RunCMake/Sbom/Setup.cmake @@ -1,4 +1,3 @@ -# Disable built-in search paths. enable_language(C) set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF) diff --git a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake index 1c698e8546..7250a93a64 100644 --- a/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake +++ b/Tests/RunCMake/Sbom/SharedTarget-install-check.cmake @@ -55,10 +55,10 @@ set(DEPENDENCY_EXPECTED [=[ set(BUILD_LINKED_LIBRARIES_EXPECTED [=[ { "creationInfo" : "_:Build#CreationInfo", - "description" : "Linked Libraries", + "description" : "Required Build-Time Libraries", "from" : "urn:shared#Package", "relationshipType" : "dependsOn", - "spdxId" : "urn:Static#Relationship", + "spdxId" : "urn:shared#StaticRelationship", "to" : [ "urn:foo:foo#Package" @@ -72,13 +72,13 @@ set(LICENSE_EXPECTED [=[ "creationInfo" : "_:Build#CreationInfo", "from" : "urn:foo:foo#Package", "relationshipType" : "hasDeclaredLicense", - "spdxId" : "urn:foo::foo#DeclaredLicenseRelationship", + "spdxId" : "urn:foo:foo#DeclaredLicenseRelationship", "to" : [ { "creationInfo" : "_:Build#CreationInfo", "simplelicensing_licenseExpression" : "BSD-3-Clause", - "spdxId" : "urn:foo::foo#LicenseExpression", + "spdxId" : "urn:foo:foo#LicenseExpression", "type" : "simplelicensing_LicenseExpression" } ], @@ -87,17 +87,20 @@ set(LICENSE_EXPECTED [=[ ]=]) -expect_value("${content}" "https://spdx.org/rdf/3.0.1/spdx-context.jsonld" "@context") -string(JSON CREATION_INFO GET "${content}" "@graph" "0") +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED "rootElement") -expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED "element") -string(JSON LINKED_LIBRARIES GET "${content}" "@graph" "3") -expect_object("${LINKED_LIBRARIES}" BUILD_LINKED_LIBRARIES_EXPECTED) +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" DEPENDENCY_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) +expect_array("${content}" 4 @graph) +expect_object("${content}" BUILD_LINKED_LIBRARIES_EXPECTED @graph) -# Check that default_license from the imported CPS package is reported -string(JSON LICENSE_RELATIONSHIP GET "${content}" "@graph" "2") -expect_object("${LICENSE_RELATIONSHIP}" LICENSE_EXPECTED) +expect_object("${content}" LICENSE_EXPECTED @graph) +foreach(index RANGE 2 3) + expect_array("${content}" 1 @graph ${index} to) +endforeach() diff --git a/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake b/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake index 76c00f7891..9ef6052945 100644 --- a/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake +++ b/Tests/RunCMake/Sbom/TargetInMultipleSets-install-check.cmake @@ -17,8 +17,13 @@ set(LIBA_EXPECTED [=[ } ]=]) -string(JSON SPDX_DOCUMENT GET "${content}" "@graph" "1") +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +expect_array("${content}" 2 @graph) expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) -# liba should only appear once. -expect_array("${SPDX_DOCUMENT}" 1 "rootElement") -expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED "rootElement") +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_object("${SPDX_DOCUMENT}" LIBA_EXPECTED rootElement) +string(JSON unused ERROR_VARIABLE error TYPE "${SPDX_DOCUMENT}" element) +if(NOT error) + expect_array("${SPDX_DOCUMENT}" 0 element) +endif() diff --git a/Tests/RunCMake/Sbom/TransitiveDependencies-install-check.cmake b/Tests/RunCMake/Sbom/TransitiveDependencies-install-check.cmake new file mode 100644 index 0000000000..26b47ec3a6 --- /dev/null +++ b/Tests/RunCMake/Sbom/TransitiveDependencies-install-check.cmake @@ -0,0 +1,43 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +string(JSON document GET "${content}" @graph 1) +expect_array("${document}" 1 rootElement) +expect_array("${document}" 4 element) +expect_array("${content}" 12 @graph) + +set(targets root middle leaf side tail) +set(licenses MIT Apache-2.0 BSD-3-Clause Zlib ISC) +foreach(target license IN ZIP_LISTS targets licenses) + set(package_expected "{\"spdxId\":\"urn:${target}#Package\"}") + if(target STREQUAL "root") + expect_object("${document}" package_expected rootElement) + else() + expect_object("${document}" package_expected element) + endif() + + set(license_expected "{ + \"from\": \"urn:${target}#Package\", + \"relationshipType\": \"hasDeclaredLicense\", + \"to\": [{\"simplelicensing_licenseExpression\": \"${license}\"}] + }") + expect_object("${content}" license_expected @graph) +endforeach() + +set(dependencies_root middle side) +set(dependencies_middle leaf) +set(dependencies_side leaf) +set(dependencies_leaf tail) +set(dependencies_tail middle) +foreach(target IN LISTS targets) + set(to) + foreach(dependency IN LISTS dependencies_${target}) + list(APPEND to "\"urn:${dependency}#Package\"") + endforeach() + list(JOIN to ", " to) + set(links_expected "{ + \"from\": \"urn:${target}#Package\", + \"relationshipType\": \"dependsOn\", + \"to\": [${to}] + }") + expect_object("${content}" links_expected @graph) +endforeach() diff --git a/Tests/RunCMake/Sbom/TransitiveDependencies.cmake b/Tests/RunCMake/Sbom/TransitiveDependencies.cmake new file mode 100644 index 0000000000..1b0938992f --- /dev/null +++ b/Tests/RunCMake/Sbom/TransitiveDependencies.cmake @@ -0,0 +1,21 @@ +project(TransitiveDependencies LANGUAGES NONE) + +add_library(root INTERFACE) +add_library(middle INTERFACE) +add_library(leaf INTERFACE) +add_library(side INTERFACE) +add_library(tail INTERFACE) + +set_property(TARGET root PROPERTY SPDX_LICENSE MIT) +set_property(TARGET middle PROPERTY SPDX_LICENSE Apache-2.0) +set_property(TARGET leaf PROPERTY SPDX_LICENSE BSD-3-Clause) +set_property(TARGET side PROPERTY SPDX_LICENSE Zlib) +set_property(TARGET tail PROPERTY SPDX_LICENSE ISC) + +target_link_libraries(root INTERFACE middle side) +target_link_libraries(middle INTERFACE leaf) +target_link_libraries(side INTERFACE leaf) +target_link_libraries(leaf INTERFACE tail) +target_link_libraries(tail INTERFACE middle) + +install(TARGETS root EXPORT traversal DESTINATION .) diff --git a/Tests/RunCMake/Sbom/UninstalledStaticLibrary-install-check.cmake b/Tests/RunCMake/Sbom/UninstalledStaticLibrary-install-check.cmake new file mode 100644 index 0000000000..7f03367429 --- /dev/null +++ b/Tests/RunCMake/Sbom/UninstalledStaticLibrary-install-check.cmake @@ -0,0 +1,75 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +set(CREATION_INFO_EXPECTED [=[ +{ + "@id": "_:Build#CreationInfo", + "comment": "This SBOM was generated from the CMakeLists.txt File", + "createdBy": [ + "https://gitlab.kitware.com/cmake/cmake" + ], + "specVersion": "3.0.1", + "type": "CreationInfo" +} +]=]) + +set(SPDX_DOCUMENT_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "baz", + "profileConformance": [ + "core", + "software" + ], + "spdxId": "urn:baz#SPDXDocument", + "type": "SpdxDocument" +} +]=]) + +set(APPLICATION_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "baz", + "software_primaryPurpose": "application", + "spdxId": "urn:baz#Package", + "type": "software_Package" +} +]=]) + +set(FOO_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "name": "foo", + "software_primaryPurpose": "library", + "spdxId": "urn:foo#Package", + "type": "software_Package" +} +]=]) + +set(LINKED_LIBRARIES_EXPECTED [=[ +{ + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:baz#Package", + "relationshipType": "dependsOn", + "spdxId": "urn:baz#StaticRelationship", + "to": [ + "urn:foo#Package" + ], + "type": "Relationship" +} +]=]) + +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON CREATION_INFO GET "${content}" @graph 0) +expect_object("${CREATION_INFO}" CREATION_INFO_EXPECTED) + +string(JSON SPDX_DOCUMENT GET "${content}" @graph 1) +expect_object("${SPDX_DOCUMENT}" SPDX_DOCUMENT_EXPECTED) +expect_object("${SPDX_DOCUMENT}" APPLICATION_EXPECTED rootElement) +expect_object("${SPDX_DOCUMENT}" FOO_EXPECTED element) +expect_array("${SPDX_DOCUMENT}" 1 rootElement) +expect_array("${SPDX_DOCUMENT}" 1 element) + +expect_object("${content}" LINKED_LIBRARIES_EXPECTED @graph) +expect_array("${content}" 3 @graph) +expect_array("${content}" 1 @graph 2 to) diff --git a/Tests/RunCMake/Sbom/UninstalledStaticLibrary.cmake b/Tests/RunCMake/Sbom/UninstalledStaticLibrary.cmake new file mode 100644 index 0000000000..225517a542 --- /dev/null +++ b/Tests/RunCMake/Sbom/UninstalledStaticLibrary.cmake @@ -0,0 +1,7 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Setup.cmake) + +add_library(foo STATIC ${CMAKE_CURRENT_LIST_DIR}/test.c) +add_executable(baz ${CMAKE_CURRENT_LIST_DIR}/main.c) + +target_link_libraries(baz PRIVATE foo) +install(TARGETS baz EXPORT baz DESTINATION .) diff --git a/Tests/RunCMake/Sbom/UnknownDependencies-install-check.cmake b/Tests/RunCMake/Sbom/UnknownDependencies-install-check.cmake new file mode 100644 index 0000000000..5da9e703f2 --- /dev/null +++ b/Tests/RunCMake/Sbom/UnknownDependencies-install-check.cmake @@ -0,0 +1,132 @@ +include(${CMAKE_CURRENT_LIST_DIR}/Assertions.cmake) + +expect_value("${content}" https://spdx.org/rdf/3.0.1/spdx-context.jsonld @context) +string(JSON document GET "${content}" @graph 1) +expect_array("${document}" 3 rootElement) +expect_array("${document}" 4 element) +expect_array("${content}" 7 @graph) + +set(document_expected [=[ +{ + "spdxId": "urn:unknown_dependencies#SPDXDocument", + "type": "SpdxDocument", + "name": "unknown_dependencies", + "creationInfo": "_:Build#CreationInfo", + "profileConformance": ["core", "software"] +} +]=]) +expect_object("${document}" document_expected) +foreach(package IN ITEMS root scoped_First scoped_Second) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@#Package", + "type": "software_Package", + "name": "@package@", + "creationInfo": "_:Build#CreationInfo", + "software_primaryPurpose": "library" + } + ]=] root_expected @ONLY) + expect_object("${document}" root_expected rootElement) +endforeach() + +set(imported_expected [=[ +{ + "spdxId": "urn:Unknown:dep#Package", + "type": "software_Package", + "name": "Unknown:dep", + "creationInfo": "_:Build#CreationInfo", + "originatedBy": [{"name": "Unknown", "type": "Organization"}], + "externalRef": [{ + "type": "ExternalRef", + "externalRefType": "buildSystem", + "locator": "cmake:find_package(Unknown)" + }] +} +]=]) +expect_object("${document}" imported_expected element) +set(bare_expected [=[ +{ + "spdxId": "urn:bare_library#Package", + "type": "software_Package", + "name": "bare_library", + "creationInfo": "_:Build#CreationInfo" +} +]=]) +expect_object("${document}" bare_expected element) + +set(links_expected [=[ +{ + "spdxId": "urn:root#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "from": "urn:root#Package", + "relationshipType": "dependsOn", + "description": "Required Build-Time Libraries", + "to": ["urn:First:Scoped::dep#Package", "urn:Second:Scoped::dep#Package"] +} +]=]) +expect_object("${content}" links_expected @graph) + +set(packages First Second) +set(licenses MIT Apache-2.0) +foreach(package license IN ZIP_LISTS packages licenses) + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@:Scoped::dep#Package", + "type": "software_Package", + "name": "@package@:Scoped::dep", + "creationInfo": "_:Build#CreationInfo", + "originatedBy": [{ + "spdxId": "urn:@package@#Organization", + "type": "Organization", + "name": "@package@", + "creationInfo": "_:Build#CreationInfo" + }], + "externalRef": [{ + "type": "ExternalRef", + "externalRefType": "buildSystem", + "locator": "cmake:find_package(@package@)" + }] + } + ]=] scoped_expected @ONLY) + expect_object("${document}" scoped_expected element) + + string(CONFIGURE [=[ + { + "spdxId": "urn:scoped_@package@#StaticRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "description": "Required Build-Time Libraries", + "from": "urn:scoped_@package@#Package", + "relationshipType": "dependsOn", + "to": ["urn:@package@:Scoped::dep#Package"] + } + ]=] scoped_links_expected @ONLY) + expect_object("${content}" scoped_links_expected @graph) + + string(CONFIGURE [=[ + { + "spdxId": "urn:@package@:Scoped::dep#DeclaredLicenseRelationship", + "type": "Relationship", + "creationInfo": "_:Build#CreationInfo", + "from": "urn:@package@:Scoped::dep#Package", + "relationshipType": "hasDeclaredLicense", + "to": [{ + "spdxId": "urn:@package@:Scoped::dep#LicenseExpression", + "type": "simplelicensing_LicenseExpression", + "creationInfo": "_:Build#CreationInfo", + "simplelicensing_licenseExpression": "@license@" + }] + } + ]=] license_expected @ONLY) + expect_object("${content}" license_expected @graph) +endforeach() + +foreach(index RANGE 2 6) + string(JSON id GET "${content}" @graph ${index} spdxId) + if(id STREQUAL "urn:root#StaticRelationship") + expect_array("${content}" 2 @graph ${index} to) + else() + expect_array("${content}" 1 @graph ${index} to) + endif() +endforeach() diff --git a/Tests/RunCMake/Sbom/UnknownDependencies.cmake b/Tests/RunCMake/Sbom/UnknownDependencies.cmake new file mode 100644 index 0000000000..e685a5970e --- /dev/null +++ b/Tests/RunCMake/Sbom/UnknownDependencies.cmake @@ -0,0 +1,22 @@ +project(UnknownDependencies LANGUAGES NONE) + +add_library(root INTERFACE) +add_library(Unknown::dep UNKNOWN IMPORTED) +set_target_properties(Unknown::dep PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/unknown.a" + EXPORT_FIND_PACKAGE_NAME Unknown +) +target_link_libraries(root INTERFACE + Unknown::dep bare_library Unknown::dep bare_library) + +install(TARGETS root EXPORT unknown_dependencies DESTINATION .) + +foreach(dependency_package IN ITEMS First Second) + if(dependency_package STREQUAL "First") + set(dependency_license MIT) + else() + set(dependency_license Apache-2.0) + endif() + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/SameNamedImportedTargets + ${dependency_package}) +endforeach() diff --git a/Tests/RunCMake/Sbom/cmake/bar-config-version.cmake b/Tests/RunCMake/Sbom/cmake/bar-config-version.cmake index ff683d6608..b687c85c2b 100644 --- a/Tests/RunCMake/Sbom/cmake/bar-config-version.cmake +++ b/Tests/RunCMake/Sbom/cmake/bar-config-version.cmake @@ -1,7 +1,6 @@ -set(PACKAGE_VERSION "1.3.5") +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 () @@ -17,7 +16,6 @@ if (PACKAGE_FIND_VERSION_RANGE) 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 () diff --git a/Tests/RunCMake/Sbom/cmake/baz-config-version.cmake b/Tests/RunCMake/Sbom/cmake/baz-config-version.cmake index bb9b80671c..9f3107028f 100644 --- a/Tests/RunCMake/Sbom/cmake/baz-config-version.cmake +++ b/Tests/RunCMake/Sbom/cmake/baz-config-version.cmake @@ -1,7 +1,6 @@ -set(PACKAGE_VERSION "1.8.5") +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 () @@ -17,7 +16,6 @@ if (PACKAGE_FIND_VERSION_RANGE) 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 ()