Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the proper way to use `pkg-config` from `cmake`?

Looking around on the net I have seen a lot of code like this:

include(FindPkgConfig) pkg_search_module(SDL2 REQUIRED sdl2)  target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS}) target_link_libraries(app ${SDL2_LIBRARIES}) 

However that seems to be the wrong way about doing it, as it only uses the include directories and libraries, but ignored defines, library paths and other flags that might be returned by pkg-config.

What would be the correct way to do this and ensure that all compile and link flags returned by pkg-config are used by the compiled app? And is there a single command to accomplish this, i.e. something like target_use(app SDL2)?

ref:

  • include()
  • FindPkgConfig
like image 889
Grumbel Avatar asked Mar 22 '15 07:03

Grumbel


People also ask

What is Pkgconfig CMake?

A pkg-config module for CMake. Finds the pkg-config executable and adds the pkg_get_variable() , pkg_check_modules() and pkg_search_module() commands. The following variables will also be set: PKG_CONFIG_FOUND. True if a pkg-config executable was found.

Where is CMake config?

cmake the corresponding version file is located next to it and named either <config-file>-version. cmake or <config-file>Version. cmake . If no such version file is available then the configuration file is assumed to not be compatible with any requested version.

Where does CMake Find_package look?

CMake ships with its own set of built-in find_package scripts, and their location is in the default CMAKE_MODULE_PATH.

What is a package in CMake?

Packages provide dependency information to CMake based buildsystems. Packages are found with the find_package() command. The result of using find_package() is either a set of IMPORTED targets, or a set of variables corresponding to build-relevant information.


2 Answers

First of, the call:

include(FindPkgConfig) 

should be replaced with:

find_package(PkgConfig) 

The find_package() call is more flexible and allows options such as REQUIRED, that do things automatically that one would have to do manually with include().

Secondly, manually calling pkg-config should be avoid when possible. CMake comes with a rich set of package definitions, found in Linux under /usr/share/cmake-3.0/Modules/Find*cmake. These provide more options and choice for the user than a raw call to pkg_search_module().

As for the mentioned hypothetical target_use() command, CMake already has that built-in in a way with PUBLIC|PRIVATE|INTERFACE. A call like target_include_directories(mytarget PUBLIC ...) will cause the include directories to be automatically used in every target that uses mytarget, e.g. target_link_libraries(myapp mytarget). However this mechanism seems to be only for libraries created within the CMakeLists.txt file and does not work for libraries acquired with pkg_search_module(). The call add_library(bar SHARED IMPORTED) might be used for that, but I haven't yet looked into that.

As for the main question, this here works in most cases:

find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED sdl2) ... target_link_libraries(testapp ${SDL2_LIBRARIES}) target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS}) target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER}) 

The SDL2_CFLAGS_OTHER contains defines and other flags necessary for a successful compile. The flags SDL2_LIBRARY_DIRS and SDL2_LDFLAGS_OTHER are however still ignored, no idea how often that would become a problem.

More documentation here http://www.cmake.org/cmake/help/v3.0/module/FindPkgConfig.html

like image 86
Grumbel Avatar answered Sep 18 '22 02:09

Grumbel


If you're using cmake and pkg-config in a pretty normal way, this solution works.

If, however, you have a library that exists in some development directory (such as /home/me/hack/lib), then using other methods seen here fail to configure the linker paths. Libraries that are not found under the typical install locations would result in linker errors, like /usr/bin/ld: cannot find -lmy-hacking-library-1.0. This solution fixes the linker error for that case.

Another issue could be that the pkg-config files are not installed in the normal place, and the pkg-config paths for the project need to be added using the PKG_CONFIG_PATH environment variable while cmake is running (see other Stack Overflow questions regarding this). This solution also works well when you use the correct pkg-config path.

Using IMPORTED_TARGET is key to solving the issues above. This solution is an improvement on this earlier answer and boils down to this final version of a working CMakeLists.txt:

cmake_minimum_required(VERSION 3.14) project(ya-project C)  # the `pkg_check_modules` function is created with this call find_package(PkgConfig REQUIRED)   # these calls create special `PkgConfig::<MODULE>` variables pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package) pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)  add_executable(program-name file.c ya.c)  target_link_libraries(program-name PUBLIC         PkgConfig::MY_PKG         PkgConfig::YOUR_PKG) 

Note that target_link_libraries does more than change the linker commands. It also propagates other PUBLIC properties of specified targets like compiler flags, compiler defines, include paths, etc., so, use the PUBLIC keyword with caution.

like image 21
activedecay Avatar answered Sep 21 '22 02:09

activedecay