With the upcoming KF6 transition we have the chance again to further untangle and clean up dependencies between KDE Frameworks. There’s a number of tools that help us with analyzing the current state of dependencies, one that we didn’t have when KF5 was started is ELF Dissector.

Library-level Dependencies

When looking at dependencies we usually start at the module or library level, with something as simple and widely available like ldd. For nicer visualization, script like KDE’s draw_lib_dependencies exist to visualize the result. CMake also provides a similar option to produce a built-time dependency graph.

Dependency graph of libKF5CoreAddons.so.
draw_lib_dependencies output for KF5CoreAddons.

While that is good enough in many cases, sometimes we need to dig deeper, and that’s where ELF Dissector can help. After opening the library or executable to analyze and switching to the dependency view, you’ll find three different ways to look at dependencies, described below.

Symbol-level Dependencies

The first view is closest to a tree view of what you’d get from ldd as well. However, it has one major addition, it shows which symbols (and how many) are used from a library. This helps with determining why a dependency exists in the first place, and to asses how strongly coupled two components are.

Screenshot of ELF Dissector's top-down dependency view.
ELF Dissector showing which symbols KF5Notifications uses from KF5CoreAddons.

This is done by looking at the imported symbols of a component, which means this cannot distinguish how often a specific symbol is used. So even a dependency on just a single symbol can mean a very strong coupling in practice. Nevertheless this is often a good indicator.

Inverse Dependencies

For answering the question why an application or library pulls in a specific dependency the symbol-level view can get hard to navigate when there’s many components involved. That’s where the inverse dependency view comes in. There you can select a library and see everything that depends on it, again down to the symbol level.

Screenshot of ELF Dissector's inverse dependency view.
ELF Dissector showing why and how KF5Contacts depends on D-Bus.

Unused Dependencies

The third view is the list of unused dependencies. What you can see in there heavily depends on whether the --as-needed linker flag has been used or not. Either way, the most common case for a dependency showing up in here is a transitive dependency that ended up in the public link interface of a library that is actually used.

This isn’t a problem as such, but it can be useful to review if everything that’s in the public link interface is really needed there, or if we are leaking implementation details to the outside. Fixing this has no immediate impact on the overall dependency situation, but it does enable future changes without breaking the public interface.

Screenshot of ELF Dissector's unused dependency view.
ELF Dissector showing an unused dependency between KF5XmlGui and KF5Auth.

It’s also worth noting that “unused” in this context merely means that no symbol from a library is used by the component linking to it. That doesn’t however mean that the library can safely be removed, there are a few special cases like ICU’s data library that are used by other means (and that’s also why we can’t just use --as-needed unconditionally everywhere).

Outlook

There’s of course more that could be done here, for example combining the graph visualization with edge weights based on the symbol counts.

I also should finally get ELF Dissector into the KDE release service to make it easier available via distribution packages. Getting it built as standalone app bundle is unfortunately tricky due to its dependency on internal binutils API.