Added initial documentation
This commit is contained in:
34
README.md
34
README.md
@@ -4,8 +4,8 @@
|
|||||||
[](https://codecov.io/gh/bkryza/clang-uml)
|
[](https://codecov.io/gh/bkryza/clang-uml)
|
||||||
[](https://github.com/bkryza/clang-uml/releases)
|
[](https://github.com/bkryza/clang-uml/releases)
|
||||||
|
|
||||||
`clang-uml` is an automatic C++ to UML class, sequence
|
`clang-uml` is an automatic C++ to UML class, sequence, package and include diagram generator, driven by
|
||||||
and package diagram generator, driven by YAML configuration files. The main idea behind the
|
YAML configuration files. The main idea behind the
|
||||||
project is to easily maintain up-to-date diagrams within a code-base or document
|
project is to easily maintain up-to-date diagrams within a code-base or document
|
||||||
legacy code. The configuration file or files for `clang-uml` define the
|
legacy code. The configuration file or files for `clang-uml` define the
|
||||||
type and contents of each generated diagram.
|
type and contents of each generated diagram.
|
||||||
@@ -42,6 +42,8 @@ Main features supported so far include:
|
|||||||
To see what `clang-uml` can do so far, checkout the diagrams generated for unit test cases [here](./docs/test_cases.md)
|
To see what `clang-uml` can do so far, checkout the diagrams generated for unit test cases [here](./docs/test_cases.md)
|
||||||
and examples in [clang-uml-examples](https://github.com/bkryza/clang-uml-examples) repository.
|
and examples in [clang-uml-examples](https://github.com/bkryza/clang-uml-examples) repository.
|
||||||
|
|
||||||
|
More comprehensive documentation can be found [here](./docs/README.md).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Distribution packages
|
### Distribution packages
|
||||||
@@ -451,34 +453,6 @@ exclude:
|
|||||||
- clanguml::common::ClassF
|
- clanguml::common::ClassF
|
||||||
```
|
```
|
||||||
|
|
||||||
### Comment decorators
|
|
||||||
|
|
||||||
`clang-uml` provides a set of in-comment directives, called decorators, which allow custom control over
|
|
||||||
generation of UML diagrams from C++ and overriding default inference rules for relationships.
|
|
||||||
|
|
||||||
The following decorators are currently supported:
|
|
||||||
|
|
||||||
- [note](docs/test_cases/t00028.md) - add a PlantUML note to a C++ entity
|
|
||||||
- [skip](docs/test_cases/t00029.md) - skip the underlying C++ entity
|
|
||||||
- [skiprelationship](docs/test_cases/t00029.md) - skip only relationship generation for a class property
|
|
||||||
- [composition](docs/test_cases/t00030.md) - document the property as composition
|
|
||||||
- [association](docs/test_cases/t00030.md) - document the property as association
|
|
||||||
- [aggregation](docs/test_cases/t00030.md) - document the property as aggregation
|
|
||||||
- [style](docs/test_cases/t00031.md) - add PlantUML style to a C++ entity
|
|
||||||
|
|
||||||
### Doxygen integration
|
|
||||||
|
|
||||||
`clang-uml` decorstors can be omitted completely in [Doxygen](https://www.doxygen.nl/index.html), by adding the
|
|
||||||
following
|
|
||||||
lines to the Doxygen config file:
|
|
||||||
|
|
||||||
```
|
|
||||||
ALIASES += clanguml=""
|
|
||||||
ALIASES += clanguml{1}=""
|
|
||||||
ALIASES += clanguml{2}=""
|
|
||||||
ALIASES += clanguml{3}=""
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test cases
|
### Test cases
|
||||||
|
|
||||||
The build-in test cases used for unit testing of the `clang-uml`, can be browsed [here](./docs/test_cases.md).
|
The build-in test cases used for unit testing of the `clang-uml`, can be browsed [here](./docs/test_cases.md).
|
||||||
|
|||||||
18
docs/README.md
Normal file
18
docs/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Documentation
|
||||||
|
|
||||||
|
|
||||||
|
* [Quick start](./quick_start.md)
|
||||||
|
* Generating diagrams
|
||||||
|
* [Common options](./common_options.md)
|
||||||
|
* [Class diagrams](./class_diagrams.md)
|
||||||
|
* [Sequence diagrams](./sequence_diagrams.md)
|
||||||
|
* [Package diagrams](./package_diagrams.md)
|
||||||
|
* [Include diagrams](./include_diagrams.md)
|
||||||
|
* [Comment decorators](./comment_decorators.md)
|
||||||
|
* [Diagram filters](./diagram_filters.md)
|
||||||
|
* [Using Jinja templates](./jinja_templates.md)
|
||||||
|
* [Interactive SVG diagrams using links](./interactive_svg_diagrams.md)
|
||||||
|
* [Configuration file reference](./configuration_file.md)
|
||||||
|
* [Doxygen integration](./doxygen_integration.md)
|
||||||
|
* [Test cases documentation](./test_cases.md)
|
||||||
|
* [Troubleshooting](./troubleshooting.md)
|
||||||
159
docs/class_diagrams.md
Normal file
159
docs/class_diagrams.md
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
# Generating class diagrams
|
||||||
|
|
||||||
|
The minimal config required to generate a class diagram is presented below:
|
||||||
|
```yaml
|
||||||
|
# Path to the directory where `compile_commands.json` can be found
|
||||||
|
compilation_database_dir: _build
|
||||||
|
# Output directory for the diagrams
|
||||||
|
output_directory: puml
|
||||||
|
# Diagrams definitions
|
||||||
|
diagrams:
|
||||||
|
# Diagram name
|
||||||
|
t00002_class:
|
||||||
|
# Type of diagram (has to be `class`)
|
||||||
|
type: class
|
||||||
|
# Include only translation units matching the following patterns
|
||||||
|
glob:
|
||||||
|
- src/*.cc
|
||||||
|
# Render all names in the diagram relative to specific namespace
|
||||||
|
using_namespace:
|
||||||
|
- ns1
|
||||||
|
# Include only classes from specific namespace
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Classes and their properties
|
||||||
|
The basic class diagram generated by `clang-uml` and rendered using PlantUML looks like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Parameter types and method return types are rendered at the end after `:` sign.
|
||||||
|
|
||||||
|
Static methods and members are underlined.
|
||||||
|
|
||||||
|
In case method argument lists are too long and not required for diagram readability, they can be suppressed completely
|
||||||
|
or abbreviated by setting `generate_method_arguments` option to either `none`, `abbreviated` or `full` (default).
|
||||||
|
|
||||||
|
|
||||||
|
### Excluding private or protected members from the diagram
|
||||||
|
In order to only include public members in the class diagrams, we can add the following inclusion filters:
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
access:
|
||||||
|
- public
|
||||||
|
```
|
||||||
|
|
||||||
|
To render only classes without any properties an exclusion filter can be added:
|
||||||
|
```yaml
|
||||||
|
exclude:
|
||||||
|
access:
|
||||||
|
- public
|
||||||
|
- protected
|
||||||
|
- private
|
||||||
|
```
|
||||||
|
|
||||||
|
## Relationships
|
||||||
|
|
||||||
|
The following table presents the PlantUML arrows representing each relationship in the class diagrams.
|
||||||
|
|
||||||
|
| UML | PlantUML |
|
||||||
|
| ---- | --- |
|
||||||
|
| Inheritance |  |
|
||||||
|
| Association |  |
|
||||||
|
| Dependency |  |
|
||||||
|
| Aggregation |  |
|
||||||
|
| Composition |  |
|
||||||
|
| Template specialization/instantiation |  |
|
||||||
|
| Nesting (inner class/enum) |  |
|
||||||
|
| Include (local) |  |
|
||||||
|
| Include (system) |  |
|
||||||
|
|
||||||
|
|
||||||
|
By default, a member from which a relationship has been added to the diagram between 2 classes will also be rendered
|
||||||
|
inside the class. This behaviour can be however disabled by adding the following option to the
|
||||||
|
diagram definition:
|
||||||
|
```yaml
|
||||||
|
include_relations_also_as_members: false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Relationships to classes in containers or smart pointers
|
||||||
|
`clang-uml` will automatically detect class members as well as method arguments, which reference or own
|
||||||
|
values of types relevant for a given diagram but wrapped in smart pointers or containers ang still generate
|
||||||
|
relationship between these classes, for instance the following code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class A { };
|
||||||
|
|
||||||
|
class B { };
|
||||||
|
|
||||||
|
class C { };
|
||||||
|
|
||||||
|
class R {
|
||||||
|
public:
|
||||||
|
std::unique_ptr<A> a;
|
||||||
|
std::shared_ptr<B> b;
|
||||||
|
std::weak_ptr<C> c;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Inheritance diagrams
|
||||||
|
|
||||||
|
A common type of class diagram is an inheritance diagram, where only subclasses of a specific base class are
|
||||||
|
included and only the inheritance relationships are rendered. This can be easily achieved in `clang-uml` through
|
||||||
|
inclusion filters:
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
subclasses:
|
||||||
|
- clanguml::t00039::A
|
||||||
|
relationships:
|
||||||
|
- inheritance
|
||||||
|
```
|
||||||
|
|
||||||
|
## Namespaces as packages
|
||||||
|
By default, `clang-uml` will render all element names including a namespace (relative to `using_namespace` property),
|
||||||
|
e.g. `ns1::ns2::MyClass`.
|
||||||
|
In order to generate packages in the diagram for each namespace instead, the following option must be set to `true`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
generate_packages: true
|
||||||
|
```
|
||||||
|
|
||||||
|
which results in the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Class context diagram
|
||||||
|
Sometimes it's helpful to generate a class diagram depicting only direct relationships of a given class, e.g.
|
||||||
|
within the classes documentation page, this can be easily achieved using `context` inclusion filter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
context:
|
||||||
|
- ns1::MyClass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disabling dependency relationships
|
||||||
|
In many cases, dependency relationships between classes can clutter the diagram too much, for instance consider this
|
||||||
|
diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
where the dependency relationships do not bring much information into the diagram. Thus in such cases it might
|
||||||
|
be useful to disable dependency relationships for this diagram completely using the following exclusion filter:
|
||||||
|
```yaml
|
||||||
|
exclude:
|
||||||
|
relationships:
|
||||||
|
- dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependency relationships are inferred whenever a class uses another class, thus often dependency relationship
|
||||||
|
will be rendered in addition to other relationships such as association or inheritance. In the future there might
|
||||||
|
be an option to remove the redundant dependency relationships from the diagram automatically.
|
||||||
|
|
||||||
|
|
||||||
222
docs/comment_decorators.md
Normal file
222
docs/comment_decorators.md
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
# Comment decorators
|
||||||
|
|
||||||
|
`clang-uml` provides a set of in-comment directives, called decorators, which allow custom control over
|
||||||
|
generation of UML diagrams from C++ and overriding default inference rules for relationships.
|
||||||
|
|
||||||
|
`clang-uml` decorators are specified in the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
\uml{<decorator>[:<diagram_name>][<options>] <text>}
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
```
|
||||||
|
@uml{<decorator>[:<diagram_name>][<options>] <text>}
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `:<diagram_name>` suffix will apply this decorator only to a specific diagram.
|
||||||
|
|
||||||
|
Currently the following decorators are supported.
|
||||||
|
|
||||||
|
## `note`
|
||||||
|
|
||||||
|
This decorator allows to specify directly in the code comments that should be included in the generated diagrams.
|
||||||
|
|
||||||
|
The following code:
|
||||||
|
```cpp
|
||||||
|
/// \uml{note[top] A class note.}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note[] B class note.}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @uml{note:t00028_class[bottom] C class note.}
|
||||||
|
/// This is class C.
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note
|
||||||
|
/// D
|
||||||
|
/// class
|
||||||
|
/// note.}
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note E template class note.}
|
||||||
|
template <typename T> class E {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note:other_diagram[left] G class note.}
|
||||||
|
class G {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{note[ bottom ] F enum note.}
|
||||||
|
enum class F { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{note[right] R class note.}
|
||||||
|
class R {
|
||||||
|
explicit R(C &c)
|
||||||
|
: ccc(c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
B *bbb;
|
||||||
|
|
||||||
|
C &ccc;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<D>> ddd;
|
||||||
|
|
||||||
|
E<int> eee;
|
||||||
|
|
||||||
|
G **ggg;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following class diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# `skip` and `skiprelationship`
|
||||||
|
This decorator allows to skip the specific classes or methods from the diagrams, for instance the following code:
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class C {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{skip:t00029_class}
|
||||||
|
template <typename T> class D {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class E { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
enum class F { red, green, blue };
|
||||||
|
|
||||||
|
class G1 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G2 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G3 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G4 {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
G1 g1;
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
G2 g2;
|
||||||
|
|
||||||
|
/// \uml{skiprelationship}
|
||||||
|
G3 &g3;
|
||||||
|
|
||||||
|
std::shared_ptr<G4> g4;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## `composition`, `association` and `aggregation`
|
||||||
|
|
||||||
|
These decorators allow to specify explicitly the type of relationship within a class diagram that should be
|
||||||
|
generated for a given class member. For instance the following code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
class E {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{association[]}
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
/// @uml{composition[0..1:1..*]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{aggregation[0..1:1..5]}
|
||||||
|
std::vector<C> ccc;
|
||||||
|
|
||||||
|
/// @uml{association[:1]}
|
||||||
|
D ddd;
|
||||||
|
|
||||||
|
/// @uml{aggregation[:1]}
|
||||||
|
E *eee;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## `style`
|
||||||
|
This decorator allows to specify in the code specific styles for diagram elements, for instance:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
/// @uml{style[#back:lightgreen|yellow;header:blue/red]}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{style[#line.dotted:blue]}
|
||||||
|
enum B { one, two, three };
|
||||||
|
|
||||||
|
/// @uml{style[#pink;line:red;line.bold;text:red]}
|
||||||
|
template <typename T> class C {
|
||||||
|
T ttt;
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{style[#red,dashed,thickness=2]}
|
||||||
|
A *aaa;
|
||||||
|
|
||||||
|
/// @uml{composition}
|
||||||
|
/// @uml{style[#green,dashed,thickness=4]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,dotted,thickness=8]}
|
||||||
|
C<int> ccc;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,plain,thickness=16]}
|
||||||
|
D *ddd;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
73
docs/common_options.md
Normal file
73
docs/common_options.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Common diagram generation options
|
||||||
|
|
||||||
|
## Overall configuration file structure
|
||||||
|
By default, `clang-uml` will look for file `.clang-uml` in the projects directory and read all diagrams definitions
|
||||||
|
from it. The file must be specified in YAML and it's overall structure is as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
<common options for all diagrams>
|
||||||
|
diagrams:
|
||||||
|
<first diagram name>:
|
||||||
|
type: [class|sequence|package|include]
|
||||||
|
<diagram specific options>
|
||||||
|
<second diagram name>:
|
||||||
|
type: [class|sequence|package|include]
|
||||||
|
<diagram specific options>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The top level common options are inherited by specific diagrams, if the option is applicable to them and they themselves
|
||||||
|
do not override this option.
|
||||||
|
|
||||||
|
For detailed reference of all configuration options see [here](./configuration_file.md).
|
||||||
|
|
||||||
|
## Translation unit glob patterns
|
||||||
|
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.
|
||||||
|
|
||||||
|
The syntax is simple and based on glob patterns, which can be added to the configuration file as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
glob:
|
||||||
|
- src/dir1/*.cc
|
||||||
|
- src/dir3/*.cc
|
||||||
|
```
|
||||||
|
|
||||||
|
The glob patterns only need to match the translation units, which are also in the `compile_commands.json` file, i.e.
|
||||||
|
any files that match the glob patterns but are not in `compile_commands.json` will be ignored. In case the `glob`
|
||||||
|
pattern set does not much any translation units an error will be printed on the standard output.
|
||||||
|
|
||||||
|
For small projects, the `glob` property can be omitted, which will result in `clang-uml` parsing all translation units
|
||||||
|
from `compile_commands.json` for the diagram. However for large projects, constraining the number of translation units
|
||||||
|
for each diagram to absolute minimum will significantly decrease the diagram generation times.
|
||||||
|
|
||||||
|
## PlantUML custom directives
|
||||||
|
In case it's necessary to add some custom PlantUML declarations before or after the generated diagram content,
|
||||||
|
it can be achieved simply using the `plantuml` configuration properties, for instance:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
plantuml:
|
||||||
|
before:
|
||||||
|
- left to right direction
|
||||||
|
after:
|
||||||
|
- note left of {{ alias("ns1::ns2::MyClass") }} This is my class.
|
||||||
|
```
|
||||||
|
|
||||||
|
These directive are useful for instance for adding notes to elements in the diagrams or customizing diagram layout
|
||||||
|
or style.
|
||||||
|
|
||||||
|
Please note that when referring to diagram elements in the PlantUML directives, they must be added using Jinja
|
||||||
|
templates `alias` command as in the example above.
|
||||||
|
|
||||||
|
More options can be found in the official PlantUML [documentation](https://plantuml.com/).
|
||||||
|
|
||||||
|
## Adding debug information in the generated diagrams
|
||||||
|
Sometimes it is useful for debugging issues with the diagrams to have information on the exact source location,
|
||||||
|
from which given declaration or call expression was derived. By adding option:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
debug_mode: true
|
||||||
|
```
|
||||||
|
|
||||||
|
the generated PlantUML diagram will contain comments before each line containing the source location of the
|
||||||
|
specific diagram element.
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
* `output_directory` - path to the directory where PlantUML diagrams will be generated
|
* `output_directory` - path to the directory where PlantUML diagrams will be generated
|
||||||
* `diagrams` - the map of diagrams to be generated, each diagram name is provided as
|
* `diagrams` - the map of diagrams to be generated, each diagram name is provided as
|
||||||
the key of the diagram YAML node
|
the key of the diagram YAML node
|
||||||
|
* `debug_mode` - add inline debug information in the generated diagrams
|
||||||
|
|
||||||
### Diagram options
|
### Diagram options
|
||||||
* `type` - type of diagram, one of [`class`, `sequence`, `package`, `include`]
|
* `type` - type of diagram, one of [`class`, `sequence`, `package`, `include`]
|
||||||
@@ -22,7 +23,7 @@
|
|||||||
* `dependants` - include all classes, which depend on the specified class
|
* `dependants` - include all classes, which depend on the specified class
|
||||||
* `dependencies` - include all classes, which are dependencies of the specified class
|
* `dependencies` - include all classes, which are dependencies of the specified class
|
||||||
* `context` - include only entities in direct relationship with specified classes
|
* `context` - include only entities in direct relationship with specified classes
|
||||||
* `exclude` - definition of excqlusion patterns:
|
* `exclude` - definition of exclusion patterns:
|
||||||
* `namespaces` - list of namespaces to exclude
|
* `namespaces` - list of namespaces to exclude
|
||||||
* `relationships` - list of relationships to exclude
|
* `relationships` - list of relationships to exclude
|
||||||
* `elements` - list of elements, i.e. specific classes, enums, templates to exclude
|
* `elements` - list of elements, i.e. specific classes, enums, templates to exclude
|
||||||
@@ -37,112 +38,6 @@
|
|||||||
* `before` - list of directives which will be added before the generated diagram
|
* `before` - list of directives which will be added before the generated diagram
|
||||||
* `after` - list of directives which will be added after the generated diagram
|
* `after` - list of directives which will be added after the generated diagram
|
||||||
|
|
||||||
### Template engine
|
|
||||||
`clang-uml` integrates [inja](https://github.com/pantor/inja) template engine, with several
|
|
||||||
additional functions which can be used in textual directives within the configuration files,
|
|
||||||
notes and to generate links and tooltips to diagrams.
|
|
||||||
|
|
||||||
The following, are the `clang-uml` additional template functions:
|
|
||||||
* `ltrim(string)` - left trims a string
|
|
||||||
* `rtrim(string)` - right trims a string
|
|
||||||
* `trim(string)` - trims a string
|
|
||||||
* `substr(string, offset, length)` - returns a substring of a string from offset of length
|
|
||||||
* `split(string)` - splits a string and returns a list of strings
|
|
||||||
* `replace(string, regex, replacement)` - returns a string with replace matches to regex with replacement string
|
|
||||||
* `abbrv(string, length)` - returns a string truncated to length including trailing ellipsis
|
|
||||||
* `element(string)` - returns the entire JSON context a given diagram element, including the following properties:
|
|
||||||
* `name` - name of the element
|
|
||||||
* `type` - type of diagram element (e.g. `class`, `enum`, `package`)
|
|
||||||
* `namespace` - fully qualified element namespace
|
|
||||||
* `full_name` - fully qualified element name
|
|
||||||
* `comment` [optional] - elements comment, if any
|
|
||||||
* `alias` - internal diagram element alias (e.g. PlantUML alias)
|
|
||||||
* `alias(string)` - returns a PlantUML alias of an C++ entity represented by string name
|
|
||||||
* `comment(string)` - returns a comment of an C++ entity represented by string name
|
|
||||||
|
|
||||||
Templates allow complex postprocessing of the diagrams, for instance creation of customized PlantUML
|
|
||||||
notes in the diagrams from comments in the code. Below is an example of using the above commands to
|
|
||||||
generate notes in the PlantUML diagram from code comments (see also test case [t00050](./test_cases/t00050.md)):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
plantuml:
|
|
||||||
after:
|
|
||||||
# Add a note left of the `A` class with the entire clas comment as content
|
|
||||||
- >
|
|
||||||
note left of {{ alias("A") }}
|
|
||||||
{{ comment("clanguml::t00050::A").formatted }}
|
|
||||||
end note
|
|
||||||
# Same as above
|
|
||||||
- >
|
|
||||||
note right of {{ element("clanguml::t00050::A").alias }}
|
|
||||||
{% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }}
|
|
||||||
end note
|
|
||||||
# Add a note left of class 'C' using trimmed text content from the class comment
|
|
||||||
- >
|
|
||||||
note left of {{ alias("C") }} #AABBCC
|
|
||||||
{{ trim(comment("clanguml::t00050::C").text) }}
|
|
||||||
end note
|
|
||||||
# For each element in the diagram (class, template, enum):
|
|
||||||
# - Add a note with \brief comment if exists
|
|
||||||
# - Add a note with \todo for each element which has it
|
|
||||||
# - Add a note with template parameter descriptions based on \tparam comment
|
|
||||||
- >
|
|
||||||
{# Render brief comments and todos, if any were written for an element #}
|
|
||||||
{% for e in diagram.elements %}
|
|
||||||
{% if existsIn(e, "comment") and existsIn(e.comment, "brief") %}
|
|
||||||
|
|
||||||
note top of {{ e.alias }} {% if e.type == "class" %} #22AA22 {% else %} #2222AA {% endif %}
|
|
||||||
{% set c=e.comment %} {{ c.brief.0 }}
|
|
||||||
end note
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{% if existsIn(e, "comment") and existsIn(e.comment, "todo") %}
|
|
||||||
{% set c=e.comment %}
|
|
||||||
{% for t in c.todo %}
|
|
||||||
note top of {{ e.alias }} #882222
|
|
||||||
**TODO**
|
|
||||||
{{ t }}
|
|
||||||
end note
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{# Render template paramete if any #}
|
|
||||||
{% if existsIn(e, "comment") and existsIn(e.comment, "tparam") %}
|
|
||||||
{% set c=e.comment %}
|
|
||||||
|
|
||||||
note top of {{ e.alias }} #AAAAFF
|
|
||||||
**Template parameters**
|
|
||||||
{% for tp in c.tparam %}
|
|
||||||
//{{ tp.name }}// {{ trim(tp.description) }}
|
|
||||||
{% endfor %}
|
|
||||||
end note
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
```
|
|
||||||
Currently there are 2 available comment parsers:
|
|
||||||
* `plain` - default
|
|
||||||
* `clang`
|
|
||||||
They can be selected using `comment_parser` config option.
|
|
||||||
|
|
||||||
#### `plain` comment parser
|
|
||||||
This parser provides only 2 options to the Jinja context:
|
|
||||||
* `comment.raw` - raw comment text, including comment markers such as `///` or `/**`
|
|
||||||
* `comment.formatted` - formatted entire comment
|
|
||||||
|
|
||||||
#### `clang` comment parser
|
|
||||||
This parser uses Clang comment parsing API to extract commands from the command:
|
|
||||||
* `comment.raw` - raw comment text, including comment markers such as `///` or `/**`
|
|
||||||
* `comment.formatted` - formatted entire comment
|
|
||||||
* `comment.<command>.<N>` - where command is the command used in the command e.g. `brief`, `todo`, etc.
|
|
||||||
and `N` is the index of the command in the array (each comment can have multiple instances of the
|
|
||||||
same command such as `\todo`)
|
|
||||||
* `comment.text` - entire text of the comment that is not attached to any command
|
|
||||||
* `comment.paragraph.<N>` - array of plain text paragraphs, for instance if you don't use `\brief`
|
|
||||||
commands but often provide brief description as first sentence of the comment separated with a new line
|
|
||||||
from the rest of the comment
|
|
||||||
|
|
||||||
## Example complete config
|
## Example complete config
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
161
docs/diagram_filters.md
Normal file
161
docs/diagram_filters.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# Diagram filters
|
||||||
|
|
||||||
|
Diagram filters are at the core of generating diagrams with `clang-uml`, as they allow to fine tune the scope
|
||||||
|
of each diagram, and thus provide you with a several small, but readable diagrams instead of a single huge diagram
|
||||||
|
that cannot be effectively browsed, printed or included in an online documentation of your project.
|
||||||
|
|
||||||
|
Filters can be specified separate for each diagram, and they can be added as either `include` or `exclude` filters,
|
||||||
|
depending on which is more appropriate for a given diagram.
|
||||||
|
|
||||||
|
For instance to include only C++ entities from a namespace `ns1::ns2` but not `ns1::ns2::detail` add the following
|
||||||
|
to your diagram configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2
|
||||||
|
exclude:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2::detail
|
||||||
|
```
|
||||||
|
|
||||||
|
The following filters are available.
|
||||||
|
|
||||||
|
## `namespaces`
|
||||||
|
|
||||||
|
Allows to include or exclude entities from specific namespaces.
|
||||||
|
|
||||||
|
## `elements`
|
||||||
|
|
||||||
|
Allows to directly include or exclude specific entities from the diagrams, for instance to exclude a specific class
|
||||||
|
from an included namespace:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2
|
||||||
|
exclude:
|
||||||
|
elements:
|
||||||
|
- ns1::ns2::MyClass
|
||||||
|
```
|
||||||
|
|
||||||
|
## `context`
|
||||||
|
|
||||||
|
This filter allows to limit the diagram elements only to classes which are in direct relationship (of any kind) with
|
||||||
|
the specified class:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
context:
|
||||||
|
- ns1::ns2::MyClass
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `relationships`
|
||||||
|
|
||||||
|
This filter allows to include or exclude specific types of relationships from the diagram, for instance to only
|
||||||
|
include inheritance and template specialization/instantiation relationships add the following to the diagram:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
relationships:
|
||||||
|
- inheritance
|
||||||
|
- instantiation
|
||||||
|
```
|
||||||
|
|
||||||
|
The following relationships can be used in this filter:
|
||||||
|
* inheritance
|
||||||
|
* composition
|
||||||
|
* aggregation
|
||||||
|
* ownership
|
||||||
|
* association
|
||||||
|
* instantiation
|
||||||
|
* friendship
|
||||||
|
* dependency
|
||||||
|
|
||||||
|
## `subclasses`
|
||||||
|
|
||||||
|
This filter allows to include or exclude all subclasses of a given class in the diagram.
|
||||||
|
|
||||||
|
## `specializations`
|
||||||
|
|
||||||
|
This filter allows to include or exclude specializations and instantiations of a specific template from the diagram.
|
||||||
|
|
||||||
|
## `dependants` and `dependencies`
|
||||||
|
|
||||||
|
These filters allow to specify that only dependants or dependencies of a given class should be included in the diagram.
|
||||||
|
This can be useful for analyzing what classes in your project depend on some other class, which could have impact for
|
||||||
|
instance on refactoring.
|
||||||
|
|
||||||
|
For instance the following code:
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
namespace dependants {
|
||||||
|
struct A {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
void b(A *a) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BB {
|
||||||
|
void bb(A *a) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct C {
|
||||||
|
void c(B *b) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D {
|
||||||
|
void d(C *c) { }
|
||||||
|
void dd(BB *bb) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct E {
|
||||||
|
void e(D *d) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct F {
|
||||||
|
};
|
||||||
|
} // namespace dependants
|
||||||
|
|
||||||
|
namespace dependencies {
|
||||||
|
|
||||||
|
struct G {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GG {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct H {
|
||||||
|
void h(G *g) { }
|
||||||
|
void hh(GG *gg) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HH {
|
||||||
|
void hh(G *g) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct I {
|
||||||
|
void i(H *h) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct J {
|
||||||
|
void i(I *i) { }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
and the following filter:
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
dependants:
|
||||||
|
- clanguml::t00043::dependants::A
|
||||||
|
dependencies:
|
||||||
|
- clanguml::t00043::dependencies::J
|
||||||
|
relationships:
|
||||||
|
- dependency
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
14
docs/doxygen_integration.md
Normal file
14
docs/doxygen_integration.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Doxygen integration
|
||||||
|
|
||||||
|
`clang-uml` diagrams can be easily added to the Doxygen documentation using the image tag, however
|
||||||
|
[Doxygen](https://www.doxygen.nl/index.html) does not support the `clang-uml` specific commands.
|
||||||
|
|
||||||
|
`clang-uml` decorators can be omitted completely in Doxygen, by adding the
|
||||||
|
following lines to the Doxygen config file:
|
||||||
|
|
||||||
|
```
|
||||||
|
ALIASES += uml=""
|
||||||
|
ALIASES += uml{1}=""
|
||||||
|
ALIASES += uml{2}=""
|
||||||
|
ALIASES += uml{3}=""
|
||||||
|
```
|
||||||
51
docs/include_diagrams.md
Normal file
51
docs/include_diagrams.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Generating include diagrams
|
||||||
|
|
||||||
|
Include diagrams allow to document the include dependencies among different parts of the project. This can be very useful
|
||||||
|
for instance to detect that a file was included from a module directory, on which specific part of the project
|
||||||
|
should not ever depend.
|
||||||
|
|
||||||
|
The minimal config required to generate an include diagram is presented below:
|
||||||
|
```yaml
|
||||||
|
# Path to the directory where `compile_commands.json` can be found
|
||||||
|
compilation_database_dir: _build
|
||||||
|
# Output directory for the diagrams
|
||||||
|
output_directory: puml
|
||||||
|
# Diagrams definitions
|
||||||
|
diagrams:
|
||||||
|
# Diagram name
|
||||||
|
my_class_diagram:
|
||||||
|
# Type of diagram (has to be `include`)
|
||||||
|
type: include
|
||||||
|
# Include only translation units matching the following patterns
|
||||||
|
glob:
|
||||||
|
- src/*.cc
|
||||||
|
# Render the paths relative to this directory
|
||||||
|
relative_to: src
|
||||||
|
# Include also external system headers
|
||||||
|
generate_system_headers: true
|
||||||
|
# Include only classes and functions from files in `src` directory
|
||||||
|
include:
|
||||||
|
# Include only files belonging to these paths
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
```
|
||||||
|
|
||||||
|
One distinctive option in `include` diagrams is `relative_to`, which tells `clang-uml` to render all filename
|
||||||
|
paths relative to this directory.
|
||||||
|
|
||||||
|
## Tracking system headers directly included by projects files
|
||||||
|
|
||||||
|
In case you would like to include the information about what system headers your projects file include simply add
|
||||||
|
the following option to the diagram:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
generate_system_headers: true
|
||||||
|
```
|
||||||
|
|
||||||
|
This will include only system headers directly included from the projects source files (matched by `glob`) and not
|
||||||
|
their dependencies, for example:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Please note that generating include diagram, which contains third party and system library headers will result
|
||||||
|
in a huge diagram that will be unlikely to be useful.
|
||||||
15
docs/interactive_svg_diagrams.md
Normal file
15
docs/interactive_svg_diagrams.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Interactive SVG diagrams
|
||||||
|
|
||||||
|
`clang-uml` in combination with PlantUML's link generation in diagrams allows to generate interactive diagrams,
|
||||||
|
where clicking on any class, method or call expression can direct the user directly to the source code or some other
|
||||||
|
diagram or document available online.
|
||||||
|
|
||||||
|
For instance to generate links to GitHub repository directly for most of diagram elements simple add this to your
|
||||||
|
`.clang-uml` file:
|
||||||
|
```yaml
|
||||||
|
generate_links:
|
||||||
|
link: 'https://github.com/myorg/myrepo/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }}'
|
||||||
|
tooltip: '{% if "comment" in element %}{{ abbrv(trim(replace(element.comment, "\n+", " ")), 256) }}{% else %}{{ element.name }}{% endif %}'
|
||||||
|
```
|
||||||
|
|
||||||
|
You can open example diagram [here](https://raw.githubusercontent.com/bkryza/clang-uml/master/docs/test_cases/t00014_class.svg) to see how it works in action.
|
||||||
111
docs/jinja_templates.md
Normal file
111
docs/jinja_templates.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# Template engine
|
||||||
|
`clang-uml` integrates [inja](https://github.com/pantor/inja) template engine, with several
|
||||||
|
additional functions which can be used in textual directives within the configuration files,
|
||||||
|
notes and to generate links and tooltips in diagrams.
|
||||||
|
|
||||||
|
The following, are the `clang-uml` additional template functions:
|
||||||
|
* `ltrim(string)` - left trims a string
|
||||||
|
* `rtrim(string)` - right trims a string
|
||||||
|
* `trim(string)` - trims a string
|
||||||
|
* `substr(string, offset, length)` - returns a substring of a string from offset of length
|
||||||
|
* `split(string)` - splits a string and returns a list of strings
|
||||||
|
* `replace(string, regex, replacement)` - returns a string with replace matches to regex with replacement string
|
||||||
|
* `abbrv(string, length)` - returns a string truncated to length including trailing ellipsis
|
||||||
|
* `element(string)` - returns the entire JSON context a given diagram element, including the following properties:
|
||||||
|
* `name` - name of the element
|
||||||
|
* `type` - type of diagram element (e.g. `class`, `enum`, `package`)
|
||||||
|
* `namespace` - fully qualified element namespace
|
||||||
|
* `full_name` - fully qualified element name
|
||||||
|
* `comment` [optional] - elements comment, if any
|
||||||
|
* `alias` - internal diagram element alias (e.g. PlantUML alias)
|
||||||
|
* `alias(string)` - returns a PlantUML alias of an C++ entity represented by string name
|
||||||
|
* `comment(string)` - returns a comment of an C++ entity represented by string name
|
||||||
|
|
||||||
|
Templates allow complex postprocessing of the diagrams, for instance creation of customized PlantUML
|
||||||
|
notes in the diagrams from comments in the code. Below is an example of using the above commands to
|
||||||
|
generate notes in the PlantUML diagram from code comments (see also test case [t00050](./test_cases/t00050.md)):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
plantuml:
|
||||||
|
after:
|
||||||
|
# Add a note left of the `A` class with the entire clas comment as content
|
||||||
|
- >
|
||||||
|
note left of {{ alias("A") }}
|
||||||
|
{{ comment("clanguml::t00050::A").formatted }}
|
||||||
|
end note
|
||||||
|
# Same as above
|
||||||
|
- >
|
||||||
|
note right of {{ element("clanguml::t00050::A").alias }}
|
||||||
|
{% set e=element("clanguml::t00050::A") %} {{ e.comment.formatted }}
|
||||||
|
end note
|
||||||
|
# Add a note left of class 'C' using trimmed text content from the class comment
|
||||||
|
- >
|
||||||
|
note left of {{ alias("C") }} #AABBCC
|
||||||
|
{{ trim(comment("clanguml::t00050::C").text) }}
|
||||||
|
end note
|
||||||
|
# For each element in the diagram (class, template, enum):
|
||||||
|
# - Add a note with \brief comment if exists
|
||||||
|
# - Add a note with \todo for each element which has it
|
||||||
|
# - Add a note with template parameter descriptions based on \tparam comment
|
||||||
|
- >
|
||||||
|
{# Render brief comments and todos, if any were written for an element #}
|
||||||
|
{% for e in diagram.elements %}
|
||||||
|
{% if existsIn(e, "comment") and existsIn(e.comment, "brief") %}
|
||||||
|
|
||||||
|
note top of {{ e.alias }} {% if e.type == "class" %} #22AA22 {% else %} #2222AA {% endif %}
|
||||||
|
{% set c=e.comment %} {{ c.brief.0 }}
|
||||||
|
end note
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if existsIn(e, "comment") and existsIn(e.comment, "todo") %}
|
||||||
|
{% set c=e.comment %}
|
||||||
|
{% for t in c.todo %}
|
||||||
|
note top of {{ e.alias }} #882222
|
||||||
|
**TODO**
|
||||||
|
{{ t }}
|
||||||
|
end note
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{# Render template paramete if any #}
|
||||||
|
{% if existsIn(e, "comment") and existsIn(e.comment, "tparam") %}
|
||||||
|
{% set c=e.comment %}
|
||||||
|
|
||||||
|
note top of {{ e.alias }} #AAAAFF
|
||||||
|
**Template parameters**
|
||||||
|
{% for tp in c.tparam %}
|
||||||
|
//{{ tp.name }}// {{ trim(tp.description) }}
|
||||||
|
{% endfor %}
|
||||||
|
end note
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessing comment content
|
||||||
|
Text available in the code comment blocks can be accessed in the templates depending on the selected comment
|
||||||
|
parser.
|
||||||
|
|
||||||
|
Currently there are 2 available comment parsers:
|
||||||
|
* `plain` - default
|
||||||
|
* `clang` - Clang's comment parser
|
||||||
|
|
||||||
|
They can be selected using `comment_parser` config option.
|
||||||
|
|
||||||
|
#### `plain` comment parser
|
||||||
|
This parser provides only 2 options to the Jinja context:
|
||||||
|
* `comment.raw` - raw comment text, including comment markers such as `///` or `/**`
|
||||||
|
* `comment.formatted` - formatted entire comment
|
||||||
|
|
||||||
|
#### `clang` comment parser
|
||||||
|
This parser uses Clang comment parsing API to extract commands from the command:
|
||||||
|
* `comment.raw` - raw comment text, including comment markers such as `///` or `/**`
|
||||||
|
* `comment.formatted` - formatted entire comment
|
||||||
|
* `comment.<command>.<N>` - where command is the command used in the command e.g. `brief`, `todo`, etc.
|
||||||
|
and `N` is the index of the command in the array (each comment can have multiple instances of the
|
||||||
|
same command such as `\todo`)
|
||||||
|
* `comment.text` - entire text of the comment that is not attached to any command
|
||||||
|
* `comment.paragraph.<N>` - array of plain text paragraphs, for instance if you don't use `\brief`
|
||||||
|
commands but often provide brief description as first sentence of the comment separated with a new line
|
||||||
|
from the rest of the comment
|
||||||
151
docs/package_diagrams.md
Normal file
151
docs/package_diagrams.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Generating package diagrams
|
||||||
|
|
||||||
|
Package diagrams are simple diagrams which can be useful to determine the high level structure of a C++ project,
|
||||||
|
by rendering all projects namespaces as UML packages and their interdependencies.
|
||||||
|
|
||||||
|
The minimal config required to generate a package diagram is presented below:
|
||||||
|
```yaml
|
||||||
|
# Path to the directory where `compile_commands.json` can be found
|
||||||
|
compilation_database_dir: _build
|
||||||
|
# Output directory for the diagrams
|
||||||
|
output_directory: puml
|
||||||
|
# Diagrams definitions
|
||||||
|
diagrams:
|
||||||
|
# Diagram name
|
||||||
|
my_class_diagram:
|
||||||
|
# Type of diagram (has to be `package`)
|
||||||
|
type: package
|
||||||
|
# Include only translation units matching the following patterns
|
||||||
|
glob:
|
||||||
|
- src/*.cc
|
||||||
|
# Include only classes and functions from files in `src` directory
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- ns1::ns2
|
||||||
|
```
|
||||||
|
|
||||||
|
For instance the following C++ code:
|
||||||
|
```
|
||||||
|
namespace A::AA {
|
||||||
|
namespace A1 {
|
||||||
|
struct CA {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A2 {
|
||||||
|
struct CB {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A3 {
|
||||||
|
struct CC {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A4 {
|
||||||
|
struct CD {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A5 {
|
||||||
|
struct CE {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A6 {
|
||||||
|
struct CF {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A7 {
|
||||||
|
struct CG {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A8 {
|
||||||
|
struct CH {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A9 {
|
||||||
|
struct CI {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A10 {
|
||||||
|
struct CJ {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A11 {
|
||||||
|
struct CK {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A12 {
|
||||||
|
struct CL {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A13 {
|
||||||
|
struct CM {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A14 {
|
||||||
|
struct CN {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A15 {
|
||||||
|
struct CO {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A16 {
|
||||||
|
struct CP {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
namespace A17 {
|
||||||
|
struct CR {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
namespace B::BB::BBB {
|
||||||
|
class CBA : public A::AA::A6::CF {
|
||||||
|
public:
|
||||||
|
A::AA::A1::CA *ca_;
|
||||||
|
A::AA::A2::CB cb_;
|
||||||
|
std::shared_ptr<A::AA::A3::CC> cc_;
|
||||||
|
std::map<std::string, std::unique_ptr<A::AA::A4::CD>> *cd_;
|
||||||
|
std::array<A::AA::A15::CO, 5> co_;
|
||||||
|
static A::AA::A16::CP *cp_;
|
||||||
|
|
||||||
|
CBA() = default;
|
||||||
|
|
||||||
|
CBA(A::AA::A14::CN *cn) { }
|
||||||
|
|
||||||
|
friend A::AA::A17::CR;
|
||||||
|
|
||||||
|
template <typename... Item> CBA(std::tuple<Item...> &items) { }
|
||||||
|
|
||||||
|
void ce(const std::vector<A::AA::A5::CE> /*ce_*/) { }
|
||||||
|
|
||||||
|
std::shared_ptr<A::AA::A7::CG> cg() { return {}; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ch(std::map<T, std::shared_ptr<A::AA::A8::CH>> &ch_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::map<T, std::shared_ptr<A::AA::A9::CI>> ci(T * /*t*/)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void cj(std::unique_ptr<A::AA::A10::CJ> /*cj_*/) { }
|
||||||
|
|
||||||
|
std::unique_ptr<A::AA::A11::CK> ck() { return {}; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void cl(std::map<T, std::shared_ptr<A::AA::A12::CL>> & /*ch_*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::map<T, std::shared_ptr<A::AA::A13::CM>> cm()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
56
docs/quick_start.md
Normal file
56
docs/quick_start.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Quick start
|
||||||
|
|
||||||
|
To add an initial class diagram to your project, follow these steps:
|
||||||
|
|
||||||
|
1. Enter your projects top level directory and run:
|
||||||
|
```bash
|
||||||
|
$ clang-uml --init
|
||||||
|
```
|
||||||
|
2. Edit the generated `.clang-uml` file and set the following:
|
||||||
|
```yaml
|
||||||
|
# Path to `compile_commands.json` directory
|
||||||
|
compilation_database_dir: .
|
||||||
|
# Path to diagram output directory
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
# This is the name of the diagram
|
||||||
|
some_class_diagram:
|
||||||
|
type: class
|
||||||
|
# Parse only translation units in `src` subdirectory
|
||||||
|
glob:
|
||||||
|
- src/*.cc
|
||||||
|
# Render all names relative to `myproject` namespace
|
||||||
|
using_namespace:
|
||||||
|
- myproject
|
||||||
|
include:
|
||||||
|
# Include only elements in `myproject` namespace
|
||||||
|
namespaces:
|
||||||
|
- myproject
|
||||||
|
exclude:
|
||||||
|
# Exclude elements in `myproject::detail` namespace
|
||||||
|
namespaces:
|
||||||
|
- myproject::detail
|
||||||
|
```
|
||||||
|
3. Run `clang-uml` in the projects top directory:
|
||||||
|
```bash
|
||||||
|
$ clang-uml
|
||||||
|
```
|
||||||
|
4. Generate SVG images from the PlantUML diagrams:
|
||||||
|
```bash
|
||||||
|
$ plantuml -tsvg puml/*.puml
|
||||||
|
```
|
||||||
|
5. Add another diagram:
|
||||||
|
```bash
|
||||||
|
$ clang-uml --add-sequence-diagram another_diagram
|
||||||
|
```
|
||||||
|
6. Now list the diagrams defined in the config:
|
||||||
|
```bash
|
||||||
|
$ clang-uml -l
|
||||||
|
The following diagrams are defined in the config file:
|
||||||
|
- another_diagram [sequence]
|
||||||
|
- some_class_diagram [class]
|
||||||
|
```
|
||||||
|
7. Generate only the new diagram:
|
||||||
|
```bash
|
||||||
|
clang-uml -n another_diagram
|
||||||
|
```
|
||||||
235
docs/sequence_diagrams.md
Normal file
235
docs/sequence_diagrams.md
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
# Generating sequence diagrams
|
||||||
|
|
||||||
|
The minimal config required to generate a sequence diagram is presented below:
|
||||||
|
```yaml
|
||||||
|
# Path to the directory where `compile_commands.json` can be found
|
||||||
|
compilation_database_dir: _build
|
||||||
|
# Output directory for the diagrams
|
||||||
|
output_directory: puml
|
||||||
|
# Diagrams definitions
|
||||||
|
diagrams:
|
||||||
|
# Diagram name
|
||||||
|
my_class_diagram:
|
||||||
|
# Type of diagram (has to be `sequence`)
|
||||||
|
type: sequence
|
||||||
|
# Include only translation units matching the following patterns
|
||||||
|
glob:
|
||||||
|
- src/*.cc
|
||||||
|
# Include only classes and functions from files in `src` directory
|
||||||
|
include:
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
# Exclude calls to/from `std` namespace
|
||||||
|
exclude:
|
||||||
|
namespaces:
|
||||||
|
- std
|
||||||
|
start_from:
|
||||||
|
- function: "main(int,const char**)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sequence diagram overview
|
||||||
|
|
||||||
|
Consider the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`clang-uml` generated sequence diagrams are not stricly speaking conforming to the UML specification, in order to
|
||||||
|
make them more useful for documenting modern C++ code, the following assumptions were made:
|
||||||
|
* Free functions are included in the sequence diagrams as standalone participants (in fact `clang-uml` can be used
|
||||||
|
to generate sequence diagrams from plain old C code). Functions can also be aggregated into file participants,
|
||||||
|
based on their place of declaration
|
||||||
|
* Call expressions in conditional expressions in block statements (e.g. `if` or `while`) are rendered inside the
|
||||||
|
UML `alt` or `loop` blocks but wrapped in `[`, `]` brackets
|
||||||
|
* Lambda expressions are generated as standalone participants, whose name comprises of the parent context where they
|
||||||
|
are defined and the exact source code location
|
||||||
|
|
||||||
|
## Specifying diagram entry point
|
||||||
|
Sequence diagrams require an entry point for the diagram in order to determine, at which point in the code the sequence
|
||||||
|
diagram should start. Currently the entry point can only be a method or a free function, both specified using `start_from`
|
||||||
|
configuration property, for instance:
|
||||||
|
```yaml
|
||||||
|
start_from:
|
||||||
|
- function: "main(int,char**)"
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```yaml
|
||||||
|
start_from:
|
||||||
|
- function: "clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *)"
|
||||||
|
```
|
||||||
|
|
||||||
|
The entrypoints must be fully qualified and they must match exactly the string representation of given function or
|
||||||
|
method in the `clang-uml` model, which can be tricky. If not sure, the best way is to put anything in the `function`
|
||||||
|
property value at first, run the `clang-uml` on the diagram with verbose set to `-vvv` and look in the logs
|
||||||
|
for the relevant function signature. At the end of the diagram generation at this verbosity level, `clang-uml` will
|
||||||
|
generate a textual representation of all discovered activities relevant for this diagram, for instance if you're looking
|
||||||
|
_for exact signature of method `translation_unit_visitor::VisitCXXRecordDecl`, look for similar_
|
||||||
|
output in the logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
[trace] [tid 3842954] [diagram.cc:194] Sequence id=1875210076312968845:
|
||||||
|
[trace] [tid 3842954] [diagram.cc:198] Activity id=1875210076312968845, from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *):
|
||||||
|
[trace] [tid 3842954] [diagram.cc:208] Message from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *), from_id=1875210076312968845, to=__UNRESOLVABLE_ID__, to_id=0, name=, type=if
|
||||||
|
[trace] [tid 3842954] [diagram.cc:217] Message from=clanguml::sequence_diagram::visitor::translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *), from_id=1875210076312968845, to=clanguml::sequence_diagram::visitor::translation_unit_visitor::should_include(const clang::TagDecl *), to_id=664596622746486441, name=should_include, type=call
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you just need to copy and paste the signature exactly and rerun `clang-uml`.
|
||||||
|
|
||||||
|
## Grouping free functions by file
|
||||||
|
By default, `clang-uml` will generate a new participant for each call to a free function (not method), which can lead
|
||||||
|
to a very large number of participants in the diagram. If it's an issue, an option can be provided in the diagram
|
||||||
|
definition:
|
||||||
|
```yaml
|
||||||
|
combine_free_functions_into_file_participants: true
|
||||||
|
```
|
||||||
|
which will aggregate free functions per source file where they were declared thus minimizing the
|
||||||
|
diagram size. An example of such diagram is presented below:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Lambda expressions in sequence diagrams
|
||||||
|
Lambda expressions in sequence diagrams are... tricky. There is currently tentative support, which follows the
|
||||||
|
following rules:
|
||||||
|
* If lambda expression is called within the scope of the diagram, the calls from the lambda will be placed
|
||||||
|
at the lambda invocation and not declaration
|
||||||
|
* If lambda expression is passed to some function or method, which is outside the scope of the diagram
|
||||||
|
(e.g. used in `std::transform` call) the call will be generated at the point where lambda is passed as parameter
|
||||||
|
* If the lambda is passed as template parameter in instantiation it will not be generated at the moment at all
|
||||||
|
|
||||||
|
Another issue is the naming of lambda participants. Currently, each lambda is rendered in the diagram as a separate
|
||||||
|
class whose name is composed of the lambda location in the code (the only unique way of identifying lambdas I was able
|
||||||
|
to find). For example the follwing code:
|
||||||
|
```cpp
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t20012 {
|
||||||
|
struct A {
|
||||||
|
void a() { aa(); }
|
||||||
|
|
||||||
|
void aa() { aaa(); }
|
||||||
|
|
||||||
|
void aaa() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
void b() { bb(); }
|
||||||
|
|
||||||
|
void bb() { bbb(); }
|
||||||
|
|
||||||
|
void bbb() { }
|
||||||
|
|
||||||
|
void eb() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct C {
|
||||||
|
void c() { cc(); }
|
||||||
|
|
||||||
|
void cc() { ccc(); }
|
||||||
|
|
||||||
|
void ccc() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D {
|
||||||
|
int add5(int arg) const { return arg + 5; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class E {
|
||||||
|
std::optional<std::shared_ptr<B>> maybe_b;
|
||||||
|
std::shared_ptr<A> a;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename F> void setup(F &&f) { f(maybe_b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F> struct R {
|
||||||
|
R(F &&f)
|
||||||
|
: f_{std::move(f)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void r() { f_(); }
|
||||||
|
|
||||||
|
F f_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tmain()
|
||||||
|
{
|
||||||
|
A a;
|
||||||
|
B b;
|
||||||
|
C c;
|
||||||
|
|
||||||
|
// The activity shouldn't be marked at the lambda definition, but
|
||||||
|
// wherever it is actually called...
|
||||||
|
auto alambda = [&a, &b]() {
|
||||||
|
a.a();
|
||||||
|
b.b();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...like here
|
||||||
|
alambda();
|
||||||
|
|
||||||
|
// There should be no call to B in the sequence diagram as the blambda
|
||||||
|
// is never called
|
||||||
|
[[maybe_unused]] auto blambda = [&b]() { b.b(); };
|
||||||
|
|
||||||
|
// Nested lambdas should also work
|
||||||
|
auto clambda = [alambda, &c]() {
|
||||||
|
c.c();
|
||||||
|
alambda();
|
||||||
|
};
|
||||||
|
clambda();
|
||||||
|
|
||||||
|
R r{[&c]() { c.c(); }};
|
||||||
|
|
||||||
|
r.r();
|
||||||
|
|
||||||
|
D d;
|
||||||
|
|
||||||
|
std::vector<int> ints{0, 1, 2, 3, 4};
|
||||||
|
std::transform(ints.begin(), ints.end(), ints.begin(),
|
||||||
|
[&d](auto i) { return d.add5(i); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
generates the following diagram:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Customizing participants order
|
||||||
|
The default participant order in the sequence diagram can be suboptimal in the sense that consecutive calls
|
||||||
|
can go right, then left, then right again depending on the specific call chain in the code. It is however
|
||||||
|
possible to override this order in the diagram definition using `participants_order` property,
|
||||||
|
for instance like this test case:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t20029_sequence:
|
||||||
|
type: sequence
|
||||||
|
glob:
|
||||||
|
- ../../tests/t20029/t20029.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t20029
|
||||||
|
exclude:
|
||||||
|
access:
|
||||||
|
- private
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t20029
|
||||||
|
start_from:
|
||||||
|
- function: clanguml::t20029::tmain()
|
||||||
|
participants_order:
|
||||||
|
- clanguml::t20029::tmain()
|
||||||
|
- clanguml::t20029::Encoder<clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>>
|
||||||
|
- clanguml::t20029::Retrier<clanguml::t20029::ConnectionPool>
|
||||||
|
- clanguml::t20029::ConnectionPool
|
||||||
|
- clanguml::t20029::encode_b64(std::string &&)
|
||||||
|
```
|
||||||
|
|
||||||
56
docs/troubleshooting.md
Normal file
56
docs/troubleshooting.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## General issues
|
||||||
|
### Diagram generated with PlantUML is cropped
|
||||||
|
When generating diagrams with PlantUML without specifying an output file format, the default is PNG.
|
||||||
|
Unfortunately PlantUML will not check if the diagram will fit in the default PNG size, and often the diagram
|
||||||
|
will be be incomplete in the picture. A better option is to specify SVG as output format and then convert
|
||||||
|
to PNG, e.g.:
|
||||||
|
```bash
|
||||||
|
$ plantuml -tsvg mydiagram.puml
|
||||||
|
$ convert +antialias mydiagram.svg mydiagram.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class diagrams
|
||||||
|
### "fatal error: 'stddef.h' file not found"
|
||||||
|
This error means that Clang cannot find some standard headers in the include paths
|
||||||
|
specified in the `compile_commands.json`. This typically happens on macos and sometimes on Linux, when
|
||||||
|
the code was compiled with different Clang version than `clang-uml` itself.
|
||||||
|
|
||||||
|
One solution to this issue is to add the following line to your `CMakeLists.txt` file:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
|
||||||
|
```
|
||||||
|
|
||||||
|
Another option is to make sure that the Clang is installed on the system (even if not used for building your
|
||||||
|
project), e.g.:
|
||||||
|
```bash
|
||||||
|
apt install clang
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sequence diagrams
|
||||||
|
### Generated diagram is empty
|
||||||
|
In order to generate sequence diagram the `start_from` configuration option must have a valid starting point
|
||||||
|
for the diagram (e.g. `function`), which must match exactly the function signature in the `clang-uml` model.
|
||||||
|
Look for error in the console output such as:
|
||||||
|
```bash
|
||||||
|
Failed to find participant mynamespace::foo(int) for start_from condition
|
||||||
|
```
|
||||||
|
which means that either you have a typo in the function signature in the configuration file, or that the function
|
||||||
|
was not defined in the translation units you specified in the `glob` patterns for this diagram. Run again the
|
||||||
|
`clang-uml` tool with `-vvv` option and look in the console output for any mentions of the function from
|
||||||
|
which the diagram should start and copy the exact signature into the configuration file.
|
||||||
|
|
||||||
|
### Generated diagram contains several empty control blocks or calls which should not be there
|
||||||
|
Currently the filtering of call expressions and purging empty control blocks (e.g. loops or conditional statements),
|
||||||
|
within which no interesting calls were included in the diagram is not perfect. In case the regular `namespaces` filter
|
||||||
|
is not enough, it is useful to add also a `paths` filter, which will only include participants and call expressions
|
||||||
|
from files in a subdirectory of your project, e.g.:
|
||||||
|
```yaml
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- myproject
|
||||||
|
paths:
|
||||||
|
- src
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user