0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
generators.h
Go to the documentation of this file.
1/**
2 * @file src/common/generators/generators.h
3 *
4 * Copyright (c) 2021-2025 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
24#include "cli/cli_handler.h"
28#include "config/config.h"
33#include "indicators/indicators.hpp"
41#include "util/util.h"
42
43#include <clang/Frontend/CompilerInstance.h>
44#include <clang/Tooling/Tooling.h>
45
46#include <cstring>
47#include <filesystem>
48#include <fstream>
49#include <future>
50#include <iostream>
51#include <map>
52#include <string>
54#include <vector>
55
57
58/** @defgroup diagram_model_t Diagram model selector
59 *
60 * Template traits for selecting diagram model type based on diagram config
61 * type
62 *
63 * @{
64 */
65template <typename DiagramConfig> struct diagram_model_t;
68};
71};
74};
77};
78/** @} */
79
80/** @defgroup diagram_visitor_t Diagram model selector
81 *
82 * Template traits for selecting diagram visitor type based on diagram config
83 * type
84 *
85 * @{
86 */
87template <typename DiagramConfig> struct diagram_visitor_t;
90};
93};
96};
99};
100/** @} */
101
102/** @defgroup diagram_generator_tag Diagram model tags
103 *
104 * Tags to determine the generator output file extension
105 *
106 * @{
107 */
109 inline static const std::string extension = "puml";
110};
112 inline static const std::string extension = "json";
113};
115 inline static const std::string extension = "mmd";
116};
118 inline static const std::string extension = "graphml";
119};
120/** @} */
121
122/** @defgroup diagram_generator_t Diagram generator selector
123 *
124 * Tags to determine the generator type based on diagram config type
125 * and output format
126 *
127 * @{
128 */
129struct not_supported { };
130
131// plantuml
132template <typename DiagramConfig, typename GeneratorType>
134template <>
138};
139template <>
143};
144template <>
148};
149template <>
153};
154// json
155template <>
159};
160template <>
164};
165template <>
169};
170template <>
174};
175// mermaid
176template <>
180};
181template <>
185};
186template <>
190};
191template <>
195};
196// graphml
197template <>
201};
202template <>
206};
207template <>
211};
212template <>
216};
217
218template <typename GeneratorTag>
221{
223
224 switch (dt) {
225 case diagram_t::kClass:
226 return !std::is_same_v<not_supported,
228 GeneratorTag>::type>;
229 case diagram_t::kSequence:
230 return !std::is_same_v<not_supported,
232 GeneratorTag>::type>;
233 case diagram_t::kPackage:
234 return !std::is_same_v<not_supported,
236 GeneratorTag>::type>;
237 case diagram_t::kInclude:
238 return !std::is_same_v<not_supported,
240 GeneratorTag>::type>;
241 default:
242 return false;
243 }
244}
245/** @} */
246
247/**
248 * @brief Assign translation units to diagrams
249 *
250 * This function assigns for each diagram to be generated the list of
251 * translation units based on it's `glob` pattern if any.
252 *
253 * If `diagram_names` is empty, this function processes all diagrams in
254 * `config`.
255 *
256 * @param diagram_names List of diagram names, applies to all if empty
257 * @param config Reference to config instance
258 * @param compilation_database_files List of files found in compilation database
259 * @param translation_units_map Resulting translation units map is stored here
260 */
262 const std::vector<std::string> &diagram_names,
264 const std::vector<std::string> &compilation_database_files,
265 std::map<std::string, std::vector<std::string>> &translation_units_map);
266
267/**
268 * @brief Specialization of
269 * [clang::ASTConsumer](https://clang.llvm.org/doxygen/classclang_1_1ASTConsumer.html)
270 *
271 * This class provides overriden HandleTranslationUnit() method, which
272 * calls a translation_unit_visitor for a specific diagram type on
273 * each translation unit assigned to the diagram.
274 *
275 * @tparam DiagramModel Type of diagram_model
276 * @tparam DiagramConfig Type of diagram_config
277 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
278 */
279template <typename DiagramModel, typename DiagramConfig,
280 typename TranslationUnitVisitor>
281class diagram_ast_consumer : public clang::ASTConsumer {
282 TranslationUnitVisitor visitor_;
283
284public:
285 explicit diagram_ast_consumer(clang::CompilerInstance &ci,
286 DiagramModel &diagram, const DiagramConfig &config)
287 : visitor_{ci.getSourceManager(), diagram, config}
288 {
289 }
290
291 TranslationUnitVisitor &visitor() { return visitor_; }
292
293 void HandleTranslationUnit(clang::ASTContext &ast_context) override
294 {
295 visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
296 visitor_.finalize();
297 }
298};
299
300/**
301 * @brief Specialization of
302 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1ASTFrontendAction.html)
303 *
304 * This class overrides the BeginSourceFileAction() and CreateASTConsumer()
305 * methods to create and setup an appropriate diagram_ast_consumer instance.
306 *
307 * @tparam DiagramModel Type of diagram_model
308 * @tparam DiagramConfig Type of diagram_config
309 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
310 */
311template <typename DiagramModel, typename DiagramConfig,
312 typename DiagramVisitor>
313class diagram_fronted_action : public clang::ASTFrontendAction {
314public:
315 explicit diagram_fronted_action(DiagramModel &diagram,
316 const DiagramConfig &config, std::function<void()> progress)
317 : diagram_{diagram}
318 , config_{config}
319 , progress_{std::move(progress)}
320 {
321 }
322
323 std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
324 clang::CompilerInstance &CI, clang::StringRef /*file*/) override
325 {
326 auto ast_consumer = std::make_unique<
328 CI, diagram_, config_);
329
330 if constexpr (!std::is_same_v<DiagramModel,
332 ast_consumer->visitor().set_tu_path(getCurrentFile().str());
333 }
334
335 return ast_consumer;
336 }
337
338protected:
339 bool BeginSourceFileAction(clang::CompilerInstance &ci) override
340 {
341 LOG_DBG("Visiting source file: {}", getCurrentFile().str());
342
343 // Update progress indicators, if enabled, on each translation
344 // unit
345 if (progress_)
346 progress_();
347
348 if constexpr (std::is_same_v<DiagramModel,
350 auto find_includes_callback =
351 std::make_unique<typename DiagramVisitor::include_visitor>(
352 ci.getSourceManager(), diagram_, config_);
353
354 clang::Preprocessor &pp = ci.getPreprocessor();
355
356 pp.addPPCallbacks(std::move(find_includes_callback));
357 }
358
359 return true;
360 }
361
362private:
363 DiagramModel &diagram_;
364 const DiagramConfig &config_;
365 std::function<void()> progress_;
366};
367
368/**
369 * @brief Specialization of
370 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
371 *
372 * This class overrides the create() method in order to create an instance
373 * of diagram_frontend_action of appropriate type.
374 *
375 * @tparam DiagramModel Type of diagram_model
376 * @tparam DiagramConfig Type of diagram_config
377 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
378 */
379template <typename DiagramModel, typename DiagramConfig,
380 typename DiagramVisitor>
382 : public clang::tooling::FrontendActionFactory {
383public:
384 explicit diagram_action_visitor_factory(DiagramModel &diagram,
385 const DiagramConfig &config, std::function<void()> progress)
386 : diagram_{diagram}
387 , config_{config}
388 , progress_{std::move(progress)}
389 {
390 }
391
392 std::unique_ptr<clang::FrontendAction> create() override
393 {
394 return std::make_unique<diagram_fronted_action<DiagramModel,
395 DiagramConfig, DiagramVisitor>>(diagram_, config_, progress_);
396 }
397
398private:
399 DiagramModel &diagram_;
400 const DiagramConfig &config_;
401 std::function<void()> progress_;
402};
403
404/**
405 * @brief Specialization of
406 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
407 *
408 * This is the entry point function to initiate AST frontend action for a
409 * specific diagram.
410 *
411 * @embed{diagram_generate_generic_sequence.svg}
412 *
413 * @tparam DiagramModel Type of diagram_model
414 * @tparam DiagramConfig Type of diagram_config
415 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
416 */
417template <typename DiagramModel, typename DiagramConfig,
418 typename DiagramVisitor>
419std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
420 const std::string &name, DiagramConfig &config,
421 const std::vector<std::string> &translation_units, bool /*verbose*/ = false,
422 std::function<void()> progress = {})
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}
452
453/**
454 * @brief Generate a single diagram
455 *
456 * @param name Name of the diagram
457 * @param diagram Effective diagram configuration
458 * @param db Reference to compilation database
459 * @param translation_units List of translation units for the diagram
460 * @param generators List of generator types to be used for the diagram
461 * @param verbose Log level
462 * @param progress Function to report translation unit progress
463 */
464void generate_diagram(const std::string &name,
465 std::shared_ptr<clanguml::config::diagram> diagram,
467 const std::vector<std::string> &translation_units,
468 const cli::runtime_config &runtime_config,
469 std::function<void()> &&progress);
470
471/**
472 * @brief Generate diagrams
473 *
474 * @param diagram_names List of diagram names to generate
475 * @param config Reference to config instance
476 * @param output_directory Path to output directory
477 * @param db Reference to compilation database
478 * @param verbose Log level
479 * @param thread_count Number of diagrams to be generated in parallel
480 * @param progress Whether progress indicators should be displayed
481 * @param generators List of generator types to use for each diagram
482 * @param translation_units_map Map of translation units for each file
483 *
484 * @return 0 if success, otherwise error code
485 */
486int generate_diagrams(const std::vector<std::string> &diagram_names,
489 const cli::runtime_config &runtime_config,
490 const std::map<std::string, std::vector<std::string>>
491 &translation_units_map);
492
493/**
494 * @brief Return indicators progress bar color for diagram type
495 *
496 * @param diagram_type Diagram type
497 * @return Progress bar color
498 */
499indicators::Color diagram_type_to_color(model::diagram_t diagram_type);
500
501} // namespace clanguml::common::generators