0.6.1
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Namespaces | Classes | Functions
clanguml::common::generators Namespace Reference

Namespaces

namespace  detail
 
namespace  graphml
 
namespace  json
 
namespace  mermaid
 
namespace  plantuml
 

Classes

class  diagram_action_visitor_factory
 Specialization of clang::ASTFrontendAction More...
 
class  diagram_ast_consumer
 Specialization of clang::ASTConsumer More...
 
class  diagram_fronted_action
 Specialization of clang::ASTFrontendAction More...
 
struct  diagram_generator_t
 
struct  diagram_generator_t< clanguml::config::class_diagram, graphml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::class_diagram, json_generator_tag >
 
struct  diagram_generator_t< clanguml::config::class_diagram, mermaid_generator_tag >
 
struct  diagram_generator_t< clanguml::config::class_diagram, plantuml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::include_diagram, graphml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::include_diagram, json_generator_tag >
 
struct  diagram_generator_t< clanguml::config::include_diagram, mermaid_generator_tag >
 
struct  diagram_generator_t< clanguml::config::include_diagram, plantuml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::package_diagram, graphml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::package_diagram, json_generator_tag >
 
struct  diagram_generator_t< clanguml::config::package_diagram, mermaid_generator_tag >
 
struct  diagram_generator_t< clanguml::config::package_diagram, plantuml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::sequence_diagram, graphml_generator_tag >
 
struct  diagram_generator_t< clanguml::config::sequence_diagram, json_generator_tag >
 
struct  diagram_generator_t< clanguml::config::sequence_diagram, mermaid_generator_tag >
 
struct  diagram_generator_t< clanguml::config::sequence_diagram, plantuml_generator_tag >
 
struct  diagram_model_t
 
struct  diagram_model_t< clanguml::config::class_diagram >
 
struct  diagram_model_t< clanguml::config::include_diagram >
 
struct  diagram_model_t< clanguml::config::package_diagram >
 
struct  diagram_model_t< clanguml::config::sequence_diagram >
 
struct  diagram_visitor_t
 
struct  diagram_visitor_t< clanguml::config::class_diagram >
 
struct  diagram_visitor_t< clanguml::config::include_diagram >
 
struct  diagram_visitor_t< clanguml::config::package_diagram >
 
struct  diagram_visitor_t< clanguml::config::sequence_diagram >
 
class  display_name_adapter
 
class  generator
 Common diagram generator interface. More...
 
struct  graphml_generator_tag
 
struct  json_generator_tag
 
class  json_logger_progress_indicator
 
struct  mermaid_generator_tag
 
class  nested_element_stack
 
struct  not_supported
 
struct  plantuml_generator_tag
 
class  progress_indicator
 Container for diagram generation progress indicators. More...
 
class  progress_indicator_base
 

Functions

void make_context_source_relative (inja::json &context, const std::string &prefix)
 
void find_translation_units_for_diagrams (const std::vector< std::string > &diagram_names, clanguml::config::config &config, const compilation_database &compilation_database, std::map< std::string, std::vector< std::string > > &translation_units_map)
 Assign translation units to diagrams.
 
void render_diagram (const clanguml::common::generator_type_t generator_type, std::shared_ptr< config::diagram > diagram_config)
 
void generate_diagram (const std::string &name, std::shared_ptr< clanguml::config::diagram > diagram, const common::compilation_database &db, const std::vector< std::string > &translation_units, const cli::runtime_config &runtime_config, std::function< void()> &&progress)
 Generate a single diagram.
 
int generate_diagrams (const std::vector< std::string > &diagram_names, clanguml::config::config &config, const common::compilation_database_ptr &db, const cli::runtime_config &runtime_config, const std::map< std::string, std::vector< std::string > > &translation_units_map)
 Generate diagrams.
 
indicators::Color diagram_type_to_color (model::diagram_t diagram_type)
 Return indicators progress bar color for diagram type.
 
template<typename GeneratorTag >
constexpr bool generator_supports_diagram_type (clanguml::common::model::diagram_t dt)
 
template<typename DiagramModel , typename DiagramConfig , typename DiagramVisitor >
std::unique_ptr< DiagramModel > generate (const common::compilation_database &db, const std::string &name, DiagramConfig &config, const std::vector< std::string > &translation_units, bool=false, std::function< void()> progress={})
 Specialization of clang::ASTFrontendAction
 

Function Documentation

◆ diagram_type_to_color()

indicators::Color clanguml::common::generators::diagram_type_to_color ( model::diagram_t  diagram_type)

Return indicators progress bar color for diagram type.

Parameters
diagram_typeDiagram type
Returns
Progress bar color

Definition at line 456 of file generators.cc.

457{
458 switch (diagram_type) {
459 case model::diagram_t::kClass:
460 return indicators::Color::yellow;
461 case model::diagram_t::kSequence:
462 return indicators::Color::blue;
463 case model::diagram_t::kPackage:
464 return indicators::Color::cyan;
465 case model::diagram_t::kInclude:
466 return indicators::Color::magenta;
467 default:
468 return indicators::Color::unspecified;
469 }
470}

◆ find_translation_units_for_diagrams()

void clanguml::common::generators::find_translation_units_for_diagrams ( const std::vector< std::string > &  diagram_names,
clanguml::config::config config,
const compilation_database compilation_database,
std::map< std::string, std::vector< std::string > > &  translation_units_map 
)

Assign translation units to diagrams.

This function assigns for each diagram to be generated the list of translation units based on it's glob pattern if any.

If diagram_names is empty, this function processes all diagrams in config.

Parameters
diagram_namesList of diagram names, applies to all if empty
configReference to config instance
compilation_databaseReference to a compilation database
translation_units_mapResulting translation units map is stored here

Definition at line 46 of file generators.cc.

51{
52 for (const auto &[name, diagram] : config.diagrams) {
53 // If there are any specific diagram names provided on the command line,
54 // and this diagram is not in that list - skip it
55 if (!diagram_names.empty() && !util::contains(diagram_names, name))
56 continue;
57
58 translation_units_map[name] =
59 diagram->glob_translation_units(compilation_database.getAllFiles(),
61
62 LOG_DBG("Found {} translation units for diagram '{}'",
63 translation_units_map.at(name).size(), name);
64 }
65}

◆ generate()

template<typename DiagramModel , typename DiagramConfig , typename DiagramVisitor >
std::unique_ptr< DiagramModel > clanguml::common::generators::generate ( const common::compilation_database db,
const std::string &  name,
DiagramConfig &  config,
const std::vector< std::string > &  translation_units,
bool  = false,
std::function< void()>  progress = {} 
)

Specialization of clang::ASTFrontendAction

This is the entry point function to initiate AST frontend action for a specific diagram.

Template Parameters
DiagramModelType of diagram_model
DiagramConfigType of diagram_config
TranslationUnitVisitorType of translation_unit_visitor

Definition at line 419 of file generators.h.

422 {})
423{
424 LOG_INFO("Generating diagram {}", name);
425
426 auto diagram = std::make_unique<DiagramModel>();
427 diagram->set_name(name);
428 diagram->set_filter(
429 model::diagram_filter_factory::create(*diagram, config));
430
431 LOG_DBG("Found translation units for diagram {}: {}", name,
432 fmt::join(translation_units, ", "));
433
434 const bool quiet_clang_tool = !!progress;
435
436 clanguml::generators::clang_tool clang_tool(diagram->type(), name, db,
437 translation_units, config.get_relative_to()(), quiet_clang_tool);
438
439 auto action_factory =
440 std::make_unique<diagram_action_visitor_factory<DiagramModel,
441 DiagramConfig, DiagramVisitor>>(
442 *diagram, config, std::move(progress));
443
444 clang_tool.run(action_factory.get());
445
446 diagram->set_complete(true);
447
448 diagram->finalize();
449
450 return diagram;
451}

◆ generate_diagram()

void clanguml::common::generators::generate_diagram ( const std::string &  name,
std::shared_ptr< clanguml::config::diagram diagram,
const common::compilation_database db,
const std::vector< std::string > &  translation_units,
const cli::runtime_config runtime_config,
std::function< void()> &&  progress 
)

Generate a single diagram.

Parameters
nameName of the diagram
diagramEffective diagram configuration
dbReference to compilation database
translation_unitsList of translation units for the diagram
generatorsList of generator types to be used for the diagram
verboseLog level
progressFunction to report translation unit progress

Definition at line 213 of file generators.cc.

218{
221
226
227 if (diagram->type() == diagram_t::kClass) {
228 detail::generate_diagram_impl<class_diagram>(name, diagram, db,
229 translation_units, runtime_config, std::move(progress));
230 }
231 else if (diagram->type() == diagram_t::kSequence) {
232 detail::generate_diagram_impl<sequence_diagram>(name, diagram, db,
233 translation_units, runtime_config, std::move(progress));
234 }
235 else if (diagram->type() == diagram_t::kPackage) {
236 detail::generate_diagram_impl<package_diagram>(name, diagram, db,
237 translation_units, runtime_config, std::move(progress));
238 }
239 else if (diagram->type() == diagram_t::kInclude) {
240 detail::generate_diagram_impl<include_diagram>(name, diagram, db,
241 translation_units, runtime_config, std::move(progress));
242 }
243}

◆ generate_diagrams()

int clanguml::common::generators::generate_diagrams ( const std::vector< std::string > &  diagram_names,
clanguml::config::config config,
const common::compilation_database_ptr db,
const cli::runtime_config runtime_config,
const std::map< std::string, std::vector< std::string > > &  translation_units_map 
)

Generate diagrams.

Parameters
diagram_namesList of diagram names to generate
configReference to config instance
output_directoryPath to output directory
dbReference to compilation database
verboseLog level
thread_countNumber of diagrams to be generated in parallel
progressWhether progress indicators should be displayed
generatorsList of generator types to use for each diagram
translation_units_mapMap of translation units for each file
Returns
0 if success, otherwise error code

Definition at line 245 of file generators.cc.

250{
251 util::thread_pool_executor generator_executor{runtime_config.thread_count};
252 std::vector<std::future<void>> futs;
253
254 std::unique_ptr<progress_indicator_base> indicator;
255
256 if (runtime_config.progress) {
257 if (clanguml::logging::logger_type() == logging::logger_type_t::text) {
258 std::cout
259 << termcolor::white
260 << "Processing translation units and generating diagrams:\n";
261 indicator = std::make_unique<progress_indicator>();
262 }
263 else {
264 indicator = std::make_unique<json_logger_progress_indicator>();
265 }
266 }
267
268 std::vector<std::exception_ptr> errors;
269
270 for (const auto &[name, diagram] : config.diagrams) {
271 // If there are any specific diagram names provided on the command
272 // line, and this diagram is not in that list - skip it
273 if (!diagram_names.empty() && !util::contains(diagram_names, name))
274 continue;
275
276 // If none of the generators supports the diagram type - skip it
277 bool at_least_one_generator_supports_diagram_type{false};
278 for (const auto generator_type : runtime_config.generators) {
279 if (generator_type == generator_type_t::plantuml) {
280 if (generator_supports_diagram_type<plantuml_generator_tag>(
281 diagram->type()))
282 at_least_one_generator_supports_diagram_type = true;
283 }
284 else if (generator_type == generator_type_t::json) {
285 if (generator_supports_diagram_type<json_generator_tag>(
286 diagram->type()))
287 at_least_one_generator_supports_diagram_type = true;
288 }
289 else if (generator_type == generator_type_t::mermaid) {
290 if (generator_supports_diagram_type<mermaid_generator_tag>(
291 diagram->type()))
292 at_least_one_generator_supports_diagram_type = true;
293 }
294 else if (generator_type == generator_type_t::graphml) {
295 if (generator_supports_diagram_type<graphml_generator_tag>(
296 diagram->type()))
297 at_least_one_generator_supports_diagram_type = true;
298 }
299 }
300
301 if (!at_least_one_generator_supports_diagram_type) {
302 LOG_INFO("Diagram '{}' not supported by any of selected "
303 "generators - skipping...",
304 name);
305 continue;
306 }
307
308 const auto &valid_translation_units = translation_units_map.at(name);
309
310 LOG_DBG("Found {} possible translation units for diagram '{}'",
311 valid_translation_units.size(), name);
312
313 const auto matching_commands_count =
314 db->count_matching_commands(valid_translation_units);
315
316 if (matching_commands_count == 0) {
317 const auto error_msg = fmt::format(
318 "Diagram '{}' generation failed: no translation units "
319 "found. Please make sure that your 'glob' patterns match "
320 "at least 1 file in 'compile_commands.json'.",
321 name);
322
323 if (indicator) {
324 indicator->add_progress_bar(
325 name, 0, diagram_type_to_color(diagram->type()));
326 indicator->fail(name);
327 try {
328 throw std::runtime_error(error_msg);
329 }
330 catch (std::runtime_error &e) {
331 errors.emplace_back(std::current_exception());
332 }
333 }
334 else {
335 LOG_ERROR(error_msg);
336 }
337
338 continue;
339 }
340
341 LOG_DBG("Found {} matching translation unit commands for diagram {}",
342 matching_commands_count, name);
343
344 auto generator = [&name = name, &diagram = diagram, &indicator,
345 db = std::ref(*db), matching_commands_count,
346 translation_units = valid_translation_units,
347 runtime_config]() mutable -> void {
348 try {
349 if (indicator) {
350 indicator->add_progress_bar(name, matching_commands_count,
351 diagram_type_to_color(diagram->type()));
352
353 generate_diagram(name, diagram, db, translation_units,
354 runtime_config, [&indicator, &name]() {
355 if (indicator)
356 indicator->increment(name);
357 });
358
359 if (indicator)
360 indicator->complete(name);
361 }
362 else {
363 generate_diagram(name, diagram, db, translation_units,
364 runtime_config, {});
365 }
366 }
368 if (indicator)
369 indicator->fail(name);
370 throw std::move(e);
371 }
372 catch (std::exception &e) {
373 if (indicator)
374 indicator->fail(name);
375
376 LOG_ERROR(
377 "Failed to generate diagram '{}': {}", name, e.what());
378
379 throw std::runtime_error(fmt::format(
380 "Failed to generate diagram '{}': {}", name, e.what()));
381 }
382 };
383
384 futs.emplace_back(generator_executor.add(std::move(generator)));
385 }
386
387 for (auto &fut : futs) {
388 try {
389 fut.get();
390 }
391 catch (std::exception &e) {
392 errors.emplace_back(std::current_exception());
393 }
394 }
395
396 if (runtime_config.progress &&
397 clanguml::logging::logger_type() == logging::logger_type_t::text) {
398 indicator->stop();
399 if (errors.empty()) {
400 std::cout << termcolor::white << "Done\n";
401 }
402 else {
403 std::cout << termcolor::white << "\n";
404 }
405 std::cout << termcolor::reset;
406 }
407
408 if (errors.empty())
409 return 0;
410
411 for (auto &e : errors) {
412 try {
413 std::rethrow_exception(e);
414 }
417 logging::logger_type_t::text) {
418
419 fmt::println("ERROR: Failed to generate {} diagram '{}' due to "
420 "following issues:",
421 e.diagram_type(), e.diagram_name());
422 for (const auto &d : e.diagnostics) {
423 fmt::println(" - {}", d);
424 }
425 fmt::println("");
426 }
427 else {
428 inja::json j;
429 j["diagram_name"] = e.diagram_name();
430 j["clang_errors"] = inja::json::array();
431 for (const auto &d : e.diagnostics) {
432 j["clang_errors"].emplace_back(d);
433 }
434
435 spdlog::get("clanguml-logger")
436 ->log(spdlog::level::err,
437 fmt::runtime(
438 R"("file": "{}", "line": {}, "message": {})"),
439 FILENAME_, __LINE__, j.dump());
440 }
441 }
442 catch (const std::exception &e) {
444 logging::logger_type_t::text) {
445 fmt::println("ERROR: {}", e.what());
446 }
447 else {
448 LOG_ERROR("{}", e.what());
449 }
450 }
451 }
452
453 return 1;
454}

◆ make_context_source_relative()

void clanguml::common::generators::make_context_source_relative ( inja::json &  context,
const std::string &  prefix 
)

Definition at line 24 of file generators.cc.

26{
27 if (!context.contains("element"))
28 return;
29
30 if (!context["element"].contains("source"))
31 return;
32
33 auto &source = context["element"]["source"];
34
35 if (source.at("path").empty())
36 return;
37
38 auto path = std::filesystem::path{source.at("path").get<std::string>()};
39 auto prefix_path = std::filesystem::path(prefix);
40 if (path.is_absolute() && util::is_relative_to(path, prefix_path)) {
41 source["path"] = relative(path, prefix_path);
42 return;
43 }
44}

◆ render_diagram()

void clanguml::common::generators::render_diagram ( const clanguml::common::generator_type_t  generator_type,
std::shared_ptr< config::diagram diagram_config 
)

Definition at line 67 of file generators.cc.

69{
70 std::string cmd;
71 switch (generator_type) {
73 cmd = diagram_config->puml().cmd;
74 break;
76 cmd = diagram_config->mermaid().cmd;
77 break;
78 default:
79 return;
80 };
81
82 if (cmd.empty())
83 throw std::runtime_error(
84 fmt::format("No render command template provided for {} diagrams",
85 to_string(diagram_config->type())));
86
87 util::replace_all(cmd, "{}", diagram_config->name);
88
89 LOG_INFO("Rendering diagram {} using {}", diagram_config->name,
90 to_string(generator_type));
91
92 util::check_process_output(cmd);
93}