From 9b479124cc4be49a4b2c6e45e733b489f7a26432 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Thu, 17 Jun 2021 17:01:28 -0400 Subject: install(TARGETS): Add FILE_SET mode --- Source/CMakeLists.txt | 2 + Source/cmInstallCommand.cxx | 119 ++++++++++++++++++++++++++++++++--- Source/cmInstallCommandArguments.cxx | 19 ++++++ Source/cmInstallCommandArguments.h | 16 +++++ Source/cmInstallFileSetGenerator.cxx | 88 ++++++++++++++++++++++++++ Source/cmInstallFileSetGenerator.h | 52 +++++++++++++++ bootstrap | 1 + 7 files changed, 290 insertions(+), 7 deletions(-) create mode 100644 Source/cmInstallFileSetGenerator.cxx create mode 100644 Source/cmInstallFileSetGenerator.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5e20736..c125378 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -318,6 +318,8 @@ set(SRCS cmInstallExportGenerator.cxx cmInstalledFile.h cmInstalledFile.cxx + cmInstallFileSetGenerator.h + cmInstallFileSetGenerator.cxx cmInstallFilesGenerator.h cmInstallFilesGenerator.cxx cmInstallImportedRuntimeArtifactsGenerator.h diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index eaf88f6..08c308d 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include "cmsys/Glob.hxx" @@ -18,11 +20,13 @@ #include "cmArgumentParser.h" #include "cmExecutionStatus.h" #include "cmExportSet.h" +#include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmInstallCommandArguments.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" #include "cmInstallFilesGenerator.h" #include "cmInstallGenerator.h" #include "cmInstallGetRuntimeDependenciesGenerator.h" @@ -89,6 +93,9 @@ public: bool MakeFilesFullPath(const char* modeName, const std::vector& relFiles, std::vector& absFiles); + bool MakeFilesFullPath(const char* modeName, const std::string& basePath, + const std::vector& relFiles, + std::vector& absFiles); bool CheckCMP0006(bool& failure) const; std::string GetDestination(const cmInstallCommandArguments* args, @@ -177,6 +184,19 @@ std::unique_ptr CreateInstallFilesGenerator( args.GetDestination()); } +std::unique_ptr CreateInstallFileSetGenerator( + Helper& helper, cmTarget& target, cmFileSet* fileSet, + const std::string& destination, const cmInstallCommandArguments& args) +{ + cmInstallGenerator::MessageLevel message = + cmInstallGenerator::SelectMessageLevel(helper.Makefile); + return cm::make_unique( + target.GetName(), fileSet, destination, args.GetPermissions(), + args.GetConfigurations(), args.GetComponent(), message, + args.GetExcludeFromAll(), args.GetOptional(), + helper.Makefile->GetBacktrace()); +} + void AddInstallRuntimeDependenciesGenerator( Helper& helper, cmInstallRuntimeDependencySet* runtimeDependencySet, const cmInstallCommandArguments& runtimeArgs, @@ -390,6 +410,7 @@ bool HandleTargetsMode(std::vector const& args, std::vector PrivateHeader; std::vector PublicHeader; std::vector Resource; + std::vector> FileSets; }; static auto const argHelper = @@ -403,7 +424,8 @@ bool HandleTargetsMode(std::vector const& args, .Bind("INCLUDES"_s, &ArgVectors::Includes) .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader) .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader) - .Bind("RESOURCE"_s, &ArgVectors::Resource); + .Bind("RESOURCE"_s, &ArgVectors::Resource) + .Bind("FILE_SET"_s, &ArgVectors::FileSets); std::vector genericArgVector; ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); @@ -442,6 +464,8 @@ bool HandleTargetsMode(std::vector const& args, cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName); cmInstallCommandArguments resourceArgs(helper.DefaultComponentName); cmInstallCommandIncludesArgument includesArgs; + std::vector fileSetArgs( + argVectors.FileSets.size(), { helper.DefaultComponentName }); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -455,6 +479,15 @@ bool HandleTargetsMode(std::vector const& args, publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs); resourceArgs.Parse(argVectors.Resource, &unknownArgs); includesArgs.Parse(&argVectors.Includes, &unknownArgs); + for (std::size_t i = 0; i < argVectors.FileSets.size(); i++) { + // We have to create a separate object for the parsing because + // cmArgumentParser::Bind() binds to a specific address, but the + // objects in the vector can move around. So we parse in an object with a + // fixed address and then copy the data into the vector. + cmInstallCommandFileSetArguments fileSetArg(helper.DefaultComponentName); + fileSetArg.Parse(argVectors.FileSets[i], &unknownArgs); + fileSetArgs[i] = std::move(fileSetArg); + } if (!unknownArgs.empty()) { // Unknown argument. @@ -473,6 +506,9 @@ bool HandleTargetsMode(std::vector const& args, privateHeaderArgs.SetGenericArguments(&genericArgs); publicHeaderArgs.SetGenericArguments(&genericArgs); resourceArgs.SetGenericArguments(&genericArgs); + for (auto& fileSetArg : fileSetArgs) { + fileSetArg.SetGenericArguments(&genericArgs); + } success = success && archiveArgs.Finalize(); success = success && libraryArgs.Finalize(); @@ -483,6 +519,9 @@ bool HandleTargetsMode(std::vector const& args, success = success && privateHeaderArgs.Finalize(); success = success && publicHeaderArgs.Finalize(); success = success && resourceArgs.Finalize(); + for (auto& fileSetArg : fileSetArgs) { + success = success && fileSetArg.Finalize(); + } if (!success) { return false; @@ -493,7 +532,10 @@ bool HandleTargetsMode(std::vector const& args, if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() || objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() || bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() || - publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly()) { + publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() || + std::any_of(fileSetArgs.begin(), fileSetArgs.end(), + [](const cmInstallCommandFileSetArguments& fileSetArg) + -> bool { return fileSetArg.GetNamelinkOnly(); })) { status.SetError( "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " "The NAMELINK_ONLY option may be specified only following LIBRARY."); @@ -502,7 +544,10 @@ bool HandleTargetsMode(std::vector const& args, if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() || objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() || bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() || - publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip()) { + publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() || + std::any_of(fileSetArgs.begin(), fileSetArgs.end(), + [](const cmInstallCommandFileSetArguments& fileSetArg) + -> bool { return fileSetArg.GetNamelinkSkip(); })) { status.SetError( "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " "The NAMELINK_SKIP option may be specified only following LIBRARY."); @@ -515,7 +560,10 @@ bool HandleTargetsMode(std::vector const& args, bundleArgs.HasNamelinkComponent() || privateHeaderArgs.HasNamelinkComponent() || publicHeaderArgs.HasNamelinkComponent() || - resourceArgs.HasNamelinkComponent()) { + resourceArgs.HasNamelinkComponent() || + std::any_of(fileSetArgs.begin(), fileSetArgs.end(), + [](const cmInstallCommandFileSetArguments& fileSetArg) + -> bool { return fileSetArg.HasNamelinkComponent(); })) { status.SetError( "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " "The NAMELINK_COMPONENT option may be specified only following " @@ -531,12 +579,21 @@ bool HandleTargetsMode(std::vector const& args, !libraryArgs.GetType().empty() || !runtimeArgs.GetType().empty() || !objectArgs.GetType().empty() || !frameworkArgs.GetType().empty() || !bundleArgs.GetType().empty() || !privateHeaderArgs.GetType().empty() || - !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty()) { + !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() || + std::any_of(fileSetArgs.begin(), fileSetArgs.end(), + [](const cmInstallCommandFileSetArguments& fileSetArg) + -> bool { return !fileSetArg.GetType().empty(); })) { status.SetError( "TARGETS given TYPE option. The TYPE option may only be specified in " " install(FILES) and install(DIRECTORIES)."); return false; } + if (std::any_of(fileSetArgs.begin(), fileSetArgs.end(), + [](const cmInstallCommandFileSetArguments& fileSetArg) + -> bool { return fileSetArg.GetFileSet().empty(); })) { + status.SetError("TARGETS given FILE_SET option without file set name."); + return false; + } cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; if (withRuntimeDependencies) { @@ -647,6 +704,7 @@ bool HandleTargetsMode(std::vector const& args, bool installsPrivateHeader = false; bool installsPublicHeader = false; bool installsResource = false; + std::vector installsFileSet(fileSetArgs.size(), false); // Generate install script code to install the given targets. for (cmTarget* ti : targets) { @@ -662,6 +720,7 @@ bool HandleTargetsMode(std::vector const& args, std::unique_ptr privateHeaderGenerator; std::unique_ptr publicHeaderGenerator; std::unique_ptr resourceGenerator; + std::vector> fileSetGenerators; // Avoid selecting default destinations for PUBLIC_HEADER and // PRIVATE_HEADER if any artifacts are specified. @@ -991,6 +1050,35 @@ bool HandleTargetsMode(std::vector const& args, } } + if (!namelinkOnly) { + for (std::size_t i = 0; i < fileSetArgs.size(); i++) { + auto* fileSet = target.GetFileSet(fileSetArgs[i].GetFileSet()); + auto interfaceFileSetEntries = cmExpandedList(target.GetSafeProperty( + cmTarget::GetInterfaceFileSetsPropertyName(fileSet->GetType()))); + if (fileSet && + std::find( + interfaceFileSetEntries.begin(), interfaceFileSetEntries.end(), + fileSetArgs[i].GetFileSet()) != interfaceFileSetEntries.end()) { + std::string destination; + if (fileSet->GetType() == "HEADERS"_s) { + destination = helper.GetIncludeDestination(&fileSetArgs[i]); + } else { + destination = fileSetArgs[i].GetDestination(); + if (destination.empty()) { + status.SetError( + cmStrCat("TARGETS given no FILE_SET DESTINATION for target \"", + target.GetName(), "\" file set \"", + fileSet->GetName(), "\".")); + return false; + } + } + fileSetGenerators.push_back(CreateInstallFileSetGenerator( + helper, target, fileSet, destination, fileSetArgs[i])); + installsFileSet[i] = true; + } + } + } + // Add this install rule to an export if one was specified. addTargetExport(); @@ -1016,6 +1104,9 @@ bool HandleTargetsMode(std::vector const& args, helper.Makefile->AddInstallGenerator(std::move(privateHeaderGenerator)); helper.Makefile->AddInstallGenerator(std::move(publicHeaderGenerator)); helper.Makefile->AddInstallGenerator(std::move(resourceGenerator)); + for (auto& gen : fileSetGenerators) { + helper.Makefile->AddInstallGenerator(std::move(gen)); + } } if (withRuntimeDependencies && !runtimeDependencySet->Empty()) { @@ -1067,6 +1158,12 @@ bool HandleTargetsMode(std::vector const& args, helper.Makefile->GetGlobalGenerator()->AddInstallComponent( resourceArgs.GetComponent()); } + for (std::size_t i = 0; i < fileSetArgs.size(); i++) { + if (installsFileSet[i]) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + fileSetArgs[i].GetComponent()); + } + } return true; } @@ -2063,12 +2160,20 @@ bool Helper::MakeFilesFullPath(const char* modeName, const std::vector& relFiles, std::vector& absFiles) { + return this->MakeFilesFullPath( + modeName, this->Makefile->GetCurrentSourceDirectory(), relFiles, absFiles); +} + +bool Helper::MakeFilesFullPath(const char* modeName, + const std::string& basePath, + const std::vector& relFiles, + std::vector& absFiles) +{ for (std::string const& relFile : relFiles) { std::string file = relFile; std::string::size_type gpos = cmGeneratorExpression::Find(file); if (gpos != 0 && !cmSystemTools::FileIsFullPath(file)) { - file = - cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', relFile); + file = cmStrCat(basePath, '/', relFile); } // Make sure the file is not a directory. diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index cc3df2a..7309316 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -152,6 +152,11 @@ const std::string& cmInstallCommandArguments::GetType() const return this->Type; } +const std::string& cmInstallCommandArguments::GetDefaultComponent() const +{ + return this->DefaultComponentName; +} + const std::vector& cmInstallCommandArguments::GetConfigurations() const { @@ -220,3 +225,17 @@ void cmInstallCommandIncludesArgument::Parse( this->IncludeDirs.push_back(std::move(dir)); } } + +cmInstallCommandFileSetArguments::cmInstallCommandFileSetArguments( + std::string defaultComponent) + : cmInstallCommandArguments(std::move(defaultComponent)) +{ + this->Bind("FILE_SET"_s, this->FileSet); +} + +void cmInstallCommandFileSetArguments::Parse( + std::vector args, std::vector* unconsumedArgs) +{ + args.insert(args.begin(), "FILE_SET"); + this->cmInstallCommandArguments::Parse(args, unconsumedArgs); +} diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index f318a1a..79bd945 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -34,6 +34,8 @@ public: bool HasNamelinkComponent() const; const std::string& GetType() const; + const std::string& GetDefaultComponent() const; + static bool CheckPermissions(const std::string& onePerm, std::string& perm); private: @@ -71,3 +73,17 @@ public: private: std::vector IncludeDirs; }; + +class cmInstallCommandFileSetArguments : public cmInstallCommandArguments +{ +public: + cmInstallCommandFileSetArguments(std::string defaultComponent); + + void Parse(std::vector args, + std::vector* unconsumedArgs); + + const std::string& GetFileSet() const { return this->FileSet; } + +private: + std::string FileSet; +}; diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx new file mode 100644 index 0000000..7121ea3 --- /dev/null +++ b/Source/cmInstallFileSetGenerator.cxx @@ -0,0 +1,88 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallFileSetGenerator.h" + +#include +#include +#include +#include + +#include "cmFileSet.h" +#include "cmGeneratorExpression.h" +#include "cmGlobalGenerator.h" +#include "cmInstallType.h" +#include "cmLocalGenerator.h" +#include "cmStringAlgorithms.h" + +cmInstallFileSetGenerator::cmInstallFileSetGenerator( + std::string targetName, cmFileSet* fileSet, std::string const& dest, + std::string file_permissions, std::vector const& configurations, + std::string const& component, MessageLevel message, bool exclude_from_all, + bool optional, cmListFileBacktrace backtrace) + : cmInstallGenerator(dest, configurations, component, message, + exclude_from_all, false, std::move(backtrace)) + , TargetName(std::move(targetName)) + , FileSet(fileSet) + , FilePermissions(std::move(file_permissions)) + , Optional(optional) +{ + this->ActionsPerConfig = true; +} + +cmInstallFileSetGenerator::~cmInstallFileSetGenerator() = default; + +bool cmInstallFileSetGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + + // Lookup this target in the current directory. + this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName); + if (!this->Target) { + // If no local target has been found, find it in the global scope. + this->Target = + lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName); + } + + return true; +} + +std::string cmInstallFileSetGenerator::GetDestination( + std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); +} + +void cmInstallFileSetGenerator::GenerateScriptForConfig( + std::ostream& os, const std::string& config, Indent indent) +{ + for (auto const& dirEntry : this->CalculateFilesPerDir(config)) { + std::string destSub; + if (!dirEntry.first.empty()) { + destSub = cmStrCat('/', dirEntry.first); + } + this->AddInstallRule(os, cmStrCat(this->GetDestination(config), destSub), + cmInstallType_FILES, dirEntry.second, + this->GetOptional(), this->FilePermissions.c_str(), + nullptr, nullptr, nullptr, indent); + } +} + +std::map> +cmInstallFileSetGenerator::CalculateFilesPerDir( + const std::string& config) const +{ + std::map> result; + + auto dirCges = this->FileSet->CompileDirectoryEntries(); + auto dirs = this->FileSet->EvaluateDirectoryEntries( + dirCges, this->LocalGenerator, config, this->Target); + + auto fileCges = this->FileSet->CompileFileEntries(); + for (auto const& fileCge : fileCges) { + this->FileSet->EvaluateFileEntry( + dirs, result, fileCge, this->LocalGenerator, config, this->Target); + } + + return result; +} diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h new file mode 100644 index 0000000..8d067d9 --- /dev/null +++ b/Source/cmInstallFileSetGenerator.h @@ -0,0 +1,52 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include +#include +#include +#include + +#include "cmInstallGenerator.h" +#include "cmListFileCache.h" +#include "cmScriptGenerator.h" + +class cmGeneratorTarget; +class cmFileSet; +class cmLocalGenerator; + +class cmInstallFileSetGenerator : public cmInstallGenerator +{ +public: + cmInstallFileSetGenerator(std::string targetName, cmFileSet* fileSet, + std::string const& dest, + std::string file_permissions, + std::vector const& configurations, + std::string const& component, MessageLevel message, + bool exclude_from_all, bool optional, + cmListFileBacktrace backtrace); + ~cmInstallFileSetGenerator() override; + + bool Compute(cmLocalGenerator* lg) override; + + std::string GetDestination(std::string const& config) const; + std::string GetDestination() const { return this->Destination; } + bool GetOptional() const { return this->Optional; } + cmFileSet* GetFileSet() const { return this->FileSet; } + cmGeneratorTarget* GetTarget() const { return this->Target; } + +protected: + void GenerateScriptForConfig(std::ostream& os, const std::string& config, + Indent indent) override; + +private: + std::string TargetName; + cmLocalGenerator* LocalGenerator; + cmFileSet* const FileSet; + std::string const FilePermissions; + bool const Optional; + cmGeneratorTarget* Target; + + std::map> CalculateFilesPerDir( + const std::string& config) const; +}; diff --git a/bootstrap b/bootstrap index 2118436..c3e1264 100755 --- a/bootstrap +++ b/bootstrap @@ -387,6 +387,7 @@ CMAKE_CXX_SOURCES="\ cmInstallCommandArguments \ cmInstallDirectoryGenerator \ cmInstallExportGenerator \ + cmInstallFileSetGenerator \ cmInstallFilesCommand \ cmInstallFilesGenerator \ cmInstallGenerator \ -- cgit v0.12