diff --git a/docs/doxygen/layout-clang-uml.xml b/docs/doxygen/layout-clang-uml.xml index 1aa2a958..7dab1e87 100644 --- a/docs/doxygen/layout-clang-uml.xml +++ b/docs/doxygen/layout-clang-uml.xml @@ -6,41 +6,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index dfad2f57..90d1025a 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -143,6 +143,7 @@ std::optional class_::doxygen_link() const auto name = name_and_ns(); util::replace_all(name, "_", "__"); util::replace_all(name, "::", "_1_1"); + util::replace_all(name, "##", "_1_1"); // nested classes return fmt::format("{}{}.html", type, name); } } // namespace clanguml::class_diagram::model diff --git a/src/common/compilation_database.cc b/src/common/compilation_database.cc index 55972fad..0e24819e 100644 --- a/src/common/compilation_database.cc +++ b/src/common/compilation_database.cc @@ -17,7 +17,7 @@ */ #include "compilation_database.h" - +#include "util/error.h" #include "util/query_driver_output_extractor.h" namespace clanguml::common { @@ -31,7 +31,7 @@ compilation_database::auto_detect_from_directory( cfg.compilation_database_dir(), error_message); if (!error_message.empty()) - throw compilation_database_error(error_message); + throw error::compilation_database_error(error_message); return std::make_unique(std::move(res), cfg); } diff --git a/src/common/compilation_database.h b/src/common/compilation_database.h index ae7105f1..58b8c670 100644 --- a/src/common/compilation_database.h +++ b/src/common/compilation_database.h @@ -22,6 +22,7 @@ #include "common/model/template_parameter.h" #include "config/config.h" #include "types.h" +#include "util/error.h" #include "util/util.h" #include @@ -34,10 +35,6 @@ namespace clanguml::common { -class compilation_database_error : public std::runtime_error { - using std::runtime_error::runtime_error; -}; - /** * @brief Custom compilation database class * diff --git a/src/main.cc b/src/main.cc index 76095a22..b6c69d23 100644 --- a/src/main.cc +++ b/src/main.cc @@ -81,12 +81,12 @@ int main(int argc, const char *argv[]) cli.effective_output_directory, db, cli.verbose, cli.thread_count, cli.progress, cli.generators, translation_units_map); } - catch (common::compilation_database_error &e) { + catch (error::compilation_database_error &e) { LOG_ERROR("Failed to load compilation database from {} due to: {}", cli.config.compilation_database_dir(), e.what()); return 1; } - catch (util::query_driver_no_paths &e) { + catch (error::query_driver_no_paths &e) { LOG_ERROR("Quering provided compiler driver {} did not provide any " "paths, please make sure the path is correct and that your " "compiler is GCC-compatible: {}", diff --git a/src/util/error.h b/src/util/error.h index 434474aa..20f1ff9b 100644 --- a/src/util/error.h +++ b/src/util/error.h @@ -21,6 +21,10 @@ namespace clanguml::error { +class query_driver_no_paths : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + struct uml_alias_missing : public virtual std::runtime_error { uml_alias_missing(const std::string &message) : std::runtime_error(message) @@ -28,4 +32,8 @@ struct uml_alias_missing : public virtual std::runtime_error { } }; +class compilation_database_error : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + } // namespace clanguml::error diff --git a/src/util/query_driver_output_extractor.cc b/src/util/query_driver_output_extractor.cc index 9f4ffc4c..fd7f3765 100644 --- a/src/util/query_driver_output_extractor.cc +++ b/src/util/query_driver_output_extractor.cc @@ -18,6 +18,7 @@ #include "query_driver_output_extractor.h" +#include "error.h" #include "util.h" #include @@ -46,7 +47,7 @@ void query_driver_output_extractor::execute() extract_target(driver_output); if (system_include_paths_.empty()) { - throw query_driver_no_paths(fmt::format( + throw error::query_driver_no_paths(fmt::format( "Compiler driver {} did not report any system include paths " "in its output: {}", command_, driver_output)); diff --git a/src/util/query_driver_output_extractor.h b/src/util/query_driver_output_extractor.h index f2e2c26c..dd5377c5 100644 --- a/src/util/query_driver_output_extractor.h +++ b/src/util/query_driver_output_extractor.h @@ -23,26 +23,58 @@ namespace clanguml::util { -class query_driver_no_paths : public std::runtime_error { - using std::runtime_error::runtime_error; -}; - +/** + * @brief Executed compiler frontend and extract default system paths + * + * This class - inspired by the `clangd` language server - will invoke the + * provided compiler command and query it for its default system paths, + * which then will be added to each compile command in the database. + */ class query_driver_output_extractor { public: + /** + * @brief Constructor. + * + * @param command Command to execute the compiler frontend + * @param language Language name to query for (C or C++) + */ query_driver_output_extractor(std::string command, std::string language); ~query_driver_output_extractor() = default; + /** + * @brief Execute the command and extract compiler flags and include paths + */ void execute(); + /** + * @brief Extract target name from the compiler output + * + * @param output Compiler query driver output + */ void extract_target(const std::string &output); + /** + * @brief Extract system include paths from the compiler output + * + * @param output Compiler query driver output + */ void extract_system_include_paths(const std::string &output); - const std::vector &system_include_paths() const; - + /** + * @brief Name of the target of the compiler command (e.g. x86_64-linux-gnu) + * + * @return Target name + */ const std::string &target() const; + /** + * @brief Return list of include system paths + * + * @return List of include system paths + */ + const std::vector &system_include_paths() const; + private: const std::string command_; const std::string language_; diff --git a/src/util/thread_pool_executor.h b/src/util/thread_pool_executor.h index e8d3c12e..9e6820e9 100644 --- a/src/util/thread_pool_executor.h +++ b/src/util/thread_pool_executor.h @@ -23,8 +23,17 @@ #include namespace clanguml::util { + +/** + * @brief Simple thread pool executor for parallelizing diagram generation. + */ class thread_pool_executor { public: + /** + * @brief Constructor + * + * @param pool_size Number of threads in the pool + */ explicit thread_pool_executor(unsigned int pool_size); thread_pool_executor(const thread_pool_executor &) = delete; @@ -34,21 +43,31 @@ public: ~thread_pool_executor(); + /** + * @brief Add a task to run on the pool. + * + * @param task Function to execute + * @return Future, allowing awaiting the result + */ std::future add(std::function &&task); + /** + * @brief Join all active threads in the pool + */ void stop(); private: + /** + * @brief Main worker pool thread method - take task from queue and execute + */ void worker(); std::packaged_task get(); std::atomic_bool done_; - std::deque> tasks_; std::mutex tasks_mutex_; std::condition_variable tasks_cond_; - std::vector threads_; }; } // namespace clanguml::util \ No newline at end of file diff --git a/src/util/util.h b/src/util/util.h index 5b943058..f7e2040b 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -54,28 +54,101 @@ namespace clanguml::util { -std::string ltrim(const std::string &s); -std::string rtrim(const std::string &s); -std::string trim(const std::string &s); -std::string trim_typename(const std::string &s); - #define FILENAME_ \ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +/** + * @brief Left trim a string + * + * @param s Input string + * @return Left trimmed string + */ +std::string ltrim(const std::string &s); + +/** + * @brief Right trim a string + * + * @param s Input string + * @return Right trimmed string + */ +std::string rtrim(const std::string &s); + +/** + * @brief Trim a string + * + * @param s Input string + * @return Trimmed string + */ +std::string trim(const std::string &s); + +/** + * @brief Remove `typename` prefix from a string if exists + * @param s Input string + * @return String without `typename` prefix + */ +std::string trim_typename(const std::string &s); + +/** + * @brief Execute a shell `command` and return console output as string + * + * @param command Shell command to execute + * @return Console output of the command + */ std::string get_process_output(const std::string &command); +/** + * @brief Get value of an environment variable + * + * @param name Name of the environment variable + * @return Value of the environment variable, or empty if it doesn't exist + */ std::string get_env(const std::string &name); +/** + * @brief Check if `$PWD` is in a Git repository + * + * This can be overridden by exporting `CLANGUML_GIT_COMMIT` environment + * variable. + * + * @return True, if the current directory is in a Git repository + */ bool is_git_repository(); +/** + * @brief Get current Git branch + * + * @return Name of the current Git branch + */ std::string get_git_branch(); +/** + * @brief Get current Git revision + * + * Generates a Git revision tag using `git describe --tags --always` command + * + * @return Current repository Git revision + */ std::string get_git_revision(); +/** + * @brief Get current Git commit + * + * @return Latest Git commit hash + */ std::string get_git_commit(); +/** + * @brief Get path to the top level Git directory + * + * @return Absolut path to the nearest directory containing `.git` folder + */ std::string get_git_toplevel_dir(); +/** + * @brief Get descriptive name of the current operating system. + * + * @return Name of the operating system + */ std::string get_os_name(); /** @@ -94,14 +167,37 @@ std::string get_os_name(); std::vector split( std::string str, std::string_view delimiter, bool skip_empty = true); +/** + * @brief Remove and erase elements from a vector + * + * @tparam T Element type + * @tparam F Functor type + * @param v Vector to remove elements from + * @param f Functor to decide which elements to remove + */ template void erase_if(std::vector &v, F &&f) { v.erase(std::remove_if(v.begin(), v.end(), std::forward(f)), v.end()); } +/** + * @brief Join `toks` into string using `delimiter` as separator + * + * @param toks Elements to join into string + * @param delimiter Separator to use to join elements + * @return Concatenated elements into one string + */ std::string join( const std::vector &toks, std::string_view delimiter); +/** + * @brief Join `args` into string using `delimiter` as separator + * + * @tparam Args Element type + * @param delimiter Separator to use to join elements + * @param args Elements to join into string + * @return Arguments concatenated into one string + */ template std::string join(std::string_view delimiter, Args... args) { @@ -287,6 +383,12 @@ template void _if(const bool condition, F &&func) _if(condition, std::forward(func), []() {}); } +/** + * @brief Generate a hash seed. + * + * @param seed Initial seed. + * @return Hash seed. + */ std::size_t hash_seed(std::size_t seed); /** diff --git a/tests/test_compilation_database.cc b/tests/test_compilation_database.cc index b0a6c4f0..f3dd6e12 100644 --- a/tests/test_compilation_database.cc +++ b/tests/test_compilation_database.cc @@ -73,7 +73,7 @@ TEST_CASE("Test compilation_database should work", "[unit-test]") REQUIRE( !contains(ccs.at(0).CommandLine, "-Wno-deprecated-declarations")); } - catch (clanguml::common::compilation_database_error &e) { + catch (clanguml::error::compilation_database_error &e) { REQUIRE(false); } } @@ -81,8 +81,8 @@ TEST_CASE("Test compilation_database should work", "[unit-test]") TEST_CASE("Test compilation_database should throw", "[unit-test]") { using clanguml::common::compilation_database; - using clanguml::common::compilation_database_error; using clanguml::common::compilation_database_ptr; + using clanguml::error::compilation_database_error; using clanguml::util::contains; auto cfg = clanguml::config::load(