From bffe9913aa5f31ffdfb72aaaba46ecd6c1208cbb Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 13 Mar 2023 00:30:04 +0100 Subject: [PATCH] Added cli options for cppidx generator --- .../cppidx/class_diagram_generator.cc | 60 +++ .../cppidx/class_diagram_generator.h | 78 ++++ src/cli/cli_handler.cc | 10 +- src/cli/cli_handler.h | 4 +- src/common/generators/cppidx/generator.h | 86 ++++ src/common/generators/generators.h | 368 ++++++++++++++++++ src/common/generators/nested_element_stack.h | 6 + src/common/generators/plantuml/generator.h | 127 +----- src/common/types.h | 2 + src/main.cc | 240 +----------- tests/test_cases.cc | 14 +- 11 files changed, 631 insertions(+), 364 deletions(-) create mode 100644 src/class_diagram/generators/cppidx/class_diagram_generator.cc create mode 100644 src/class_diagram/generators/cppidx/class_diagram_generator.h create mode 100644 src/common/generators/cppidx/generator.h create mode 100644 src/common/generators/generators.h diff --git a/src/class_diagram/generators/cppidx/class_diagram_generator.cc b/src/class_diagram/generators/cppidx/class_diagram_generator.cc new file mode 100644 index 00000000..47cdd504 --- /dev/null +++ b/src/class_diagram/generators/cppidx/class_diagram_generator.cc @@ -0,0 +1,60 @@ +/** + * src/class_diagram/generators/cppidx/class_diagram_generator.cc + * + * Copyright (c) 2021-2023 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 "class_diagram_generator.h" + +#include "util/error.h" + +namespace clanguml::class_diagram::generators::cppidx { + +generator::generator(diagram_config &config, diagram_model &model) + : common_generator{config, model} +{ +} + +void generator::generate(std::ostream &ostr) const +{ + generate_top_level_elements(ostr); + + ostr << json_; +} + +void generator::generate_top_level_elements(std::ostream &ostr) const +{ + for (const auto &p : m_model) { + if (auto *cls = dynamic_cast(p.get()); cls) { + if (m_model.should_include(*cls)) { + generate(*cls, ostr); + } + } + } +} + +void generator::generate(const class_ &c, std::ostream & /*ostr*/) const +{ + + nlohmann::json object; + object["id"] = std::to_string(c.id()); + object["name"] = c.name(); + object["namespace"] = c.get_namespace().to_string(); + object["type"] = c.type_name(); + object["display_name"] = c.full_name(false); + json_["elements"].push_back(std::move(object)); +} + +} // namespace clanguml::class_diagram::generators::plantuml diff --git a/src/class_diagram/generators/cppidx/class_diagram_generator.h b/src/class_diagram/generators/cppidx/class_diagram_generator.h new file mode 100644 index 00000000..28841fbd --- /dev/null +++ b/src/class_diagram/generators/cppidx/class_diagram_generator.h @@ -0,0 +1,78 @@ +/** + * src/class_diagram/generators/cppidx/class_diagram_generator.h + * + * Copyright (c) 2021-2023 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 "class_diagram/model/class.h" +#include "class_diagram/model/concept.h" +#include "class_diagram/model/diagram.h" +#include "class_diagram/model/enum.h" +#include "class_diagram/visitor/translation_unit_visitor.h" +#include "common/generators/cppidx/generator.h" +#include "common/generators/nested_element_stack.h" +#include "common/model/relationship.h" +#include "config/config.h" +#include "util/util.h" + +#include +#include + +#include +#include +#include +#include + +namespace clanguml { +namespace class_diagram { +namespace generators { +namespace cppidx { + +using diagram_config = clanguml::config::class_diagram; +using diagram_model = clanguml::class_diagram::model::diagram; +template +using common_generator = clanguml::common::generators::cppidx::generator; + +using clanguml::class_diagram::model::class_; +using clanguml::class_diagram::model::class_element; +using clanguml::class_diagram::model::concept_; +using clanguml::class_diagram::model::enum_; +using clanguml::common::model::access_t; +using clanguml::common::model::package; +using clanguml::common::model::relationship_t; + +using namespace clanguml::util; + +class generator : public common_generator { +public: + generator(diagram_config &config, diagram_model &model); + + void generate(const class_ &c, std::ostream &ostr) const; + + void generate_top_level_elements(std::ostream &ostr) const; + + void generate(std::ostream &ostr) const override; + +private: + std::string render_name(std::string name) const; + + mutable nlohmann::json json_; +}; + +} // namespace cppidx +} // namespace generators +} // namespace class_diagram +} // namespace clanguml diff --git a/src/cli/cli_handler.cc b/src/cli/cli_handler.cc index 44a4c4f2..084f9188 100644 --- a/src/cli/cli_handler.cc +++ b/src/cli/cli_handler.cc @@ -1,5 +1,5 @@ /** - * src/options/cli_options.cc + * src/options/cli_handler.cc * * Copyright (c) 2021-2023 Bartek Kryza * @@ -58,12 +58,20 @@ void cli_handler::setup_logging() cli_flow_t cli_handler::parse(int argc, const char **argv) { + static const std::map + generator_type_names{ + {"plantuml", clanguml::common::generator_type_t::plantuml}, + {"cppidx", clanguml::common::generator_type_t::cppidx}}; + app.add_option("-c,--config", config_path, "Location of configuration file, when '-' read from stdin"); app.add_option("-d,--compile-database", compilation_database_dir, "Location of compilation database directory"); app.add_option("-n,--diagram-name", diagram_names, "List of diagram names to generate"); + app.add_option("-g,--generator", generators, + "Name of the generator (default: plantuml)") + ->transform(CLI::CheckedTransformer(generator_type_names)); app.add_option("-o,--output-directory", output_directory, "Override output directory specified in config file"); app.add_option("-t,--thread-count", thread_count, diff --git a/src/cli/cli_handler.h b/src/cli/cli_handler.h index 7c917c77..2581ec9c 100644 --- a/src/cli/cli_handler.h +++ b/src/cli/cli_handler.h @@ -1,5 +1,5 @@ /** - * src/options/cli_options.h + * src/options/cli_handler.h * * Copyright (c) 2021-2023 Bartek Kryza * @@ -131,6 +131,8 @@ public: std::vector template_variables{}; bool list_templates{false}; std::optional show_template; + std::vector generators{ + clanguml::common::generator_type_t::plantuml}; clanguml::config::config config; diff --git a/src/common/generators/cppidx/generator.h b/src/common/generators/cppidx/generator.h new file mode 100644 index 00000000..9a70063e --- /dev/null +++ b/src/common/generators/cppidx/generator.h @@ -0,0 +1,86 @@ +/** + * src/common/generators/cppidx/generator.h + * + * Copyright (c) 2021-2023 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 "common/model/diagram_filter.h" +#include "config/config.h" +#include "util/error.h" +#include "util/util.h" + +#include +#include +#include +#include + +#include + +namespace clanguml::common::generators::cppidx { + +using clanguml::common::model::access_t; +using clanguml::common::model::element; +using clanguml::common::model::message_t; +using clanguml::common::model::relationship_t; + +/** + * @brief Base class for diagram generators + * + * @tparam ConfigType Configuration type + * @tparam DiagramType Diagram model type + */ +template class generator { +public: + /** + * @brief Constructor + * + * @param config Reference to instance of @link clanguml::config::diagram + * @param model Reference to instance of @link clanguml::model::diagram + */ + generator(ConfigType &config, DiagramType &model) + : m_config{config} + , m_model{model} + { + } + + virtual ~generator() = default; + + /** + * @brief Generate diagram + * + * This method must be implemented in subclasses for specific diagram + * types. It is responsible for calling other methods in appropriate + * order to generate the diagram into the output stream. + * + * @param ostr Output stream + */ + virtual void generate(std::ostream &ostr) const = 0; + +private: +protected: + ConfigType &m_config; + DiagramType &m_model; +}; + +template +std::ostream &operator<<( + std::ostream &os, const generator &g) +{ + g.generate(os); + return os; +} + +} // namespace clanguml::common::generators::cppidx \ No newline at end of file diff --git a/src/common/generators/generators.h b/src/common/generators/generators.h new file mode 100644 index 00000000..17e4d3a0 --- /dev/null +++ b/src/common/generators/generators.h @@ -0,0 +1,368 @@ +/** + * src/common/generators/generators.h + * + * Copyright (c) 2021-2023 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 "class_diagram/generators/cppidx/class_diagram_generator.h" +#include "class_diagram/generators/plantuml/class_diagram_generator.h" +#include "cli/cli_handler.h" +#include "common/generators/generators.h" +#include "common/model/diagram_filter.h" +#include "config/config.h" +#include "include_diagram/generators/plantuml/include_diagram_generator.h" +#include "package_diagram/generators/plantuml/package_diagram_generator.h" +#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" +#include "util/util.h" +#include "version.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace clanguml::common::generators { + +void find_translation_units_for_diagrams( + const std::vector &diagram_names, + clanguml::config::config &config, + const std::vector &compilation_database_files, + std::map> &translation_units_map) +{ + 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; + + // If glob is not defined use all translation units from the + // compilation database + if (!diagram->glob.has_value) { + translation_units_map[name] = compilation_database_files; + } + // Otherwise, get all translation units matching the glob from diagram + // configuration + else { + const std::vector translation_units = + diagram->get_translation_units(); + + std::vector valid_translation_units{}; + std::copy_if(compilation_database_files.begin(), + compilation_database_files.end(), + std::back_inserter(valid_translation_units), + [&translation_units](const auto &tu) { + return util::contains(translation_units, tu); + }); + + translation_units_map[name] = std::move(valid_translation_units); + } + } +} + +template +class diagram_ast_consumer : public clang::ASTConsumer { + TranslationUnitVisitor visitor_; + +public: + explicit diagram_ast_consumer(clang::CompilerInstance &ci, + DiagramModel &diagram, const DiagramConfig &config) + : visitor_{ci.getSourceManager(), diagram, config} + { + } + + void HandleTranslationUnit(clang::ASTContext &ast_context) override + { + visitor_.TraverseDecl(ast_context.getTranslationUnitDecl()); + visitor_.finalize(); + } +}; + +template +class diagram_fronted_action : public clang::ASTFrontendAction { +public: + explicit diagram_fronted_action( + DiagramModel &diagram, const DiagramConfig &config) + : diagram_{diagram} + , config_{config} + { + } + + std::unique_ptr CreateASTConsumer( + clang::CompilerInstance &CI, clang::StringRef /*file*/) override + { + return std::make_unique< + diagram_ast_consumer>( + CI, diagram_, config_); + } + +protected: + bool BeginSourceFileAction(clang::CompilerInstance &ci) override + { + LOG_DBG("Visiting source file: {}", getCurrentFile().str()); + + if constexpr (std::is_same_v) { + auto find_includes_callback = + std::make_unique( + ci.getSourceManager(), diagram_, config_); + + clang::Preprocessor &pp = ci.getPreprocessor(); + + pp.addPPCallbacks(std::move(find_includes_callback)); + } + + return true; + } + +private: + DiagramModel &diagram_; + const DiagramConfig &config_; +}; + +template +class diagram_action_visitor_factory + : public clang::tooling::FrontendActionFactory { +public: + explicit diagram_action_visitor_factory( + DiagramModel &diagram, const DiagramConfig &config) + : diagram_{diagram} + , config_{config} + { + } + + std::unique_ptr create() override + { + return std::make_unique>(diagram_, config_); + } + +private: + DiagramModel &diagram_; + const DiagramConfig &config_; +}; + +template +std::unique_ptr generate( + const clang::tooling::CompilationDatabase &db, const std::string &name, + DiagramConfig &config, const std::vector &translation_units, + bool /*verbose*/ = false) +{ + LOG_INFO("Generating diagram {}.puml", name); + + auto diagram = std::make_unique(); + diagram->set_name(name); + diagram->set_filter( + std::make_unique(*diagram, config)); + + LOG_DBG("Found translation units for diagram {}: {}", name, + fmt::join(translation_units, ", ")); + + clang::tooling::ClangTool clang_tool(db, translation_units); + auto action_factory = + std::make_unique>(*diagram, config); + + auto res = clang_tool.run(action_factory.get()); + + if (res != 0) { + throw std::runtime_error("Diagram " + name + " generation failed"); + } + + diagram->set_complete(true); + + return diagram; +} + +void generate_diagram(const std::string &od, const std::string &name, + std::shared_ptr diagram, + const clang::tooling::CompilationDatabase &db, + const std::vector &translation_units, + const std::vector generators, + bool verbose) +{ + using clanguml::common::generator_type_t; + using clanguml::common::model::diagram_t; + using clanguml::config::class_diagram; + using clanguml::config::include_diagram; + using clanguml::config::package_diagram; + using clanguml::config::sequence_diagram; + + if (diagram->type() == diagram_t::kClass) { + 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::generate(db, diagram->name, + dynamic_cast(*diagram), translation_units, + verbose); + + for (const auto generator_type : generators) { + if (generator_type == generator_type_t::plantuml) { + auto path = + std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + ofs << clanguml::class_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + else if (generator_type == generator_type_t::cppidx) { + auto path = + std::filesystem::path{od} / fmt::format("{}.json", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + ofs << clanguml::class_diagram::generators::cppidx::generator( + dynamic_cast(*diagram), *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + } + } + else if (diagram->type() == diagram_t::kSequence) { + 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::generate(db, diagram->name, + dynamic_cast(*diagram), translation_units, + verbose); + + auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + ofs << clanguml::sequence_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), + *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + else if (diagram->type() == diagram_t::kPackage) { + 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::generate(db, diagram->name, + dynamic_cast(*diagram), translation_units, + verbose); + + auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + + ofs << clanguml::package_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } + else if (diagram->type() == diagram_t::kInclude) { + using diagram_config = include_diagram; + using diagram_model = clanguml::include_diagram::model::diagram; + using diagram_visitor = + clanguml::include_diagram::visitor::translation_unit_visitor; + + auto model = clanguml::common::generators::generate(db, diagram->name, + dynamic_cast(*diagram), translation_units, + verbose); + + auto path = std::filesystem::path{od} / fmt::format("{}.puml", name); + std::ofstream ofs; + ofs.open(path, std::ofstream::out | std::ofstream::trunc); + + ofs << clanguml::include_diagram::generators::plantuml::generator( + dynamic_cast(*diagram), *model); + + ofs.close(); + + LOG_INFO("Written {} diagram to {}", name, path.string()); + } +} + +void generate_diagrams(const std::vector &diagram_names, + clanguml::config::config &config, const std::string &od, + const std::unique_ptr &db, + const int verbose, const unsigned int thread_count, + const std::vector generators, + const std::map> + &translation_units_map) +{ + 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; + + const auto &valid_translation_units = translation_units_map.at(name); + + if (valid_translation_units.empty()) { + LOG_ERROR( + "Diagram {} generation failed: no translation units found", + name); + continue; + } + + futs.emplace_back(generator_executor.add( + [&od, &generators, &name = name, &diagram = diagram, + db = std::ref(*db), translation_units = valid_translation_units, + verbose]() { + try { + generate_diagram(od, name, diagram, db, translation_units, + generators, verbose != 0); + } + catch (std::runtime_error &e) { + LOG_ERROR(e.what()); + } + })); + } + + for (auto &fut : futs) { + fut.get(); + } +} + +} // namespace clanguml::common::generators \ No newline at end of file diff --git a/src/common/generators/nested_element_stack.h b/src/common/generators/nested_element_stack.h index c18770c9..1bc77d6b 100644 --- a/src/common/generators/nested_element_stack.h +++ b/src/common/generators/nested_element_stack.h @@ -23,6 +23,12 @@ namespace clanguml::common::generators { +/** + * This is a helper class for generating nested groups of elements + * in the diagrams, e.g. PlantUML `together` option. + * + * @tparam T Type of stack elements + */ template class nested_element_stack { public: nested_element_stack(bool is_flat) diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 6fd4d0cc..e6663061 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -167,13 +167,6 @@ protected: mutable inja::Environment m_env; }; -template -std::ostream &operator<<(std::ostream &os, const generator &g) -{ - g.generate(os); - return os; -} - template const inja::json &generator::context() const { @@ -436,122 +429,12 @@ void generator::print_debug( ostr << "' " << e.file() << ":" << e.line() << '\n'; } -template -class diagram_ast_consumer : public clang::ASTConsumer { - TranslationUnitVisitor visitor_; - -public: - explicit diagram_ast_consumer(clang::CompilerInstance &ci, - DiagramModel &diagram, const DiagramConfig &config) - : visitor_{ci.getSourceManager(), diagram, config} - { - } - - void HandleTranslationUnit(clang::ASTContext &ast_context) override - { - visitor_.TraverseDecl(ast_context.getTranslationUnitDecl()); - visitor_.finalize(); - } -}; - -template -class diagram_fronted_action : public clang::ASTFrontendAction { -public: - explicit diagram_fronted_action( - DiagramModel &diagram, const DiagramConfig &config) - : diagram_{diagram} - , config_{config} - { - } - - std::unique_ptr CreateASTConsumer( - clang::CompilerInstance &CI, clang::StringRef /*file*/) override - { - return std::make_unique< - diagram_ast_consumer>( - CI, diagram_, config_); - } - -protected: - bool BeginSourceFileAction(clang::CompilerInstance &ci) override - { - LOG_DBG("Visiting source file: {}", getCurrentFile().str()); - - if constexpr (std::is_same_v) { - auto find_includes_callback = - std::make_unique( - ci.getSourceManager(), diagram_, config_); - - clang::Preprocessor &pp = ci.getPreprocessor(); - - pp.addPPCallbacks(std::move(find_includes_callback)); - } - - return true; - } - -private: - DiagramModel &diagram_; - const DiagramConfig &config_; -}; - -template -class diagram_action_visitor_factory - : public clang::tooling::FrontendActionFactory { -public: - explicit diagram_action_visitor_factory( - DiagramModel &diagram, const DiagramConfig &config) - : diagram_{diagram} - , config_{config} - { - } - - std::unique_ptr create() override - { - return std::make_unique>(diagram_, config_); - } - -private: - DiagramModel &diagram_; - const DiagramConfig &config_; -}; - -template -std::unique_ptr generate( - const clang::tooling::CompilationDatabase &db, const std::string &name, - DiagramConfig &config, const std::vector &translation_units, - bool /*verbose*/ = false) +template +std::ostream &operator<<( + std::ostream &os, const generator &g) { - LOG_INFO("Generating diagram {}.puml", name); - - auto diagram = std::make_unique(); - diagram->set_name(name); - diagram->set_filter( - std::make_unique(*diagram, config)); - - LOG_DBG("Found translation units for diagram {}: {}", name, - fmt::join(translation_units, ", ")); - - clang::tooling::ClangTool clang_tool(db, translation_units); - auto action_factory = - std::make_unique>(*diagram, config); - - auto res = clang_tool.run(action_factory.get()); - - if (res != 0) { - throw std::runtime_error("Diagram " + name + " generation failed"); - } - - diagram->set_complete(true); - - return diagram; + g.generate(os); + return os; } template void generator::init_context() diff --git a/src/common/types.h b/src/common/types.h index 5122afff..557a3c3c 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -27,6 +27,8 @@ namespace clanguml::common { using id_t = int64_t; +enum class generator_type_t { plantuml, cppidx }; + template class optional_ref { public: using optional_type = T; diff --git a/src/main.cc b/src/main.cc index 06ca3449..f8ef1031 100644 --- a/src/main.cc +++ b/src/main.cc @@ -16,14 +16,10 @@ * limitations under the License. */ -#include "class_diagram/generators/plantuml/class_diagram_generator.h" #include "cli/cli_handler.h" -#include "config/config.h" +#include "common/generators/generators.h" #include "include_diagram/generators/plantuml/include_diagram_generator.h" -#include "package_diagram/generators/plantuml/package_diagram_generator.h" -#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h" #include "util/util.h" -#include "version.h" #ifdef ENABLE_BACKWARD_CPP #define BACKWARD_HAS_DW 1 @@ -35,9 +31,6 @@ #include #include -#include -#include -#include #include #include @@ -49,65 +42,6 @@ backward::SignalHandling sh; // NOLINT using namespace clanguml; -/** - * Generate specific diagram identified by name - * - * @param od Diagram output directory - * @param name Name of the diagram as specified in the config file - * @param diagram Diagram model instance - * @param db Compilation database - * @param translation_units List of translation units to be used for this - * diagram - * @param verbose Logging level - */ -void generate_diagram(const std::string &od, const std::string &name, - std::shared_ptr diagram, - const clang::tooling::CompilationDatabase &db, - const std::vector &translation_units, bool verbose); - -/** - * Find translation units for diagrams. - * - * For each diagram to be generated, this function selects translation units - * to be used for this diagram. The files are selected as an intersection - * between all translation units found in the compilation database and the - * `glob` patterns specified for each diagram in the configuration file. - * - * @param diagram_names List of diagram names to be generated - * @param config Configuration instance - * @param compilation_database_files All translation units in compilation - * database - * @param translation_units_map The output map containing translation - * units for each diagram by name - */ -void find_translation_units_for_diagrams( - const std::vector &diagram_names, - clanguml::config::config &config, - const std::vector &compilation_database_files, - std::map> &translation_units_map); - -/** - * Generate diagrams. - * - * This function generates all diagrams specified in the configuration file - * and in the command line. - * - * @param diagram_names List of diagram names to be generated - * @param config Configuration instance - * @param od Output directory where diagrams should be written - * @param db Compilation database instance - * @param verbose Verbosity level - * @param thread_count Number of diagrams to be generated in parallel - * @param translation_units_map List of translation units to be used for each - * diagram - */ -void generate_diagrams(const std::vector &diagram_names, - clanguml::config::config &config, const std::string &od, - const std::unique_ptr &db, int verbose, - unsigned int thread_count, - const std::map> - &translation_units_map); - int main(int argc, const char *argv[]) { cli::cli_handler cli; @@ -138,173 +72,13 @@ int main(int argc, const char *argv[]) // We have to generate the translation units list for each diagram before // scheduling tasks, because std::filesystem::current_path cannot be trusted // with multiple threads - find_translation_units_for_diagrams(cli.diagram_names, cli.config, - compilation_database_files, translation_units_map); - - generate_diagrams(cli.diagram_names, cli.config, - cli.effective_output_directory, db, cli.verbose, cli.thread_count, + clanguml::common::generators::find_translation_units_for_diagrams( + cli.diagram_names, cli.config, compilation_database_files, translation_units_map); + clanguml::common::generators::generate_diagrams(cli.diagram_names, + cli.config, cli.effective_output_directory, db, cli.verbose, + cli.thread_count, cli.generators, translation_units_map); + return 0; } - -void generate_diagram(const std::string &od, const std::string &name, - std::shared_ptr diagram, - const clang::tooling::CompilationDatabase &db, - const std::vector &translation_units, bool verbose) -{ - using clanguml::common::model::diagram_t; - using clanguml::config::class_diagram; - using clanguml::config::include_diagram; - 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_t::kClass) { - 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), translation_units, - verbose); - - ofs << clanguml::class_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), *model); - } - else if (diagram->type() == diagram_t::kSequence) { - 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), translation_units, - verbose); - - ofs << clanguml::sequence_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), - *model); - } - else if (diagram->type() == diagram_t::kPackage) { - 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), translation_units, - verbose); - - ofs << clanguml::package_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), *model); - } - else if (diagram->type() == diagram_t::kInclude) { - using diagram_config = include_diagram; - using diagram_model = clanguml::include_diagram::model::diagram; - using diagram_visitor = - clanguml::include_diagram::visitor::translation_unit_visitor; - - auto model = - clanguml::common::generators::plantuml::generate(db, diagram->name, - dynamic_cast(*diagram), translation_units, - verbose); - - ofs << clanguml::include_diagram::generators::plantuml::generator( - dynamic_cast(*diagram), *model); - } - - LOG_INFO("Written {} diagram to {}", name, path.string()); - - ofs.close(); -} - -void generate_diagrams(const std::vector &diagram_names, - clanguml::config::config &config, const std::string &od, - const std::unique_ptr &db, - const int verbose, const unsigned int thread_count, - const std::map> - &translation_units_map) -{ - 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; - - const auto &valid_translation_units = translation_units_map.at(name); - - if (valid_translation_units.empty()) { - LOG_ERROR( - "Diagram {} generation failed: no translation units found", - name); - continue; - } - - futs.emplace_back(generator_executor.add( - [&od, &name = name, &diagram = diagram, db = std::ref(*db), - translation_units = valid_translation_units, verbose]() { - try { - generate_diagram( - od, name, diagram, db, translation_units, verbose != 0); - } - catch (std::runtime_error &e) { - LOG_ERROR(e.what()); - } - })); - } - - for (auto &fut : futs) { - fut.get(); - } -} - -void find_translation_units_for_diagrams( - const std::vector &diagram_names, - clanguml::config::config &config, - const std::vector &compilation_database_files, - std::map> &translation_units_map) -{ - 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; - - // If glob is not defined use all translation units from the - // compilation database - if (!diagram->glob.has_value) { - translation_units_map[name] = compilation_database_files; - } - // Otherwise, get all translation units matching the glob from diagram - // configuration - else { - const std::vector translation_units = - diagram->get_translation_units(); - - std::vector valid_translation_units{}; - std::copy_if(compilation_database_files.begin(), - compilation_database_files.end(), - std::back_inserter(valid_translation_units), - [&translation_units](const auto &tu) { - return util::contains(translation_units, tu); - }); - - translation_units_map[name] = std::move(valid_translation_units); - } - } -} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 926083ff..e3434ef0 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -19,7 +19,7 @@ #include "test_cases.h" #include "cli/cli_handler.h" -#include "common/generators/plantuml/generator.h" +#include "common/generators/generators.h" #include @@ -67,7 +67,7 @@ generate_sequence_diagram(clang::tooling::CompilationDatabase &db, inject_diagram_options(diagram); - auto model = clanguml::common::generators::plantuml::generate(db, diagram->name, dynamic_cast(*diagram), diagram->get_translation_units()); @@ -86,7 +86,7 @@ std::unique_ptr generate_class_diagram( inject_diagram_options(diagram); - auto model = clanguml::common::generators::plantuml::generate(db, diagram->name, dynamic_cast(*diagram), diagram->get_translation_units()); @@ -105,8 +105,8 @@ generate_package_diagram(clang::tooling::CompilationDatabase &db, inject_diagram_options(diagram); - return clanguml::common::generators::plantuml::generate(db, diagram->name, + return clanguml::common::generators::generate(db, diagram->name, dynamic_cast(*diagram), diagram->get_translation_units()); } @@ -122,8 +122,8 @@ generate_include_diagram(clang::tooling::CompilationDatabase &db, inject_diagram_options(diagram); - return clanguml::common::generators::plantuml::generate(db, diagram->name, + return clanguml::common::generators::generate(db, diagram->name, dynamic_cast(*diagram), diagram->get_translation_units()); }