CMake: link order of imported targets incorrect
I have the following scenario:
- I import two prebuilt libraries into my project (
libA
,libB
)
libB
has a dependency onlibA
- The executable depends on both
libA
andlibB
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:
- Remove the depedency of
bin
onlibA
(i.e. replace the last line bytarget_link_libraries(bin PUBLIC MY::libB)
. This works but I cannot remove the dependency in real code. - Replace the target type
IMPORTED INTERFACE
byIMPORTED STATIC
. UseIMPORTED_LOCATION
instead ofINTERFACE_LINK_LIBRARIES
and usetarget_link_libraries
to express the dependency oflibB
onlibA
. In this case thelink.txt
yields:[...] -o bin ../libA.a ../libB.a ../libA.a
. As soon as I revert the target type forlibB
the link order breaks down again. In the production environment, however, one of the targets is created by conan asIMPORTED INTERFACE
.
Attempts without success (same behaviour as described):
- Create a separate
IMPORTED
target (useIMPORTED_LOCATION
) for every lib and group them inside anINTERFACE
target - Sprinkle the code with
ADD_DEPENDENCIES
- Remove
libA
from theINTERFACE_LINK_LIBRARIES
in line 9 and usetarget_link_libraries(MY::libB INTERFACE MY::libA)
instead. Same result.
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)
cmake
add a comment |
I have the following scenario:
- I import two prebuilt libraries into my project (
libA
,libB
)
libB
has a dependency onlibA
- The executable depends on both
libA
andlibB
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:
- Remove the depedency of
bin
onlibA
(i.e. replace the last line bytarget_link_libraries(bin PUBLIC MY::libB)
. This works but I cannot remove the dependency in real code. - Replace the target type
IMPORTED INTERFACE
byIMPORTED STATIC
. UseIMPORTED_LOCATION
instead ofINTERFACE_LINK_LIBRARIES
and usetarget_link_libraries
to express the dependency oflibB
onlibA
. In this case thelink.txt
yields:[...] -o bin ../libA.a ../libB.a ../libA.a
. As soon as I revert the target type forlibB
the link order breaks down again. In the production environment, however, one of the targets is created by conan asIMPORTED INTERFACE
.
Attempts without success (same behaviour as described):
- Create a separate
IMPORTED
target (useIMPORTED_LOCATION
) for every lib and group them inside anINTERFACE
target - Sprinkle the code with
ADD_DEPENDENCIES
- Remove
libA
from theINTERFACE_LINK_LIBRARIES
in line 9 and usetarget_link_libraries(MY::libB INTERFACE MY::libA)
instead. Same result.
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)
cmake
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example usingA=libB
andB=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e.libA
must come afterlibB
.
– user823255
17 hours ago
add a comment |
I have the following scenario:
- I import two prebuilt libraries into my project (
libA
,libB
)
libB
has a dependency onlibA
- The executable depends on both
libA
andlibB
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:
- Remove the depedency of
bin
onlibA
(i.e. replace the last line bytarget_link_libraries(bin PUBLIC MY::libB)
. This works but I cannot remove the dependency in real code. - Replace the target type
IMPORTED INTERFACE
byIMPORTED STATIC
. UseIMPORTED_LOCATION
instead ofINTERFACE_LINK_LIBRARIES
and usetarget_link_libraries
to express the dependency oflibB
onlibA
. In this case thelink.txt
yields:[...] -o bin ../libA.a ../libB.a ../libA.a
. As soon as I revert the target type forlibB
the link order breaks down again. In the production environment, however, one of the targets is created by conan asIMPORTED INTERFACE
.
Attempts without success (same behaviour as described):
- Create a separate
IMPORTED
target (useIMPORTED_LOCATION
) for every lib and group them inside anINTERFACE
target - Sprinkle the code with
ADD_DEPENDENCIES
- Remove
libA
from theINTERFACE_LINK_LIBRARIES
in line 9 and usetarget_link_libraries(MY::libB INTERFACE MY::libA)
instead. Same result.
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)
cmake
I have the following scenario:
- I import two prebuilt libraries into my project (
libA
,libB
)
libB
has a dependency onlibA
- The executable depends on both
libA
andlibB
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:
- Remove the depedency of
bin
onlibA
(i.e. replace the last line bytarget_link_libraries(bin PUBLIC MY::libB)
. This works but I cannot remove the dependency in real code. - Replace the target type
IMPORTED INTERFACE
byIMPORTED STATIC
. UseIMPORTED_LOCATION
instead ofINTERFACE_LINK_LIBRARIES
and usetarget_link_libraries
to express the dependency oflibB
onlibA
. In this case thelink.txt
yields:[...] -o bin ../libA.a ../libB.a ../libA.a
. As soon as I revert the target type forlibB
the link order breaks down again. In the production environment, however, one of the targets is created by conan asIMPORTED INTERFACE
.
Attempts without success (same behaviour as described):
- Create a separate
IMPORTED
target (useIMPORTED_LOCATION
) for every lib and group them inside anINTERFACE
target - Sprinkle the code with
ADD_DEPENDENCIES
- Remove
libA
from theINTERFACE_LINK_LIBRARIES
in line 9 and usetarget_link_libraries(MY::libB INTERFACE MY::libA)
instead. Same result.
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)
cmake
cmake
edited 15 hours ago
asked 21 hours ago
user823255
670514
670514
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example usingA=libB
andB=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e.libA
must come afterlibB
.
– user823255
17 hours ago
add a comment |
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example usingA=libB
andB=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e.libA
must come afterlibB
.
– user823255
17 hours ago
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example using
A=libB
and B=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e. libA
must come after libB
.– user823255
17 hours ago
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example using
A=libB
and B=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e. libA
must come after libB
.– user823255
17 hours ago
add a comment |
1 Answer
1
active
oldest
votes
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)
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib usingIMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?
– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may useforeach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.
– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may uselink_libraries
orforeach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.
– Tsyvarev
15 hours ago
|
show 3 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53942683%2fcmake-link-order-of-imported-targets-incorrect%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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)
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib usingIMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?
– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may useforeach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.
– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may uselink_libraries
orforeach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.
– Tsyvarev
15 hours ago
|
show 3 more comments
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)
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib usingIMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?
– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may useforeach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.
– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may uselink_libraries
orforeach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.
– Tsyvarev
15 hours ago
|
show 3 more comments
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)
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)
edited 10 hours ago
answered 17 hours ago
Tsyvarev
25.6k42558
25.6k42558
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib usingIMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?
– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may useforeach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.
– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may uselink_libraries
orforeach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.
– Tsyvarev
15 hours ago
|
show 3 more comments
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib usingIMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?
– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may useforeach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.
– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may uselink_libraries
orforeach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.
– Tsyvarev
15 hours ago
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib using
IMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?– user823255
16 hours ago
Thanks a lot, I already had that suspicion. However, how can I solve the problem if I have multiple libs that I want to import? If I import a single lib using
IMPORT_LOCATION
I get indeed the correct result (point 2 of my success-list). In the real code, however, I have a usecase of importing multiple libs. I tried two approaches: 1) Putting one lib as IMPORTED_LOCATION and the rest in its INTERFACE_LINK_LIBRARIES. 2) Creating multiple IMPORTED targets and "grouping" them in a single INTERFACE target. Both approach fail me. Can this be solved without importing+linking each lib individually?– user823255
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may use
foreach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.– Tsyvarev
16 hours ago
"Can this be solved without importing+linking each lib individually?" - As far as I understand, you cannot. If you need to link several libraries with a single one, you may use
foreach
loop. You still may wrap several IMPORTED library targets into INTERFACE one, so linking with that single library would automatically link with all IMPORTED libraries, and order of those IMPORTED libraries would be correct.– Tsyvarev
16 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
That is terrible news :( I cannot believe there is no mechanism for such an obvious use case. How can one 1) Grouping together multiple libs into a target and export them (as a library builder) 2) Or build higher level abstractions? I read all the time about modern CMake but if this simple building block cannot be realized then I don't understand how one can ever move past the terrifying usage of global variables and spreading them all over the place.
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
just for the sake of completeness I have attached a slightly altered example where I try to order dependencies in INTERFACES. However, as soon as I reach the point that one interface depends on another one, the logic starts to break down again. So in the above example I have a libA with multiple libs and libB with multiple libs (one of them could be CUDA, another one OpenCV). When I link two interfaces together, the order is not consistent anymore
– user823255
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may use
link_libraries
or foreach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.– Tsyvarev
15 hours ago
Not sure that I understand your problem. If you are developer of CUDA, and know that your libraries should be linked with OpenCV, you need to link every your library with OpenCV separately. (You may use
link_libraries
or foreach
commands for reduce copy-pasting). Then you may export your libraries. Additionally, you may create an INTERFACE library which combines your libraries together, and export it too. When a user imports your INTERFACE library and links with it, he/she will get desired effect.– Tsyvarev
15 hours ago
|
show 3 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53942683%2fcmake-link-order-of-imported-targets-incorrect%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
@Tsyvarev thanks for sharing the thread but unless I miss the obvious it emphasizes my point. Quote "if library A depends on library B, then libA MUST appear before libB in the linker flags." In my example using
A=libB
andB=libA
(unfortunate naming), I come to the conclusion: libB must appear before libA i.e.libA
must come afterlibB
.– user823255
17 hours ago