From 13791767624a8732a9a64f694baa9700555f2170 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 17 Mar 2022 22:40:36 +0100 Subject: [PATCH 1/5] Added simple thread pool executor class --- src/util/thread_pool_executor.cc | 23 ++++++ src/util/thread_pool_executor.h | 109 +++++++++++++++++++++++++++++ tests/CMakeLists.txt | 17 +++++ tests/test_thread_pool_executor.cc | 45 ++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 src/util/thread_pool_executor.cc create mode 100644 src/util/thread_pool_executor.h create mode 100644 tests/test_thread_pool_executor.cc diff --git a/src/util/thread_pool_executor.cc b/src/util/thread_pool_executor.cc new file mode 100644 index 00000000..b496d2e9 --- /dev/null +++ b/src/util/thread_pool_executor.cc @@ -0,0 +1,23 @@ +/** +* src/util/thread_pool_executor.cc +* +* Copyright (c) 2021-2022 Bartek Kryza +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "thread_pool_executor.h" + +namespace clanguml::util { + +} diff --git a/src/util/thread_pool_executor.h b/src/util/thread_pool_executor.h new file mode 100644 index 00000000..4a570c72 --- /dev/null +++ b/src/util/thread_pool_executor.h @@ -0,0 +1,109 @@ +/** + * src/util/thread_pool_executor.h + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include +#include + +namespace clanguml::util { +class thread_pool_executor { +public: + thread_pool_executor() + : thread_pool_executor{0} + { + } + + thread_pool_executor(unsigned int pool_size) + : done_{false} + { + if (pool_size == 0U) + pool_size = std::thread::hardware_concurrency(); + + for (auto i = 0U; i < pool_size; i++) { + threads_.push_back( + std::thread{&thread_pool_executor::worker, this}); + } + } + + ~thread_pool_executor() { stop(); } + + std::future add(std::function &&task) + { + std::unique_lock l(tasks_mutex_); + + std::packaged_task ptask{std::move(task)}; + auto res = ptask.get_future(); + + tasks_.emplace_back(std::move(ptask)); + + l.unlock(); + tasks_cond_.notify_one(); + + return res; + } + + void stop() + { + done_ = true; + for (auto &thread : threads_) { + if (thread.joinable()) + thread.join(); + } + } + +private: + void worker() + { + try { + while (!done_) { + auto task = get(); + + task(); + } + } + catch (std::runtime_error &e) { + } + } + + std::packaged_task get() + { + std::unique_lock l(tasks_mutex_); + + while (tasks_.empty()) { + if (done_) + throw std::runtime_error("Thread pool closing..."); + + tasks_cond_.wait_for(l, std::chrono::seconds(1)); + } + + auto res = std::move(tasks_.front()); + tasks_.pop_front(); + return res; + } + + std::atomic_bool done_; + + std::deque> tasks_; + std::mutex tasks_mutex_; + std::condition_variable tasks_cond_; + + std::vector threads_; +}; +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b825fc09..2c4f6eed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -48,6 +48,13 @@ set(CLANG_UML_TEST_CONFIG_HEADER catch.h ) +set(CLANG_UML_TEST_THREAD_POOL_EXECUTOR_SRC + test_thread_pool_executor.cc + ) +set(CLANG_UML_TEST_THREAD_POOL_EXECUTOR_HEADER + catch.h + ) + add_executable(test_util ${CLANG_UML_TEST_UTIL_SRC} ${CLANG_UML_TEST_UTIL_HEADER}) @@ -88,6 +95,16 @@ target_link_libraries(test_config ${YAML_CPP_LIBRARIES} spdlog::spdlog clang-umllib cppast) +add_executable(test_thread_pool_executor + ${CLANG_UML_TEST_THREAD_POOL_EXECUTOR_SRC} + ${CLANG_UML_TEST_THREAD_POOL_EXECUTOR_HEADER}) + +target_link_libraries(test_thread_pool_executor + PRIVATE + ${LIBCLANG_LIBRARIES} + ${YAML_CPP_LIBRARIES} + spdlog::spdlog clang-umllib cppast) + add_executable(test_cases ${CLANG_UML_TEST_CASES_SRC} ${CLANG_UML_TEST_CASES_HEADER}) diff --git a/tests/test_thread_pool_executor.cc b/tests/test_thread_pool_executor.cc new file mode 100644 index 00000000..e4a209e3 --- /dev/null +++ b/tests/test_thread_pool_executor.cc @@ -0,0 +1,45 @@ +/** + * tests/test_thread_pool_executor.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define CATCH_CONFIG_MAIN + +#include "catch.h" + +#include "util/thread_pool_executor.h" + +TEST_CASE("Test thread_pool_executor", "[unit-test]") +{ + using clanguml::util::thread_pool_executor; + + thread_pool_executor pool{4}; + + std::atomic_int counter{0}; + + std::vector> futs; + + const unsigned int kTaskCount = 1000; + + for (auto i = 0U; i < kTaskCount; i++) { + futs.emplace_back(pool.add([&counter]() { counter++; })); + } + + for (auto &f : futs) { + f.get(); + } + + CHECK(counter == kTaskCount); +} From c17a1cb1aa1c617ad93ec7d93ac689c130bd60c9 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 18 Mar 2022 22:51:58 +0100 Subject: [PATCH 2/5] Updated cppast ref --- thirdparty/cppast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thirdparty/cppast b/thirdparty/cppast index 7c460ccc..87beb6e2 160000 --- a/thirdparty/cppast +++ b/thirdparty/cppast @@ -1 +1 @@ -Subproject commit 7c460cccc32d7ee692b35380316bd80b0e000ffa +Subproject commit 87beb6e2b877db0254013c65df03c993b6c1427c From 82737df05ce0a47255ba54a0eb5fa5c2c7065edd Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 18 Mar 2022 22:52:23 +0100 Subject: [PATCH 3/5] Refactored thread_pool_executor methods --- src/util/thread_pool_executor.cc | 102 ++++++++++++++++++++++++++----- src/util/thread_pool_executor.h | 72 +++------------------- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/util/thread_pool_executor.cc b/src/util/thread_pool_executor.cc index b496d2e9..231c3921 100644 --- a/src/util/thread_pool_executor.cc +++ b/src/util/thread_pool_executor.cc @@ -1,23 +1,93 @@ /** -* src/util/thread_pool_executor.cc -* -* Copyright (c) 2021-2022 Bartek Kryza -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * src/util/thread_pool_executor.cc + * + * Copyright (c) 2021-2022 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ #include "thread_pool_executor.h" namespace clanguml::util { +thread_pool_executor::thread_pool_executor() + : thread_pool_executor{0} +{ +} + +thread_pool_executor::thread_pool_executor(unsigned int pool_size) + : done_{false} +{ + if (pool_size == 0U) + pool_size = std::thread::hardware_concurrency(); + + for (auto i = 0U; i < pool_size; i++) { + threads_.push_back(std::thread{&thread_pool_executor::worker, this}); + } +} + +~thread_pool_executor::thread_pool_executor() { stop(); } + +std::future thread_pool_executor::add(std::function &&task) +{ + std::unique_lock l(tasks_mutex_); + + std::packaged_task ptask{std::move(task)}; + auto res = ptask.get_future(); + + tasks_.emplace_back(std::move(ptask)); + + l.unlock(); + tasks_cond_.notify_one(); + + return res; +} + +void thread_pool_executor::stop() +{ + done_ = true; + for (auto &thread : threads_) { + if (thread.joinable()) + thread.join(); + } +} + +void thread_pool_executor::worker() +{ + try { + while (!done_) { + auto task = get(); + + task(); + } + } + catch (std::runtime_error &e) { + } +} + +std::packaged_task thread_pool_executor::get() +{ + std::unique_lock l(tasks_mutex_); + + while (tasks_.empty()) { + if (done_) + throw std::runtime_error("Thread pool closing..."); + + tasks_cond_.wait_for(l, std::chrono::seconds(1)); + } + + auto res = std::move(tasks_.front()); + tasks_.pop_front(); + return res; +} } diff --git a/src/util/thread_pool_executor.h b/src/util/thread_pool_executor.h index 4a570c72..52e19fdf 100644 --- a/src/util/thread_pool_executor.h +++ b/src/util/thread_pool_executor.h @@ -25,78 +25,20 @@ namespace clanguml::util { class thread_pool_executor { public: - thread_pool_executor() - : thread_pool_executor{0} - { - } + thread_pool_executor(); - thread_pool_executor(unsigned int pool_size) - : done_{false} - { - if (pool_size == 0U) - pool_size = std::thread::hardware_concurrency(); + thread_pool_executor(unsigned int pool_size); - for (auto i = 0U; i < pool_size; i++) { - threads_.push_back( - std::thread{&thread_pool_executor::worker, this}); - } - } + ~thread_pool_executor(); - ~thread_pool_executor() { stop(); } + std::future add(std::function &&task); - std::future add(std::function &&task) - { - std::unique_lock l(tasks_mutex_); - - std::packaged_task ptask{std::move(task)}; - auto res = ptask.get_future(); - - tasks_.emplace_back(std::move(ptask)); - - l.unlock(); - tasks_cond_.notify_one(); - - return res; - } - - void stop() - { - done_ = true; - for (auto &thread : threads_) { - if (thread.joinable()) - thread.join(); - } - } + void stop(); private: - void worker() - { - try { - while (!done_) { - auto task = get(); + void worker(); - task(); - } - } - catch (std::runtime_error &e) { - } - } - - std::packaged_task get() - { - std::unique_lock l(tasks_mutex_); - - while (tasks_.empty()) { - if (done_) - throw std::runtime_error("Thread pool closing..."); - - tasks_cond_.wait_for(l, std::chrono::seconds(1)); - } - - auto res = std::move(tasks_.front()); - tasks_.pop_front(); - return res; - } + std::packaged_task get(); std::atomic_bool done_; From c49969495fb8e749180317b2f1b60563429ef721 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 18 Mar 2022 22:55:15 +0100 Subject: [PATCH 4/5] Added thread pool to parallelize diagram generation --- .../plantuml/class_diagram_generator.cc | 12 +- .../visitor/translation_unit_visitor.cc | 15 +- src/common/generators/plantuml/generator.h | 12 +- src/common/model/element.cc | 6 +- src/common/model/nested_trait.h | 3 +- src/config/config.cc | 9 +- src/config/config.h | 4 - src/main.cc | 147 ++++++++++-------- .../plantuml/package_diagram_generator.cc | 4 +- src/util/thread_pool_executor.cc | 2 +- src/util/util.cc | 40 ++--- src/util/util.h | 43 ++--- tests/CMakeLists.txt | 2 + tests/test_cases.cc | 5 +- 14 files changed, 143 insertions(+), 161 deletions(-) diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index dccfadd9..3d5e3ef1 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -179,8 +179,8 @@ void generator::generate( } } catch (error::uml_alias_missing &e) { - LOG_ERROR("=== Skipping {} relation from {} to {} due " - "to: {}", + LOG_DBG("=== Skipping {} relation from {} to {} due " + "to: {}", plantuml_common::to_plantuml(r.type(), r.style()), c.full_name(), destination, e.what()); } @@ -215,8 +215,8 @@ void generator::generate( all_relations_str << relstr.str(); } catch (error::uml_alias_missing &e) { - LOG_ERROR("=== Skipping inheritance relation from {} to {} due " - "to: {}", + LOG_DBG("=== Skipping inheritance relation from {} to {} due " + "to: {}", b.name(), c.name(), e.what()); } } @@ -271,8 +271,8 @@ void generator::generate( relationships_ostr << relstr.str(); } catch (error::uml_alias_missing &ex) { - LOG_ERROR("Skipping {} relation from {} to {} due " - "to: {}", + LOG_DBG("Skipping {} relation from {} to {} due " + "to: {}", clanguml::common::generators::plantuml::to_plantuml( r.type(), r.style()), e.full_name(), destination, ex.what()); diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index a326d07e..a36e6288 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -200,7 +200,7 @@ void translation_unit_visitor::process_type_alias_template( { if (at.type_alias().underlying_type().kind() == cppast::cpp_type_kind::unexposed_t) { - LOG_WARN("Template alias has unexposed underlying type: {}", + LOG_DBG("Template alias has unexposed underlying type: {}", static_cast( at.type_alias().underlying_type()) .name()); @@ -407,8 +407,8 @@ bool translation_unit_visitor::process_template_parameters( process_scope_template_parameters(c, scope); } else { - LOG_WARN("Class {} is templated but it's scope {} is not - " - "probably this is a specialization", + LOG_DBG("Class {} is templated but it's scope {} is not - " + "probably this is a specialization", cls.name(), scope.name()); // Add specialization arguments @@ -541,7 +541,7 @@ void translation_unit_visitor:: {relationship_t::kInstantiation, base_template_full_name}); } else { - LOG_WARN( + LOG_DBG( "No user data for base template {}", primary_template_ref.name()); } } @@ -1321,8 +1321,7 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( found = find_relationships(args[0u].type().value(), relationships, relationship_t::kAggregation); else - LOG_WARN( - "Failed to process template argument of std::vector at: {}", + LOG_DBG("Failed to process template argument of std::vector at: {}", fn); } else if (ctx.config().should_include(ns, name)) { @@ -1470,7 +1469,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( t, tinst, template_base_params, parent, full_template_name); } else { - LOG_WARN("Template instantiation {} has no primary template", + LOG_DBG("Template instantiation {} has no primary template", cppast::to_string(t)); full_template_name = cppast::to_string(t); @@ -1769,7 +1768,7 @@ void translation_unit_visitor::build_template_instantiation_primary_template( LOG_DBG("Primary template ref set to: {}", tinst.base_template()); } else - LOG_WARN( + LOG_DBG( "No user data for base template {}", primary_template_ref.name()); } diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 6169c183..3d6f3309 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -90,8 +90,8 @@ void generator::generate_config_layout_hints(std::ostream &ostr) const ostr << hint_str.str(); } catch (clanguml::error::uml_alias_missing &e) { - LOG_ERROR("=== Skipping layout hint from {} to {} due " - "to: {}", + LOG_DBG("=== Skipping layout hint from {} to {} due " + "to: {}", entity, hint.entity, e.what()); } } @@ -133,8 +133,8 @@ void generator::generate_notes( template -DiagramModel generate(cppast::libclang_compilation_database &db, - const std::string &name, DiagramConfig &diagram) +DiagramModel generate(const cppast::libclang_compilation_database &db, + const std::string &name, DiagramConfig &diagram, bool verbose = false) { LOG_INFO("Generating diagram {}.puml", name); DiagramModel d; @@ -151,8 +151,10 @@ DiagramModel generate(cppast::libclang_compilation_database &db, } cppast::cpp_entity_index idx; + auto logger = + verbose ? cppast::default_logger() : cppast::default_quiet_logger(); cppast::simple_file_parser parser{ - type_safe::ref(idx)}; + type_safe::ref(idx), std::move(logger)}; // Process all matching translation units DiagramVisitor ctx(idx, d, diagram); diff --git a/src/common/model/element.cc b/src/common/model/element.cc index 3c912c36..310a4340 100644 --- a/src/common/model/element.cc +++ b/src/common/model/element.cc @@ -37,15 +37,15 @@ std::string element::alias() const { return fmt::format("C_{:010}", m_id); } void element::add_relationship(relationship &&cr) { if (cr.destination().empty()) { - LOG_WARN("Skipping relationship '{}' - {} - '{}' due empty " - "destination", + LOG_DBG("Skipping relationship '{}' - {} - '{}' due empty " + "destination", cr.destination(), to_string(cr.type()), full_name(true)); return; } if ((cr.type() == relationship_t::kInstantiation) && (cr.destination() == full_name(true))) { - LOG_WARN("Skipping self instantiation relationship for {}", + LOG_DBG("Skipping self instantiation relationship for {}", cr.destination()); return; } diff --git a/src/common/model/nested_trait.h b/src/common/model/nested_trait.h index 4c2de809..91515d1f 100644 --- a/src/common/model/nested_trait.h +++ b/src/common/model/nested_trait.h @@ -81,8 +81,7 @@ public: LOG_DBG("Getting nested element at path: {}", path.to_string()); if (path.is_empty() || !has_element(path[0])) { - LOG_WARN( - "Nested element {} not found in element", path.to_string()); + LOG_DBG("Nested element {} not found in element", path.to_string()); return type_safe::optional_ref{}; } diff --git a/src/config/config.cc b/src/config/config.cc index c40de9a1..778aba9d 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -135,13 +135,6 @@ bool diagram::should_include( return should_include(std::get<0>(name), std::get<1>(name)); } -// bool diagram::should_include( -// const common::model::namespace_ &ns, const std::string &name) const -//{ -// auto ns_and_name = ns | name; -// return should_include(ns_and_name.to_string()); -//} - bool diagram::should_include( const common::model::namespace_ &ns, const std::string &name) const { @@ -346,7 +339,7 @@ std::shared_ptr parse_diagram_config(const Node &d) return std::make_shared(d.as()); } - LOG_WARN("Diagrams of type {} are not supported... ", diagram_type); + LOG_ERROR("Diagrams of type {} are not supported... ", diagram_type); return {}; } diff --git a/src/config/config.h b/src/config/config.h index 7a7c607d..632b53a3 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -110,10 +110,6 @@ struct diagram : public inheritable_diagram_options { bool should_include( const std::pair &name) const; - // bool should_include( - // const std::vector &ns, const std::string &name) - // const; - bool should_include( const common::model::namespace_ &ns, const std::string &name) const; diff --git a/src/main.cc b/src/main.cc index eb3b0851..44151e8b 100644 --- a/src/main.cc +++ b/src/main.cc @@ -16,25 +16,25 @@ * limitations under the License. */ -#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG - #include "class_diagram/generators/plantuml/class_diagram_generator.h" #include "config/config.h" #include "cx/compilation_database.h" #include "package_diagram/generators/plantuml/package_diagram_generator.h" #include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" -#include "config/config.h" #include "util/util.h" #include #include +#include #include #include #include +#include #include #include +#include using namespace clanguml; using config::config; @@ -44,6 +44,10 @@ void print_diagrams_list(const clanguml::config::config &cfg); bool check_output_directory(const std::string &dir); +void generate_diagram(const std::string &od, const std::string &name, + std::shared_ptr diagram, + const cppast::libclang_compilation_database &db, bool verbose); + int main(int argc, const char *argv[]) { CLI::App app{"Clang-based PlantUML diagram generator for C++"}; @@ -52,6 +56,7 @@ int main(int argc, const char *argv[]) std::string compilation_database_dir{'.'}; std::vector diagram_names{}; std::optional output_directory; + unsigned int thread_count{0}; bool verbose{false}; bool list_diagrams{false}; @@ -63,16 +68,15 @@ int main(int argc, const char *argv[]) "List of diagram names to generate"); app.add_option("-o,--output-directory", output_directory, "Override output directory specified in config file"); + app.add_option("-t,--thread-count", thread_count, + "Thread pool size (0 = hardware concurrency)"); app.add_flag("-v,--verbose", verbose, "Verbose logging"); app.add_flag("-l,--list-diagrams", list_diagrams, "Print list of diagrams defined in the config file"); CLI11_PARSE(app, argc, argv); - if (verbose) { - spdlog::default_logger_raw()->set_level(spdlog::level::debug); - spdlog::default_logger_raw()->set_pattern("[%l] %v"); - } + clanguml::util::setup_logging(verbose); clanguml::config::config config; try { @@ -93,7 +97,7 @@ int main(int argc, const char *argv[]) LOG_INFO("Loading compilation database from {} directory", config.compilation_database_dir()); - cppast::libclang_compilation_database db(config.compilation_database_dir()); + cppast::libclang_compilation_database db{config.compilation_database_dir()}; auto od = config.output_directory(); if (output_directory) @@ -102,73 +106,90 @@ int main(int argc, const char *argv[]) if (!check_output_directory(od)) return 1; + util::thread_pool_executor generator_executor{thread_count}; + std::vector> futs; + for (const auto &[name, diagram] : config.diagrams) { // If there are any specific diagram names provided on the command line, // and this diagram is not in that list - skip it if (!diagram_names.empty() && !util::contains(diagram_names, name)) continue; - using clanguml::config::class_diagram; - using clanguml::config::diagram_type; - using clanguml::config::package_diagram; - using clanguml::config::sequence_diagram; + futs.emplace_back(generator_executor.add( + [&od, &name = name, &diagram = diagram, &db = db, verbose]() { + generate_diagram(od, name, diagram, db, verbose); + })); + } - auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); - std::ofstream ofs; - ofs.open(path, std::ofstream::out | std::ofstream::trunc); - - if (diagram->type() == diagram_type::class_diagram) { - using diagram_config = clanguml::config::class_diagram; - using diagram_model = clanguml::class_diagram::model::diagram; - using diagram_visitor = - clanguml::class_diagram::visitor::translation_unit_visitor; - - auto model = - clanguml::common::generators::plantuml::generate(db, diagram->name, - dynamic_cast(*diagram)); - - ofs << clanguml::class_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), model); - } - else if (diagram->type() == diagram_type::sequence_diagram) { - using diagram_config = clanguml::config::sequence_diagram; - using diagram_model = clanguml::sequence_diagram::model::diagram; - using diagram_visitor = - clanguml::sequence_diagram::visitor::translation_unit_visitor; - - auto model = - clanguml::common::generators::plantuml::generate(db, diagram->name, - dynamic_cast(*diagram)); - - ofs << clanguml::sequence_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), - model); - } - else if (diagram->type() == diagram_type::package_diagram) { - using diagram_config = clanguml::config::package_diagram; - using diagram_model = clanguml::package_diagram::model::diagram; - using diagram_visitor = - clanguml::package_diagram::visitor::translation_unit_visitor; - - auto model = - clanguml::common::generators::plantuml::generate(db, diagram->name, - dynamic_cast(*diagram)); - - ofs << clanguml::package_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), model); - } - - LOG_INFO("Written {} diagram to {}", name, path.string()); - - ofs.close(); + for (auto &fut : futs) { + fut.get(); } return 0; } +void generate_diagram(const std::string &od, const std::string &name, + std::shared_ptr diagram, + const cppast::libclang_compilation_database &db, bool verbose) +{ + using clanguml::config::class_diagram; + using clanguml::config::diagram_type; + using clanguml::config::package_diagram; + using clanguml::config::sequence_diagram; + + auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + + if (diagram->type() == diagram_type::class_diagram) { + using diagram_config = class_diagram; + using diagram_model = clanguml::class_diagram::model::diagram; + using diagram_visitor = + clanguml::class_diagram::visitor::translation_unit_visitor; + + auto model = + clanguml::common::generators::plantuml::generate(db, diagram->name, + dynamic_cast(*diagram), verbose); + + ofs << clanguml::class_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), model); + } + else if (diagram->type() == diagram_type::sequence_diagram) { + using diagram_config = sequence_diagram; + using diagram_model = clanguml::sequence_diagram::model::diagram; + using diagram_visitor = + clanguml::sequence_diagram::visitor::translation_unit_visitor; + + auto model = + clanguml::common::generators::plantuml::generate(db, diagram->name, + dynamic_cast(*diagram), verbose); + + ofs << clanguml::sequence_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), + model); + } + else if (diagram->type() == diagram_type::package_diagram) { + using diagram_config = package_diagram; + using diagram_model = clanguml::package_diagram::model::diagram; + using diagram_visitor = + clanguml::package_diagram::visitor::translation_unit_visitor; + + auto model = + clanguml::common::generators::plantuml::generate(db, diagram->name, + dynamic_cast(*diagram), verbose); + + ofs << clanguml::package_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), model); + } + + LOG_INFO("Written {} diagram to {}", name, path.string()); + + ofs.close(); +} + bool check_output_directory(const std::string &dir) { namespace fs = std::filesystem; diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index 86512a82..ba72bfde 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -46,8 +46,8 @@ void generator::generate_relationships( ostr << relstr.str(); } catch (error::uml_alias_missing &e) { - LOG_ERROR("=== Skipping dependency relation from {} to {} due " - "to: {}", + LOG_DBG("=== Skipping dependency relation from {} to {} due " + "to: {}", p.full_name(false), r.destination(), e.what()); } } diff --git a/src/util/thread_pool_executor.cc b/src/util/thread_pool_executor.cc index 231c3921..b72c60c9 100644 --- a/src/util/thread_pool_executor.cc +++ b/src/util/thread_pool_executor.cc @@ -35,7 +35,7 @@ thread_pool_executor::thread_pool_executor(unsigned int pool_size) } } -~thread_pool_executor::thread_pool_executor() { stop(); } +thread_pool_executor::~thread_pool_executor() { stop(); } std::future thread_pool_executor::add(std::function &&task) { diff --git a/src/util/util.cc b/src/util/util.cc index 69926a49..33bac99d 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -26,6 +26,18 @@ namespace util { const std::string WHITESPACE = " \n\r\t\f\v"; +void setup_logging(bool verbose) +{ + auto console = + spdlog::stdout_color_mt("console", spdlog::color_mode::automatic); + + console->set_pattern("[%^%l%^] [tid %t] %v"); + + if (verbose) { + console->set_level(spdlog::level::debug); + } +} + std::string ltrim(const std::string &s) { size_t start = s.find_first_not_of(WHITESPACE); @@ -68,34 +80,6 @@ std::string join(const std::vector &toks, std::string delimiter) return fmt::format("{}", fmt::join(toks, delimiter)); } -/* -std::string ns_relative( - const std::vector &namespaces, const std::string &n) -{ - std::vector namespaces_sorted{namespaces}; - - std::sort(namespaces_sorted.rbegin(), namespaces_sorted.rend()); - - auto res = n; - - for (const auto &ns : namespaces_sorted) { - if (ns.empty()) - continue; - - if (n == ns) - return split(n, "::").back(); - - auto ns_prefix = ns + "::"; - auto it = res.find(ns_prefix); - while (it != std::string::npos) { - res.erase(it, ns_prefix.size()); - it = res.find(ns_prefix); - } - } - return res; -} -*/ - std::string unqualify(const std::string &s) { auto toks = clanguml::util::split(s, " "); diff --git a/src/util/util.h b/src/util/util.h index e6243fa6..8a63a5e6 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -17,6 +17,7 @@ */ #pragma once +#include #include #include @@ -36,20 +37,27 @@ std::string trim(const std::string &s); (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define LOG_ERROR(fmt__, ...) \ - spdlog::error(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \ - ##__VA_ARGS__) + spdlog::get("console")->error(std::string("[{}:{}] ") + fmt__, \ + __FILENAME__, __LINE__, ##__VA_ARGS__) #define LOG_WARN(fmt__, ...) \ - spdlog::warn(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \ - ##__VA_ARGS__) + spdlog::get("console")->warn(std::string("[{}:{}] ") + fmt__, \ + __FILENAME__, __LINE__, ##__VA_ARGS__) #define LOG_INFO(fmt__, ...) \ - spdlog::info(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \ - ##__VA_ARGS__) + spdlog::get("console")->info(std::string("[{}:{}] ") + fmt__, \ + __FILENAME__, __LINE__, ##__VA_ARGS__) #define LOG_DBG(fmt__, ...) \ - spdlog::debug(std::string("[{}:{}] ") + fmt__, __FILENAME__, __LINE__, \ - ##__VA_ARGS__) + spdlog::get("console")->debug(std::string("[{}:{}] ") + fmt__, \ + __FILENAME__, __LINE__, ##__VA_ARGS__) + +/** + * @brief Setup spdlog logger. + * + * @param verbose Whether the logging should be verbose or not. + */ +void setup_logging(bool verbose); /** * @brief Split a string using delimiter @@ -67,25 +75,6 @@ std::vector split(std::string str, std::string delimiter); std::string join(const std::vector &toks, std::string delimiter); -/** - * @brief Get name of the identifier relative to a set of namespaces - * - * This function tries to match a given C++ identifier (e.g. - * clanguml::util::split) to the longest namespace from the provided list - * matching the identifier from the left. - * If a match is found, the relative identifier is returned. If none of - * the namespaces match the identifier or if nothing is left after - * removing the matching namespace from the identifier, original identifier is - * returned. - * - * @param namespaces List of C++ namespaces to consider - * @param n Identifier to relativize - * - * @return Identifier relative to one of the matching namespaces. - */ -// std::string ns_relative( -// const std::vector &namespaces, const std::string &n); - /** * @brief Remove any qualifiers (e.g. const) from type. * diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2c4f6eed..6dcf8e55 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -142,4 +142,6 @@ endforeach() add_test(NAME test_util COMMAND test_util) add_test(NAME test_decorator_parser COMMAND test_decorator_parser) add_test(NAME test_config COMMAND test_config) +add_test(NAME test_model COMMAND test_model) +add_test(NAME test_thread_pool_executor COMMAND test_thread_pool_executor) add_test(NAME test_cases COMMAND test_cases) diff --git a/tests/test_cases.cc b/tests/test_cases.cc index f6e3a939..7f3216db 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -222,10 +222,7 @@ int main(int argc, char *argv[]) if (returnCode != 0) return returnCode; - if (debug_log) { - spdlog::default_logger_raw()->set_level(spdlog::level::debug); - spdlog::default_logger_raw()->set_pattern("[%l] %v"); - } + clanguml::util::setup_logging(debug_log); return session.run(); } From 71bd9d6e1cc2e82546fb80ba88bf62bb93edd99b Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 18 Mar 2022 23:08:58 +0100 Subject: [PATCH 5/5] Removed redundant header --- src/main.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.cc b/src/main.cc index 44151e8b..b1ca2acc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -26,7 +26,6 @@ #include #include -#include #include #include