Compare commits

...

10 Commits

Author SHA1 Message Date
Bartek Kryza
a7b4efa80e Fix diagram generation using LLVM 17 (Fixes #190) 2023-10-13 18:46:30 +02:00
Bartek Kryza
e7d6c94a14 Removed redundant requirement for std::regex::multiline (Fixes #191) 2023-10-13 18:46:02 +02:00
Bartek Kryza
f339c34dd8 Enabled building against LLVM 17 2023-10-13 11:49:59 +02:00
Bartek Kryza
26abb71b4c Fix handling of compile_commands.json files with relative include paths (Fixes #189) 2023-10-12 17:08:25 +02:00
Bartek Kryza
8865a42ff0 Change std::same_as to std::is_same_v to maintain C++17 compatibility (Fixes #188) 2023-10-12 11:13:00 +02:00
Bartek Kryza
13a1012e96 Merge pull request #187 from bkryza/remove-package-dependencies-for-parent-packages 2023-10-10 23:31:04 +02:00
Bartek Kryza
92422624cb Skip dependencies between parent and child packages in package diagrams (Fixes #186) 2023-10-10 22:29:23 +02:00
Bartek Kryza
9d43281bdd Excluded package diagram relationships to rejected packages (#185) 2023-10-10 12:19:31 +02:00
Bartek Kryza
c398c6ffda Updated CHANGELOG 2023-10-09 17:56:27 +02:00
Bartek Kryza
1983a609a0 Merge pull request #184 from bkryza/add-title-property
Added 'title' diagram property
2023-10-09 16:05:36 +02:00
20 changed files with 139 additions and 39 deletions

View File

@@ -1,6 +1,8 @@
compilation_database_dir: debug
output_directory: docs/diagrams
comment_parser: clang
remove_compile_flags:
- -Wno-class-memaccess
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 %}"
@@ -72,4 +74,4 @@ diagrams:
include!: uml/package/architecture_package.yml
# Include diagrams
include_graph:
include!: uml/include/include.yml
include!: uml/include/include.yml

View File

@@ -1,5 +1,10 @@
# CHANGELOG
* Excluded package diagram relationships to rejected packages (#185)
* Added 'title' diagram property (#184)
* Make sure sequence diagram messages generated during static variable
initialization are rendered only once (#183)
### 0.4.0
* Added MermaidJS diagram generators (#27)

View File

@@ -132,6 +132,20 @@ if(APPLE OR (LLVM_VERSION_MAJOR GREATER_EQUAL 16))
${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
endif()
#
# Setup custom compile options depending on various compiler
# and environment quirks
#
if(LLVM_VERSION_MAJOR GREATER_EQUAL 17)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CUSTOM_COMPILE_OPTIONS "-Wno-class-memaccess")
endif()
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CUSTOM_COMPILE_OPTIONS
"${CUSTOM_COMPILE_OPTIONS} -Wno-unused-private-field")
endif()
#
# Setup threads library
#

View File

@@ -5,7 +5,7 @@
[![Build status](https://github.com/bkryza/clang-uml/actions/workflows/build.yml/badge.svg)](https://github.com/bkryza/clang-uml/actions)
[![Coverage](https://codecov.io/gh/bkryza/clang-uml/branch/master/graph/badge.svg)](https://codecov.io/gh/bkryza/clang-uml)
[![Version](https://img.shields.io/badge/version-0.4.0-blue)](https://github.com/bkryza/clang-uml/releases)
[![Version](https://img.shields.io/badge/LLVM-12,13,14,15,16-orange)](https://github.com/bkryza/clang-uml/releases)
[![Version](https://img.shields.io/badge/LLVM-12,13,14,15,16,17-orange)](https://github.com/bkryza/clang-uml/releases)
[![Doxygen](https://img.shields.io/badge/Docs-Doxygen-gainsboro)](https://clang-uml.github.io)
`clang-uml` is an automatic C++ to UML class, sequence, package and include diagram generator, driven by
@@ -80,6 +80,7 @@ Nowadays, this file can be generated rather easily using multiple methods:
* For Boost-based projects
try [commands_to_compilation_database](https://github.com/tee3/commands_to_compilation_database)
* For SCons, invoke `compilation_db` tool (requires SCons > 4.0.0)
* For Bazel, try [bazel-compile-commands-extractor](https://github.com/hedronvision/bazel-compile-commands-extractor)
* For Microsoft Visual Studio projects try [Clang Power Tools](https://www.clangpowertools.com)
### Invocation

View File

@@ -25,8 +25,8 @@ endif(MSVC)
target_compile_features(clang-umllib INTERFACE cxx_std_17)
target_compile_options(clang-umllib PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:
-Werror -Wall -Wextra -Wno-unused-parameter -Wno-unused-private-field
-Wno-deprecated-declarations>
-Werror -Wall -Wextra -Wno-unused-parameter
-Wno-deprecated-declarations ${CUSTOM_COMPILE_OPTIONS}>
$<$<CXX_COMPILER_ID:MSVC>:/MP /W1 /bigobj /wd4291 /wd4624 /wd4244>)
target_compile_definitions(clang-umllib PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:
@@ -40,8 +40,8 @@ add_executable(clang-uml ${MAIN_SOURCE_FILE})
target_compile_features(clang-uml PUBLIC cxx_std_17)
target_compile_options(clang-uml PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:
-Werror -Wall -Wextra -Wno-unused-parameter -Wno-unused-private-field
-Wno-deprecated-declarations>
-Werror -Wall -Wextra -Wno-unused-parameter
-Wno-deprecated-declarations ${CUSTOM_COMPILE_OPTIONS}>
$<$<CXX_COMPILER_ID:MSVC>:/MP /W1 /bigobj /wd4291 /wd4624 /wd4244>)
target_compile_definitions(clang-uml PRIVATE
${ENABLE_BACKWARD_CPP})

View File

@@ -30,6 +30,16 @@ diagram_element::id_t diagram_element::id() const { return id_; }
void diagram_element::set_id(diagram_element::id_t id) { id_ = id; }
std::optional<id_t> diagram_element::parent_element_id() const
{
return parent_element_id_;
}
void diagram_element::set_parent_element_id(diagram_element::id_t id)
{
parent_element_id_ = id;
}
std::string diagram_element::alias() const
{
assert(id_ >= 0);

View File

@@ -64,6 +64,20 @@ public:
*/
void set_id(id_t id);
/**
* Get elements parent package id.
*
* @return Parent package id if element is nested.
*/
std::optional<id_t> parent_element_id() const;
/**
* Set elements parent package id.
*
* @param id Id of parent package.
*/
void set_parent_element_id(diagram_element::id_t id);
/**
* @brief Return elements' diagram alias.
*
@@ -174,6 +188,7 @@ public:
private:
id_t id_{0};
std::optional<id_t> parent_element_id_{0};
std::string name_;
std::vector<relationship> relationships_;
bool nested_{false};

View File

@@ -248,10 +248,6 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
e.full_name(false);
});
if (tvl::is_false(result))
LOG_DBG("Element {} rejected by namespace_filter 1",
e.full_name(false));
return result;
}
@@ -273,10 +269,6 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
e.full_name(false);
});
if (tvl::is_false(result))
LOG_DBG("Element {} rejected by namespace_filter (package diagram)",
e.full_name(false));
return result;
}
@@ -290,9 +282,6 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
return std::get<common::regex>(nsit.value()) %= e.full_name(false);
});
if (tvl::is_false(result))
LOG_DBG("Element {} rejected by namespace_filter", e.full_name(false));
return result;
}

View File

@@ -94,9 +94,11 @@ public:
auto parent = get_element(path);
if (parent && dynamic_cast<nested_trait<T, Path> *>(&parent.value()))
if (parent && dynamic_cast<nested_trait<T, Path> *>(&parent.value())) {
p->set_parent_element_id(parent.value().id());
return dynamic_cast<nested_trait<T, Path> &>(parent.value())
.template add_element<V>(std::move(p));
}
LOG_INFO("No parent element found at: {}", path.to_string());

View File

@@ -504,8 +504,6 @@ private:
/*! Whether template argument is ellipsis (...) */
bool is_ellipsis_{false};
bool is_noexcept_{false};
/*! Whether the template parameter is variadic */
bool is_variadic_{false};

View File

@@ -123,6 +123,12 @@ void translation_unit_visitor::set_source_location(
}
}
if (std::filesystem::path file_path{file}; !file_path.is_absolute()) {
file_path =
std::filesystem::canonical(std::filesystem::absolute(file_path));
file = file_path.string();
}
element.set_file(file);
element.set_file_relative(util::path_to_url(
std::filesystem::relative(element.file(), relative_to_path_).string()));

View File

@@ -36,6 +36,14 @@ void generator::generate_relationships(
if (model().should_include(relationship_t::kDependency)) {
for (const auto &r : p.relationships()) {
nlohmann::json rel = r;
auto destination_package = model().get(r.destination());
if (!destination_package ||
!model().should_include(
dynamic_cast<const package &>(*destination_package)))
continue;
rel["source"] = std::to_string(p.id());
parent["relationships"].push_back(std::move(rel));
}

View File

@@ -45,9 +45,17 @@ void generator::generate_relationships(
for (const auto &r : p.relationships()) {
std::stringstream relstr;
try {
auto destination = model().to_alias(r.destination());
if (!destination.empty()) {
relstr << p.alias() << " -.-> " << destination << '\n';
auto destination_package = model().get(r.destination());
if (!destination_package ||
!model().should_include(
dynamic_cast<const package &>(*destination_package)))
continue;
auto destination_alias = model().to_alias(r.destination());
if (!destination_alias.empty()) {
relstr << p.alias() << " -.-> " << destination_alias
<< '\n';
ostr << indent(1) << relstr.str();
}
}

View File

@@ -38,9 +38,17 @@ void generator::generate_relationships(
for (const auto &r : p.relationships()) {
std::stringstream relstr;
try {
auto destination = model().to_alias(r.destination());
if (!destination.empty()) {
relstr << p.alias() << " ..> " << destination << '\n';
auto destination_package = model().get(r.destination());
if (!destination_package ||
!model().should_include(
dynamic_cast<const package &>(*destination_package)))
continue;
auto destination_alias = model().to_alias(r.destination());
if (!destination_alias.empty()) {
relstr << p.alias() << " ..> " << destination_alias << '\n';
ostr << relstr.str();
}
}
@@ -54,8 +62,9 @@ void generator::generate_relationships(
// Process it's subpackages relationships
for (const auto &subpackage : p) {
generate_relationships(
dynamic_cast<const package &>(*subpackage), ostr);
if (model().should_include(dynamic_cast<const package &>(*subpackage)))
generate_relationships(
dynamic_cast<const package &>(*subpackage), ostr);
}
}

View File

@@ -76,12 +76,12 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
p->set_name(name);
p->set_namespace(package_parent);
p->set_id(common::to_id(*ns));
set_source_location(*ns, *p);
assert(p->id() > 0);
if (diagram().should_include(*p) && !diagram().get(p->id())) {
process_comment(*ns, *p);
set_source_location(*ns, *p);
p->set_style(p->style_spec());
@@ -232,8 +232,10 @@ void translation_unit_visitor::add_relationships(
pkg->set_name(pkg_name);
pkg->set_id(get_package_id(cls));
set_source_location(*cls, *pkg);
diagram().add(parent_path, std::move(pkg));
if (diagram().should_include(*pkg))
diagram().add(parent_path, std::move(pkg));
}
auto current_package_id = get_package_id(cls);
@@ -246,8 +248,21 @@ void translation_unit_visitor::add_relationships(
auto current_package = diagram().get(current_package_id);
if (current_package) {
std::vector<common::model::diagram_element::id_t> parent_ids =
get_parent_package_ids(current_package_id);
for (const auto &dependency : relationships) {
const auto destination_id = std::get<0>(dependency);
// Skip dependency relationships to parent packages
if (util::contains(parent_ids, destination_id))
continue;
// Skip dependency relationship to child packages
if (util::contains(
get_parent_package_ids(destination_id), current_package_id))
continue;
relationship r{relationship_t::kDependency, destination_id,
common::model::access_t::kNone};
if (destination_id != current_package_id)
@@ -605,4 +620,23 @@ translation_unit_visitor::config() const
void translation_unit_visitor::finalize() { }
std::vector<common::model::diagram_element::id_t>
translation_unit_visitor::get_parent_package_ids(
common::model::diagram_element::id_t id)
{
std::vector<common::model::diagram_element::id_t> parent_ids;
std::optional<common::model::diagram_element::id_t> parent_id = id;
while (parent_id.has_value()) {
parent_ids.push_back(parent_id.value());
auto parent = this->diagram().get(parent_id.value());
if (parent)
parent_id = parent.value().parent_element_id();
else
break;
}
return parent_ids;
}
} // namespace clanguml::package_diagram::visitor

View File

@@ -214,6 +214,9 @@ private:
void add_relationships(
clang::Decl *cls, found_relationships_t &relationships);
std::vector<common::model::diagram_element::id_t> get_parent_package_ids(
common::model::diagram_element::id_t id);
// Reference to the output diagram model
clanguml::package_diagram::model::diagram &diagram_;

View File

@@ -328,8 +328,6 @@ private:
return block_end_types.count(mt) > 0;
};
bool started_{false};
std::map<common::model::diagram_element::id_t, activity> sequences_;
std::map<common::model::diagram_element::id_t, std::unique_ptr<participant>>

View File

@@ -58,8 +58,9 @@ foreach(TEST_NAME ${TEST_CASES})
target_compile_options(${TEST_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:
-Wno-unused-parameter -Wno-unused-private-field -Wno-unused-variable
-Wno-attributes -Wno-nonnull -Wno-deprecated-enum-enum-conversion>
-Wno-unused-parameter -Wno-unused-variable
-Wno-attributes -Wno-nonnull -Wno-deprecated-enum-enum-conversion
${CUSTOM_COMPILE_OPTIONS}>
$<$<CXX_COMPILER_ID:MSVC>:/W1 /bigobj /wd4624>>)
target_link_libraries(${TEST_NAME} PRIVATE ${CLANG_UML_TEST_LIBRARIES})
endforeach()

View File

@@ -13301,9 +13301,6 @@ bool RegexMatcher::match(std::string const &matchee) const
{
auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax
// option anyway
#if !defined(_WIN32)
flags |= std::regex::multiline;
#endif
if (m_caseSensitivity == CaseSensitive::Choice::No) {
flags |= std::regex::icase;

View File

@@ -263,12 +263,12 @@ template <typename T>
void save_diagram(const std::filesystem::path &path, const T &diagram)
{
static_assert(
std::same_as<T, std::string> || std::same_as<T, nlohmann::json>);
std::is_same_v<T, std::string> || std::is_same_v<T, nlohmann::json>);
std::filesystem::create_directories(path.parent_path());
std::ofstream ofs;
ofs.open(path, std::ofstream::out | std::ofstream::trunc);
if constexpr (std::same_as<T, nlohmann::json>) {
if constexpr (std::is_same_v<T, nlohmann::json>) {
ofs << std::setw(2) << diagram;
}
else {