Compare commits
7 Commits
add-radius
...
add-option
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c1cd3578f | ||
|
|
146c79ac54 | ||
|
|
15a32bdc4c | ||
|
|
7d607848cc | ||
|
|
35f45a07e6 | ||
|
|
429e1c47a9 | ||
|
|
19233e3a62 |
@@ -4,6 +4,10 @@ comment_parser: clang
|
||||
remove_compile_flags:
|
||||
- -Wno-class-memaccess
|
||||
- -Wno-dangling-reference
|
||||
plantuml:
|
||||
cmd: "plantuml -tsvg \"docs/diagrams/{}.puml\""
|
||||
mermaid:
|
||||
cmd: "mmdc -i \"docs/diagrams/{}.mmd\" -o \"docs/diagrams/{}.svg\""
|
||||
generate_links:
|
||||
link: "{% if existsIn(element, \"doxygen_link\") %}{{ element.doxygen_link }}{% endif %}"
|
||||
tooltip: "{% if existsIn(element, \"comment\") and existsIn(element.comment, \"brief\") %}{{ abbrv(trim(replace(element.comment.brief.0, \"\\n+\", \" \")), 256) }}{% else %}{{ element.name }}{% endif %}"
|
||||
@@ -29,7 +33,7 @@ diagrams:
|
||||
include!: uml/class/stylable_element_hierarchy_class.yml
|
||||
source_location_hierarchy_class:
|
||||
include!: uml/class/source_location_hierarchy_class.yml
|
||||
filter_visitor_hierarchy_class:
|
||||
"filter_visitor_hierarchy_class":
|
||||
include!: uml/class/filter_visitor_hierarchy_class.yml
|
||||
diagram_filter_context_class:
|
||||
include!: uml/class/diagram_filter_context_class.yml
|
||||
|
||||
@@ -47,6 +47,9 @@ endif(APPLE)
|
||||
option(LINK_LLVM_SHARED "Should LLVM be linked using shared libraries" ON)
|
||||
set(LLVM_VERSION CACHE STRING "Major LLVM version to use (e.g. 15)")
|
||||
set(LLVM_CONFIG_PATH CACHE STRING "Path to llvm-config binary")
|
||||
set(CMAKE_PREFIX CACHE STRING "Path to custom cmake modules")
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_PREFIX}")
|
||||
|
||||
#
|
||||
# Setup version string
|
||||
@@ -96,8 +99,8 @@ if(APPLE)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(YAML_CPP yaml-cpp)
|
||||
find_path(YAML_CPP_INCLUDE_DIR
|
||||
NAMES yaml.h
|
||||
PATHS ${YAML_CPP_INCLUDE_DIR} ${HOMEBREW_PREFIX}/include/yaml-cpp)
|
||||
NAMES yaml-cpp/yaml.h
|
||||
PATHS ${YAML_CPP_INCLUDE_DIR} ${HOMEBREW_PREFIX}/include)
|
||||
find_library(YAML_CPP_LIBRARY
|
||||
NAMES yaml-cpp
|
||||
PATHS ${YAML_CPP_LIBRARIES} ${HOMEBREW_PREFIX}/lib)
|
||||
@@ -138,11 +141,11 @@ set(THIRDPARTY_HEADERS_DIR ${PROJECT_SOURCE_DIR}/thirdparty/)
|
||||
#
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
include_directories(${CLANG_UML_INSTALL_INCLUDE_DIR})
|
||||
include_directories(${YAML_CPP_INCLUDE_DIRS})
|
||||
include_directories(${UML_HEADERS_DIR})
|
||||
include_directories(${THIRDPARTY_HEADERS_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src/)
|
||||
include_directories(${PROJECT_BINARY_DIR}/src/version)
|
||||
include_directories(${YAML_CPP_INCLUDE_DIR})
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -32,6 +32,7 @@ endif
|
||||
|
||||
LLVM_VERSION ?=
|
||||
LLVM_CONFIG_PATH ?=
|
||||
CMAKE_PREFIX ?=
|
||||
CMAKE_CXX_FLAGS ?=
|
||||
CMAKE_EXE_LINKER_FLAGS ?=
|
||||
|
||||
@@ -54,7 +55,8 @@ debug/CMakeLists.txt:
|
||||
-DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS)" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \
|
||||
-DLLVM_VERSION=${LLVM_VERSION} \
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH}
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \
|
||||
-DCMAKE_PREFIX=${CMAKE_PREFIX}
|
||||
|
||||
release/CMakeLists.txt:
|
||||
cmake -S . -B release \
|
||||
@@ -64,7 +66,8 @@ release/CMakeLists.txt:
|
||||
-DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS)" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \
|
||||
-DLLVM_VERSION=${LLVM_VERSION} \
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH}
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \
|
||||
-DCMAKE_PREFIX=${CMAKE_PREFIX}
|
||||
|
||||
debug_tidy/CMakeLists.txt:
|
||||
cmake -S . -B debug_tidy \
|
||||
@@ -75,7 +78,8 @@ debug_tidy/CMakeLists.txt:
|
||||
-DCMAKE_CXX_FLAGS="$(CMAKE_CXX_FLAGS)" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$(CMAKE_EXE_LINKER_FLAGS)" \
|
||||
-DLLVM_VERSION=${LLVM_VERSION} \
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH}
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \
|
||||
-DCMAKE_PREFIX=${CMAKE_PREFIX}
|
||||
|
||||
debug: debug/CMakeLists.txt
|
||||
echo "Using ${NUMPROC} cores"
|
||||
|
||||
@@ -43,6 +43,14 @@ will add before the diagram contents (right after `@startuml`) the title and
|
||||
direction hint, and after each diagram contents (right before `@enduml`)
|
||||
2 notes attached to elements.
|
||||
|
||||
This generator also accepts a `cmd` parameter to specify a command to execute
|
||||
on the generated PlantUML source file to generate actual diagram image, for
|
||||
instance:
|
||||
```yaml
|
||||
plantuml:
|
||||
cmd: "/usr/bin/plantuml -tsvg \"diagrams/{}.puml\""
|
||||
```
|
||||
|
||||
An example PlantUML diagram is presented below:
|
||||
|
||||
```plantuml
|
||||
@@ -117,6 +125,14 @@ will add before the diagram contents (right after diagram type,
|
||||
e.g. `classDiagram`) diagram direction hint, and after each diagram contents
|
||||
2 notes attached to elements.
|
||||
|
||||
This generator also accepts a `cmd` parameter to specify a command to execute
|
||||
on the generated MermaidJS source file to generate actual diagram image, for
|
||||
instance:
|
||||
```yaml
|
||||
mermaid:
|
||||
cmd: "mmdc -i \"diagrams/{}.mmd\" -o \"diagrams/{}_mermaid.svg\""
|
||||
```
|
||||
|
||||
An example MermaidJS diagram is presented below:
|
||||
|
||||
```
|
||||
|
||||
@@ -52,6 +52,12 @@ To add an initial class diagram to your project, follow these steps:
|
||||
mmdc -i diagrams/some_class_diagram.mmd -o diagrams/some_class_diagram.svg
|
||||
```
|
||||
|
||||
Steps 3 and 4 can be combined into one step like follows:
|
||||
```
|
||||
clang-uml -p -n some_class_diagram -g plantuml -r --plantuml-cmd="plantuml -tsvg diagrams/{}.puml"
|
||||
```
|
||||
where `-r` enables diagram rendering and `--plantuml-cmd` specifies command
|
||||
to execute on each generated diagram.
|
||||
5. Add another diagram:
|
||||
```bash
|
||||
clang-uml --add-sequence-diagram another_diagram
|
||||
|
||||
@@ -138,4 +138,4 @@ conda:
|
||||
$(call subst_conda_meta_yaml,PKG_SOURCE,..\/_BUILD\/conda\/clang-uml-$(VERSION).tar.$(TAR_EXT),conda)
|
||||
$(call subst_conda_meta_yaml,GIT_COMMIT,${COMMIT},conda)
|
||||
$(call subst_conda_meta_yaml,GIT_BRANCH,${BRANCH},conda)
|
||||
conda build --user bkryza --token $(CONDA_TOKEN) ./conda
|
||||
conda build --user bkryza --token $(CONDA_TOKEN) --label clang-uml ./conda
|
||||
|
||||
@@ -60,5 +60,8 @@ _arguments \
|
||||
"--print-from[Print all possible 'from' values for a given diagram]" \
|
||||
"--print-to[Print all possible 'to' values for a given diagram]" \
|
||||
'--no-validate[Do not perform configuration file schema validation]' \
|
||||
'--validate-only[Perform configuration file schema validation and exit]'
|
||||
'--validate-only[Perform configuration file schema validation and exit]' \
|
||||
{-r,--render_diagrams}'[Automatically render generated diagrams using appropriate command]' \
|
||||
'--plantuml-cmd[Command template to render PlantUML diagram, `{}` will be replaced with diagram name]' \
|
||||
'--mermaid-cmd[Command template to render MermaidJS diagram, `{}` will be replaced with diagram name]'
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ _clanguml() {
|
||||
-p --progress \
|
||||
-q --quiet \
|
||||
-l --list-diagrams \
|
||||
-r --render_diagrams \
|
||||
--init \
|
||||
--add-compile-flag \
|
||||
--remove-compile-flag \
|
||||
@@ -111,7 +112,9 @@ _clanguml() {
|
||||
--print-from \
|
||||
--print-to \
|
||||
--no-validate \
|
||||
--validate-only' -- $cur ) )
|
||||
--validate-only \
|
||||
--plantuml-cmd \
|
||||
--mermaid-cmd' -- $cur ) )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -138,6 +138,14 @@ cli_flow_t cli_handler::parse(int argc, const char **argv)
|
||||
"Do not perform configuration file schema validation");
|
||||
app.add_flag("--validate-only", validate_only,
|
||||
"Perform configuration file schema validation and exit");
|
||||
app.add_flag("-r,--render_diagrams", render_diagrams,
|
||||
"Automatically render generated diagrams using appropriate command");
|
||||
app.add_option("--plantuml-cmd", plantuml_cmd,
|
||||
"Command template to render PlantUML diagram, `{}` will be replaced "
|
||||
"with diagram name.");
|
||||
app.add_option("--mermaid-cmd", mermaid_cmd,
|
||||
"Command template to render MermaidJS diagram, `{}` will be replaced "
|
||||
"with diagram name.");
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
@@ -184,6 +192,8 @@ cli_flow_t cli_handler::handle_options(int argc, const char **argv)
|
||||
|
||||
res = handle_post_config_options();
|
||||
|
||||
config.inherit();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -250,8 +260,8 @@ cli_flow_t cli_handler::handle_pre_config_options()
|
||||
cli_flow_t cli_handler::load_config()
|
||||
{
|
||||
try {
|
||||
config = clanguml::config::load(
|
||||
config_path, paths_relative_to_pwd, no_metadata, !no_validate);
|
||||
config = clanguml::config::load(config_path, false,
|
||||
paths_relative_to_pwd, no_metadata, !no_validate);
|
||||
if (validate_only) {
|
||||
std::cout << "Configuration file " << config_path << " is valid.\n";
|
||||
|
||||
@@ -334,6 +344,20 @@ cli_flow_t cli_handler::handle_post_config_options()
|
||||
config.remove_compile_flags.has_value = true;
|
||||
}
|
||||
|
||||
if (plantuml_cmd) {
|
||||
if (!config.puml)
|
||||
config.puml.set({});
|
||||
|
||||
config.puml().cmd = plantuml_cmd.value();
|
||||
}
|
||||
|
||||
if (mermaid_cmd) {
|
||||
if (!config.mermaid)
|
||||
config.mermaid.set({});
|
||||
|
||||
config.mermaid().cmd = mermaid_cmd.value();
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
if (query_driver) {
|
||||
config.query_driver.set(*query_driver);
|
||||
@@ -352,6 +376,8 @@ runtime_config cli_handler::get_runtime_config() const
|
||||
cfg.print_to = print_to;
|
||||
cfg.progress = progress;
|
||||
cfg.thread_count = thread_count;
|
||||
cfg.render_diagrams = render_diagrams;
|
||||
cfg.output_directory = effective_output_directory;
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ struct runtime_config {
|
||||
bool print_to{};
|
||||
bool progress{};
|
||||
unsigned int thread_count{};
|
||||
bool render_diagrams{};
|
||||
std::string output_directory{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -185,6 +187,9 @@ public:
|
||||
clanguml::common::generator_type_t::plantuml};
|
||||
bool no_validate{false};
|
||||
bool validate_only{false};
|
||||
bool render_diagrams{false};
|
||||
std::optional<std::string> plantuml_cmd;
|
||||
std::optional<std::string> mermaid_cmd;
|
||||
|
||||
clanguml::config::config config;
|
||||
|
||||
|
||||
@@ -57,6 +57,34 @@ void find_translation_units_for_diagrams(
|
||||
}
|
||||
}
|
||||
|
||||
void render_diagram(const clanguml::common::generator_type_t generator_type,
|
||||
std::shared_ptr<config::diagram> diagram_config)
|
||||
{
|
||||
std::string cmd;
|
||||
switch (generator_type) {
|
||||
case clanguml::common::generator_type_t::plantuml:
|
||||
cmd = diagram_config->puml().cmd;
|
||||
break;
|
||||
case clanguml::common::generator_type_t::mermaid:
|
||||
cmd = diagram_config->mermaid().cmd;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
};
|
||||
|
||||
if (cmd.empty())
|
||||
throw std::runtime_error(
|
||||
fmt::format("No render command template provided for {} diagrams",
|
||||
to_string(diagram_config->type())));
|
||||
|
||||
util::replace_all(cmd, "{}", diagram_config->name);
|
||||
|
||||
LOG_INFO("Rendering diagram {} using {}", diagram_config->name,
|
||||
to_string(generator_type));
|
||||
|
||||
util::check_process_output(cmd);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename DiagramConfig, typename GeneratorTag, typename DiagramModel>
|
||||
@@ -79,11 +107,11 @@ void generate_diagram_select_generator(const std::string &od,
|
||||
}
|
||||
|
||||
template <typename DiagramConfig>
|
||||
void generate_diagram_impl(const std::string &od, const std::string &name,
|
||||
void generate_diagram_impl(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 &rc, std::function<void()> &&progress)
|
||||
const cli::runtime_config &runtime_config, std::function<void()> &&progress)
|
||||
{
|
||||
using diagram_config = DiagramConfig;
|
||||
using diagram_model = typename diagram_model_t<DiagramConfig>::type;
|
||||
@@ -91,11 +119,11 @@ void generate_diagram_impl(const std::string &od, const std::string &name,
|
||||
|
||||
auto model = clanguml::common::generators::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(db, diagram->name,
|
||||
dynamic_cast<diagram_config &>(*diagram), translation_units, rc.verbose,
|
||||
std::move(progress));
|
||||
dynamic_cast<diagram_config &>(*diagram), translation_units,
|
||||
runtime_config.verbose, std::move(progress));
|
||||
|
||||
if constexpr (std::is_same_v<DiagramConfig, config::sequence_diagram>) {
|
||||
if (rc.print_from) {
|
||||
if (runtime_config.print_from) {
|
||||
auto from_values = model->list_from_values();
|
||||
|
||||
for (const auto &from : from_values) {
|
||||
@@ -104,7 +132,7 @@ void generate_diagram_impl(const std::string &od, const std::string &name,
|
||||
|
||||
return;
|
||||
}
|
||||
if (rc.print_to) {
|
||||
if (runtime_config.print_to) {
|
||||
auto to_values = model->list_to_values();
|
||||
|
||||
for (const auto &to : to_values) {
|
||||
@@ -115,24 +143,31 @@ void generate_diagram_impl(const std::string &od, const std::string &name,
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto generator_type : rc.generators) {
|
||||
for (const auto generator_type : runtime_config.generators) {
|
||||
if (generator_type == generator_type_t::plantuml) {
|
||||
generate_diagram_select_generator<diagram_config,
|
||||
plantuml_generator_tag>(od, name, diagram, model);
|
||||
plantuml_generator_tag>(
|
||||
runtime_config.output_directory, name, diagram, model);
|
||||
}
|
||||
else if (generator_type == generator_type_t::json) {
|
||||
generate_diagram_select_generator<diagram_config,
|
||||
json_generator_tag>(od, name, diagram, model);
|
||||
json_generator_tag>(
|
||||
runtime_config.output_directory, name, diagram, model);
|
||||
}
|
||||
else if (generator_type == generator_type_t::mermaid) {
|
||||
generate_diagram_select_generator<diagram_config,
|
||||
mermaid_generator_tag>(od, name, diagram, model);
|
||||
mermaid_generator_tag>(
|
||||
runtime_config.output_directory, name, diagram, model);
|
||||
}
|
||||
|
||||
if (runtime_config.render_diagrams) {
|
||||
render_diagram(generator_type, diagram);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
void generate_diagram(const std::string &od, const std::string &name,
|
||||
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,
|
||||
@@ -147,26 +182,25 @@ void generate_diagram(const std::string &od, const std::string &name,
|
||||
using clanguml::config::sequence_diagram;
|
||||
|
||||
if (diagram->type() == diagram_t::kClass) {
|
||||
detail::generate_diagram_impl<class_diagram>(od, name, diagram, db,
|
||||
detail::generate_diagram_impl<class_diagram>(name, diagram, db,
|
||||
translation_units, runtime_config, std::move(progress));
|
||||
}
|
||||
else if (diagram->type() == diagram_t::kSequence) {
|
||||
detail::generate_diagram_impl<sequence_diagram>(od, name, diagram, db,
|
||||
detail::generate_diagram_impl<sequence_diagram>(name, diagram, db,
|
||||
translation_units, runtime_config, std::move(progress));
|
||||
}
|
||||
else if (diagram->type() == diagram_t::kPackage) {
|
||||
detail::generate_diagram_impl<package_diagram>(od, name, diagram, db,
|
||||
detail::generate_diagram_impl<package_diagram>(name, diagram, db,
|
||||
translation_units, runtime_config, std::move(progress));
|
||||
}
|
||||
else if (diagram->type() == diagram_t::kInclude) {
|
||||
detail::generate_diagram_impl<include_diagram>(od, name, diagram, db,
|
||||
detail::generate_diagram_impl<include_diagram>(name, diagram, db,
|
||||
translation_units, runtime_config, std::move(progress));
|
||||
}
|
||||
}
|
||||
|
||||
void generate_diagrams(const std::vector<std::string> &diagram_names,
|
||||
config::config &config, const std::string &od,
|
||||
const common::compilation_database_ptr &db,
|
||||
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)
|
||||
@@ -206,7 +240,7 @@ void generate_diagrams(const std::vector<std::string> &diagram_names,
|
||||
continue;
|
||||
}
|
||||
|
||||
auto generator = [&od, &name = name, &diagram = diagram, &indicator,
|
||||
auto generator = [&name = name, &diagram = diagram, &indicator,
|
||||
db = std::ref(*db),
|
||||
translation_units = valid_translation_units,
|
||||
runtime_config]() mutable {
|
||||
@@ -215,7 +249,7 @@ void generate_diagrams(const std::vector<std::string> &diagram_names,
|
||||
indicator->add_progress_bar(name, translation_units.size(),
|
||||
diagram_type_to_color(diagram->type()));
|
||||
|
||||
generate_diagram(od, name, diagram, db, translation_units,
|
||||
generate_diagram(name, diagram, db, translation_units,
|
||||
runtime_config, [&indicator, &name]() {
|
||||
if (indicator)
|
||||
indicator->increment(name);
|
||||
|
||||
@@ -418,7 +418,7 @@ void generate_diagram(const std::string &od, const std::string &name,
|
||||
*
|
||||
* @param diagram_names List of diagram names to generate
|
||||
* @param config Reference to config instance
|
||||
* @param od Path to output directory
|
||||
* @param output_directory Path to output directory
|
||||
* @param db Reference to compilation database
|
||||
* @param verbose Log level
|
||||
* @param thread_count Number of diagrams to be generated in parallel
|
||||
@@ -427,7 +427,7 @@ void generate_diagram(const std::string &od, const std::string &name,
|
||||
* @param translation_units_map Map of translation units for each file
|
||||
*/
|
||||
void generate_diagrams(const std::vector<std::string> &diagram_names,
|
||||
clanguml::config::config &config, const std::string &od,
|
||||
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>>
|
||||
|
||||
@@ -234,7 +234,7 @@ inja::json generator<C, D>::element_context(const E &e) const
|
||||
ctx["element"]["source"]["line"] = e.line();
|
||||
}
|
||||
|
||||
const auto maybe_comment = e.comment();
|
||||
const auto &maybe_comment = e.comment();
|
||||
if (maybe_comment) {
|
||||
ctx["element"]["comment"] = maybe_comment.value();
|
||||
}
|
||||
|
||||
@@ -670,7 +670,7 @@ void context_filter::initialize(const diagram &d) const
|
||||
|
||||
// Prepare effective_contexts_
|
||||
for (auto i = 0U; i < context_.size(); i++) {
|
||||
effective_contexts_.push_back({});
|
||||
effective_contexts_.push_back({}); // NOLINT
|
||||
initialize_effective_context(d, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,17 @@ std::string to_string(const std::string &s) { return s; }
|
||||
|
||||
std::string to_string(const string_or_regex &sr) { return sr.to_string(); }
|
||||
|
||||
};
|
||||
std::string to_string(const generator_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case generator_type_t::plantuml:
|
||||
return "plantuml";
|
||||
case generator_type_t::mermaid:
|
||||
return "mermaid";
|
||||
case generator_type_t::json:
|
||||
return "json";
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
} // namespace clanguml::common
|
||||
|
||||
@@ -42,6 +42,8 @@ enum class generator_type_t {
|
||||
|
||||
std::string to_string(const std::string &s);
|
||||
|
||||
std::string to_string(generator_type_t type);
|
||||
|
||||
/**
|
||||
* @brief Simple optional reference type.
|
||||
*
|
||||
|
||||
@@ -170,12 +170,16 @@ void plantuml::append(const plantuml &r)
|
||||
{
|
||||
before.insert(before.end(), r.before.begin(), r.before.end());
|
||||
after.insert(after.end(), r.after.begin(), r.after.end());
|
||||
if (cmd.empty())
|
||||
cmd = r.cmd;
|
||||
}
|
||||
|
||||
void mermaid::append(const mermaid &r)
|
||||
{
|
||||
before.insert(before.end(), r.before.begin(), r.before.end());
|
||||
after.insert(after.end(), r.after.begin(), r.after.end());
|
||||
if (cmd.empty())
|
||||
cmd = r.cmd;
|
||||
}
|
||||
|
||||
void inheritable_diagram_options::inherit(
|
||||
@@ -188,6 +192,7 @@ void inheritable_diagram_options::inherit(
|
||||
include.override(parent.include);
|
||||
exclude.override(parent.exclude);
|
||||
puml.override(parent.puml);
|
||||
mermaid.override(parent.mermaid);
|
||||
generate_method_arguments.override(parent.generate_method_arguments);
|
||||
generate_packages.override(parent.generate_packages);
|
||||
generate_template_argument_dependencies.override(
|
||||
|
||||
@@ -121,6 +121,8 @@ struct plantuml {
|
||||
std::vector<std::string> before;
|
||||
/*! List of directives to add before diagram */
|
||||
std::vector<std::string> after;
|
||||
/*! Command template to render diagram using PlantUML */
|
||||
std::string cmd;
|
||||
|
||||
void append(const plantuml &r);
|
||||
};
|
||||
@@ -137,6 +139,8 @@ struct mermaid {
|
||||
std::vector<std::string> before;
|
||||
/*! List of directives to add before diagram */
|
||||
std::vector<std::string> after;
|
||||
/*! Command template to render diagram using MermaidJS */
|
||||
std::string cmd;
|
||||
|
||||
void append(const mermaid &r);
|
||||
};
|
||||
@@ -448,6 +452,10 @@ struct source_location {
|
||||
struct inheritable_diagram_options {
|
||||
virtual ~inheritable_diagram_options() = default;
|
||||
|
||||
void inherit(const inheritable_diagram_options &parent);
|
||||
|
||||
std::string simplify_template_type(std::string full_name) const;
|
||||
|
||||
option<std::vector<std::string>> glob{"glob"};
|
||||
option<common::model::namespace_> using_namespace{"using_namespace"};
|
||||
option<bool> include_relations_also_as_members{
|
||||
@@ -494,10 +502,6 @@ struct inheritable_diagram_options {
|
||||
"message_comment_width", clanguml::util::kDefaultMessageCommentWidth};
|
||||
option<bool> debug_mode{"debug_mode", false};
|
||||
option<bool> generate_metadata{"generate_metadata", true};
|
||||
|
||||
void inherit(const inheritable_diagram_options &parent);
|
||||
|
||||
std::string simplify_template_type(std::string full_name) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -648,6 +652,8 @@ struct config : public inheritable_diagram_options {
|
||||
* Initialize predefined diagram templates.
|
||||
*/
|
||||
void initialize_diagram_templates();
|
||||
|
||||
void inherit();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -659,6 +665,7 @@ struct config : public inheritable_diagram_options {
|
||||
* @embed{load_config_sequence.svg}
|
||||
*
|
||||
* @param config_file Path to the configuration file
|
||||
* @param inherit If true, common options will be propagated to diagram configs
|
||||
* @param paths_relative_to_pwd Whether the paths in the configuration file
|
||||
* should be relative to the parent directory of
|
||||
* the configuration file or to the current
|
||||
@@ -667,7 +674,7 @@ struct config : public inheritable_diagram_options {
|
||||
* @param validate If true, perform schema validation
|
||||
* @return Configuration instance
|
||||
*/
|
||||
config load(const std::string &config_file,
|
||||
config load(const std::string &config_file, bool inherit = true,
|
||||
std::optional<bool> paths_relative_to_pwd = {},
|
||||
std::optional<bool> no_metadata = {}, bool validate = true);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace clanguml {
|
||||
namespace config {
|
||||
|
||||
template <typename T> void append_value(T &l, const T &r) { l = r; }
|
||||
|
||||
/**
|
||||
* Possible option inheritance methods from top level to diagram level.
|
||||
*/
|
||||
|
||||
@@ -157,9 +157,11 @@ types:
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
mermaid: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
relative_to: !optional string
|
||||
using_namespace: !optional [string, [string]]
|
||||
generate_metadata: !optional bool
|
||||
@@ -194,9 +196,11 @@ types:
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
mermaid: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
relative_to: !optional string
|
||||
using_namespace: !optional [string, [string]]
|
||||
generate_metadata: !optional bool
|
||||
@@ -231,9 +235,11 @@ types:
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
mermaid: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
relative_to: !optional string
|
||||
using_namespace: !optional [string, [string]]
|
||||
generate_metadata: !optional bool
|
||||
@@ -260,9 +266,11 @@ types:
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
mermaid: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
relative_to: !optional string
|
||||
using_namespace: !optional [string, [string]]
|
||||
generate_metadata: !optional bool
|
||||
@@ -306,9 +314,11 @@ root:
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
mermaid: !optional
|
||||
before: !optional [string]
|
||||
after: !optional [string]
|
||||
cmd: !optional string
|
||||
relative_to: !optional string
|
||||
using_namespace: !optional [string, [string]]
|
||||
generate_metadata: !optional bool
|
||||
|
||||
@@ -387,6 +387,10 @@ template <> struct convert<plantuml> {
|
||||
|
||||
if (node["after"])
|
||||
rhs.after = node["after"].as<decltype(rhs.after)>();
|
||||
|
||||
if (node["cmd"])
|
||||
rhs.cmd = node["cmd"].as<decltype(rhs.cmd)>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -399,6 +403,10 @@ template <> struct convert<mermaid> {
|
||||
|
||||
if (node["after"])
|
||||
rhs.after = node["after"].as<decltype(rhs.after)>();
|
||||
|
||||
if (node["cmd"])
|
||||
rhs.cmd = node["cmd"].as<decltype(rhs.cmd)>();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -831,7 +839,6 @@ template <> struct convert<config> {
|
||||
diagram_config = parse_diagram_config(d.second);
|
||||
if (diagram_config) {
|
||||
diagram_config->name = name;
|
||||
diagram_config->inherit(rhs);
|
||||
rhs.diagrams[name] = diagram_config;
|
||||
}
|
||||
else {
|
||||
@@ -860,6 +867,13 @@ void config::initialize_diagram_templates()
|
||||
predefined_templates.as<std::map<std::string, diagram_template>>());
|
||||
}
|
||||
|
||||
void config::inherit()
|
||||
{
|
||||
for (auto &[name, diagram] : diagrams) {
|
||||
diagram->inherit(*this);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void resolve_option_path(YAML::Node &doc, const std::string &option)
|
||||
{
|
||||
@@ -881,7 +895,7 @@ void resolve_option_path(YAML::Node &doc, const std::string &option)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
config load(const std::string &config_file,
|
||||
config load(const std::string &config_file, bool inherit,
|
||||
std::optional<bool> paths_relative_to_pwd, std::optional<bool> no_metadata,
|
||||
bool validate)
|
||||
{
|
||||
@@ -997,6 +1011,9 @@ config load(const std::string &config_file,
|
||||
|
||||
auto d = doc.as<config>();
|
||||
|
||||
if (inherit)
|
||||
d.inherit();
|
||||
|
||||
d.initialize_diagram_templates();
|
||||
|
||||
return d;
|
||||
|
||||
@@ -67,7 +67,7 @@ int main(int argc, const char *argv[])
|
||||
const auto compilation_database_files = db->getAllFiles();
|
||||
|
||||
std::map<std::string /* diagram name */,
|
||||
std::vector<std::string> /*translation units*/>
|
||||
std::vector<std::string> /* translation units */>
|
||||
translation_units_map;
|
||||
|
||||
// We have to generate the translation units list for each diagram
|
||||
@@ -77,9 +77,8 @@ int main(int argc, const char *argv[])
|
||||
cli.diagram_names, cli.config, compilation_database_files,
|
||||
translation_units_map);
|
||||
|
||||
common::generators::generate_diagrams(cli.diagram_names, cli.config,
|
||||
cli.effective_output_directory, db, cli.get_runtime_config(),
|
||||
translation_units_map);
|
||||
common::generators::generate_diagrams(cli.diagram_names, cli.config, db,
|
||||
cli.get_runtime_config(), translation_units_map);
|
||||
}
|
||||
catch (error::compilation_database_error &e) {
|
||||
LOG_ERROR("Failed to load compilation database from {} due to: {}",
|
||||
@@ -87,7 +86,7 @@ int main(int argc, const char *argv[])
|
||||
return 1;
|
||||
}
|
||||
catch (error::query_driver_no_paths &e) {
|
||||
LOG_ERROR("Quering provided compiler driver {} did not provide any "
|
||||
LOG_ERROR("Querying provided compiler driver {} did not provide any "
|
||||
"paths, please make sure the path is correct and that your "
|
||||
"compiler is GCC-compatible: {}",
|
||||
cli.config.query_driver(), e.what());
|
||||
|
||||
@@ -80,7 +80,7 @@ void generator::generate_message_comment(
|
||||
if (comment_generated_from_note_decorators)
|
||||
return;
|
||||
|
||||
if (auto &cmt = m.comment();
|
||||
if (const auto &cmt = m.comment();
|
||||
config().generate_message_comments() && cmt.has_value()) {
|
||||
|
||||
ostr << indent(1) << "note over " << generate_alias(from.value())
|
||||
|
||||
@@ -35,7 +35,7 @@ std::string get_process_output(const std::string &command)
|
||||
std::string result;
|
||||
|
||||
#if defined(__linux) || defined(__unix) || defined(__APPLE__)
|
||||
const std::unique_ptr<FILE, decltype(&pclose)> pipe(
|
||||
std::unique_ptr<FILE, decltype(&pclose)> pipe(
|
||||
popen(command.c_str(), "r"), pclose);
|
||||
#elif defined(_WIN32)
|
||||
std::unique_ptr<FILE, decltype(&_pclose)> pipe(
|
||||
@@ -53,6 +53,44 @@ std::string get_process_output(const std::string &command)
|
||||
return result;
|
||||
}
|
||||
|
||||
void check_process_output(const std::string &command)
|
||||
{
|
||||
constexpr size_t kBufferSize{1024};
|
||||
std::array<char, kBufferSize> buffer{};
|
||||
int result{EXIT_FAILURE};
|
||||
std::string output;
|
||||
auto finalize = [&result](FILE *f) {
|
||||
#if defined(__linux) || defined(__unix) || defined(__APPLE__)
|
||||
result = pclose(f);
|
||||
#elif defined(_WIN32)
|
||||
result = _pclose(f);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(__linux) || defined(__unix) || defined(__APPLE__)
|
||||
std::unique_ptr<FILE, decltype(finalize)> pipe(
|
||||
popen(command.c_str(), "r"), finalize);
|
||||
#elif defined(_WIN32)
|
||||
std::unique_ptr<FILE, decltype(finalize)> pipe(
|
||||
_popen(command.c_str(), "r"), finalize);
|
||||
#endif
|
||||
|
||||
if (!pipe) {
|
||||
throw std::runtime_error("popen() failed!");
|
||||
}
|
||||
|
||||
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
|
||||
output += buffer.data();
|
||||
}
|
||||
|
||||
pipe.reset();
|
||||
|
||||
if (result != EXIT_SUCCESS) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("External command '{}' failed: {}", command, output));
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_env(const std::string &name)
|
||||
{
|
||||
#if defined(__linux) || defined(__unix)
|
||||
|
||||
@@ -98,6 +98,13 @@ std::string trim_typename(const std::string &s);
|
||||
*/
|
||||
std::string get_process_output(const std::string &command);
|
||||
|
||||
/**
|
||||
* @brief Execute command shell and throw exception if command fails
|
||||
*
|
||||
* @param command Command to execute
|
||||
*/
|
||||
void check_process_output(const std::string &command);
|
||||
|
||||
/**
|
||||
* @brief Get value of an environment variable
|
||||
*
|
||||
|
||||
@@ -41,7 +41,7 @@ load_config(const std::string &test_name)
|
||||
clanguml::common::compilation_database_ptr>
|
||||
res;
|
||||
|
||||
res.first = clanguml::config::load(test_name + "/.clang-uml", true);
|
||||
res.first = clanguml::config::load(test_name + "/.clang-uml", true, true);
|
||||
|
||||
LOG_DBG("Loading compilation database from {}",
|
||||
res.first.compilation_database_dir());
|
||||
|
||||
@@ -183,4 +183,32 @@ TEST_CASE(
|
||||
REQUIRE(contains(cli.config.add_compile_flags(), "-Wno-error"));
|
||||
REQUIRE(contains(cli.config.add_compile_flags(), "-Wno-warning"));
|
||||
REQUIRE(contains(cli.config.remove_compile_flags(), "-I/usr/include"));
|
||||
}
|
||||
|
||||
TEST_CASE(
|
||||
"Test cli handler puml config inheritance with render cmd", "[unit-test]")
|
||||
{
|
||||
using clanguml::cli::cli_flow_t;
|
||||
using clanguml::cli::cli_handler;
|
||||
using clanguml::util::contains;
|
||||
|
||||
std::vector<const char *> argv{"clang-uml", "--config",
|
||||
"./test_config_data/render_cmd.yml", "-r",
|
||||
"--mermaid-cmd=mmdc -i output/{}.mmd -o output/{}.svg"};
|
||||
|
||||
std::ostringstream ostr;
|
||||
cli_handler cli{ostr, make_sstream_logger(ostr)};
|
||||
|
||||
auto res = cli.handle_options(argv.size(), argv.data());
|
||||
|
||||
REQUIRE(res == cli_flow_t::kContinue);
|
||||
|
||||
REQUIRE(contains(cli.get_runtime_config().output_directory, "output"));
|
||||
REQUIRE(cli.get_runtime_config().render_diagrams);
|
||||
REQUIRE(cli.config.diagrams.at("class_main")->puml().cmd ==
|
||||
"plantuml -tsvg output/{}.puml");
|
||||
REQUIRE(cli.config.diagrams.at("class_main")->mermaid().cmd ==
|
||||
"mmdc -i output/{}.mmd -o output/{}.svg");
|
||||
REQUIRE(cli.config.diagrams.at("class_main")->puml().after.at(0) ==
|
||||
"' test comment");
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
|
||||
diagrams:
|
||||
include_test:
|
||||
type: include
|
||||
|
||||
13
tests/test_config_data/render_cmd.yml
Normal file
13
tests/test_config_data/render_cmd.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
plantuml:
|
||||
cmd: "plantuml -tsvg output/{}.puml"
|
||||
diagrams:
|
||||
class_main:
|
||||
type: class
|
||||
glob:
|
||||
- src/**/*.cc
|
||||
- src/**/*.h
|
||||
plantuml:
|
||||
after:
|
||||
- "' test comment"
|
||||
Reference in New Issue
Block a user