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 209666113 : 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 209666113 : if (!registry_singleton)
30 54389 : registry_singleton = new Registry();
31 209666113 : return *registry_singleton;
32 : }
33 :
34 : void
35 133997 : Registry::registerObjectsTo(Factory & f, const std::set<std::string> & labels)
36 : {
37 133997 : auto & r = getRegistry();
38 :
39 267994 : for (const auto & label : labels)
40 : {
41 133997 : r._known_labels.insert(label);
42 133997 : if (r._per_label_objects.count(label) == 0)
43 10 : continue;
44 :
45 112381446 : for (const auto & obj : r._per_label_objects[label])
46 : {
47 112247459 : const auto name = obj->name();
48 112247459 : r._name_to_entry[name] = obj;
49 :
50 112247459 : f.reg(obj);
51 112247459 : if (!obj->_alias.empty())
52 4689035 : f.associateNameToClass(name, obj->_classname);
53 112247459 : }
54 : }
55 133997 : }
56 :
57 : const RegistryEntryBase &
58 0 : Registry::objData(const std::string & name)
59 : {
60 0 : auto & r = getRegistry();
61 :
62 0 : if (const auto it = r._name_to_entry.find(name); it != r._name_to_entry.end())
63 0 : return *it->second;
64 : else
65 0 : mooseError("Object ", name, " is not registered yet");
66 : }
67 :
68 : void
69 133451 : Registry::registerActionsTo(ActionFactory & f, const std::set<std::string> & labels)
70 : {
71 133451 : auto & r = getRegistry();
72 :
73 266902 : for (const auto & label : labels)
74 : {
75 133451 : r._known_labels.insert(label);
76 133451 : if (r._per_label_actions.count(label) == 0)
77 17 : continue;
78 :
79 15008879 : for (const auto & obj : r._per_label_actions[label])
80 14875445 : f.reg(obj);
81 : }
82 133451 : }
83 :
84 : char
85 67062 : Registry::addKnownLabel(const std::string & label)
86 : {
87 67062 : getRegistry()._known_labels.insert(label);
88 67062 : return 0;
89 : }
90 :
91 : void
92 133494 : 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 133494 : 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 133492 : if (is_app)
102 : {
103 133490 : const std::string folder = std::filesystem::path(in_tree_path).filename().c_str();
104 133490 : 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 133490 : }
113 :
114 : // Find either the installed or in-tree path
115 133490 : const auto path = determineDataFilePath(name, in_tree_path);
116 :
117 133490 : auto & dfp = getRegistry()._data_file_paths;
118 133490 : const auto it = dfp.find(name);
119 : // Not registered yet
120 133490 : if (it == dfp.end())
121 : {
122 108756 : dfp.emplace(name, path);
123 108756 : addDataFilePathCapability(name, path, info);
124 : }
125 : // Registered, but with a different value
126 24734 : 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 133490 : }
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 133416 : Registry::addAppDataFilePath(const std::string & app_name, const std::string & app_path)
145 : {
146 : // split the *App.C filename from its containing directory
147 133416 : 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 133416 : addDataFilePath(app_name, MooseUtils::pathjoin(dir, "../../data"));
151 133416 : }
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 66997 : Registry::addRepository(const std::string & repo_name, const std::string & repo_url)
177 : {
178 66997 : auto & repos = getRegistry()._repos;
179 66997 : const auto [it, inserted] = repos.emplace(repo_name, repo_url);
180 66997 : 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 66995 : }
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 : std::string
198 133494 : Registry::determineDataFilePath(const std::string & name, const std::string & in_tree_path)
199 : {
200 : // TODO: Track whether or not the application is installed in a better way
201 : // than this, which will enable us to pick one or the other based on
202 : // the install state. This probably also won't work with dynamic loading, where
203 : // we can't necessarily get this information from the binary (as there could be
204 : // multiple binary paths)
205 :
206 : // Installed data
207 : const auto installed_path =
208 133494 : MooseUtils::pathjoin(Moose::getExecutablePath(), "..", "share", name, "data");
209 133494 : if (MooseUtils::checkFileReadable(installed_path, false, false, false))
210 0 : return MooseUtils::canonicalPath(installed_path);
211 :
212 : // In tree data
213 133494 : if (MooseUtils::checkFileReadable(in_tree_path, false, false, false))
214 133492 : return MooseUtils::canonicalPath(in_tree_path);
215 :
216 2 : mooseError("Failed to determine data file path for '",
217 : name,
218 : "'. Paths searched:\n\n installed: ",
219 : installed_path,
220 : "\n in-tree: ",
221 : in_tree_path);
222 133494 : }
223 :
224 : std::string
225 6 : Registry::appNameFromAppPath(const std::string & app_path)
226 : {
227 : // This is for deprecated use only. It assumes that the application name
228 : // (binary name) in the build follows our normal naming of FooBarApp -> foo_bar.
229 : // We need to convert the application source file to the above, for example:
230 : // /path/to/FooBarBazApp.C -> foo_bar_baz
231 : // Ideally, we would instead have the user specify this manually so that
232 : // there is no ambiguity.
233 6 : std::smatch match;
234 6 : if (std::regex_search(app_path, match, std::regex("\\/([a-zA-Z0-9_]+)App\\.C$")))
235 : {
236 4 : std::string name = match[1]; // FooBarBaz
237 4 : name = std::regex_replace(name, std::regex("(?!^)([A-Z])"), "_$1"); // Foo_Bar_Baz
238 4 : name = MooseUtils::toLower(name); // foo_bar_baz
239 8 : return name;
240 4 : }
241 :
242 2 : mooseError(
243 : "Registry::appNameFromAppPath(): Failed to parse application name from '", app_path, "'");
244 6 : }
245 :
246 : void
247 133498 : Registry::checkDataFilePathName(const std::string & name)
248 : {
249 133498 : if (!std::regex_search(name, std::regex("[a-z0-9_]+")))
250 4 : mooseError("Unallowed characters in data file path name '",
251 : name,
252 : "'; allowed characters = a-z, 0-9, _");
253 133494 : }
254 :
255 : void
256 108758 : Registry::addDataFilePathCapability(const std::string & name,
257 : const std::optional<std::string> & path /* = {} */,
258 : const std::optional<std::string> & extra_info /* = {}*/)
259 : {
260 108758 : std::string doc = "Named data path '" + name + "' is ";
261 108758 : if (path)
262 108756 : doc += "available at '" + *path + "'";
263 : else
264 2 : doc += "not available";
265 108758 : if (extra_info)
266 4 : doc += "; " + *extra_info;
267 108758 : doc += ".";
268 :
269 108758 : auto & capabilities = Moose::internal::Capabilities::getCapabilities({});
270 108758 : capabilities.add("data_" + name, bool(path), doc);
271 108758 : }
|