From ea20413107744161f716e181c5a9d3a7c676455f Mon Sep 17 00:00:00 2001 From: Aaron Fiore Date: Wed, 26 Nov 2025 17:34:25 -0800 Subject: [PATCH] container: root: common: utility: refactor/deprecate, add/enhance free functions - Factors out (and deprecates) `Command` - Deprecates certain free functions - Adds better error handling - Adds more free functions - Refactors file handling - Improves documentation Notes on file loading (and general file handlers): The interpreter command `.L` will load files but appears to not fail if the file isn't source code or a library. Calling `gSystem->Load()` will load a given library but not source file, and appears to not accept paths (which will be needed for pluggables). Because `gInterpreter->LoadFile("path")` will load either a source file or library by given "path", and is consistent with `dfi`'s approach to path handling, this function appears to be the winner (for now). --- container/src/root/common/utility.hh | 178 +++++++++++++++++++++------ 1 file changed, 140 insertions(+), 38 deletions(-) diff --git a/container/src/root/common/utility.hh b/container/src/root/common/utility.hh index f3a385c..1d50aa4 100644 --- a/container/src/root/common/utility.hh +++ b/container/src/root/common/utility.hh @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -86,8 +87,48 @@ inline void throw_ex_if( } } -//! \brief Wrapper to ROOT Cling commands +// TODO(afiore): add logger with loglevels + +//! \brief Process a line of code or interpreter command +//! \since docker-finance 1.1.0 +inline void line(const std::string& line) +{ + using EErrorCode = ::TInterpreter::EErrorCode; + auto ecode = std::make_unique(); + + // TODO(afiore): log levels + std::cout << "Interpreting: '" << line << "'" << std::endl; + gInterpreter->ProcessLine(line.c_str(), ecode.get()); + throw_ex_if(*ecode != EErrorCode::kNoError, line); +} + +//! \brief Load file with given path into interpreter +//! \details Can load a source file or library file +//! \param path Operating system path (absolute or relative) +//! \since docker-finance 1.1.0 +inline void load(const std::string& path) +{ + throw_ex_if( + !std::filesystem::is_regular_file(path), "invalid file: '" + path + "'"); + + // TODO(afiore): log levels + std::cout << "Loading: '" << path << "'" << std::endl; + gInterpreter->LoadFile(path.c_str()); +} + +//! \brief Load files with given paths into interpreter +//! \details Can load source files and/or library files +//! \param path Operating system paths (absolute or relative) +//! \since docker-finance 1.1.0 +inline void load(const std::initializer_list& paths) +{ + for (const auto& path : paths) + load(path); +} + +//! \brief Wrappers to commonly used interpreter abstractions //! \ingroup cpp_common_impl +//! \deprecated This will be removed in the v2 API; use the `dfi::common` free functions instead //! \since docker-finance 1.0.0 class Command { @@ -101,45 +142,106 @@ class Command Command(Command&&) = default; Command& operator=(Command&&) = default; - private: - static void cmd_handler(const std::initializer_list& command) - { - for (const auto& cmd : command) - { - std::cout << "Interpreting: '" << cmd << "'" << std::endl; - gInterpreter->ProcessLine(cmd.c_str()); - } - } - public: - //! \brief Load given file path - static void load(const std::string& path) + //! \deprecated This will be removed in the v2 API; use the `dfi::common` free function instead + //! \since docker-finance 1.0.0 + static void load(const std::string& path) { ::dfi::common::load(path); } + + //! \deprecated This will be removed in the v2 API; use the `dfi::common` free function instead + //! \since docker-finance 1.0.0 + static void load(const std::initializer_list& paths) { - std::filesystem::path p(path); - - throw_ex_if( - !std::filesystem::exists(p), "'" + path + "' does not exist!"); - - throw_ex_if( - !std::filesystem::is_regular_file(p), - "'" + path + "' is not a regular file!"); - - std::string cmd{".L " + path}; - Command::cmd_handler({cmd}); + ::dfi::common::load(paths); } - - //! \brief Load given file paths - static void load(const std::initializer_list& commands) - { - for (const auto& cmd : commands) - Command::load(cmd); - } - - // TODO(afiore): unload }; -//! \brief Get ROOT environment variable -//! \param var ROOT variable +//! \brief Unload file with given path out of interpreter +//! \details Can unload a source file or library file +//! \param path Operating system path (absolute or relative) +//! \since docker-finance 1.1.0 +inline void unload(const std::string& path) +{ + throw_ex_if( + !std::filesystem::is_regular_file(path), "invalid file: '" + path + "'"); + + // TODO(afiore): log levels + std::cout << "Unloading: '" << path << "'" << std::endl; + gInterpreter->UnloadFile(path.c_str()); +} + +//! \brief Unload files with given paths out of interpreter +//! \details Can unload source files and/or library files +//! \param path Operating system paths (absolute or relative) +//! \since docker-finance 1.1.0 +inline void unload(const std::initializer_list& paths) +{ + for (const auto& path : paths) + unload(path); +} + +//! \brief Reload file with given path out of/into interpreter +//! \details Can reload source files and/or library files +//! \param path Operating system paths (absolute or relative) +//! \since docker-finance 1.1.0 +inline void reload(const std::string& path) +{ + unload(path); + load(path); +} + +//! \brief Reload files with given paths out of/into interpreter +//! \details Can reload source files and/or library files +//! \param path Operating system paths (absolute or relative) +//! \since docker-finance 1.1.0 +inline void reload(const std::initializer_list& paths) +{ + unload(paths); + load(paths); +} + +//! \brief Add an include directory to interpreter +//! \param path Path to include (absolute or relative) +//! \warning Only pass the complete path to directory and not "-I" +//! \warning To pass multiple paths, call this function multiple times +//! \exception type::RuntimeError if path doesn't exist or cannot be included +//! \since docker-finance 1.1.0 +inline void add_include_dir(const std::string& path) +{ + throw_ex_if( + !std::filesystem::is_directory(path), + "invalid directory: '" + path + "'"); + + // TODO(afiore): log levels + std::cout << "Adding: '" << path << "'" << std::endl; + gInterpreter->AddIncludePath(path.c_str()); +} + +//! \brief Add a linked library into interpreter +//! \param path Path of library to add (absolute or relative) +//! \warning Only pass the complete path to library and not "-l" +//! \warning To pass multiple libraries, call this function multiple times +//! \exception type::RuntimeError if library doesn't exist or cannot be linked +//! \since docker-finance 1.1.0 +inline void add_linked_lib(const std::string& path) +{ + load(path); +} + +//! \brief Remove a linked library from interpreter +//! \param path Path of library to remove (absolute or relative) +//! \warning Only pass the complete path to library and not "-l" +//! \warning To pass multiple libraries, call this function multiple times +//! \exception type::RuntimeError if library doesn't exist or cannot be linked +//! \since docker-finance 1.1.0 +inline void remove_linked_lib(const std::string& path) +{ + unload(path); +} + +//! \brief Get underlying environment variable +//! \param var Environment variable +//! \return Value of given environment variable +//! \exception type::RuntimeError Throws if env var is not set or unavailable //! \note ROOT environment variables include shell (and shell caller) environment //! \since docker-finance 1.0.0 std::string get_env(const std::string& var) @@ -152,9 +254,9 @@ std::string get_env(const std::string& var) return std::string{env}; } -//! \brief Execute command in shell -//! \param cmd Shell command [args] -//! \returns Return value of command +//! \brief Execute a command in operating system shell +//! \param cmd Operating system shell command [args] +//! \return Return value of command //! \since docker-finance 1.0.0 int exec(const std::string& cmd) {