I got a project for learning cmake build process, the source files are pretty simple, 3 of them are look like below:
# exe.c
void a();
int main() {
a();
}
# a.c
void d();
void a() {
d();
}
# d.c
void d(){}
The dependency chain is: exe -> a -> d
I assume the lib a,d are all preinstalled libraries in my machine, so I "install" them using the commands below:
gcc -c a.c -o a.o
ar rcs liba.a a.o
gcc -c d.c -o d.o
ar rcs libd.a d.o
These commands produce following outputs in the project root:
liba.a libd.a
Then comes the cmake list :
# define a library
add_library(a INTERFACE)
target_link_libraries(a INTERFACE
${PROJECT_SOURCE_DIR}/liba.a
${PROJECT_SOURCE_DIR}/libd.a
)
# build executable exe
add_executable(exe exe.c)
target_link_libraries(exe PUBLIC a)
cmake build command:
cd {project_root}
mkdir build && cd build
cmake ..
make
This works well.
But if I make a little adjustment to the cmake list:
add_library(a INTERFACE)
target_link_libraries(a INTERFACE
${PROJECT_SOURCE_DIR}/liba.a
# ${PROJECT_SOURCE_DIR}/libd.a
)
add_executable(exe exe.c)
# put lib d here
target_link_libraries(exe PUBLIC a ${PROJECT_SOURCE_DIR}/libd.a)
The make command gave me this error:
/usr/bin/ld: /home/chaowen/projects/c_link/liba.a(a.o): in function `a':
a.c:(.text+0xe): undefined reference to `d'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/exe.dir/build.make:99: exe] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/exe.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
I already know that the target_link_libraries property of cmake INTERFACE target is to be consumed by dependent, in this case, the exe target, so I think I have put the libs in the correct order when linking them to the executable.
But why it's not working? What's the difference between these 2 situations?
I tried to use raw gcc commands without cmake build system to compile the exe, using the command:
gcc -o exe exe.c liba.a libd.a
This works fine, but this do not contribute to the resolve the problem above.
The only order in target_link_libraries
which is propagated to the linker with guarantee is the order of "normal" and IMPORTED targets within the single invocation of target_link_libraries
command.
For everything more complex it is better to think not in the terms of "link order", but in terms of dependencies between libraries. Tell CMake about these dependencies by calling target_link_libraries
, and CMake will prepare correct linking order.
# Define 'a' as a target
add_library(a IMPORTED)
set_target_properties(a PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/liba.a)
# Define 'd' as a target
add_library(d IMPORTED)
set_target_properties(d PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libd.a)
# 'a' depends on 'd'
target_link_libraries(a INTERFACE d)
With such setup linking an executable with the a
target will just work:
target_link_libraries(exe PUBLIC a)
If you executable depends on both a
and d
, then you could link with them in any order:
target_link_libraries(exe PUBLIC a d)
or
target_link_libraries(exe PUBLIC d a)
or even
target_link_libraries(exe PUBLIC a d a)
By knowing that a
depends from d
CMake will prepare correct linking order in all of these scenarios.