How to make CMake find real static libraries instead of dynamic wrappers? - cmake

I use find_package to include external library into my CMake project. Because I wanted to add support for static linking, I set set(BUILD_SHARED_LIBS FALSE). However, I still get libraries like libglew32.dll.a which are just wrappers that make dynamic linking easier. Instead, I want CMake to find libglew32.a which exists in the same directory. This is the module to find GLEW I use.

You can always link to an exact library using the filename. Here are the flags you would use
-l:[filename]
For cmake
target_link_libraries(target :libglew32.a)

Doing this on linux will use all static libraries
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
if you are building external libraries, usually i just include them in my target
target_link_libraries(myprogram
${LIBROCKET_LIBS_DIRS}/libRocketCore.a
${LIBROCKET_LIBS_DIRS}/libRocketControls.a)

Related

Will cmake target_link_libraries() call target_include_directories()?

My understanding is that if I want to use a 3rd library, I need the header file path and the link obj path.
in CMake, I think a successful find_package() will set 2 variables: somelib_INCLUDE_DIRS and somelib_LIBRARIES.
But in many case, I only call
target_link_libraries(myexe
${somelib_LIBRARIES} )
and it seems to be sufficient. (somelib_INCLUDE_DIRS is not used)
My question is: Is my experience correct? somelib_INCLUDE_DIRS is not necessary?
If it's correct, can I say the reason is that target_link_libraries calls target_include_directories internally?
Thanks.
Will cmake target_link_libraries() call target_include_directories()?
If target_link_libraries is called with a library target name that has PUBLIC or INTERFACE include directories, then target_link_libraries will also add those include directories to the target. I.e. INTERFACE properties are "transitive" or are "inherited".
Is my experience correct? somelib_INCLUDE_DIRS is not necessary?
That 100% just depends on the specific library configuration. If the condition described above is satisfied, then yes. If not, somelib_LIBRARIES has libraries or link flags and not CMake library target names, or those library target names do not have any PUBLIC or INTERFACE include directories, etc...., then not.
Some find_package configuration files have been rewritten with add_library(... INTERFACE. Some not. It depends on the specific library CMake configuration. Consult the documentation and source code of the specific library CMake configuration files to know how to use it.
If it's correct, can I say the reason is that target_link_libraries calls target_include_directories internally?
Yes.

Is there a declarative way to copy dlls using CMake?

Existing solutions suggest using add_custom_command, and that works, but I consider it ugly and error prone, so I wonder if there is a way when I declare my 3rdparty library to say that any executable using my library should have dll copied to where executable is built. Obviously support for different dlls in Release/Debug is also necessary.
You are clearly on Windows since you are building dll's. But on Linux I would just comment that one can set the RPATH for a binary. In Cmake this looks something like
set_target_properties(target PROPERTIES INSTALL_RPATH ${path_to_libs})
# Maybe also this line...
target_link_libraries(target PUBLIC -Wl,--disable-new-dtags)
I have no idea what effect this has in Windows. However, in Linux this basically tells the binary where to look for libraries that it will need to link to, thus avoiding the need to either copy the libraries to the same directory or to copy the libraries to the standard library path.
If you have a target A in Cmake, and you either export that target, or include it in another Cmake project as a subdirectory (via add_subdirectory), then you could easily just bundle all of the shared libraries together into the target like this:
CMakeLists.txt for target A:
add_library(A ... )
target_link_libraries(A PUBLIC other_libs_needed_by_consumers_of_A)
CMakeLists.txt for target B:
target_link_libraries(B PUBLIC A)

How to rewrite a find_package based library into one which can be embedded into the parent project directly?

The library libwebrtc from https://github.com/cloudwebrtc/libwebrtc-build/blob/dev/CMakeLists.txt was built to be used with make; make install and the project which wants to use the library must later use the find_package from CMake.
I, however, want to change libwebrtc so it can be added as a git submodule into my current project as a custom library, as, for instance, https://github.com/itay-grudev/SingleApplication which is compiled when I type: cmake ..; make into a static/dynamic library and then linked in my main application. (The Qt library example I references earlier was confusing since this is build outside of my main project and only linked to afterwards - which is not what I want). Sorry for that confusion.
To be able to do that, I think that the ExternalProject_Add at https://github.com/cloudwebrtc/libwebrtc-build/blob/a24a5e5947658d43339d4bfd85d3f4c52fc71057/CMakeLists.txt#L100 must be changed into a add_library call.
The problem here is that the include_directories is used by the main project before the library has been completely built.
Question
How to rewrite libwebrtc to be used as a simple static library with proper build dependencies so that my main project is only compiled/linked after the libwebrtc build was finished and custom header files were generated in the CMAKE_CURRENT_BINARY_DIR of libwebrtc.
Or in other words, how to rewrite libwebrtc to be used without having to call make install for the library and then use find_package to use that library.
The hack (which is working already)
With this hack I am already able to:
Build the library from my parent project
Depend on the generated header files which exist only after the libwebrtc has been built completely (thus, delay main project building until dependencies are meet)
Depend on the generated webrtc.a static library for the linker step
I imaging that make install will work since libwebrtc is statically linked.
add_dependencies(${PROJECT_NAME} libwebrtcx)
add_subdirectory(third-party/libwebrtcx)
include_directories(
${CMAKE_BINARY_DIR}/sources/third-party/libwebrtcx/include/webrtc
${CMAKE_BINARY_DIR}/sources/third-party/libwebrtcx/include/webrtc/third_party/libyuv/include/
${CMAKE_BINARY_DIR}/sources/third-party/libwebrtcx/webrtc/src/third_party/abseil-cpp
)
add_library(libwebrtc STATIC IMPORTED)
set_property(TARGET libwebrtc PROPERTY IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/sources/third-party/libwebrtcx/webrtc/src/out/Release/obj/libwebrtc.a")
target_link_libraries(${PROJECT_NAME} libwebrtc)
Note: It requires to rename the libwebrtc project to libwebrtcx and also the ExternalProject_Add at https://github.com/cloudwebrtc/libwebrtc-build/blob/a24a5e5947658d43339d4bfd85d3f4c52fc71057/CMakeLists.txt#L100 must be renamed to libwebrtcx.
Note: It also requires to rename all CMAKE_BINARY_DIR into CMAKE_CURRENT_BINARY_DIR and CMAKE_SOURCE_DIR to CMAKE_CURRENT_SOURCE_DIR. Details can be found here: CMake: Using add_subproject with a library using Include ends up in wrong relative path

How to force CMake to link to system library instead of target with same name

I have a library with the same name as a system library. Most of the executables in my project link to my own library but one executable needs to link to the system library. CMake is generating a g++ command line linking to ../foo/libfoo.a and I need it to link to -lfoo instead.
I have a structure something like this:
/CMakeLists.txt
add_directory(foo)
add_directory(program)
/foo/CMakeLists.txt
add_library(foo foo.cpp)
/program/CMakeLists.txt
add_executable(program program.cpp)
target_link_libraries(program foo)
One solution would be to change the name of my library so it doesn't conflict but I'd rather not do that because reasons. I'm looking for some CMake magic to let me tell it to use the system library.
I'd rather not do that because reasons.
I can see your hands are tied here :) so I would recommend "finding" the system library using find_library and linking to that. If successful, find_library will yield the absolute path to the library, and if you use that variable in your subsequent target_link_libraries call, it should leave no room for ambiguity.
So in "/program/CMakeLists.txt", something like:
find_library(SystemFoo NAMES foo)
add_executable(program program.cpp)
target_link_libraries(program ${SystemFoo})
You may even want to include a few of the NO_xxx_PATH args (e.g. NO_CMAKE_ENVIRONMENT_PATH and/or NO_CMAKE_PATH) to the find_library call to reduce the number of locations CMake will search for the library.

How to link a static library to an executable using CMake

In CMake, we use TARGET_LINK_LIBRARIES() to link a shared library to an library/executable.
For example:
TARGET_LINK_LIBRARIES(ExecutableName xxx)
where ExecutableName - is the name of executable
xxx - is the library name.
As of my understanding CMake searches for "libxxx.so" at the paths mentioned in LINK_DIRECTORIES() macro.
But if I have a third party library with name "libxxx.a" then how do I link the library to the executable using CMake.
Thank you for your help in advance!
You should always try to give either full paths or CMake targets to target_link_libraries.
Since you do not seem to build the dependency as part of the CMake project, the only way to obtain a CMake target to link against, is to create an imported target. This is usually quite tedious to do manuall, so unless the dependency already provides a config file with an imported target, you probably do not want to go down that road. Imported targets are the most convenient to use, but only if you can get CMake to write the for you.
So, absolute paths it is then. You obviously would not want to hardcode absolute library paths in your CMakeLists. As pointed out in your question, the desired behavior is that you specify just the name of a library and CMake should be able to figure out its location automatically. This is exactly what find_library does for you.
To link against a library xxx, you would do something like this:
find_library(LIB_LOCATION xxx)
target_link_libraries(ExecutableName ${LIB_LOCATION})
Note that find_library provides a plethora of options to further specify where to look for the requested library. Get rid of your existing link_directories call and add the respective paths as hints to find_library instead.
This approach is both more flexible when porting your CMake code to other platforms and more easy to debug if something goes wrong than your initial approach.
Just specifying the library filename should work:
TARGET_LINK_LIBRARIES(ExecutableName libxxx.a)
and
TARGET_LINK_LIBRARIES(ExecutableName xxx)
actually should work too as that would not look for the .so but for a libxxx.a file in the search paths.