This is my simple CMakeLists.txt
:
project(MyProject)
# Create the library from server.cpp
add_library(server OBJECT tcp_server.cpp tcp_server_priv.cpp)
# Add the current directory to the include path for this target
target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
# Create the executable from main.cpp and link it to the library
add_executable(my_server main.cpp $<TARGET_OBJECTS:server>)
I recently learned about the add_library(x OBJECT y z ...)
construct; my understanding: this doesn't create a static or shared library (.a or .so), but rather forms some temporary/logical grouping of object files, which can be referenced elsewhere as that same group. I.e. elsewhere, I can refer to $<TARGET_OBJECTS:server>
to refer to tcp_server.o and tcp_server_priv.o in aggregate, so the line add_executable(my_server main.cpp $<TARGET_OBJECTS:server>)
means "compile main.cpp, and link it with tcp_server.o and tcp_server_priv.o to make the executable my_server".
I.e. the add_executable
line does both compiling and linking.
I wanted my executable to also link with libpthread, so I tried making this change to the add_executable
line:
add_executable(my_server main.cpp $<TARGET_OBJECTS:server> pthread)
...but that generated this error with cmake:
-- Configuring done
CMake Error at src/CMakeLists.txt:9 (add_executable):
Cannot find source file:
pthread
Tried extensions .c .C .c .cc .cpp .cxx .cu .m .M .mm .h .hh .h .hm
.hpp .hxx .in .txx
CMake Error at src/CMakeLists.txt:9 (add_executable):
No SOURCES given to target: med_server
CMake Generate step failed. Build files cannot be regenerated correctly.
make: *** [Makefile:160: cmake_check_build_system] Error 1
Isn't the above analogous to the following, where I compile main.cpp, link object-file dependencies, and libraries in a single g
invocation:
// tcp_server.cpp
#include <iostream>
extern void private_func();
void public_func() {
std::cout << __FUNCTION__ << std::endl;
private_func();
}
// tcp_server_priv.cpp
#include <iostream>
void private_func() {
std::cout << __FUNCTION__ << std::endl;
}
// main.cpp
#include <semaphore.h>
extern void public_func();
int main(int argc, char* argv[]) {
sem_t my_sem;
public_func();
sem_init(&my_sem, 0, 0);
sem_destroy(&my_sem);
return 0;
}
$ g -c ./tcp_server.cpp \
> && g -c ./tcp_server_priv.cpp \
> && g main.cpp tcp_server.o tcp_server_priv.o -lpthread \
> && ./a.out
public_func
private_func
$
?
Why can I not link a library with the add_executable
statement when I can link object files?
Isn't the attempted add_executable(my_server main.cpp $<TARGET_OBJECTS:server> pthread)
effectively the same as g main.cpp tcp_server.o tcp_server_priv.o -lpthread
?
CodePudding user response:
In your
add_executable(my_server main.cpp $<TARGET_OBJECTS:server> pthread)
, the problem is with the usage of "pthread" in add_executable
- not $<TARGET_OBJECTS:server>
. You should be doing this to link to pthread:
target_link_libraries(my_server PRIVATE pthread)
or
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)
See also: cmake and libpthread and the CMake object library docs.
As stated in the object library docs, this is just how CMake works. You can link to object libraries like you are doing, but you can also do it via target_link_libraries
. You can use target_link_libraries
to link to both object libraries (since v3.12) and shared/static libraries. With respect to target_link_libraries
, there is no "distinction".
CodePudding user response:
TLDR: The distinction is more apparent when you realize what the compilation process looks like.
EDIT: Link to compilation models for better understanding, NOTE: you're interested just in the first picture.
For this example I will use a static library, i.e. on linux it would be suffixed with .a
on windows .lib
. Let's ask ourselves what happens when you build and link this library:
- The steps before are not important for the distinction, so let's start with the following step
- The compiler compiles the individual
.cpp
(or multiple) into.o
object files - These files are then linked together / archived into a library
.a
/.lib
Now if we want to "link" a static library:
- We will actually unpack this archive
.a
back into the object files - We will then grab the
.o
object files that we compiled in our executable - Compiler does some optimizations and throws out unwanted object files
- Repacks and links all the remaining object files we have, into an executable
So you can think of the distinction as such (using static library as an example):
Object files: We don't have to unpack the archive, we just need to link them with the newly created object files
Static library: We need to tell the compiler that hey, we need to unpack this first so we can link it.
The only role of CMake is to "create some sort of a configuration" for your compiler/linker in order to get the job done.
One interesting thing about the distinction between OBJECT
and STATIC
libraries is ( which further demonstrates what I mentioned ) is that if you use OBJECT
libraries you will not take advantage of optimization.
Suppose your static library is constructed of three objects a.o
b.o
and c.o
. If you pack it into lib.a
and then link your executable against it. The optimization might exclude c.o
(for example) and this will make the resulting executable smaller.
BUT if you link with object libraries a.o
b.o
and c.o
then all of the objects will be packed with the executable.