Need some help understanding libraries and linking with cmake. Sorry for the wall of text.
I create two static libraries, foo
and bar
, bar
depends on foo
. bar
is just an extension of foo
. I then install bar
somewhere for another project to use. When I got to compile this other project, I run into linker issues. The issues are undefined references to functions. Functions that are defined in foo
but simply not used in bar
I had a basic understanding that libraries were a collection of objects/symbols, so I had assumed that by linking foo
to bar
, bar
would also have those objects/symbols, but that is not the case, so I am wondering if I am missing something or if I am doing something wrong.
This question was helpful and a little bit similar: CMake: how create a single shared library from all static libraries of subprojects?
Like the link suggested, I tried adding the -Wl,--whole-archive
flag like so
set_target_properties(bar PROPERTIES LINK_FLAGS "-Wl,--whole-archive")
But setting those link flags did not work.
I do not want to use SHARED libraries, I would prefer to stay with STATIC libraries, how can I have my libraries keep all the objects/symbols? Am I forced to link both bar
and foo
?
Or is the only solution combining libraries using a custom command like it is suggested in this question: Combining several static libraries into one using CMake
Here I try to explain with an example:
I have a project with the following structure:
src/
|
|- foo/
| | - foo.h
| | - foo.c
| | - CMakeLists.txt
|- bar/
| | - bar.h
| | - bar.c
| | - CMakeLists.txt
Here is the source code for the files under foo
////////////////// foo.c
#include <stdio.h>
#include <Python.h>
#include "foo.h"
static PyObject * module_global;
void init_interpreter()
{
printf("Initializing interpreter\n");
Py_Initialize();
}
void fin_interpreter()
{
printf("Finalizing interpreter\n");
Py_FinalizeEx();
}
void import_module(const char* module_name)
{
if(module_name)
{
module_global = PyImport_ImportModule(module_name);
}
else
{
printf("No good module provided\n");
}
}
////////////////// foo.h
#ifndef FOO_H
#define FOO_H
void init_interpreter();
void fin_interpreter();
void import_module(const char* module_name);
#endif // FOO_H
/////////////////// foo/CMakeLists.txt
find_package (Python REQUIRED COMPONENTS Development)
add_library(foo STATIC
foo.c
)
target_link_libraries(foo PUBLIC
${Python_LIBRARIES}
)
target_include_directories(foo PUBLIC
${Python_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
)
Here is the source code for the files under bar
////////////////// bar.c
#include <stdio.h>
#include "foo.h"
void pretty_init_interpreter()
{
printf("This is a very pretty call!!\n");
init_interpreter();
}
////////////////// bar.h
#ifndef BAR_H
#define BAR_H
void pretty_init_interpreter();
#endif // BAR_H
/////////////////// bar/CMakeLists.txt
add_library(bar STATIC
bar.c
)
target_link_libraries(bar PUBLIC
foo
)
target_include_directories(bar PUBLIC
$<TARGET_PROPERTY:foo,INCLUDE_DIRECTORIES>
${CMAKE_CURRENT_SOURCE_DIR}
)
I build those libraries and install the library bar.a
on another project that looks like:
src/
|
|- main.c
|- CMakeLists.txt
|- install/
| | - lib
| | | - libbar.a
| | - include
| | | - foo.h
| | | - bar.h
Source code in this project:
/////////////// main.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main(int argc, char** argv)
{
pretty_init_interpreter();
import_module(NULL);
fin_interpreter();
return 0;
}
//////////////// CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
set (CMAKE_C_STANDAR 99)
project(foo-project LANGUAGES C)
find_package (Python REQUIRED COMPONENTS Development)
add_executable(foo-exec main.c)
find_library(BAR_LIB
NAMES
bar
HINTS
${PROJECT_SOURCE_DIR}/install/lib/
NO_DEFAULT_PATH
)
target_link_libraries(foo-exec PUBLIC
${BAR_LIB}
${Python_LIBRARIES}
)
target_include_directories(foo-exec PUBLIC
${Python_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/install/include/
)
Compiling the executable with mingw, I get the following error:
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: CMakeFiles\foo-exec.dir/objects.a(main.c.obj): in function `main':
main.c:9: undefined reference to `import_module'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: main.c:11: undefined reference to `fin_interpreter'
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: ../../install/lib/libbar.a(bar.c.obj): in function `pretty_init_interpreter':
bar/bar.c:8: undefined reference to `init_interpreter'
collect2.exe: error: ld returned 1 exit status
Using nm
to look at the symbols of bar
shows that the functions are missing
$ nm install/lib/libbar.a
bar.c.obj:
00000000 b .bss
00000000 d .data
00000000 N .debug_abbrev
00000000 N .debug_aranges
00000000 N .debug_info
00000000 N .debug_line
00000000 r .eh_frame
00000000 r .rdata
00000000 r .rdata$zzz
00000000 t .text
U _init_interpreter
00000000 T _pretty_init_interpreter
U _puts