install(TARGETS): Add FILE_SET mode

stage/master/nightly/2021/10/28^2
Kyle Edwards 2021-06-17 17:01:28 -04:00
parent d8af2d954f
commit 9b479124cc
7 changed files with 290 additions and 7 deletions

View File

@ -318,6 +318,8 @@ set(SRCS
cmInstallExportGenerator.cxx
cmInstalledFile.h
cmInstalledFile.cxx
cmInstallFileSetGenerator.h
cmInstallFileSetGenerator.cxx
cmInstallFilesGenerator.h
cmInstallFilesGenerator.cxx
cmInstallImportedRuntimeArtifactsGenerator.h

View File

@ -6,11 +6,13 @@
#include <cassert>
#include <cstddef>
#include <iterator>
#include <map>
#include <set>
#include <sstream>
#include <utility>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/string_view>
#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<std::string>& relFiles,
std::vector<std::string>& absFiles);
bool MakeFilesFullPath(const char* modeName, const std::string& basePath,
const std::vector<std::string>& relFiles,
std::vector<std::string>& absFiles);
bool CheckCMP0006(bool& failure) const;
std::string GetDestination(const cmInstallCommandArguments* args,
@ -177,6 +184,19 @@ std::unique_ptr<cmInstallFilesGenerator> CreateInstallFilesGenerator(
args.GetDestination());
}
std::unique_ptr<cmInstallFileSetGenerator> 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<cmInstallFileSetGenerator>(
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<std::string> const& args,
std::vector<std::string> PrivateHeader;
std::vector<std::string> PublicHeader;
std::vector<std::string> Resource;
std::vector<std::vector<std::string>> FileSets;
};
static auto const argHelper =
@ -403,7 +424,8 @@ bool HandleTargetsMode(std::vector<std::string> 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<std::string> genericArgVector;
ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector);
@ -442,6 +464,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
cmInstallCommandArguments publicHeaderArgs(helper.DefaultComponentName);
cmInstallCommandArguments resourceArgs(helper.DefaultComponentName);
cmInstallCommandIncludesArgument includesArgs;
std::vector<cmInstallCommandFileSetArguments> 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<std::string> 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<void>::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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> const& args,
bool installsPrivateHeader = false;
bool installsPublicHeader = false;
bool installsResource = false;
std::vector<bool> 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<std::string> const& args,
std::unique_ptr<cmInstallFilesGenerator> privateHeaderGenerator;
std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator;
std::unique_ptr<cmInstallFilesGenerator> resourceGenerator;
std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators;
// Avoid selecting default destinations for PUBLIC_HEADER and
// PRIVATE_HEADER if any artifacts are specified.
@ -991,6 +1050,35 @@ bool HandleTargetsMode(std::vector<std::string> 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<std::string> 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<std::string> 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;
}
@ -2062,13 +2159,21 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
bool Helper::MakeFilesFullPath(const char* modeName,
const std::vector<std::string>& relFiles,
std::vector<std::string>& absFiles)
{
return this->MakeFilesFullPath(
modeName, this->Makefile->GetCurrentSourceDirectory(), relFiles, absFiles);
}
bool Helper::MakeFilesFullPath(const char* modeName,
const std::string& basePath,
const std::vector<std::string>& relFiles,
std::vector<std::string>& 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.

View File

@ -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<std::string>& 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<std::string> args, std::vector<std::string>* unconsumedArgs)
{
args.insert(args.begin(), "FILE_SET");
this->cmInstallCommandArguments::Parse(args, unconsumedArgs);
}

View File

@ -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<std::string> IncludeDirs;
};
class cmInstallCommandFileSetArguments : public cmInstallCommandArguments
{
public:
cmInstallCommandFileSetArguments(std::string defaultComponent);
void Parse(std::vector<std::string> args,
std::vector<std::string>* unconsumedArgs);
const std::string& GetFileSet() const { return this->FileSet; }
private:
std::string FileSet;
};

View File

@ -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 <map>
#include <string>
#include <utility>
#include <vector>
#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<std::string> 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<std::string, std::vector<std::string>>
cmInstallFileSetGenerator::CalculateFilesPerDir(
const std::string& config) const
{
std::map<std::string, std::vector<std::string>> 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;
}

View File

@ -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 <iosfwd>
#include <map>
#include <string>
#include <vector>
#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<std::string> 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<std::string, std::vector<std::string>> CalculateFilesPerDir(
const std::string& config) const;
};

View File

@ -387,6 +387,7 @@ CMAKE_CXX_SOURCES="\
cmInstallCommandArguments \
cmInstallDirectoryGenerator \
cmInstallExportGenerator \
cmInstallFileSetGenerator \
cmInstallFilesCommand \
cmInstallFilesGenerator \
cmInstallGenerator \