0.5.4
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-2024 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
23#include "cli/cli_handler.h"
26#include "config/config.h"
30#include "indicators/indicators.hpp"
37#include "util/util.h"
38#include "version.h"
39
40#include <clang/Frontend/CompilerInstance.h>
41#include <clang/Tooling/Tooling.h>
42
43#include <cstring>
44#include <filesystem>
45#include <fstream>
46#include <future>
47#include <iostream>
48#include <map>
49#include <string>
51#include <vector>
52
54
55/** @defgroup diagram_model_t Diagram model selector
56 *
57 * Template traits for selecting diagram model type based on diagram config
58 * type
59 *
60 * @{
61 */
62template <typename DiagramConfig> struct diagram_model_t;
65};
68};
71};
74};
75/** @} */
76
77/** @defgroup diagram_visitor_t Diagram model selector
78 *
79 * Template traits for selecting diagram visitor type based on diagram config
80 * type
81 *
82 * @{
83 */
84template <typename DiagramConfig> struct diagram_visitor_t;
87};
90};
93};
96};
97/** @} */
98
99/** @defgroup diagram_generator_tag Diagram model tags
100 *
101 * Tags to determine the generator output file extension
102 *
103 * @{
104 */
106 inline static const std::string extension = "puml";
107};
109 inline static const std::string extension = "json";
110};
112 inline static const std::string extension = "mmd";
113};
114/** @} */
115
116/** @defgroup diagram_generator_t Diagram generator selector
117 *
118 * Tags to determine the generator type based on diagram config type
119 * and output format
120 *
121 * @{
122 */
123// plantuml
124template <typename DiagramConfig, typename GeneratorType>
126template <>
130};
131template <>
135};
136template <>
140};
141template <>
145};
146// json
147template <>
151};
152template <>
156};
157template <>
161};
162template <>
166};
167// mermaid
168template <>
172};
173template <>
177};
178template <>
182};
183template <>
187};
188/** @} */
189
190/**
191 * @brief Assign translation units to diagrams
192 *
193 * This function assigns for each diagram to be generated the list of
194 * translation units based on it's `glob` pattern if any.
195 *
196 * If `diagram_names` is empty, this function processes all diagrams in
197 * `config`.
198 *
199 * @param diagram_names List of diagram names, applies to all if empty
200 * @param config Reference to config instance
201 * @param compilation_database_files List of files found in compilation database
202 * @param translation_units_map Resulting translation units map is stored here
203 */
205 const std::vector<std::string> &diagram_names,
207 const std::vector<std::string> &compilation_database_files,
208 std::map<std::string, std::vector<std::string>> &translation_units_map);
209
210/**
211 * @brief Specialization of
212 * [clang::ASTConsumer](https://clang.llvm.org/doxygen/classclang_1_1ASTConsumer.html)
213 *
214 * This class provides overriden HandleTranslationUnit() method, which
215 * calls a translation_unit_visitor for a specific diagram type on
216 * each translation unit assigned to the diagram.
217 *
218 * @tparam DiagramModel Type of diagram_model
219 * @tparam DiagramConfig Type of diagram_config
220 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
221 */
222template <typename DiagramModel, typename DiagramConfig,
223 typename TranslationUnitVisitor>
224class diagram_ast_consumer : public clang::ASTConsumer {
225 TranslationUnitVisitor visitor_;
226
227public:
228 explicit diagram_ast_consumer(clang::CompilerInstance &ci,
229 DiagramModel &diagram, const DiagramConfig &config)
230 : visitor_{ci.getSourceManager(), diagram, config}
231 {
232 }
233
234 TranslationUnitVisitor &visitor() { return visitor_; }
235
236 void HandleTranslationUnit(clang::ASTContext &ast_context) override
237 {
238 visitor_.TraverseDecl(ast_context.getTranslationUnitDecl());
239 visitor_.finalize();
240 }
241};
242
243/**
244 * @brief Specialization of
245 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1ASTFrontendAction.html)
246 *
247 * This class overrides the BeginSourceFileAction() and CreateASTConsumer()
248 * methods to create and setup an appropriate diagram_ast_consumer instance.
249 *
250 * @tparam DiagramModel Type of diagram_model
251 * @tparam DiagramConfig Type of diagram_config
252 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
253 */
254template <typename DiagramModel, typename DiagramConfig,
255 typename DiagramVisitor>
256class diagram_fronted_action : public clang::ASTFrontendAction {
257public:
258 explicit diagram_fronted_action(DiagramModel &diagram,
259 const DiagramConfig &config, std::function<void()> progress)
260 : diagram_{diagram}
261 , config_{config}
262 , progress_{std::move(progress)}
263 {
264 }
265
266 std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
267 clang::CompilerInstance &CI, clang::StringRef /*file*/) override
268 {
269 auto ast_consumer = std::make_unique<
271 CI, diagram_, config_);
272
273 if constexpr (!std::is_same_v<DiagramModel,
275 ast_consumer->visitor().set_tu_path(getCurrentFile().str());
276 }
277
278 return ast_consumer;
279 }
280
281protected:
282 bool BeginSourceFileAction(clang::CompilerInstance &ci) override
283 {
284 LOG_DBG("Visiting source file: {}", getCurrentFile().str());
285
286 // Update progress indicators, if enabled, on each translation
287 // unit
288 if (progress_)
289 progress_();
290
291 if constexpr (std::is_same_v<DiagramModel,
293 auto find_includes_callback =
294 std::make_unique<typename DiagramVisitor::include_visitor>(
295 ci.getSourceManager(), diagram_, config_);
296
297 clang::Preprocessor &pp = ci.getPreprocessor();
298
299 pp.addPPCallbacks(std::move(find_includes_callback));
300 }
301
302 return true;
303 }
304
305private:
306 DiagramModel &diagram_;
307 const DiagramConfig &config_;
308 std::function<void()> progress_;
309};
310
311/**
312 * @brief Specialization of
313 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
314 *
315 * This class overrides the create() method in order to create an instance
316 * of diagram_frontend_action of appropriate type.
317 *
318 * @tparam DiagramModel Type of diagram_model
319 * @tparam DiagramConfig Type of diagram_config
320 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
321 */
322template <typename DiagramModel, typename DiagramConfig,
323 typename DiagramVisitor>
325 : public clang::tooling::FrontendActionFactory {
326public:
327 explicit diagram_action_visitor_factory(DiagramModel &diagram,
328 const DiagramConfig &config, std::function<void()> progress)
329 : diagram_{diagram}
330 , config_{config}
331 , progress_{std::move(progress)}
332 {
333 }
334
335 std::unique_ptr<clang::FrontendAction> create() override
336 {
337 return std::make_unique<diagram_fronted_action<DiagramModel,
338 DiagramConfig, DiagramVisitor>>(diagram_, config_, progress_);
339 }
340
341private:
342 DiagramModel &diagram_;
343 const DiagramConfig &config_;
344 std::function<void()> progress_;
345};
346
347/**
348 * @brief Specialization of
349 * [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
350 *
351 * This is the entry point function to initiate AST frontend action for a
352 * specific diagram.
353 *
354 * @embed{diagram_generate_generic_sequence.svg}
355 *
356 * @tparam DiagramModel Type of diagram_model
357 * @tparam DiagramConfig Type of diagram_config
358 * @tparam TranslationUnitVisitor Type of translation_unit_visitor
359 */
360template <typename DiagramModel, typename DiagramConfig,
361 typename DiagramVisitor>
362std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
363 const std::string &name, DiagramConfig &config,
364 const std::vector<std::string> &translation_units, bool /*verbose*/ = false,
365 std::function<void()> progress = {})
366{
367 LOG_INFO("Generating diagram {}", name);
368
369 auto diagram = std::make_unique<DiagramModel>();
370 diagram->set_name(name);
371 diagram->set_filter(
372 model::diagram_filter_factory::create(*diagram, config));
373
374 LOG_DBG("Found translation units for diagram {}: {}", name,
375 fmt::join(translation_units, ", "));
376
377 clang::tooling::ClangTool clang_tool(db, translation_units);
378 auto action_factory =
379 std::make_unique<diagram_action_visitor_factory<DiagramModel,
380 DiagramConfig, DiagramVisitor>>(
381 *diagram, config, std::move(progress));
382
383 auto res = clang_tool.run(action_factory.get());
384
385 if (res != 0) {
386 throw std::runtime_error("Diagram " + name + " generation failed");
387 }
388
389 diagram->set_complete(true);
390
391 diagram->finalize();
392
393 return diagram;
394}
395
396/**
397 * @brief Generate a single diagram
398 *
399 * @param name Name of the diagram
400 * @param diagram Effective diagram configuration
401 * @param db Reference to compilation database
402 * @param translation_units List of translation units for the diagram
403 * @param generators List of generator types to be used for the diagram
404 * @param verbose Log level
405 * @param progress Function to report translation unit progress
406 */
407void generate_diagram(const std::string &name,
408 std::shared_ptr<clanguml::config::diagram> diagram,
409 const common::compilation_database &db,
410 const std::vector<std::string> &translation_units,
411 const cli::runtime_config &runtime_config,
412 std::function<void()> &&progress);
413
414/**
415 * @brief Generate diagrams
416 *
417 * @param diagram_names List of diagram names to generate
418 * @param config Reference to config instance
419 * @param output_directory Path to output directory
420 * @param db Reference to compilation database
421 * @param verbose Log level
422 * @param thread_count Number of diagrams to be generated in parallel
423 * @param progress Whether progress indicators should be displayed
424 * @param generators List of generator types to use for each diagram
425 * @param translation_units_map Map of translation units for each file
426 */
427void generate_diagrams(const std::vector<std::string> &diagram_names,
430 const cli::runtime_config &runtime_config,
431 const std::map<std::string, std::vector<std::string>>
432 &translation_units_map);
433
434/**
435 * @brief Return indicators progress bar color for diagram type
436 *
437 * @param diagram_type Diagram type
438 * @return Progress bar color
439 */
440indicators::Color diagram_type_to_color(model::diagram_t diagram_type);
441
442} // namespace clanguml::common::generators