diff --git a/framework/doc/content/source/interfaces/DataFileInterface.md b/framework/doc/content/source/interfaces/DataFileInterface.md index c4e431e3c9da..874fd3f7d15d 100644 --- a/framework/doc/content/source/interfaces/DataFileInterface.md +++ b/framework/doc/content/source/interfaces/DataFileInterface.md @@ -19,8 +19,13 @@ If the provided path is absolute, no searching will take place and the absolute path will be used. Otherwise, `getDataFileName` will search (in this order) - relative to the input file -- relative to the running binary in the shared directory (assuming the application is installed) -- relative to all registered data file directories (which are determined by the source file locations when compiling and registered using the `registerDataFilePath` macro in `Registry.h`) +- relative to all installed and registered data file directories (for an installed application) +- relative to all in-tree registered data file directories (for an in-tree build) + +The "registered" data file directories are directories that are registered via: + +- the `registerAppDataFilePath` macro in `Registry.h`, where an applications data in its root `data` directory is registered +- the `registerDataFilePath` macro in `Registry.h`, where a general data directory is registered ## `getDataFileNameByName` diff --git a/framework/include/base/Registry.h b/framework/include/base/Registry.h index e8796fab3212..bd3a6f8d068e 100644 --- a/framework/include/base/Registry.h +++ b/framework/include/base/Registry.h @@ -76,7 +76,11 @@ #define registerADMooseObjectRenamed(app, orig_class, time, new_class) \ registerMooseObjectRenamed(app, orig_class, time, new_class) -#define registerDataFilePath() Registry::addDataFilePath(__FILE__) +/// register a data file path (folder name must be data) +#define registerDataFilePath(name, path) Registry::addDataFilePath(app, path) +/// register a data file path for the given application name. uses the current file to register +/// ../../data as a path +#define registerAppDataFilePath(app) Registry::addAppDataFilePath(app, __FILE__) #define registerRepository(repo_name, repo_url) Registry::addRepository(repo_name, repo_url); @@ -198,8 +202,11 @@ class Registry /// addKnownLabel whitelists a label as valid for purposes of the checkLabels function. static char addKnownLabel(const std::string & label); - /// register search paths for built-in data files - static void addDataFilePath(const std::string & path); + /// register general search paths (folder name must be data) + static void addDataFilePath(const std::string & name, const std::string & path); + /// register search paths for an application (path determined relative to app_path); + /// app_path should be passed as __FILE__ from the application source file + static void addAppDataFilePath(const std::string & app_name, const std::string & app_path); /// register a repository static void addRepository(const std::string & repo_name, const std::string & repo_url); @@ -226,11 +233,24 @@ class Registry } /// Returns a vector of all registered data file paths - static const std::vector & getDataFilePaths() + static const std::map & getDataFilePaths() { return getRegistry()._data_file_paths; } + /** + * Attempt to get a data path for the registered name. + * + * Searches the installed path and the in-tree path. + */ + static std::optional queryDataPath(const std::string & name); + /** + * Gets a data path for the registered name. + * + * Same as queryDataPath(), but errors if one is not found. + */ + static std::string getDataPath(const std::string & name); + /// Returns the repository URL associated with \p repo_name static const std::string & getRepositoryURL(const std::string & repo_name); @@ -253,7 +273,8 @@ class Registry std::map>> _per_label_objects; std::map>> _per_label_actions; std::set _known_labels; - std::vector _data_file_paths; + /// Data file registry; name -> in-tree path + std::map _data_file_paths; /// Repository name -> repository URL; used for mooseDocumentedError std::map _repos; std::map _type_to_classname; diff --git a/framework/src/base/Moose.C b/framework/src/base/Moose.C index 83eeff4ea832..0e3c2edf14e6 100644 --- a/framework/src/base/Moose.C +++ b/framework/src/base/Moose.C @@ -59,7 +59,7 @@ registerAll(Factory & f, ActionFactory & af, Syntax & s) registerObjects(f, {"MooseApp"}); associateSyntaxInner(s, af); registerActions(s, af, {"MooseApp"}); - registerDataFilePath(); + registerAppDataFilePath("MooseApp"); registerRepository("moose", "github.com/idaholab/moose"); } diff --git a/framework/src/base/Registry.C b/framework/src/base/Registry.C index e6acd84addd7..937526630f5d 100644 --- a/framework/src/base/Registry.C +++ b/framework/src/base/Registry.C @@ -16,6 +16,7 @@ #include "libmesh/libmesh_common.h" #include +#include Registry & Registry::getRegistry() @@ -86,21 +87,73 @@ Registry::addKnownLabel(const std::string & label) } void -Registry::addDataFilePath(const std::string & fullpath) +Registry::addDataFilePath(const std::string & name, const std::string & path) { + mooseAssert(std::filesystem::path(path).parent_path().filename() == "data", "Must end with data"); + + const std::string abs_path = std::filesystem::canonical(path).c_str(); + if (!MooseUtils::pathIsDirectory(abs_path)) + mooseError("While registering data file path '", + abs_path, + "' for '", + name, + "': the path was not found"); + auto & dfp = getRegistry()._data_file_paths; + const auto it = dfp.find(name); + // Not registered yet + if (it == dfp.end()) + dfp.emplace(name, abs_path); + // Registered, but with a different value + else if (it->second != abs_path) + mooseError("While registering data file path '", + abs_path, + "' for ", + name, + ", the path '", + it->second, + "' is already registered"); +} +void +Registry::addAppDataFilePath(const std::string & app_name, const std::string & app_path) +{ // split the *App.C filename from its containing directory - const auto path = MooseUtils::splitFileName(fullpath).first; - + const auto dir = MooseUtils::splitFileName(app_path).first; // This works for both build/unity_src/ and src/base/ as the *App.C file location, // in case __FILE__ doesn't get overriden in unity build - const auto data_dir = MooseUtils::pathjoin(path, "../../data"); + addDataFilePath(app_name, MooseUtils::pathjoin(dir, "../../data")); +} + +std::optional +Registry::queryDataPath(const std::string & name) +{ + const auto & dfps = getRegistry()._data_file_paths; + const auto it = dfps.find(name); + if (it == dfps.end()) + mooseError("Registry::getDataPath(): A data path for '", name, "' is not registered"); + + // Installed data + const auto installed_path = + MooseUtils::pathjoin(Moose::getExecutablePath(), "..", "share", name, "data"); + if (MooseUtils::checkFileReadable(installed_path, false, false, false)) + return std::filesystem::canonical(installed_path).c_str(); + + // In tree data + if (MooseUtils::checkFileReadable(it->second, false, false, false)) + return std::filesystem::canonical(it->second).c_str(); + + // No success + return {}; +} - // if the data directory exists and hasn't been added before, add it - if (MooseUtils::pathIsDirectory(data_dir) && - std::find(dfp.begin(), dfp.end(), data_dir) == dfp.end()) - dfp.push_back(data_dir); +std::string +Registry::getDataPath(const std::string & name) +{ + const auto path = queryDataPath(name); + if (!path) + mooseError("Failed to determine data path for '", name, "'"); + return *path; } void diff --git a/framework/src/interfaces/DataFileInterface.C b/framework/src/interfaces/DataFileInterface.C index f192d77cac8e..0e808c71f824 100644 --- a/framework/src/interfaces/DataFileInterface.C +++ b/framework/src/interfaces/DataFileInterface.C @@ -48,40 +48,26 @@ std::string DataFileInterface::getDataFileNameByName(const std::string & relative_path, const std::string * param) const { - /// - relative to the running binary (assuming the application is installed) - const auto share_dir = MooseUtils::pathjoin(Moose::getExecutablePath(), "..", "share"); - if (MooseUtils::pathIsDirectory(share_dir)) + // Search each registered data path for the relative path + for (const auto & name_dir_path : Registry::getRegistry().getDataFilePaths()) { - const auto dirs = MooseUtils::listDir(share_dir, false); - for (const auto & data_dir : dirs) + const auto & name = name_dir_path.first; + const auto data_path = Registry::getRegistry().queryDataPath(name); + if (data_path) { - const auto path = MooseUtils::pathjoin(data_dir, "data", relative_path); + const auto path = MooseUtils::pathjoin(*data_path, relative_path); if (MooseUtils::checkFileReadable(path, false, false, false)) { + const std::string abs_path = std::filesystem::canonical(path).c_str(); if (param) - _parent.paramInfo( - *param, "Data file '", path, "' found in an installed app distribution."); + _parent.paramInfo(*param, "Data file '", abs_path, "' found from ", name); else - mooseInfo("Data file '", path, "' found in an installed app distribution."); - return path; + mooseInfo("Data file '", abs_path, "' found from ", name); + return abs_path; } } } - /// - relative to all registered data file directories - for (const auto & data_dir : Registry::getRegistry().getDataFilePaths()) - { - const auto path = MooseUtils::pathjoin(data_dir, relative_path); - if (MooseUtils::checkFileReadable(path, false, false, false)) - { - if (param) - _parent.paramInfo(*param, "Data file '", path, "' found in a source repository."); - else - mooseInfo("Data file '", path, "' found in a source repository."); - return path; - } - } - mooseException(param ? _parent.parameters().inputLocation(*param) : _parent.name(), ": Unable to find data file '", relative_path, diff --git a/modules/solid_mechanics/src/base/SolidMechanicsApp.C b/modules/solid_mechanics/src/base/SolidMechanicsApp.C index 3681d801920e..eff11b87669c 100644 --- a/modules/solid_mechanics/src/base/SolidMechanicsApp.C +++ b/modules/solid_mechanics/src/base/SolidMechanicsApp.C @@ -191,7 +191,7 @@ SolidMechanicsApp::registerAll(Factory & f, ActionFactory & af, Syntax & s) Registry::registerObjectsTo(f, {"SolidMechanicsApp"}); Registry::registerActionsTo(af, {"SolidMechanicsApp"}); associateSyntaxInner(s, af); - registerDataFilePath(); + registerAppDataFilePath("SolidMechanicsApp"); } void