diff options
author | Brad King <brad.king@kitware.com> | 2022-05-25 11:24:40 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-05-25 11:24:48 (GMT) |
commit | 5dcf505f63037e23094146730704b031c57c5d06 (patch) | |
tree | b9921b81b3741cad9d1ce13a5a5a0b69fed4d678 /Source | |
parent | 7120221e2464f3ee0f4d511783a78d0d83e9cb03 (diff) | |
parent | 2aa83fa15b01941f0267e20a1a4e29793651fefd (diff) | |
download | CMake-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.txt | 1 | ||||
-rw-r--r-- | Source/cmCMakeLanguageCommand.cxx | 93 | ||||
-rw-r--r-- | Source/cmDependencyProvider.h | 38 | ||||
-rw-r--r-- | Source/cmFindPackageCommand.cxx | 75 | ||||
-rw-r--r-- | Source/cmFindPackageCommand.h | 5 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 5 | ||||
-rw-r--r-- | Source/cmState.cxx | 9 | ||||
-rw-r--r-- | Source/cmState.h | 25 |
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; }; |