How to create a static library (not executable) in CMake? - c++

I'm new to cmake and I was wondering how to create a static library. In gcc, it can be done by:
ar rsv
Well, how do you do it using CMake?
add_library(mylib STATIC file1.cpp file2.cpp)
add_executable(myexe main.cpp)
target_link_libraries(myexe mylib)
This generates a static library (.a file) but how do you compile it without adding an executable?
if I remove add_executable(myexe main.cpp), it gives me an error. I only want this file:
mylib.a
and NOT
myexe.exe
mylib.a

add_library can be used by itself, without using add_executable at all. Simply remove line 2 to get rid of the executable. The error is most likely caused by line 3, which needs myexe to function. Line 3 should also be removed, because you are only building the library and not linking it.

My bad, I just need to remove:
add_executable(myexe main.cpp)
target_link_libraries(myexe mylib)
and this will work.

Related

CMake refuses to link shared library

Probably the error is trivial for CMake experts but I seem to be blind to it. I've tried two approaches.
I have a library written in C and compiled using gcc 9.4.0 as both, static and dynamic library. I've compiled the library apart and it successfully builds and links against its main.c to produce an executable using a Makefile. When I do nm -D mylib.so I get all the symbols and functions I need.
Now, I am trying to use this library with ROS (meaning build and link with CMake) but the program fails to link. I've tried both approaches: use the library as is, and re-build the library using CMake. Both fail. For the sake of keeping the example short I've removed the lines used by catkin that basically tell CMake to link against all ROS infrastructure. Suffices to say that the main.cpp of the target (my_node) consumes a void* CreateEnvironment(); function from the library which is not found (same with any other function). Say:
#include <mylib/mylib.h>
int main(int argc, char** argv){
foo();
return 0;
}
void foo(){
void ptr = CreateEnvironment();
}
Approach #1: Use pre-built library (preferred)
With CMake it refuses to link. CMakeLists.txt as follows:
cmake_minimum_required(VERSION 3.0.2)
find_library(
MY_LIB
NAME mylib
PATHS "${PROJECT_SOURCE_DIR}/lib"
NO_DEFAULT_PATH # Can add or remove this, no difference
)
add_library(mylib SHARED IMPORTED)
set_target_properties(mylib PROPERTIES IMPORTED_LOCATION "${MY_LIB}")
add_executable(my_node src/main.cpp)
target_link_libraries(my_node mylib ${catkin_LIBRARIES})
It runs:
/usr/bin/c++ -rdynamic CMakeFiles/…/main.cpp.o -o /home/…/clips_node -Wl,-rpath,/home/…/lib:/opt/ros/noetic/lib /home/…/lib/libmylib.so […]
/usr/bin/ld: CMakeFiles/…/main.cpp.o: in function `foo()':
main.cpp:(.text+0x11): undefined reference to `CreateEnvironment()'
Approach #2: Build library with CMake
Tried this approach thinking maybe the library I have was built with different standard, or not all symbols were exported, or exports were incompatible or whatever. A build made by the same system and forcing C++ rather than C should make it linkable... but did not.
Once again, the SO is created and all symbols are visible with nm -D. It is the linking with the executable target what fails.
cmake_minimum_required(VERSION 3.0.2)
## Build library
SET(MYLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/mylib")
AUX_SOURCE_DIRECTORY(${PROJECT_SOURCE_DIR}/src/mylib MYLIB_SOURCES)
add_library(mylib ${MYLIB_SOURCES})
target_include_directories(mylib PRIVATE ${MYLIB_INCLUDE_DIR})
set_target_properties(mylib PROPERTIES
LINKER_LANGUAGE "CXX"
ENABLE_EXPORTS 1
)
## Build node
add_executable(my_node src/main.cpp)
target_include_directories(my_node PUBLIC ${MYLIB_INCLUDE_DIR})
target_link_libraries(my_node mylib ${catkin_LIBRARIES})
Output is almost identical to that of the Approach #1, only the path of the library changes.
What else have I tried
Changing the order of the elements in target_link_libraries
Changing PRIVATE for PUBLIC
Add -lmylib to target_link_libraries
Add the full path to libmylib.so to target_link_libraries
Replace the shared object with a static library
So far it seems that the shared object is passed to the linker but, for some reason, it refuses to link the file. With a Makefile I know I can change the order since some libraries must be prepended and some appended, but CMake seems to be all automagic and I failed to find the error so far.

cmake to link to dll without lib

I've been given a dll without libs.
The dll comes with hpp and h files.
I used dumpbin to create an exports.def file and lib to create a library.
I'm using the following CMakeLists.txt
cmake_minimum_required ( VERSION 3.22 )
project ( mytest )
include_directories("${PROJECT_SOURCE_DIR}/libincludedir")
add_executable ( mytest main.cpp)
target_link_libraries ( mytest LINK_PUBLIC ${PROJECT_SOURCE_DIR}/libincludedir/anewlib.lib )
The original dll and created lib and h and hpp files are all in the libincludedir. Dll is also copied to the bin dir where the exe would be.
I get no linker errors with the lib, but no functions defined in the include headers have bodies found. Types are Undefined. Classes are incomplete.
How do I fix this? Can I progress with what I was given or should I ask for more?
Assuming that you created the .lib correctly, this is how you would set up something linkable:
cmake_minimum_required(VERSION 3.22)
project(example)
add_library(anewlib::anewlib SHARED IMPORTED)
set_target_properties(
anewlib::anewlib
PROPERTIES
IMPORTED_IMPLIB "${PROJECT_SOURCE_DIR}/libincludedir/anewlib.lib"
IMPORTED_LOCATION "${PROJECT_SOURCE_DIR}/libincludedir/anewlib.dll"
IMPORTED_NO_SONAME "TRUE"
INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/libincludedir"
)
add_executable(mytest main.cpp)
target_link_libraries(mytest PRIVATE anewlib::anewlib)
Always link to targets since they provide CMake with much more information than a raw library file. Here, we're telling CMake about the locations of both the .lib and the .dll, that it doesn't have a SONAME (a *nix only thing, anyway, but good to include), and the include path that linkees should use to find its associated headers.
Then when you link mytest to it, mytest will have a correct link line constructed and it will see the right headers.

CMAKE static library builds well but shared library linking fails [duplicate]

I have the following CMakeLists.txt file to generate my project based on Qt:
cmake_minimum_required(VERSION 2.8.12)
project(MyProject)
find_package(Qt5Widgets)
set(MyProjectLib_src ${PROJECT_SOURCE_DIR}/gui.cpp)
set(MyProjectLib_hdr ${PROJECT_SOURCE_DIR}/gui.h)
set(MyProjectLib_ui ${PROJECT_SOURCE_DIR}/gui.ui)
set(MyProjectBin_src ${PROJECT_SOURCE_DIR}/main.cpp)
qt5_wrap_cpp(MyProjectLib_hdr_moc ${MyProjectLib_hdr})
qt5_wrap_ui (MyProjectLib_ui_moc ${MyProjectLib_ui})
include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_BINARY_DIR})
add_library(MyProjectLib SHARED
${MyProjectLib_src}
${MyProjectLib_hdr_moc}
${MyProjectLib_ui_moc}
)
target_link_libraries(MyProjectLib Qt5::Widgets)
add_executable(MyProject ${MyProjectBin_src})
target_link_libraries(MyProject MyProjectLib)
When I try to compile the generated project, I got the following error:
error LNK1104: cannot open file 'Debug\MyProjectLib.lib'
The corresponding directory Debug contains:
MyPtojectLib.dll
MyProjectLib.ilk
MyProjectLib.pdb
You declared MyProjectLib as a shared library, so unless you exported all or part of the symbols of the library, you will only have a .dll designed to be loaded at runtime, and no .lib to link against at compile time as you're trying to do.
A quick solution may be to declare MyProjectLib as a static library:
add_library(MyProjectLib STATIC ...)
Another option could be to use "new" cmake features to export all symbols (see this article):
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
You can also use the "traditional" way by explicitly declaring the symbols to be exported, like in this answer (the long answer). You will first need to declare some API macro somewhere in your code:
#ifdef MyProjectLib_EXPORTS
#define MyProjectLib_API __declspec(dllexport)
#else
#define MyProjectLib_API __declspec(dllimport)
#endif
Note that MyProjectLib_EXPORTS is automatically generated by cmake for shared libraries: you don't need to care about this. Then for each of your class in your code, use the macro in the declaration:
class MyProjectLib_API MyClass { /* ... */ };
MyClass will be an exported symbol when compiling MyProjectLib because MyProjectLib_EXPORTS will be defined, and MyProjectLib_API will expand to __declspec(dllexport). So it will be exported in a .lib file.
It will be an imported symbol when linking against MyProjectLib because MyProjectLib_EXPORTS will be undefined, and MyProjectLib_API will expand to __declspec(dllimport).
You may also improve your cmake file like this:
qt5_wrap_cpp(MyProjectLib_hdr_moc ${MyProjectLib_hdr})
qt5_wrap_ui (MyProjectLib_ui_moc ${MyProjectLib_ui})
You may use AUTOMOC and AUTOUIC instead to let cmake automatically handle the call to Qt's utilities.
include_directories (${PROJECT_SOURCE_DIR})
include_directories (${PROJECT_BINARY_DIR})
PROJECT_SOURCE_DIR is an include directory by default, and I can't see why you need to add PROJECT_BINARY_DIR here: just remove these lines.
Once cleaned, your cmake file may become something like this:
cmake_minimum_required(VERSION 2.8.12)
project(MyProject)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5Widgets)
set(MyProjectLib_src
${PROJECT_SOURCE_DIR}/gui.cpp
${PROJECT_SOURCE_DIR}/gui.h
${PROJECT_SOURCE_DIR}/gui.ui
)
add_library(MyProjectLib STATIC
${MyProjectLib_src}
)
target_link_libraries(MyProjectLib Qt5::Widgets)
set(MyProjectBin_src ${PROJECT_SOURCE_DIR}/main.cpp)
add_executable(MyProject
${MyProjectBin_src}
)
target_link_libraries (MyProject MyProjectLib)
TLDR: Make sure you have source files, not just headers!
For web searchers who arrive here and may not have the OP's problem, I just encountered a very similar linking error that occurred because I had split libraries and left one of them without any source files, a header-only library:
add_library(hsm STATIC
StateAbstract.hpp # header-only
StateMachineAbstract.hpp # header-only
StateMachineBase.hpp # header-only
)
My fix was to insert a cpp file pending restoration of class implementation files:
add_library(hsm STATIC
Placeholder.cpp # this file causes hsm.lib to get generated on Windows
StateAbstract.hpp # header-only
StateMachineAbstract.hpp # header-only
StateMachineBase.hpp # header-only
)
This may seem like it would be obvious, but I'm doing Linux/QNX development on this project and gcc creates a nearly empty library:
$ wc -c build/lib/libhsm.a
8 build/lib/libhsm.a
$ strings build/lib/libhsm.a
!<arch>
That library links quite happily. It was only later when the project was building the Windows version on a build server that I saw the error:
[4/6] cmd.exe /C "cd . && C:\PROGRA~2\MICROS~2\2017\COMMUN~1\VC\Tools\MSVC\1413~1.261\bin\Hostx64\x64\link.exe /lib /nologo /machine:x64 /out:build\lib\hsm.lib && cd ."
[5/6] ...
[6/6] cmd.exe /C "cd . && "C:\Program Files\CMake\bin\cmake.exe" -E vs_link_exe ... /out:build\gtest\hsm_test.exe ... build\lib\hsm.lib ...
LINK : fatal error LNK1104: cannot open file 'build\lib\hsm.lib'
Since I saw the command to create the lib file in the build server output, I could not initially figure out why hsm.lib would not be there (and I cannot get on the build server). Fortunately, as I read the other answer and triple-checked that it was statically linked, I noticed it was a header-only library - whew! I feel lucky!

Linking multiple objects files generated by Makefile to CMake subdirectory

I have a list of object files generated by a Makefile stored under say mylib directory. I am trying to link these object files while compiling one of the sub-directories in my project (I don't want to generate an executable). Here is my CMakeLists.txt file
cmake_minimum_required(VERSION 3.5.1)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g")
set( CMAKE_EXPORT_COMPILE_COMMANDS ON )
file(GLOB SOURCES "*.cpp" ".hpp")
include_directories(mylib)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/mylib)
add_library(slib SHARED ${SOURCES})
That is, mylib directory contains .h, .cc and .o files I generated after running make on mylib.
When I try to compile this, I get an Undefined symbols for architecture x86_64 error for mylib functions.
How can I link multiple precompiled object files generated by an external make? This question (how to add prebuilt object files to executable in cmake) gives a method to link a single object file. How do I do this for all the object files and generated a shared library instead of an executable?
I suggest to compile library "mylib" with ExternalProject (by direct call to gcc, for example) and, then use code like this:
add_library (slib SHARED ${SOURCES})
target_link_libraries (slib "mylib")
add_dependencies may be useful in some cases.

CMake dynamically links `.a` files in `/usr/local/lib`

I want to statically compile my program against another static library, for this example I'm using zeromq. Here is my CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(ZMQ NAMES libzmq.a)
message(STATUS ${ZMQ})
target_link_libraries( test ${ZMQ} )
It finds the .a file when I run mkdir build && cd build && cmake ..
-- /usr/local/lib/libzmq.a
However, if I examine the link.txt file, the library is dynamically linked:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic /usr/local/lib/libzmq.a
The weird bit is that if I move the file to a different directory, say /usr/lib and run cmake .. once more, it locates the new path to the library:
-- /usr/lib/libzmq.a
But now it has magically changed to static linking:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic -Wl,-Bstatic -lzmq -Wl,-Bdynamic
The same thing applies to other libraries I'm linking to.
Why are all my libraries in /usr/local/lib being dynamically linked?
You should not use the path directly, and create an imported target instead, so you can explicitly declare it static:
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(zmq_location NAMES libzmq.a)
message(STATUS ${zmq_location})
add_library(zmq STATIC IMPORTED)
set_target_properties(zmq PROPERTIES IMPORTED_LOCATION ${zmq_location})
target_link_libraries( test zmq )
This may lead to a situation where the library appears to be linked dynamically, but the cmake source code has the answer:
If the target is not a static library make sure the link
type is shared. This is because dynamic-mode linking can handle
both shared and static libraries but static-mode can handle only
static libraries. If a previous user item changed the link type to
static we need to make sure it is back to shared.
Essentially, it's letting the linker handle detecting that the library is static if currently in dynamic-mode.
The answer to my original question about the difference between /usr/local/lib and /usr/lib is that, by default, /usr/local/lib is not one of the implicit link directories. Therefore, a quick fix is to include this line in the config:
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
However, as pointed out in this other answer, referencing files directly is not the way to go, instead one should use add_library.
Here is my DEMO:
find_library(VLQ_LIB NAMES vlq-shared PATHS /usr/local/lib/ REQUIRED)
find_path(VLQ_HEADER vlq.hpp /usr/local/include)
target_link_libraries(myproj PUBLIC VLQ_LIB)
target_include_directories(myproj PUBLIC VLQ_HEADER)
but during run, you still need to copy the shared lib to /lib/ folder, for static lib, you can keep it under /usr/lib