Added --dump-config command line option (Fixes #77)

This commit is contained in:
Bartek Kryza
2023-01-17 23:43:44 +01:00
parent 74353603f8
commit 00b9321034
10 changed files with 1106 additions and 606 deletions

View File

@@ -1,5 +1,6 @@
# CHANGELOG # CHANGELOG
* Added command line option (--dump-config) to print effective config (#77)
* Added support for building with Microsoft Visual Studio * Added support for building with Microsoft Visual Studio
### 0.3.0 ### 0.3.0

View File

@@ -21,6 +21,12 @@ do not override this option.
For detailed reference of all configuration options see [here](./configuration_file.md). For detailed reference of all configuration options see [here](./configuration_file.md).
Effective configuration, including default values can be printed out in YAML format using the following option:
```bash
clang-uml --dump-config
```
## Translation unit glob patterns ## Translation unit glob patterns
One of the key options of the diagram configuration is the list of translation units, which should be parsed to One of the key options of the diagram configuration is the list of translation units, which should be parsed to
get all necessary information for a diagram. get all necessary information for a diagram.

File diff suppressed because it is too large Load Diff

View File

@@ -37,8 +37,12 @@ namespace config {
enum class method_arguments { full, abbreviated, none }; enum class method_arguments { full, abbreviated, none };
std::string to_string(method_arguments ma);
enum class comment_parser_t { plain, clang }; enum class comment_parser_t { plain, clang };
std::string to_string(comment_parser_t cp);
struct plantuml { struct plantuml {
std::vector<std::string> before; std::vector<std::string> before;
std::vector<std::string> after; std::vector<std::string> after;
@@ -78,6 +82,8 @@ struct filter {
enum class hint_t { up, down, left, right }; enum class hint_t { up, down, left, right };
std::string to_string(hint_t t);
struct layout_hint { struct layout_hint {
hint_t hint{hint_t::up}; hint_t hint{hint_t::up};
std::string entity; std::string entity;
@@ -120,7 +126,14 @@ using relationship_hints_t = std::map<std::string, relationship_hint_t>;
using type_aliases_t = std::map<std::string, std::string>; using type_aliases_t = std::map<std::string, std::string>;
std::string to_string(hint_t t); enum class location_t { marker, fileline, function };
std::string to_string(location_t cp);
struct source_location {
location_t location_type{location_t::function};
std::string location;
};
struct inheritable_diagram_options { struct inheritable_diagram_options {
option<std::vector<std::string>> glob{"glob"}; option<std::vector<std::string>> glob{"glob"};
@@ -165,18 +178,11 @@ struct diagram : public inheritable_diagram_options {
std::string name; std::string name;
}; };
struct source_location {
enum class location_t { usr, marker, fileline, function };
location_t location_type{location_t::function};
std::string location;
};
struct class_diagram : public diagram { struct class_diagram : public diagram {
~class_diagram() override = default; ~class_diagram() override = default;
common::model::diagram_t type() const override; common::model::diagram_t type() const override;
option<std::vector<std::string>> classes{"classes"};
option<layout_hints> layout{"layout"}; option<layout_hints> layout{"layout"};
void initialize_relationship_hints(); void initialize_relationship_hints();
@@ -216,6 +222,60 @@ struct config : public inheritable_diagram_options {
std::map<std::string, std::shared_ptr<diagram>> diagrams; std::map<std::string, std::shared_ptr<diagram>> diagrams;
}; };
//
// YAML serialization emitters
//
YAML::Emitter &operator<<(YAML::Emitter &out, const config &c);
YAML::Emitter &operator<<(
YAML::Emitter &out, const inheritable_diagram_options &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f);
YAML::Emitter &operator<<(YAML::Emitter &out, const plantuml &p);
YAML::Emitter &operator<<(YAML::Emitter &out, const method_arguments &ma);
YAML::Emitter &operator<<(YAML::Emitter &out, const generate_links_config &glc);
YAML::Emitter &operator<<(YAML::Emitter &out, const git_config &gc);
YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_hint_t &rh);
YAML::Emitter &operator<<(YAML::Emitter &out, const comment_parser_t &cp);
YAML::Emitter &operator<<(YAML::Emitter &out, const hint_t &h);
YAML::Emitter &operator<<(YAML::Emitter &out, const class_diagram &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const sequence_diagram &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const include_diagram &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const layout_hint &c);
YAML::Emitter &operator<<(YAML::Emitter &out, const source_location &sc);
template <typename T>
YAML::Emitter &operator<<(YAML::Emitter &out, const option<T> &o)
{
if (o.has_value) {
out << YAML::Key << o.name;
out << YAML::Value << o.value;
}
return out;
}
config load(const std::string &config_file); config load(const std::string &config_file);
} // namespace config } // namespace config
namespace common::model {
YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n);
YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r);
YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &r);
YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d);
} // namespace common::model
} // namespace clanguml } // namespace clanguml

610
src/config/yaml_decoders.cc Normal file

File diff suppressed because it is too large Load Diff

263
src/config/yaml_emitters.cc Normal file
View File

@@ -0,0 +1,263 @@
/**
* src/config/yaml_emitters.cc
*
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
namespace clanguml::common::model {
YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n)
{
out << n.to_string();
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r)
{
out << to_string(r);
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &a)
{
out << to_string(a);
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d)
{
out << to_string(d);
return out;
}
} // namespace clanguml::common::model
namespace clanguml::config {
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
{
out << YAML::BeginMap;
if (!f.namespaces.empty())
out << YAML::Key << "namespaces" << YAML::Value << f.namespaces;
if (!f.access.empty())
out << YAML::Key << "access" << YAML::Value << f.access;
if (!f.context.empty())
out << YAML::Key << "context" << YAML::Value << f.context;
if (!f.dependants.empty())
out << YAML::Key << "dependants" << YAML::Value << f.dependants;
if (!f.dependencies.empty())
out << YAML::Key << "dependencies" << YAML::Value << f.dependencies;
if (!f.elements.empty())
out << YAML::Key << "elements" << YAML::Value << f.elements;
if (!f.paths.empty())
out << YAML::Key << "paths" << YAML::Value << f.paths;
if (!f.relationships.empty())
out << YAML::Key << "relationships" << YAML::Value << f.relationships;
if (!f.specializations.empty())
out << YAML::Key << "specializations" << YAML::Value
<< f.specializations;
if (!f.subclasses.empty())
out << YAML::Key << "subclasses" << YAML::Value << f.subclasses;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const plantuml &p)
{
if (p.before.empty() && p.after.empty())
return out;
out << YAML::BeginMap;
if (!p.before.empty())
out << YAML::Key << "before" << YAML::Value << p.before;
if (!p.after.empty())
out << YAML::Key << "after" << YAML::Value << p.after;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const method_arguments &ma)
{
out << to_string(ma);
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const generate_links_config &glc)
{
out << YAML::BeginMap;
out << YAML::Key << "link" << YAML::Value << glc.link;
out << YAML::Key << "tooltip" << YAML::Value << glc.tooltip;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const git_config &gc)
{
out << YAML::BeginMap;
out << YAML::Key << "branch" << YAML::Value << gc.branch;
out << YAML::Key << "revision" << YAML::Value << gc.revision;
out << YAML::Key << "commit" << YAML::Value << gc.commit;
out << YAML::Key << "toplevel" << YAML::Value << gc.toplevel;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_hint_t &rh)
{
out << YAML::BeginMap;
out << YAML::Key << "default" << YAML::Value << rh.default_hint;
for (const auto &[k, v] : rh.argument_hints)
out << YAML::Key << k << YAML::Value << v;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const hint_t &h)
{
out << to_string(h);
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const comment_parser_t &cp)
{
out << to_string(cp);
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const layout_hint &c)
{
out << YAML::BeginMap;
out << YAML::Key << c.hint << YAML::Value << c.entity;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const source_location &sc)
{
out << YAML::BeginMap;
out << YAML::Key << "location" << YAML::Value << sc.location;
out << YAML::Key << "location_type" << YAML::Value
<< to_string(sc.location_type);
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const config &c)
{
out << YAML::BeginMap;
out << c.compilation_database_dir;
out << c.output_directory;
out << dynamic_cast<const inheritable_diagram_options &>(c);
out << YAML::Key << "diagrams";
out << YAML::BeginMap;
for (const auto &[k, v] : c.diagrams) {
out << YAML::Key << k;
if (v->type() == common::model::diagram_t::kClass) {
out << YAML::Value << dynamic_cast<class_diagram &>(*v);
}
else if (v->type() == common::model::diagram_t::kSequence) {
out << YAML::Value << dynamic_cast<sequence_diagram &>(*v);
}
else if (v->type() == common::model::diagram_t::kInclude) {
out << YAML::Value << dynamic_cast<include_diagram &>(*v);
}
else if (v->type() == common::model::diagram_t::kPackage) {
out << YAML::Value << dynamic_cast<package_diagram &>(*v);
}
}
out << YAML::EndMap;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(
YAML::Emitter &out, const inheritable_diagram_options &c)
{
out << c.glob;
out << c.using_namespace;
out << c.include_relations_also_as_members;
out << c.include;
out << c.exclude;
out << c.puml;
out << c.generate_method_arguments;
out << c.generate_packages;
out << c.generate_links;
out << c.git;
out << c.base_directory;
out << c.relative_to;
out << c.generate_system_headers;
if (c.relationship_hints) {
out << YAML::Key << "relationship_hints" << YAML::Value
<< c.relationship_hints();
}
if (c.type_aliases) {
out << YAML::Key << "type_aliases" << YAML::Value << c.type_aliases();
}
out << c.comment_parser;
out << c.combine_free_functions_into_file_participants;
out << c.participants_order;
out << c.debug_mode;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const class_diagram &c)
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << c.type();
out << c.layout;
out << dynamic_cast<const inheritable_diagram_options &>(c);
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const sequence_diagram &c)
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << c.type();
out << c.start_from;
out << dynamic_cast<const inheritable_diagram_options &>(c);
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const include_diagram &c)
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << c.type();
out << c.layout;
out << dynamic_cast<const inheritable_diagram_options &>(c);
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const package_diagram &c)
{
out << YAML::BeginMap;
out << YAML::Key << "type" << YAML::Value << c.type();
out << c.layout;
out << dynamic_cast<const inheritable_diagram_options &>(c);
out << YAML::EndMap;
return out;
}
} // namespace clanguml::config

View File

@@ -61,6 +61,13 @@ void print_version();
*/ */
void print_diagrams_list(const clanguml::config::config &cfg); void print_diagrams_list(const clanguml::config::config &cfg);
/**
* Print effective config after loading and setting default values.
*
* @param cfg Configuration instance loaded from configuration file
*/
void print_config(const clanguml::config::config &cfg);
/** /**
* Generate sample configuration file and exit. * Generate sample configuration file and exit.
* *
@@ -164,6 +171,7 @@ int main(int argc, const char *argv[])
std::optional<std::string> add_sequence_diagram; std::optional<std::string> add_sequence_diagram;
std::optional<std::string> add_package_diagram; std::optional<std::string> add_package_diagram;
std::optional<std::string> add_include_diagram; std::optional<std::string> add_include_diagram;
bool dump_config{false};
app.add_option( app.add_option(
"-c,--config", config_path, "Location of configuration file"); "-c,--config", config_path, "Location of configuration file");
@@ -190,6 +198,8 @@ int main(int argc, const char *argv[])
"Add package diagram config"); "Add package diagram config");
app.add_option("--add-include-diagram", add_include_diagram, app.add_option("--add-include-diagram", add_include_diagram,
"Add include diagram config"); "Add include diagram config");
app.add_flag(
"--dump-config", dump_config, "Print effective config to stdout");
CLI11_PARSE(app, argc, argv); CLI11_PARSE(app, argc, argv);
@@ -243,6 +253,11 @@ int main(int argc, const char *argv[])
return 0; return 0;
} }
if (dump_config) {
print_config(config);
return 0;
}
LOG_INFO("Loaded clang-uml config from {}", config_path); LOG_INFO("Loaded clang-uml config from {}", config_path);
if (!compilation_database_dir.empty()) { if (!compilation_database_dir.empty()) {
@@ -621,3 +636,14 @@ int add_config_diagram(clanguml::common::model::diagram_t type,
return 0; return 0;
} }
void print_config(const clanguml::config::config &cfg)
{
YAML::Emitter out;
out.SetIndent(2);
out << cfg;
out << YAML::Newline;
std::cout << out.c_str();
}

View File

@@ -21,7 +21,7 @@
namespace clanguml::sequence_diagram::generators::plantuml { namespace clanguml::sequence_diagram::generators::plantuml {
using clanguml::common::model::message_t; using clanguml::common::model::message_t;
using clanguml::config::source_location; using clanguml::config::location_t;
using clanguml::sequence_diagram::model::activity; using clanguml::sequence_diagram::model::activity;
using clanguml::sequence_diagram::model::message; using clanguml::sequence_diagram::model::message;
using namespace clanguml::util; using namespace clanguml::util;
@@ -383,7 +383,7 @@ void generator::generate(std::ostream &ostr) const
} }
for (const auto &sf : m_config.start_from()) { for (const auto &sf : m_config.start_from()) {
if (sf.location_type == source_location::location_t::function) { if (sf.location_type == location_t::function) {
common::model::diagram_element::id_t start_from{0}; common::model::diagram_element::id_t start_from{0};
for (const auto &[k, v] : m_model.sequences()) { for (const auto &[k, v] : m_model.sequences()) {
const auto &caller = *m_model.participants().at(v.from()); const auto &caller = *m_model.participants().at(v.from());

View File

@@ -188,3 +188,29 @@ TEST_CASE("Test config layout", "[unit-test]")
*cfg.diagrams["package_main"]), *cfg.diagrams["package_main"]),
clanguml::common::model::diagram_t::kPackage); clanguml::common::model::diagram_t::kPackage);
} }
TEST_CASE("Test config emitters", "[unit-test]")
{
auto cfg = clanguml::config::load("./test_config_data/complete.yml");
YAML::Emitter out;
out.SetIndent(2);
out << cfg;
out << YAML::Newline;
// Write the emitted YAML to a temp file
auto tmp_file = std::filesystem::temp_directory_path() /
fmt::format("clang-uml-{:16}", rand());
{
std::ofstream stream(tmp_file.string().c_str(), std::ios::binary);
stream << out.c_str();
}
auto cfg_emitted = clanguml::config::load(tmp_file.string());
REQUIRE(cfg.diagrams.size() == cfg_emitted.diagrams.size());
std::filesystem::remove(tmp_file);
}

View File

@@ -0,0 +1,61 @@
# Directory containing the compile_commands.json file
compilation_database_dir: debug
# The directory where *.puml files will be generated
output_directory: docs/diagrams
# Set this as default for all diagrams
generate_method_arguments: none
# Enable generation of hyperlinks to diagram elements
generate_links:
# Link pattern
link: "https://github.com/bkryza/clang-uml/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }}"
# Tooltip pattern
tooltip: "{{ element.name }}"
# The map of diagrams - keys are also diagram file names
diagrams:
config_class:
type: class
# Do not include rendered relations in the class box
include_relations_also_as_members: false
# Limiting the number of files to include can significantly
# improve the generation time
glob:
- src/common/model/*.h
- src/common/model/*.cc
- src/class_diagram/model/*.h
- src/class_diagram/model/*.cc
include:
# Only include entities from the following namespaces
namespaces:
- clanguml::common::model
- clanguml::class_diagram::model
# Only include elements in direct relationship with ClassA
context:
- ClassA
exclude:
# Do not include private members and methods in the diagram
access:
- private
layout:
# Add layout hints for PlantUML
ClassA:
- up: ClassB
- left: ClassC
# Specify customized relationship hints for types which are
# arguments in template instantiations
relationship_hints:
# All tuple arguments should be treated as aggregation
std::tuple: aggregation
# All some_template arguments should be treated as associations
# except for arguments with indexes 2 and 10
ns1::n2::some_template:
default: association
2: composition
10: aggregation
# Entities from this namespace will be shortened
# (can only contain one element at the moment)
using_namespace:
- clanguml::class_diagram::model
plantuml:
# Add this line to the beginning of the resulting puml file
before:
- 'title clang-uml class diagram model'