Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "InputParameters.h"
11 : #include "Registry.h"
12 : #include "Factory.h"
13 : #include "ActionFactory.h"
14 : #include "MooseUtils.h"
15 : #include "Capabilities.h"
16 :
17 : #include "libmesh/libmesh_common.h"
18 :
19 : #include <memory>
20 : #include <filesystem>
21 : #include <regex>
22 :
23 : Registry &
24 210897677 : Registry::getRegistry()
25 : {
26 : // We need a naked new here (_not_ a smart pointer or object instance) due to what seems like a
27 : // bug in clang's static object destruction when using dynamic library loading.
28 : static Registry * registry_singleton = nullptr;
29 210897677 : if (!registry_singleton)
30 54624 : registry_singleton = new Registry();
31 210897677 : return *registry_singleton;
32 : }
33 :
34 : void
35 134739 : Registry::registerObjectsTo(Factory & f, const std::set<std::string> & labels)
36 : {
37 134739 : auto & r = getRegistry();
38 :
39 269478 : for (const auto & label : labels)
40 : {
41 134739 : r._known_labels.insert(label);
42 134739 : if (r._per_label_objects.count(label) == 0)
43 10 : continue;
44 :
45 113161005 : for (const auto & obj : r._per_label_objects[label])
46 : {
47 113026276 : const auto name = obj->name();
48 113026276 : r._name_to_entry[name] = obj;
49 :
50 113026276 : f.reg(obj);
51 113026276 : if (!obj->_alias.empty())
52 4715007 : f.associateNameToClass(name, obj->_classname);
53 113026276 : }
54 : }
55 134739 : }
56 :
57 : const RegistryEntryBase &
58 234 : Registry::objData(const std::string & name)
59 : {
60 234 : auto & r = getRegistry();
61 :
62 234 : if (const auto it = r._name_to_entry.find(name); it != r._name_to_entry.end())
63 468 : return *it->second;
64 : else
65 0 : mooseError("Object ", name, " is not registered yet");
66 : }
67 :
68 : void
69 134181 : Registry::registerActionsTo(ActionFactory & f, const std::set<std::string> & labels)
70 : {
71 134181 : auto & r = getRegistry();
72 :
73 268362 : for (const auto & label : labels)
74 : {
75 134181 : r._known_labels.insert(label);
76 134181 : if (r._per_label_actions.count(label) == 0)
77 15 : continue;
78 :
79 15092051 : for (const auto & obj : r._per_label_actions[label])
80 14957885 : f.reg(obj);
81 : }
82 134181 : }
83 :
84 : char
85 67414 : Registry::addKnownLabel(const std::string & label)
86 : {
87 67414 : getRegistry()._known_labels.insert(label);
88 67414 : return 0;
89 : }
90 :
91 : void
92 134232 : Registry::addDataFilePath(const std::string & name,
93 : const std::string & in_tree_path,
94 : const bool is_app /* = true */,
95 : const std::optional<std::string> & info /* = {} */)
96 : {
97 134232 : checkDataFilePathName(name);
98 :
99 : // Enforce that the folder is called "data", because we rely on the installed path
100 : // to be within PREFIX/share/<name>/data (see determineDataFilePath())
101 134230 : if (is_app)
102 : {
103 134228 : const std::string folder = std::filesystem::path(in_tree_path).filename().c_str();
104 134228 : if (folder != "data")
105 2 : mooseError("While registering data file path '",
106 : in_tree_path,
107 : "' for '",
108 : name,
109 : "': The folder must be named 'data' and it is named '",
110 : folder,
111 : "'");
112 134228 : }
113 :
114 : // Find either the installed or in-tree path
115 134228 : const auto path = determineDataFilePath(name, in_tree_path);
116 :
117 134228 : auto & dfp = getRegistry()._data_file_paths;
118 134228 : const auto it = dfp.find(name);
119 : // Not registered yet
120 134228 : if (it == dfp.end())
121 : {
122 109232 : dfp.emplace(name, path);
123 109232 : addDataFilePathCapability(name, path, info);
124 : }
125 : // Registered, but with a different value
126 24996 : else if (it->second != path)
127 2 : mooseError("While registering data file path '",
128 : path,
129 : "' for '",
130 : name,
131 : "': the path '",
132 2 : it->second,
133 : "' is already registered");
134 134228 : }
135 :
136 : void
137 4 : Registry::addMissingDataFilePath(const std::string & name, const std::string & info)
138 : {
139 4 : checkDataFilePathName(name);
140 2 : addDataFilePathCapability(name, {}, info);
141 2 : }
142 :
143 : void
144 134154 : Registry::addAppDataFilePath(const std::string & app_name, const std::string & app_path)
145 : {
146 : // split the *App.C filename from its containing directory
147 134154 : const auto dir = MooseUtils::splitFileName(app_path).first;
148 : // This works for both build/unity_src/ and src/base/ as the *App.C file location,
149 : // in case __FILE__ doesn't get overriden in unity build
150 134154 : addDataFilePath(app_name, MooseUtils::pathjoin(dir, "../../data"));
151 134154 : }
152 :
153 : void
154 2 : Registry::addDeprecatedAppDataFilePath(const std::string & app_path)
155 : {
156 2 : const auto app_name = appNameFromAppPath(app_path);
157 2 : mooseDeprecated("In ",
158 : app_path,
159 : ":\nregisterDataFilePath() is deprecated. Use registerAppDataFilePath(\"",
160 : app_name,
161 : "\") instead.");
162 2 : addAppDataFilePath(app_name, app_path);
163 2 : }
164 :
165 : std::string
166 8 : Registry::getDataFilePath(const std::string & name)
167 : {
168 8 : const auto & dfps = getRegistry()._data_file_paths;
169 8 : const auto it = dfps.find(name);
170 8 : if (it == dfps.end())
171 2 : mooseError("Registry::getDataFilePath(): A data file path for '", name, "' is not registered");
172 12 : return it->second;
173 : }
174 :
175 : void
176 67369 : Registry::addRepository(const std::string & repo_name, const std::string & repo_url)
177 : {
178 67369 : auto & repos = getRegistry()._repos;
179 67369 : const auto [it, inserted] = repos.emplace(repo_name, repo_url);
180 67369 : if (!inserted && it->second != repo_url)
181 2 : mooseError("Registry::registerRepository(): The repository '",
182 : repo_name,
183 : "' is already registered with a different URL '",
184 2 : it->second,
185 : "'.");
186 67367 : }
187 :
188 : const std::string &
189 11 : Registry::getRepositoryURL(const std::string & repo_name)
190 : {
191 11 : const auto & repos = getRegistry()._repos;
192 11 : if (const auto it = repos.find(repo_name); it != repos.end())
193 9 : return it->second;
194 2 : mooseError("Registry::getRepositoryURL(): The repository '", repo_name, "' is not registered.");
195 : }
196 :
197 : void
198 67379 : Registry::addAppCitation(const std::string & app_name,
199 : const std::string & key,
200 : const std::string & bibtex)
201 : {
202 67379 : auto & app_citations = getRegistry()._app_citations[app_name];
203 67379 : const auto [it, inserted] = app_citations.emplace(key, bibtex);
204 67379 : if (!inserted && it->second != bibtex)
205 2 : mooseError(
206 : "Registry: the citation '", key, "' is already registered with different BibTeX text.");
207 67377 : }
208 :
209 : const std::map<std::string, std::string> &
210 242 : Registry::getCitations(const std::string & app_name)
211 : {
212 242 : static const std::map<std::string, std::string> empty;
213 242 : const auto & app_citations = getRegistry()._app_citations;
214 242 : const auto it = app_citations.find(app_name);
215 242 : return it != app_citations.end() ? it->second : empty;
216 : }
217 :
218 : std::string
219 134232 : Registry::determineDataFilePath(const std::string & name, const std::string & in_tree_path)
220 : {
221 : // TODO: Track whether or not the application is installed in a better way
222 : // than this, which will enable us to pick one or the other based on
223 : // the install state. This probably also won't work with dynamic loading, where
224 : // we can't necessarily get this information from the binary (as there could be
225 : // multiple binary paths)
226 :
227 : // Installed data
228 : const auto installed_path =
229 134232 : MooseUtils::pathjoin(Moose::getExecutablePath(), "..", "share", name, "data");
230 134232 : if (MooseUtils::checkFileReadable(installed_path, false, false, false))
231 0 : return MooseUtils::canonicalPath(installed_path);
232 :
233 : // In tree data
234 134232 : if (MooseUtils::checkFileReadable(in_tree_path, false, false, false))
235 134230 : return MooseUtils::canonicalPath(in_tree_path);
236 :
237 2 : mooseError("Failed to determine data file path for '",
238 : name,
239 : "'. Paths searched:\n\n installed: ",
240 : installed_path,
241 : "\n in-tree: ",
242 : in_tree_path);
243 134232 : }
244 :
245 : std::string
246 6 : Registry::appNameFromAppPath(const std::string & app_path)
247 : {
248 : // This is for deprecated use only. It assumes that the application name
249 : // (binary name) in the build follows our normal naming of FooBarApp -> foo_bar.
250 : // We need to convert the application source file to the above, for example:
251 : // /path/to/FooBarBazApp.C -> foo_bar_baz
252 : // Ideally, we would instead have the user specify this manually so that
253 : // there is no ambiguity.
254 6 : std::smatch match;
255 6 : if (std::regex_search(app_path, match, std::regex("\\/([a-zA-Z0-9_]+)App\\.C$")))
256 : {
257 4 : std::string name = match[1]; // FooBarBaz
258 4 : name = std::regex_replace(name, std::regex("(?!^)([A-Z])"), "_$1"); // Foo_Bar_Baz
259 4 : name = MooseUtils::toLower(name); // foo_bar_baz
260 8 : return name;
261 4 : }
262 :
263 2 : mooseError(
264 : "Registry::appNameFromAppPath(): Failed to parse application name from '", app_path, "'");
265 6 : }
266 :
267 : void
268 134236 : Registry::checkDataFilePathName(const std::string & name)
269 : {
270 134236 : if (!std::regex_search(name, std::regex("[a-z0-9_]+")))
271 4 : mooseError("Unallowed characters in data file path name '",
272 : name,
273 : "'; allowed characters = a-z, 0-9, _");
274 134232 : }
275 :
276 : void
277 109234 : Registry::addDataFilePathCapability(const std::string & name,
278 : const std::optional<std::string> & path /* = {} */,
279 : const std::optional<std::string> & extra_info /* = {}*/)
280 : {
281 109234 : std::string doc = "Named data path '" + name + "' is ";
282 109234 : if (path)
283 109232 : doc += "available at '" + *path + "'";
284 : else
285 2 : doc += "not available";
286 109234 : if (extra_info)
287 4 : doc += "; " + *extra_info;
288 109234 : doc += ".";
289 :
290 109234 : auto & capabilities = Moose::internal::Capabilities::getCapabilities({});
291 109234 : capabilities.add("data_" + name, bool(path), doc);
292 109234 : }
|