0.6.0
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 std::vector< std::string > &compilation_database_files, 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 455 of file generators.cc.

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

◆ 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 std::vector< std::string > &  compilation_database_files,
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_database_filesList of files found in 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_files);
60
61 LOG_DBG("Found {} translation units for diagram '{}'",
62 translation_units_map.at(name).size(), name);
63 }
64}

◆ 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 212 of file generators.cc.

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

◆ 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 244 of file generators.cc.

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

◆ 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 66 of file generators.cc.

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