Tronic/cmake-modules

Help working with static libraries

jamessan opened this issue · 6 comments

We're using your macros in neovim and I'm having troubles getting it to work with our static build of libuv. It works fine with the shared build (maybe by accident), but the static build is missing libraries when linking.

In our top-level CMakeLists.txt, we have:

find_package(LIBUV 1.12.0 REQUIRED)

Our FindLIBUV.cmake is:

include(LibFindMacros)

libfind_pkg_detect(LIBUV libuv FIND_PATH uv.h FIND_LIBRARY uv)
libfind_process(LIBUV REQUIRED)

The relevant portion of the resulting CMakeCache.txt is:

$ grep ^LIBUV build/CMakeCache.txt
LIBUV_INCLUDE_DIR:PATH=/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_LIBRARY:FILEPATH=/home/jamessan/src/github.com/neovim/.deps/usr/lib/libuv.a
LIBUV_INCLUDE_DIR-ADVANCED:INTERNAL=1
LIBUV_LIBRARY-ADVANCED:INTERNAL=1
LIBUV_PKGCONF_CFLAGS:INTERNAL=-I/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_PKGCONF_CFLAGS_I:INTERNAL=
LIBUV_PKGCONF_CFLAGS_OTHER:INTERNAL=
LIBUV_PKGCONF_FOUND:INTERNAL=1
LIBUV_PKGCONF_INCLUDEDIR:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_PKGCONF_INCLUDE_DIRS:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_PKGCONF_LDFLAGS:INTERNAL=-L/home/jamessan/src/github.com/neovim/.deps/usr/lib;-luv;-lrt;-lpthread;-lnsl;-ldl
LIBUV_PKGCONF_LDFLAGS_OTHER:INTERNAL=
LIBUV_PKGCONF_LIBDIR:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/lib
LIBUV_PKGCONF_LIBRARIES:INTERNAL=uv;rt;pthread;nsl;dl
LIBUV_PKGCONF_LIBRARY_DIRS:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/lib
LIBUV_PKGCONF_LIBS:INTERNAL=
LIBUV_PKGCONF_LIBS_L:INTERNAL=
LIBUV_PKGCONF_LIBS_OTHER:INTERNAL=
LIBUV_PKGCONF_LIBS_PATHS:INTERNAL=
LIBUV_PKGCONF_MODULE_NAME:INTERNAL=libuv
LIBUV_PKGCONF_PREFIX:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr
LIBUV_PKGCONF_STATIC_CFLAGS:INTERNAL=-I/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_PKGCONF_STATIC_CFLAGS_I:INTERNAL=
LIBUV_PKGCONF_STATIC_CFLAGS_OTHER:INTERNAL=
LIBUV_PKGCONF_STATIC_INCLUDE_DIRS:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/include
LIBUV_PKGCONF_STATIC_LDFLAGS:INTERNAL=-L/home/jamessan/src/github.com/neovim/.deps/usr/lib;-luv;-lrt;-lpthread;-lnsl;-ldl
LIBUV_PKGCONF_STATIC_LDFLAGS_OTHER:INTERNAL=
LIBUV_PKGCONF_STATIC_LIBDIR:INTERNAL=
LIBUV_PKGCONF_STATIC_LIBRARIES:INTERNAL=uv;rt;pthread;nsl;dl
LIBUV_PKGCONF_STATIC_LIBRARY_DIRS:INTERNAL=/home/jamessan/src/github.com/neovim/.deps/usr/lib
LIBUV_PKGCONF_STATIC_LIBS:INTERNAL=
LIBUV_PKGCONF_STATIC_LIBS_L:INTERNAL=
LIBUV_PKGCONF_STATIC_LIBS_OTHER:INTERNAL=
LIBUV_PKGCONF_STATIC_LIBS_PATHS:INTERNAL=
LIBUV_PKGCONF_VERSION:INTERNAL=1.32.0
LIBUV_PKGCONF_libuv_INCLUDEDIR:INTERNAL=
LIBUV_PKGCONF_libuv_LIBDIR:INTERNAL=
LIBUV_PKGCONF_libuv_PREFIX:INTERNAL=
LIBUV_PKGCONF_libuv_VERSION:INTERNAL=

When we try to build, linking fails to because symbols from some of the libraries mentioned in LIBUV_PKGCONF_STATIC_LIBRARIES can't be found.

Is there something wrong with our FindLIBUV or is this something that needs to be fixed in the libfind macros?

Linking a dynamic library automatically pulls in its dependencies. This does not happen with static libraries but instead all dependencies need to be specified manually.

You need to make separate CMake modules for each library in the full dependency chain and use libfind_package to depend on them. Then in your application, use only the LIBUV module and it will automatically pull the rest into LIBUV_LIBRARIES so that everything gets linked.

Can't there be an attempt to handle this automatically based on the information provided by pkg-config? It already specifies what libraries are needed. I get in some situations there may need to be extra work done in the Find module, but it seems like there may be many where there isn't.

No, CMake doesn't work that way. It only uses pkg-config for hints on where to find things. Notice that on Windows pkg-config is usually not even available.

Check out Meson Build System if you want something that can directly use pkg-config.
https://mesonbuild.com/

No, CMake doesn't work that way. It only uses pkg-config for hints on where to find things.

When pkg-config is available, why shouldn't it also use pkg-config's hints about what libraries are needed? Why else would pkg_check_modules() provide the information on the required libraries that the pkgconfig file is advertising?

Notice that on Windows pkg-config is usually not even available.

Understood. I'm not suggesting adding a requirement for pkg-config but fully leveraging its information when it is available.

CMake configures include dirs and libraries by the LibFoo_INCLUDE_DIR and LibFoo_LIBRARY variables, which may be modified via CMake GUI. These are singular and only contain one folder and library, each. The correct paths are determined by checking if certain header or library files exist in any of the locations that CMake looks in. The only function of pkg-config in this is to provide extra locations to look in. CMake is not designed to look at which libraries pkg-config would link or which defines it would use because CMake modules are supposed to provide those in a way that can be altered via config GUI.

Thank you for the support. I'll look into implementing your suggestion.