1da995a4b254e9e7cf5716bc3ba87f1cc077a32b
[platform/upstream/cmake.git] / Source / cmFindBase.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmFindBase.h"
4
5 #include <cstddef>
6 #include <deque>
7 #include <map>
8 #include <utility>
9
10 #include <cm/optional>
11 #include <cmext/algorithm>
12
13 #include "cmCMakePath.h"
14 #include "cmMakefile.h"
15 #include "cmMessageType.h"
16 #include "cmPolicies.h"
17 #include "cmRange.h"
18 #include "cmSearchPath.h"
19 #include "cmState.h"
20 #include "cmStateTypes.h"
21 #include "cmStringAlgorithms.h"
22 #include "cmSystemTools.h"
23 #include "cmValue.h"
24 #include "cmWindowsRegistry.h"
25 #include "cmake.h"
26
27 class cmExecutionStatus;
28
29 cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status)
30   : cmFindCommon(status)
31   , FindCommandName(std::move(findCommandName))
32 {
33 }
34
35 bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
36 {
37   if (argsIn.size() < 2) {
38     this->SetError("called with incorrect number of arguments");
39     return false;
40   }
41
42   // copy argsIn into args so it can be modified,
43   // in the process extract the DOC "documentation"
44   // and handle options NO_CACHE and ENV
45   size_t size = argsIn.size();
46   std::vector<std::string> args;
47   bool foundDoc = false;
48   for (unsigned int j = 0; j < size; ++j) {
49     if (foundDoc || argsIn[j] != "DOC") {
50       if (argsIn[j] == "NO_CACHE") {
51         this->StoreResultInCache = false;
52       } else if (argsIn[j] == "ENV") {
53         if (j + 1 < size) {
54           j++;
55           cmSystemTools::GetPath(args, argsIn[j].c_str());
56         }
57       } else {
58         args.push_back(argsIn[j]);
59       }
60     } else {
61       if (j + 1 < size) {
62         foundDoc = true;
63         this->VariableDocumentation = argsIn[j + 1];
64         j++;
65         if (j >= size) {
66           break;
67         }
68       }
69     }
70   }
71   if (args.size() < 2) {
72     this->SetError("called with incorrect number of arguments");
73     return false;
74   }
75   this->VariableName = args[0];
76   if (this->CheckForVariableDefined()) {
77     this->AlreadyDefined = true;
78     return true;
79   }
80
81   // Find what search path locations have been enabled/disable
82   this->SelectDefaultSearchModes();
83
84   // Find the current root path mode.
85   this->SelectDefaultRootPathMode();
86
87   // Find the current bundle/framework search policy.
88   this->SelectDefaultMacMode();
89
90   bool newStyle = false;
91   enum Doing
92   {
93     DoingNone,
94     DoingNames,
95     DoingPaths,
96     DoingPathSuffixes,
97     DoingHints
98   };
99   Doing doing = DoingNames; // assume it starts with a name
100   for (unsigned int j = 1; j < args.size(); ++j) {
101     if (args[j] == "NAMES") {
102       doing = DoingNames;
103       newStyle = true;
104     } else if (args[j] == "PATHS") {
105       doing = DoingPaths;
106       newStyle = true;
107     } else if (args[j] == "HINTS") {
108       doing = DoingHints;
109       newStyle = true;
110     } else if (args[j] == "PATH_SUFFIXES") {
111       doing = DoingPathSuffixes;
112       newStyle = true;
113     } else if (args[j] == "NAMES_PER_DIR") {
114       doing = DoingNone;
115       if (this->NamesPerDirAllowed) {
116         this->NamesPerDir = true;
117       } else {
118         this->SetError("does not support NAMES_PER_DIR");
119         return false;
120       }
121     } else if (args[j] == "NO_SYSTEM_PATH") {
122       doing = DoingNone;
123       this->NoDefaultPath = true;
124     } else if (args[j] == "REQUIRED") {
125       doing = DoingNone;
126       this->Required = true;
127       newStyle = true;
128     } else if (args[j] == "REGISTRY_VIEW") {
129       if (++j == args.size()) {
130         this->SetError("missing required argument for \"REGISTRY_VIEW\"");
131         return false;
132       }
133       auto view = cmWindowsRegistry::ToView(args[j]);
134       if (view) {
135         this->RegistryView = *view;
136       } else {
137         this->SetError(
138           cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
139         return false;
140       }
141     } else if (this->CheckCommonArgument(args[j])) {
142       doing = DoingNone;
143     } else {
144       // Some common arguments were accidentally supported by CMake
145       // 2.4 and 2.6.0 in the short-hand form of the command, so we
146       // must support it even though it is not documented.
147       if (doing == DoingNames) {
148         this->Names.push_back(args[j]);
149       } else if (doing == DoingPaths) {
150         this->UserGuessArgs.push_back(args[j]);
151       } else if (doing == DoingHints) {
152         this->UserHintsArgs.push_back(args[j]);
153       } else if (doing == DoingPathSuffixes) {
154         this->AddPathSuffix(args[j]);
155       }
156     }
157   }
158
159   if (this->VariableDocumentation.empty()) {
160     this->VariableDocumentation = "Where can ";
161     if (this->Names.empty()) {
162       this->VariableDocumentation += "the (unknown) library be found";
163     } else if (this->Names.size() == 1) {
164       this->VariableDocumentation +=
165         "the " + this->Names.front() + " library be found";
166     } else {
167       this->VariableDocumentation += "one of the ";
168       this->VariableDocumentation +=
169         cmJoin(cmMakeRange(this->Names).retreat(1), ", ");
170       this->VariableDocumentation +=
171         " or " + this->Names.back() + " libraries be found";
172     }
173   }
174
175   // look for old style
176   // FIND_*(VAR name path1 path2 ...)
177   if (!newStyle && !this->Names.empty()) {
178     // All the short-hand arguments have been recorded as names.
179     std::vector<std::string> shortArgs = this->Names;
180     this->Names.clear(); // clear out any values in Names
181     this->Names.push_back(shortArgs[0]);
182     cm::append(this->UserGuessArgs, shortArgs.begin() + 1, shortArgs.end());
183   }
184   this->ExpandPaths();
185
186   this->ComputeFinalPaths(IgnorePaths::Yes);
187
188   return true;
189 }
190
191 void cmFindBase::ExpandPaths()
192 {
193   if (!this->NoDefaultPath) {
194     if (!this->NoPackageRootPath) {
195       this->FillPackageRootPath();
196     }
197     if (!this->NoCMakePath) {
198       this->FillCMakeVariablePath();
199     }
200     if (!this->NoCMakeEnvironmentPath) {
201       this->FillCMakeEnvironmentPath();
202     }
203   }
204   this->FillUserHintsPath();
205   if (!this->NoDefaultPath) {
206     if (!this->NoSystemEnvironmentPath) {
207       this->FillSystemEnvironmentPath();
208     }
209     if (!this->NoCMakeSystemPath) {
210       this->FillCMakeSystemVariablePath();
211     }
212   }
213   this->FillUserGuessPath();
214 }
215
216 void cmFindBase::FillCMakeEnvironmentPath()
217 {
218   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeEnvironment];
219
220   // Add CMAKE_*_PATH environment variables
221   std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
222   paths.AddEnvPrefixPath("CMAKE_PREFIX_PATH");
223   paths.AddEnvPath(var);
224
225   if (this->CMakePathName == "PROGRAM") {
226     paths.AddEnvPath("CMAKE_APPBUNDLE_PATH");
227   } else {
228     paths.AddEnvPath("CMAKE_FRAMEWORK_PATH");
229   }
230   paths.AddSuffixes(this->SearchPathSuffixes);
231 }
232
233 void cmFindBase::FillPackageRootPath()
234 {
235   cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot];
236
237   // Add the PACKAGE_ROOT_PATH from each enclosing find_package call.
238   for (std::vector<std::string> const& pkgPaths :
239        cmReverseRange(this->Makefile->FindPackageRootPathStack)) {
240     paths.AddPrefixPaths(pkgPaths);
241   }
242
243   paths.AddSuffixes(this->SearchPathSuffixes);
244 }
245
246 void cmFindBase::FillCMakeVariablePath()
247 {
248   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMake];
249
250   // Add CMake variables of the same name as the previous environment
251   // variables CMAKE_*_PATH to be used most of the time with -D
252   // command line options
253   std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH");
254   paths.AddCMakePrefixPath("CMAKE_PREFIX_PATH");
255   paths.AddCMakePath(var);
256
257   if (this->CMakePathName == "PROGRAM") {
258     paths.AddCMakePath("CMAKE_APPBUNDLE_PATH");
259   } else {
260     paths.AddCMakePath("CMAKE_FRAMEWORK_PATH");
261   }
262   paths.AddSuffixes(this->SearchPathSuffixes);
263 }
264
265 void cmFindBase::FillSystemEnvironmentPath()
266 {
267   cmSearchPath& paths = this->LabeledPaths[PathLabel::SystemEnvironment];
268
269   // Add LIB or INCLUDE
270   if (!this->EnvironmentPath.empty()) {
271     paths.AddEnvPath(this->EnvironmentPath);
272 #if defined(_WIN32) || defined(__CYGWIN__)
273     paths.AddEnvPrefixPath("PATH", true);
274 #endif
275   }
276   // Add PATH
277   paths.AddEnvPath("PATH");
278   paths.AddSuffixes(this->SearchPathSuffixes);
279 }
280
281 namespace {
282 struct entry_to_remove
283 {
284   entry_to_remove(std::string const& name, cmMakefile* makefile)
285     : value()
286   {
287     if (cmValue to_skip = makefile->GetDefinition(
288           cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_COUNT"))) {
289       cmStrToLong(to_skip, &count);
290     }
291     if (cmValue prefix_value = makefile->GetDefinition(
292           cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_VALUE"))) {
293       value = *prefix_value;
294     }
295   }
296   bool valid() const { return count > 0 && !value.empty(); }
297
298   void remove_self(std::vector<std::string>& entries) const
299   {
300     if (this->valid()) {
301       long to_skip = this->count;
302       long index_to_remove = 0;
303       for (const auto& path : entries) {
304         if (path == this->value && --to_skip == 0) {
305           break;
306         }
307         ++index_to_remove;
308       }
309       entries.erase(entries.begin() + index_to_remove);
310     }
311   }
312
313   long count = -1;
314   std::string value;
315 };
316 }
317
318 void cmFindBase::FillCMakeSystemVariablePath()
319 {
320   cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem];
321
322   const bool install_prefix_in_list =
323     !this->Makefile->IsOn("CMAKE_FIND_NO_INSTALL_PREFIX");
324   const bool remove_install_prefix = this->NoCMakeInstallPath;
325   const bool add_install_prefix = !this->NoCMakeInstallPath &&
326     this->Makefile->IsDefinitionSet("CMAKE_FIND_USE_INSTALL_PREFIX");
327
328   // We have 3 possible states for `CMAKE_SYSTEM_PREFIX_PATH` and
329   // `CMAKE_INSTALL_PREFIX`.
330   // Either we need to remove `CMAKE_INSTALL_PREFIX`, add
331   // `CMAKE_INSTALL_PREFIX`, or do nothing.
332   //
333   // When we need to remove `CMAKE_INSTALL_PREFIX` we remove the Nth occurrence
334   // of `CMAKE_INSTALL_PREFIX` from `CMAKE_SYSTEM_PREFIX_PATH`, where `N` is
335   // computed by `CMakeSystemSpecificInformation.cmake` while constructing
336   // `CMAKE_SYSTEM_PREFIX_PATH`. This ensures that if projects / toolchains
337   // have removed `CMAKE_INSTALL_PREFIX` from the list, we don't remove
338   // some other entry by mistake ( likewise for `CMAKE_STAGING_PREFIX` )
339   entry_to_remove install_entry("INSTALL", this->Makefile);
340   entry_to_remove staging_entry("STAGING", this->Makefile);
341
342   if (remove_install_prefix && install_prefix_in_list &&
343       (install_entry.valid() || staging_entry.valid())) {
344     cmValue prefix_paths =
345       this->Makefile->GetDefinition("CMAKE_SYSTEM_PREFIX_PATH");
346
347     // remove entries from CMAKE_SYSTEM_PREFIX_PATH
348     std::vector<std::string> expanded = cmExpandedList(*prefix_paths);
349     install_entry.remove_self(expanded);
350     staging_entry.remove_self(expanded);
351
352     paths.AddPrefixPaths(expanded,
353                          this->Makefile->GetCurrentSourceDirectory().c_str());
354   } else if (add_install_prefix && !install_prefix_in_list) {
355     paths.AddCMakePrefixPath("CMAKE_INSTALL_PREFIX");
356     paths.AddCMakePrefixPath("CMAKE_STAGING_PREFIX");
357     paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
358   } else {
359     // Otherwise the current setup of `CMAKE_SYSTEM_PREFIX_PATH` is correct
360     paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH");
361   }
362
363   std::string var = cmStrCat("CMAKE_SYSTEM_", this->CMakePathName, "_PATH");
364   paths.AddCMakePath(var);
365
366   if (this->CMakePathName == "PROGRAM") {
367     paths.AddCMakePath("CMAKE_SYSTEM_APPBUNDLE_PATH");
368   } else {
369     paths.AddCMakePath("CMAKE_SYSTEM_FRAMEWORK_PATH");
370   }
371   paths.AddSuffixes(this->SearchPathSuffixes);
372 }
373
374 void cmFindBase::FillUserHintsPath()
375 {
376   cmSearchPath& paths = this->LabeledPaths[PathLabel::Hints];
377
378   for (std::string const& p : this->UserHintsArgs) {
379     paths.AddUserPath(p);
380   }
381   paths.AddSuffixes(this->SearchPathSuffixes);
382 }
383
384 void cmFindBase::FillUserGuessPath()
385 {
386   cmSearchPath& paths = this->LabeledPaths[PathLabel::Guess];
387
388   for (std::string const& p : this->UserGuessArgs) {
389     paths.AddUserPath(p);
390   }
391   paths.AddSuffixes(this->SearchPathSuffixes);
392 }
393
394 bool cmFindBase::CheckForVariableDefined()
395 {
396   if (cmValue value = this->Makefile->GetDefinition(this->VariableName)) {
397     cmState* state = this->Makefile->GetState();
398     cmValue cacheEntry = state->GetCacheEntryValue(this->VariableName);
399     bool found = !cmIsNOTFOUND(*value);
400     bool cached = cacheEntry != nullptr;
401     auto cacheType = cached ? state->GetCacheEntryType(this->VariableName)
402                             : cmStateEnums::UNINITIALIZED;
403
404     if (cached && cacheType != cmStateEnums::UNINITIALIZED) {
405       this->VariableType = cacheType;
406       if (const auto& hs =
407             state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) {
408         this->VariableDocumentation = *hs;
409       }
410     }
411
412     if (found) {
413       // If the user specifies the entry on the command line without a
414       // type we should add the type and docstring but keep the
415       // original value.  Tell the subclass implementations to do
416       // this.
417       if (cached && cacheType == cmStateEnums::UNINITIALIZED) {
418         this->AlreadyInCacheWithoutMetaInfo = true;
419       }
420       return true;
421     }
422   }
423   return false;
424 }
425
426 void cmFindBase::NormalizeFindResult()
427 {
428   if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) ==
429       cmPolicies::NEW) {
430     // ensure the path returned by find_* command is absolute
431     const auto& existingValue =
432       this->Makefile->GetDefinition(this->VariableName);
433     std::string value;
434     if (!existingValue->empty()) {
435       value =
436         cmCMakePath(*existingValue, cmCMakePath::auto_format)
437           .Absolute(cmCMakePath(
438             this->Makefile->GetCMakeInstance()->GetCMakeWorkingDirectory()))
439           .Normal()
440           .GenericString();
441       // value = cmSystemTools::CollapseFullPath(*existingValue);
442       if (!cmSystemTools::FileExists(value, false)) {
443         value = *existingValue;
444       }
445     }
446
447     if (this->StoreResultInCache) {
448       // If the user specifies the entry on the command line without a
449       // type we should add the type and docstring but keep the original
450       // value.
451       if (value != *existingValue || this->AlreadyInCacheWithoutMetaInfo) {
452         this->Makefile->GetCMakeInstance()->AddCacheEntry(
453           this->VariableName, value, this->VariableDocumentation.c_str(),
454           this->VariableType);
455         if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
456             cmPolicies::NEW) {
457           if (this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
458             this->Makefile->AddDefinition(this->VariableName, value);
459           }
460         } else {
461           // if there was a definition then remove it
462           // This is required to ensure same behavior as
463           // cmMakefile::AddCacheDefinition.
464           this->Makefile->RemoveDefinition(this->VariableName);
465         }
466       }
467     } else {
468       // ensure a normal variable is defined.
469       this->Makefile->AddDefinition(this->VariableName, value);
470     }
471   } else {
472     // If the user specifies the entry on the command line without a
473     // type we should add the type and docstring but keep the original
474     // value.
475     if (this->StoreResultInCache) {
476       if (this->AlreadyInCacheWithoutMetaInfo) {
477         this->Makefile->AddCacheDefinition(this->VariableName, "",
478                                            this->VariableDocumentation.c_str(),
479                                            this->VariableType);
480         if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
481               cmPolicies::NEW &&
482             this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
483           this->Makefile->AddDefinition(
484             this->VariableName,
485             *this->Makefile->GetCMakeInstance()->GetCacheDefinition(
486               this->VariableName));
487         }
488       }
489     } else {
490       // ensure a normal variable is defined.
491       this->Makefile->AddDefinition(
492         this->VariableName,
493         this->Makefile->GetSafeDefinition(this->VariableName));
494     }
495   }
496 }
497
498 void cmFindBase::StoreFindResult(const std::string& value)
499 {
500   bool force =
501     this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) == cmPolicies::NEW;
502   bool updateNormalVariable =
503     this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == cmPolicies::NEW;
504
505   if (!value.empty()) {
506     if (this->StoreResultInCache) {
507       this->Makefile->AddCacheDefinition(this->VariableName, value,
508                                          this->VariableDocumentation.c_str(),
509                                          this->VariableType, force);
510       if (updateNormalVariable &&
511           this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
512         this->Makefile->AddDefinition(this->VariableName, value);
513       }
514     } else {
515       this->Makefile->AddDefinition(this->VariableName, value);
516     }
517
518     return;
519   }
520
521   auto notFound = cmStrCat(this->VariableName, "-NOTFOUND");
522   if (this->StoreResultInCache) {
523     this->Makefile->AddCacheDefinition(this->VariableName, notFound,
524                                        this->VariableDocumentation.c_str(),
525                                        this->VariableType, force);
526     if (updateNormalVariable &&
527         this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
528       this->Makefile->AddDefinition(this->VariableName, notFound);
529     }
530   } else {
531     this->Makefile->AddDefinition(this->VariableName, notFound);
532   }
533
534   if (this->Required) {
535     this->Makefile->IssueMessage(
536       MessageType::FATAL_ERROR,
537       cmStrCat("Could not find ", this->VariableName, " using the following ",
538                (this->FindCommandName == "find_file" ||
539                     this->FindCommandName == "find_path"
540                   ? "files"
541                   : "names"),
542                ": ", cmJoin(this->Names, ", ")));
543     cmSystemTools::SetFatalErrorOccurred();
544   }
545 }
546
547 cmFindBaseDebugState::cmFindBaseDebugState(std::string commandName,
548                                            cmFindBase const* findBase)
549   : FindCommand(findBase)
550   , CommandName(std::move(commandName))
551 {
552 }
553
554 cmFindBaseDebugState::~cmFindBaseDebugState()
555 {
556   if (this->FindCommand->DebugMode) {
557     std::string buffer =
558       cmStrCat(this->CommandName, " called with the following settings:\n");
559     buffer += cmStrCat("  VAR: ", this->FindCommand->VariableName, "\n");
560     buffer += cmStrCat(
561       "  NAMES: ", cmWrap("\"", this->FindCommand->Names, "\"", "\n         "),
562       "\n");
563     buffer += cmStrCat(
564       "  Documentation: ", this->FindCommand->VariableDocumentation, "\n");
565     buffer += "  Framework\n";
566     buffer += cmStrCat("    Only Search Frameworks: ",
567                        this->FindCommand->SearchFrameworkOnly, "\n");
568
569     buffer += cmStrCat("    Search Frameworks Last: ",
570                        this->FindCommand->SearchFrameworkLast, "\n");
571     buffer += cmStrCat("    Search Frameworks First: ",
572                        this->FindCommand->SearchFrameworkFirst, "\n");
573     buffer += "  AppBundle\n";
574     buffer += cmStrCat("    Only Search AppBundle: ",
575                        this->FindCommand->SearchAppBundleOnly, "\n");
576     buffer += cmStrCat("    Search AppBundle Last: ",
577                        this->FindCommand->SearchAppBundleLast, "\n");
578     buffer += cmStrCat("    Search AppBundle First: ",
579                        this->FindCommand->SearchAppBundleFirst, "\n");
580
581     if (this->FindCommand->NoDefaultPath) {
582       buffer += "  NO_DEFAULT_PATH Enabled\n";
583     } else {
584       buffer += cmStrCat(
585         "  CMAKE_FIND_USE_CMAKE_PATH: ", !this->FindCommand->NoCMakePath, "\n",
586         "  CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: ",
587         !this->FindCommand->NoCMakeEnvironmentPath, "\n",
588         "  CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: ",
589         !this->FindCommand->NoSystemEnvironmentPath, "\n",
590         "  CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: ",
591         !this->FindCommand->NoCMakeSystemPath, "\n",
592         "  CMAKE_FIND_USE_INSTALL_PREFIX: ",
593         !this->FindCommand->NoCMakeInstallPath, "\n");
594     }
595
596     buffer +=
597       cmStrCat(this->CommandName, " considered the following locations:\n");
598     for (auto const& state : this->FailedSearchLocations) {
599       std::string path = cmStrCat("  ", state.path);
600       if (!state.regexName.empty()) {
601         path = cmStrCat(path, "/", state.regexName);
602       }
603       buffer += cmStrCat(path, "\n");
604     }
605
606     if (!this->FoundSearchLocation.path.empty()) {
607       buffer += cmStrCat("The item was found at\n  ",
608                          this->FoundSearchLocation.path, "\n");
609     } else {
610       buffer += "The item was not found.\n";
611     }
612
613     this->FindCommand->DebugMessage(buffer);
614   }
615 }
616
617 void cmFindBaseDebugState::FoundAt(std::string const& path,
618                                    std::string regexName)
619 {
620   if (this->FindCommand->DebugMode) {
621     this->FoundSearchLocation = DebugLibState{ std::move(regexName), path };
622   }
623 }
624
625 void cmFindBaseDebugState::FailedAt(std::string const& path,
626                                     std::string regexName)
627 {
628   if (this->FindCommand->DebugMode) {
629     this->FailedSearchLocations.emplace_back(std::move(regexName), path);
630   }
631 }