summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-05-25 11:24:40 (GMT)
committerKitware Robot <kwrobot@kitware.com>2022-05-25 11:24:48 (GMT)
commit5dcf505f63037e23094146730704b031c57c5d06 (patch)
treeb9921b81b3741cad9d1ce13a5a5a0b69fed4d678 /Source
parent7120221e2464f3ee0f4d511783a78d0d83e9cb03 (diff)
parent2aa83fa15b01941f0267e20a1a4e29793651fefd (diff)
downloadCMake-5dcf505f63037e23094146730704b031c57c5d06.zip
CMake-5dcf505f63037e23094146730704b031c57c5d06.tar.gz
CMake-5dcf505f63037e23094146730704b031c57c5d06.tar.bz2
Merge topic 'dependency-providers'
2aa83fa15b Dependency providers: Add find_package and FetchContent support 8a28368feb FetchContent: Don't discard non-empty SOURCE_DIR and BINARY_DIR 8ce9bb8a0c FetchContent: Don't leak internal variables 74a6ddc339 cmFindPackageCommand: Handle Makefile variable definitions more robustly Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: Gerhard Olsson <gerhard.nospam@gmail.com> Merge-request: !7276
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt1
-rw-r--r--Source/cmCMakeLanguageCommand.cxx93
-rw-r--r--Source/cmDependencyProvider.h38
-rw-r--r--Source/cmFindPackageCommand.cxx75
-rw-r--r--Source/cmFindPackageCommand.h5
-rw-r--r--Source/cmGlobalGenerator.cxx5
-rw-r--r--Source/cmState.cxx9
-rw-r--r--Source/cmState.h25
8 files changed, 239 insertions, 12 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 2deaaaa..95b07cb 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -199,6 +199,7 @@ set(SRCS
cmCustomCommandTypes.h
cmDefinitions.cxx
cmDefinitions.h
+ cmDependencyProvider.h
cmDepends.cxx
cmDepends.h
cmDependsC.cxx
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index 27d8cb8..7d05e88 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -13,11 +13,14 @@
#include <cm/string_view>
#include <cmext/string_view>
+#include "cmArgumentParser.h"
+#include "cmDependencyProvider.h"
#include "cmExecutionStatus.h"
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmRange.h"
+#include "cmState.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -215,6 +218,91 @@ bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args,
return makefile.ReadListFileAsString(
code, cmStrCat(context.FilePath, ":", context.Line, ":EVAL"));
}
+
+bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
+ std::vector<std::string> const& args, cmExecutionStatus& status)
+{
+ cmState* state = status.GetMakefile().GetState();
+ if (!state->InTopLevelIncludes()) {
+ return FatalError(
+ status,
+ "Dependency providers can only be set as part of the first call to "
+ "project(). More specifically, cmake_language(SET_DEPENDENCY_PROVIDER) "
+ "can only be called while the first project() command processes files "
+ "listed in CMAKE_PROJECT_TOP_LEVEL_INCLUDES.");
+ }
+
+ struct SetProviderArgs
+ {
+ std::string Command;
+ std::vector<std::string> Methods;
+ };
+
+ auto const ArgsParser =
+ cmArgumentParser<SetProviderArgs>()
+ .Bind("SET_DEPENDENCY_PROVIDER"_s, &SetProviderArgs::Command)
+ .Bind("SUPPORTED_METHODS"_s, &SetProviderArgs::Methods);
+
+ std::vector<std::string> unparsed;
+ auto parsedArgs = ArgsParser.Parse(args, &unparsed);
+
+ if (!unparsed.empty()) {
+ return FatalError(
+ status, cmStrCat("Unrecognized keyword: \"", unparsed.front(), "\""));
+ }
+
+ // We store the command that FetchContent_MakeAvailable() can call in a
+ // global (but considered internal) property. If the provider doesn't
+ // support this method, we set this property to an empty string instead.
+ // This simplifies the logic in FetchContent_MakeAvailable() and doesn't
+ // require us to define a new internal command or sub-command.
+ std::string fcmasProperty = "__FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER";
+
+ if (parsedArgs.Command.empty()) {
+ if (!parsedArgs.Methods.empty()) {
+ return FatalError(status,
+ "Must specify a non-empty command name when provider "
+ "methods are given");
+ }
+ state->ClearDependencyProvider();
+ state->SetGlobalProperty(fcmasProperty, "");
+ return true;
+ }
+
+ cmState::Command command = state->GetCommand(parsedArgs.Command);
+ if (!command) {
+ return FatalError(status,
+ cmStrCat("Command \"", parsedArgs.Command,
+ "\" is not a defined command"));
+ }
+
+ if (parsedArgs.Methods.empty()) {
+ return FatalError(status, "Must specify at least one provider method");
+ }
+
+ bool supportsFetchContentMakeAvailableSerial = false;
+ std::vector<cmDependencyProvider::Method> methods;
+ for (auto const& method : parsedArgs.Methods) {
+ if (method == "FIND_PACKAGE") {
+ methods.emplace_back(cmDependencyProvider::Method::FindPackage);
+ } else if (method == "FETCHCONTENT_MAKEAVAILABLE_SERIAL") {
+ supportsFetchContentMakeAvailableSerial = true;
+ methods.emplace_back(
+ cmDependencyProvider::Method::FetchContentMakeAvailableSerial);
+ } else {
+ return FatalError(
+ status,
+ cmStrCat("Unknown dependency provider method \"", method, "\""));
+ }
+ }
+
+ state->SetDependencyProvider({ parsedArgs.Command, methods });
+ state->SetGlobalProperty(
+ fcmasProperty,
+ supportsFetchContentMakeAvailableSerial ? parsedArgs.Command.c_str() : "");
+
+ return true;
+}
}
bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
@@ -246,6 +334,11 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
return FatalError(status, "called with incorrect number of arguments");
}
+ if (expArgs[expArg] == "SET_DEPENDENCY_PROVIDER"_s) {
+ finishArgs();
+ return cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(expArgs, status);
+ }
+
cm::optional<Defer> maybeDefer;
if (expArgs[expArg] == "DEFER"_s) {
++expArg; // Consume "DEFER".
diff --git a/Source/cmDependencyProvider.h b/Source/cmDependencyProvider.h
new file mode 100644
index 0000000..a6670b4
--- /dev/null
+++ b/Source/cmDependencyProvider.h
@@ -0,0 +1,38 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
+class cmDependencyProvider
+{
+public:
+ enum class Method
+ {
+ FindPackage,
+ FetchContentMakeAvailableSerial,
+ };
+
+ cmDependencyProvider(std::string command, std::vector<Method> methods)
+ : Command(std::move(command))
+ , Methods(std::move(methods))
+ {
+ }
+
+ std::string const& GetCommand() const { return this->Command; }
+ std::vector<Method> const& GetMethods() const { return this->Methods; }
+ bool SupportsMethod(Method method) const
+ {
+ return std::find(this->Methods.begin(), this->Methods.end(), method) !=
+ this->Methods.end();
+ }
+
+private:
+ std::string Command;
+ std::vector<Method> Methods;
+};
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 6586c69..e41d5e4 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -23,6 +23,7 @@
#include "cmsys/String.h"
#include "cmAlgorithms.h"
+#include "cmDependencyProvider.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
@@ -238,6 +239,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
const char* components_sep = "";
std::set<std::string> requiredComponents;
std::set<std::string> optionalComponents;
+ std::vector<std::pair<std::string, const char*>> componentVarDefs;
+ bool bypassProvider = false;
// Always search directly in a generated path.
this->SearchPathSuffixes.emplace_back();
@@ -268,6 +271,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
if (args[i] == "QUIET") {
this->Quiet = true;
doing = DoingNone;
+ } else if (args[i] == "BYPASS_PROVIDER") {
+ bypassProvider = true;
+ doing = DoingNone;
} else if (args[i] == "EXACT") {
this->VersionExact = true;
doing = DoingNone;
@@ -356,7 +362,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
- this->AddFindDefinition(req_var, isRequired);
+ componentVarDefs.emplace_back(req_var, isRequired);
// Append to the list of required components.
components += components_sep;
@@ -408,7 +414,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return false;
}
- // Maybe choose one mode exclusively.
+ // Check and eliminate search modes not allowed by the args provided
this->UseFindModules = configArgs.empty();
this->UseConfigFiles = moduleArgs.empty();
if (!this->UseFindModules && !this->UseConfigFiles) {
@@ -543,6 +549,48 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return true;
}
+ // Now choose what method(s) we will use to satisfy the request. Note that
+ // we still want all the above checking of arguments, etc. regardless of the
+ // method used. This will ensure ill-formed arguments are caught earlier,
+ // before things like dependency providers need to deal with them.
+
+ // A dependency provider (if set) gets first look before other methods.
+ // We do this before modifying the package root path stack because a
+ // provider might use methods that ignore that.
+ cmState* state = this->Makefile->GetState();
+ cmState::Command providerCommand = state->GetDependencyProviderCommand(
+ cmDependencyProvider::Method::FindPackage);
+ if (bypassProvider) {
+ if (this->DebugMode && providerCommand) {
+ this->DebugMessage(
+ "BYPASS_PROVIDER given, skipping dependency provider");
+ }
+ } else if (providerCommand) {
+ if (this->DebugMode) {
+ this->DebugMessage(cmStrCat("Trying dependency provider command: ",
+ state->GetDependencyProvider()->GetCommand(),
+ "()"));
+ }
+ std::vector<cmListFileArgument> listFileArgs(args.size() + 1);
+ listFileArgs[0] =
+ cmListFileArgument("FIND_PACKAGE", cmListFileArgument::Unquoted, 0);
+ std::transform(args.begin(), args.end(), listFileArgs.begin() + 1,
+ [](const std::string& arg) {
+ return cmListFileArgument(arg,
+ cmListFileArgument::Bracket, 0);
+ });
+ if (!providerCommand(listFileArgs, this->Status)) {
+ return false;
+ }
+ if (this->Makefile->IsOn(cmStrCat(this->Name, "_FOUND"))) {
+ if (this->DebugMode) {
+ this->DebugMessage("Package was found by the dependency provider");
+ }
+ this->AppendSuccessInformation();
+ return true;
+ }
+ }
+
{
// Allocate a PACKAGE_ROOT_PATH for the current find_package call.
this->Makefile->FindPackageRootPathStack.emplace_back();
@@ -573,7 +621,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
- this->SetModuleVariables(components);
+ this->SetModuleVariables(components, componentVarDefs);
// See if we have been told to delegate to FetchContent or some other
// redirected config package first. We have to check all names that
@@ -697,6 +745,12 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
this->AppendSuccessInformation();
+ // Restore original state of "_FIND_" variables set in SetModuleVariables()
+ this->RestoreFindDefinitions();
+
+ // Pop the package stack
+ this->Makefile->FindPackageRootPathStack.pop_back();
+
if (!this->DebugBuffer.empty()) {
this->DebugMessage(this->DebugBuffer);
}
@@ -778,13 +832,18 @@ void cmFindPackageCommand::SetVersionVariables(
addDefinition(prefix + "_COUNT", buf);
}
-void cmFindPackageCommand::SetModuleVariables(const std::string& components)
+void cmFindPackageCommand::SetModuleVariables(
+ const std::string& components,
+ const std::vector<std::pair<std::string, const char*>>& componentVarDefs)
{
this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name);
- // Store the list of components.
+ // Store the list of components and associated variable definitions
std::string components_var = this->Name + "_FIND_COMPONENTS";
this->AddFindDefinition(components_var, components);
+ for (const auto& varDef : componentVarDefs) {
+ this->AddFindDefinition(varDef.first, varDef.second);
+ }
if (this->Quiet) {
// Tell the module that is about to be read that it should find
@@ -1388,12 +1447,6 @@ void cmFindPackageCommand::AppendSuccessInformation()
this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName,
"REQUIRED");
}
-
- // Restore original state of "_FIND_" variables we set.
- this->RestoreFindDefinitions();
-
- // Pop the package stack
- this->Makefile->FindPackageRootPathStack.pop_back();
}
inline std::size_t collectPathsForDebug(std::string& buffer,
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 902fa32..80fd8f8 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -9,6 +9,7 @@
#include <map>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include <cm/string_view>
@@ -97,7 +98,9 @@ private:
const std::string& prefix, const std::string& version, unsigned int count,
unsigned int major, unsigned int minor, unsigned int patch,
unsigned int tweak);
- void SetModuleVariables(const std::string& components);
+ void SetModuleVariables(
+ const std::string& components,
+ const std::vector<std::pair<std::string, const char*>>& componentVarDefs);
bool FindModule(bool& found);
void AddFindDefinition(const std::string& var, cm::string_view value);
void RestoreFindDefinitions();
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 4d636e4..9d61de9 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -690,6 +690,7 @@ void cmGlobalGenerator::EnableLanguage(
}
// One-time includes of user-provided project setup files
+ mf->GetState()->SetInTopLevelIncludes(true);
std::string includes =
mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
std::vector<std::string> includesList = cmExpandedList(includes);
@@ -700,22 +701,26 @@ void cmGlobalGenerator::EnableLanguage(
cmSystemTools::Error(
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: " +
setupFile);
+ mf->GetState()->SetInTopLevelIncludes(false);
return;
}
if (cmSystemTools::FileIsDirectory(absSetupFile)) {
cmSystemTools::Error(
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: " +
setupFile);
+ mf->GetState()->SetInTopLevelIncludes(false);
return;
}
if (!mf->ReadListFile(absSetupFile)) {
cmSystemTools::Error(
"Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: " +
setupFile);
+ mf->GetState()->SetInTopLevelIncludes(false);
return;
}
}
}
+ mf->GetState()->SetInTopLevelIncludes(false);
// Check that the languages are supported by the generator and its
// native build tool found above.
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index f1144e1..b753373 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -1072,3 +1072,12 @@ bool cmState::ParseCacheEntry(const std::string& entry, std::string& var,
return flag;
}
+
+cmState::Command cmState::GetDependencyProviderCommand(
+ cmDependencyProvider::Method method) const
+{
+ return (this->DependencyProvider &&
+ this->DependencyProvider->SupportsMethod(method))
+ ? this->GetCommand(this->DependencyProvider->GetCommand())
+ : Command{};
+}
diff --git a/Source/cmState.h b/Source/cmState.h
index ee133fc..2d0c521 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -8,11 +8,16 @@
#include <memory>
#include <set>
#include <string>
+#include <type_traits>
#include <unordered_map>
#include <unordered_set>
+#include <utility>
#include <vector>
+#include <cm/optional>
+
#include "cmDefinitions.h"
+#include "cmDependencyProvider.h"
#include "cmLinkedTree.h"
#include "cmPolicies.h"
#include "cmProperty.h"
@@ -227,6 +232,24 @@ public:
ProjectKind GetProjectKind() const;
+ void ClearDependencyProvider() { this->DependencyProvider.reset(); }
+ void SetDependencyProvider(cmDependencyProvider provider)
+ {
+ this->DependencyProvider = std::move(provider);
+ }
+ cm::optional<cmDependencyProvider> const& GetDependencyProvider() const
+ {
+ return this->DependencyProvider;
+ }
+ Command GetDependencyProviderCommand(
+ cmDependencyProvider::Method method) const;
+
+ void SetInTopLevelIncludes(bool inTopLevelIncludes)
+ {
+ this->ProcessingTopLevelIncludes = inTopLevelIncludes;
+ }
+ bool InTopLevelIncludes() const { return this->ProcessingTopLevelIncludes; }
+
private:
friend class cmake;
void AddCacheEntry(const std::string& key, const char* value,
@@ -288,4 +311,6 @@ private:
bool NinjaMulti = false;
Mode StateMode = Unknown;
ProjectKind StateProjectKind = ProjectKind::Normal;
+ cm::optional<cmDependencyProvider> DependencyProvider;
+ bool ProcessingTopLevelIncludes = false;
};