5

I have a circular dependency between the two static libraries libA and libB:

add_library(LibA STATIC a.c)
add_library(LibB STATIC b.c)

# Circular dependency: LibA <-> LibB
target_link_libraries(LibA PRIVATE LibB)
target_link_libraries(LibB PRIVATE LibA)

add_executable(Example main.c)
target_link_libraries(Example PRIVATE LibA LibB)

When generating the build configuration CMake tries to resolve these dependencies by passing LibA and LibB twice each to the linker:

ilinkarm.exe --silent CMakeFiles\Example.dir\main.c.o  LibA.lib  LibB.lib  LibA.lib  LibB.lib -o Example.exe

However the IAR linker I use already does multiple passes through the list of libraries and it would be sufficient if LibA and LibB are only listed once. Linking with the duplications does still work, but I get the following warnings:

[build] Warning[Li065]: duplicate file: "LibA.lib"
[build] Warning[Li065]: duplicate file: "LibB.lib"

Is there a possibility to tell CMake that every library in the linker command has to be unique even with circular dependencies?

I tried setting CMAKE_C_LINK_EXECUTABLE but I couldn't find an alternative to <LINK_LIBRARIES> which already seems to contain the duplicates. That's why this line results in the same warning:

set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")

CMake version: 3.26.3
Ninja version: 1.10.2
IAR version: 9.32.2

  • What would be the outcome if you remove `target_link_libraries(LibA PRIVATE LibB)` and `target_link_libraries(LibB PRIVATE LibA)`? In my experience, __ilinkarm__ is not used to "link a library against each other". – sharpgeek May 19 '23 at 07:41
  • To keep the example as simple as possible I omitted `target_include_directories` for `LibA` and `LibB`. If `target_link_libraries` would be removed, CMake can't resolve the includes between the two libraries and the build fails. `ilinkarm` is only used in the final step to link the executable, at which point CMake adds the duplications. – JiaemTheCode May 22 '23 at 08:02

1 Answers1

2

Normally this is controlled by the LINK_INTERFACE_MULTIPLICITY property. However, CMake does not respect values of this property below 2, so this cannot be done as of CMake 3.26. See the code here:

https://github.com/Kitware/CMake/blob/d9641980d2a223e0a6fe42ff23499e55f49fd6d5/Source/cmComputeLinkDepends.cxx#L1314-L1328

size_t cmComputeLinkDepends::ComputeComponentCount(NodeList const& nl)
{
  size_t count = 2;
  for (size_t ni : nl) {
    if (cmGeneratorTarget const* target = this->EntryList[ni].Target) {
      if (cmLinkInterface const* iface =
            target->GetLinkInterface(this->Config, this->Target)) {
        if (iface->Multiplicity > count) {
          count = iface->Multiplicity;
        }
      }
    }
  }
  return count;
}

Here is a link to the CMake issue tracker: https://gitlab.kitware.com/cmake/cmake/-/issues

Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • 1
    From my understanding, the property `LINK_INTERFACE_MULTIPLICITY` is for **projects** which are faced with a compiler bug when build a very **specific** libraries or executables. The fact that the linker accepts only unique libraries should be expressed in a **toolchain/platform** file. Because that file is parsed before a project creates a library target, then toolchain/platform file shouldn't set target properties, but should set some *variables* (normal or CACHE). – Tsyvarev May 18 '23 at 07:49
  • @AlexReinking Thank you for your answer! Though it is unfortunate to hear that CMake doesn't support this at the moment... – JiaemTheCode May 22 '23 at 08:09