bkryza / clang-uml

Customizable automatic UML diagram generator for C++ based on Clang.
Apache License 2.0
602 stars 42 forks source link

Cannot Generate UML for Header Only Classes/Projects #111

Closed DeveloperPaul123 closed 1 year ago

DeveloperPaul123 commented 1 year ago

Not sure if I'm doing something wrong, but while trying to generate UML diagrams on my header only thread-pool library, I get the following output:

[info] [tid 72992] [C:\clang-uml-tmp\clang-uml\src\main.cc:261] Loaded clang-uml config from .clang-uml
[info] [tid 72992] [C:\clang-uml-tmp\clang-uml\src\main.cc:277] Loading compilation database from D:\Repositories\thread-pool\out\build\x64-debug-clang directory
[error] [tid 72992] [C:\clang-uml-tmp\clang-uml\src\main.cc:422] Diagram classes generation failed: no translation units found
DeveloperPaul123 commented 1 year ago

You can see my setup here: https://github.com/DeveloperPaul123/thread-pool/tree/feature/clang-uml

bkryza commented 1 year ago

The problem here is in glob section - header files are not translation units, i.e. they do not match to any entries in your compilation database.

Try either removing the glob section or put there one of the example or test .cpp files...

DeveloperPaul123 commented 1 year ago

Ok that makes sense.

But to be clear, do you mean the glob entry in the .clang-uml file or the file(GLOB_RECURSE...) in my library's CMakeLists.txt?

bkryza commented 1 year ago

@DeveloperPaul123 Sorry, I meant .clang-uml file. I've just tried your branch on Ubuntu with the following config:

# Directory containing the compile_commands.json file
compilation_database_dir: out/build/x64-debug-clang
# The directory where *.puml files will be generated
output_directory: documentation/diagrams
# Set this as default for all diagrams
generate_method_arguments: none
debug_mode: false
# Enable generation of hyperlinks to diagram elements
generate_links:
  # Link pattern
  link: "https://github.com/DeveloperPaul123/thread-pool/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:
  classes:
    type: class
    # Limiting the number of files to include can significantly
    # improve the generation time
    glob:
      - test/source/thread_pool.cpp
      - test/source/thread_safe_queue.cpp
    include:
      # Only include entities from the following namespaces
      namespaces:
        - dp
    # 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
    plantuml:
      # Add this line to the beginning of the resulting puml file
      before:
        - 'title thread-pool class diagram model'

However I had to put this line at the very top of thread_pool.cpp and thread_safe_queue.cpp, as doctest uses some deprecated variable length arrays, which clang does not accept any more and fails:

#pragma clang diagnostic ignored "-Wvla-extension"

After that I got the diagram:

@startuml
title thread-pool class diagram model
class "dp::is_lockable<Lock>" as C_0000853308359594655592
class C_0000853308359594655592 <<concept>> [[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_safe_queue.h#L16{is_lockable}]] {
(Lock && dp::lock)
..
lock.lock()
lock.unlock()
{lock.try_lock()} -> std::convertible_to<bool>
}
class "dp::thread_safe_queue<T,Lock=std::mutex>" as C_0001566136155499624351
class C_0001566136155499624351 [[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_safe_queue.h#L24{thread_safe_queue}]] {
+thread_safe_queue<T, Lock>() = default : void
+push() : void
+empty() const : bool
+pop() : std::optional<T>
+steal() : std::optional<T>
-data_ : std::deque<T> [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_safe_queue.h#L60{data_}]]]
-mutex_ : Lock [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_safe_queue.h#L61{mutex_}]]]
}
class "dp::thread_pool<FunctionType=details::default_function_type,ThreadType=std::jthread>" as C_0002048829996581534495
class C_0002048829996581534495 [[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L35{thread_pool}]] {
+thread_pool<FunctionType, ThreadType>() : void
+~thread_pool<FunctionType, ThreadType>() : void
+thread_pool<FunctionType, ThreadType>() : void
+operator=() : thread_pool<FunctionType,ThreadType> &
+size() const : auto
+enqueue<Function,Args...,ReturnType=std::invoke_result_t<Function &&, Args &&...>>() : std::future<ReturnType>
+enqueue_detach<Function,Args...>() : void
-enqueue_task<Function>() : void
-threads_ : std::vector<ThreadType> [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L194{threads_}]]]
-tasks_ : std::deque<task_item> [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L195{tasks_}]]]
-count_ : std::size_t [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L196{count_}]]]
-pending_tasks_ : std::atomic_int_fast64_t [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L197{pending_tasks_}]]]
}
class "dp::thread_safe_queue<FunctionType>" as C_0000103771545600101984
class C_0000103771545600101984 {
}
class "dp::thread_pool::task_item" as C_0001317959859440874329
class C_0001317959859440874329 [[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L189{thread_pool##task_item}]] {
+tasks : dp::thread_safe_queue<FunctionType> [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L190{tasks}]]]
+signal : std::binary_semaphore [[[https://github.com/DeveloperPaul123/thread-pool/blob/695d0efd0f4a89342bbc5f389e9ee194ba8c150a/include/thread_pool/thread_pool.h#L191{signal}]]]
}
C_0001566136155499624351 ..> C_0000853308359594655592 : Lock
C_0000103771545600101984 ..|> C_0001566136155499624351
C_0001317959859440874329 --+ C_0002048829996581534495
C_0001317959859440874329 o-- C_0000103771545600101984 : +tasks
@enduml
DeveloperPaul123 commented 1 year ago

Sweet! Thanks for trying that out.

So basically, i need to include sources that are part of a translation unit (i.e. the unit test) and then i can select the classes that are in the headers that i want to generate the diagram for by specifying the namespace in the yml file. Does that seem about right?

bkryza commented 1 year ago

Yes, you need to specify glob: patterns in .clang-uml that will match at least on file in your compile_commands.json, in other words the glob: section tells clang which translation units and their includes should be traversed.

The less files these glob: patterns match the faster it will work - but make sure that these files include all headers that are necessary to discover all relationships - I now this is inconvenient in practice but I haven't found a more automated way for this.

You can always just skip the glob: section but then it will traverse all translation units which will be very slow especially if you have multiple diagrams.

As to the include filters, one option is the namespace filter i.e.:

include:
  namespaces: [dp]

but there are many others: https://github.com/bkryza/clang-uml/blob/master/docs/diagram_filters.md

Actually I've just noticed there is one undocumented - paths - which allows you to limit the diagram to definitions in specific files (not necessarily translation units), e.g.:

include:
  paths:
    - include/thread_pool/thread_pool.h
    - include/thread_pool/thread_safe_queue.h

You can also browse through the documentation generated by test cases, which contains many examples of things that should work as they are tested on every build :-)

DeveloperPaul123 commented 1 year ago

I see, thanks for the explanation!