user823255
user823255

Reputation: 1010

CMake: link order of imported targets incorrect

I have the following scenario:

However, the relative linking order in my link.txt is incorrect

/usr/bin/c++     CMakeFiles/bin.dir/main.cpp.o  -o bin ../libA.a ../libB.a

I would expect libA.a to be listed after libB.a. The CMakeLists.txt looks something along the following lines

cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})

add_library(MY::libA IMPORTED INTERFACE)
set_target_properties(MY::libA PROPERTIES INTERFACE_LINK_LIBRARIES "${lib_dir}/libA.a")

add_library(MY::libB IMPORTED INTERFACE)
set_target_properties(MY::libB PROPERTIES INTERFACE_LINK_LIBRARIES "MY::libA;${lib_dir}/libB.a")

add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC MY::libB MY::libA)

Below a description of my attempts to solve the problem. Some without success and some with sucecss but using modifications that render the code usless for the production environment.

Successful attempts:

Attempts without success (same behaviour as described):


Example code that shows the same failure using INTERFACES as a building block

cmake_minimum_required(VERSION 3.13)
project(cmake_test)
set(lib_dir ${CMAKE_CURRENT_SOURCE_DIR})

# libA
add_library(MY::libA_file1 IMPORTED STATIC)
set_target_properties(MY::libA_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libA.a")

add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE MY::libA_file1)

# libB
add_library(MY::libB_file1 IMPORTED STATIC)
set_target_properties(MY::libB_file1 PROPERTIES IMPORTED_LOCATION "${lib_dir}/libB.a")

add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE MY::libB_file1 libA)


add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)

Upvotes: 0

Views: 1893

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 66071

You incorrectly think about INTERFACE_LINK_LIBRARIES property as a "content" of the library's target, which is ordered by target_link_libraries call.

Using

target_link_libraries(MY::libB INTERFACE MY::libA)

you setup link dependency between library targets MY::libB and MY::libA. That is, "content" of MY::libB target should come before "content" of MY::libA target in the linking command line.

But INTERFACE_LINK_LIBRARIES property is NOT a "content" of the library target! It is just an additional link dependency.

As opposite, IMPORTED_LOCATION (for non-INTERFACE IMPORTED target) is a "content" of the library, and target_link_libraries affects on its ordering.

It seems that you cannot add link dependency for a library, using INTERFACE library target. You should use IMPORTED library target for that purpose:

# Collect libraries related to 'libA'
file(GLOB libs_A "${lib_dir}/libA*.a")
# For each library create IMPORTED target with IMPORTED_LOCATION property.
set(libs_A_targets)
foreach(lib_A ${libs_A})
    # Form a unique name for the IMPORTED target: subtarget_A_*
    string(REGEX REPLACE "^${lib_dir}/libA([^.]*).a$" "subtarget_A_\\1" lib_A_target ${lib_A})
    # Create a target with this name
    add_library(${lib_A_target} STATIC IMPORTED)
    set_target_properties(${lib_A_target} PROPERTIES IMPORTED_LOCATION ${lib_A})
    # And add the target into the list
    list(APPEND libs_A_targets ${lib_A_target})
endforeach()

# In a similar way collect libraries for libB.
set(lib_B_targets ...)

# Now link each libB* library with each libA*.
foreach(lib_B_target ${libs_B_targets})
    target_link_libraries(${lib_B_target} INTERFACE ${libs_A_targets})
endforeach()

# Now interface libraries, which combine libA* and libB*, can be created
add_library(libA INTERFACE)
target_link_libraries(libA INTERFACE ${libs_A_targets})

add_library(libB INTERFACE)
target_link_libraries(libB INTERFACE ${libs_B_targets})

# Now these INTERFACE libraries can be linked into an executable in any order
add_executable(bin ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp)
target_link_libraries(bin PUBLIC libA libB)

Upvotes: 1

Related Questions