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
?
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:
.cpp
(or multiple) into .o
object files.a
/ .lib
Now if we want to "link" a static library:
.a
back into the object files.o
object files that we compiled in our executableSo 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.