How to create pkgconfig file

I am trying to generate a pkg-config (.pc) file in cmake for my library.
This requires finding the public compile flags and linker flags.
I’m currently using

  • get_target_property(MYTARGET_LINK_LIBRARIES my_target INTERFACE_LINK_LIBRARIES)
  • get_target_property(MYTARGET_INCLUDE_DIRECTORIES my_target INTERFACE_INCLUDE_DIRECTORIES)
  • and a few others

Then I use configure_file using the following .in file

libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@

Name: @PROJECT_NAME@
Description: My special library
Version: @VERSION@
Libs: -L${libdir} -lmylib @MYTARGET_LINK_LIBRARIES@
Cflags: -I${includedir} @MYTARGET_LINK_LIBRARIES@

My problem is MYTARGET_LINK_LIBRARIES and MYTARGET_LINK_LIBRARIES still have generator expressions and MYTARGET_LINK_LIBRARIES references imported targets of the form blah::blah which of course pkg-config doesn’t understand.

How do I resolve the full linker and compiler options?

Thank you

1 Like

It would also be really useful when generating cmake config files at installation to avoid having to use things like find_dependency and installing custom cmake module files.

you may find an example here docopt.pc.in

configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docopt.pc.in ${CMAKE_CURRENT_BINARY_DIR}/docopt.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/docopt.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

That has no library dependencies

… but it export it cmake config package

see Export cmake config set with namespace by ClausKlein · Pull Request #165 · docopt/docopt.cpp · GitHub

you need something like this:

include(CMakeFindDependencyMacro)
find_dependency(GTest 1.14 CONFIG)

include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")

see i.e. cmake-module-error/module-lib/CMakeLists.txt at develop · ClausKlein/cmake-module-error · GitHub

So the problem is with pkgConfig files not cmake config files, specifically when using generator expressions and imported targets. Neither configure_file or install() resolves the generator expressions or converts imported targets like blah::blah down to compile options and linker options like -I and -L where blah::blah is a dependency of the library you’re trying to install

I might write a complete reproducible example to illustrate the problem. Then you’ll see

I understand your problems, but I don’t know what you want?

  • a pkg config package
  • a cmake config package
  • or both?

Note: pkgconfig files are not portable! (does not work on windows)

bash-5.2$ cat /usr/local/Cellar/openblas/0.3.26/lib/pkgconfig/openblas.pc

libdir=/usr/local/Cellar/openblas/0.3.26/lib
libsuffix=
includedir=/usr/local/Cellar/openblas/0.3.26/include
openblas_config= USE_64BITINT= DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS= NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP=1 NEHALEM MAX_THREADS=56
version=0.3.26
extralib=-lpthread -lgfortran -lpthread -lgfortran -lgomp
Name: openblas
Description: OpenBLAS is an optimized BLAS library based on GotoBLAS2 1.13 BSD version
Version: ${version}
URL: https://github.com/xianyi/OpenBLAS
Libs: -L${libdir} -lopenblas${libsuffix}
Libs.private: ${extralib}
Cflags: -I${includedir}

I’ve got a cmake config file working no problem. But I now want a pkg config file working as well.

Why do you think so? In fact they are (and even relocatable by default) but uncommon anyway.

Cross-compiler is a different thing but that only works for C anyway.

OK, than first you should read man pkg-config

METADATA FILE SYNTAX
       To add a library to the set of packages pkg-config knows about, simply install a .pc file. You should install
       this file to libdir/pkgconfig.

       Here is an example file:
       # This is a comment
       prefix=/home/hp/unst   # this defines a variable
       exec_prefix=${prefix}  # defining another variable in terms of the first
       libdir=${exec_prefix}/lib
       includedir=${prefix}/include

       Name: GObject                            # human-readable name
       Description: Object/type system for GLib # human-readable description
       Version: 1.3.1
       URL: http://www.gtk.org
       Requires: glib-2.0 = 1.3.1
       Conflicts: foobar <= 4.5
       Libs: -L${libdir} -lgobject-1.3
       Libs.private: -lm
       Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib/include

You have add all your

  • compiler flags
  • link flags
  • link libraies
  • all dependencies,

Precisely so if my library has dependencies which have been linked like so:
target_link_libraries(my_library X11::X11 JPEG::JPEG PNG::PNG CUDA::cublas CUDA::curand MKL::MKL ...)

how do i populate Libs and Cflags in the pkg config file?
As i wrote in my original post i tried using:

get_target_property(MYTARGET_LINK_LIBRARIES my_library INTERFACE_LINK_LIBRARIES)
get_target_property(MYTARGET_INCLUDE_DIRECTORIES my_library INTERFACE_INCLUDE_DIRECTORIES)

and a few others then tried using configure_file() with

Libs: -L${libdir} -lmylib @MYTARGET_LINK_LIBRARIES@
Cflags: -I${includedir} @MYTARGET_INCLUDE_DIRECTORIES@

however, in the case of MYTARGET_LINK_LIBRARIES it populates the pkg config file with

Libs: -L${libdir} -lmylib X11::X11 JPEG::JPEG PNG::PNG CUDA::cublas CUDA::curand MKL::MKL 

which of course makes no sense for pkg config.
So i need a way to resolve all the imported targets, generator expressions etc down to explicit compiler and linker flags

At the end of the day, cmake should know how to do this because it has to generate makefiles build-ninja files etc which must use explicit compiler/linker flags. I just wish there was a way to expose this functionality at configure time.

Normally you use find_package(X11 v1.2.3) or something like that in your cmake project files.

So you have a required entry for package config like in the man page.

You have to know your direct dependencies, write it in the package config file.

That’s all.

Yeah that’s not sufficient.
I think I’m gonna have to build a complete reproducible example to prove my point.
Maybe I’m not being clear enough.

mylibrary.tgz (2.2 KB)

Unpack then run:

cd cmake_bug
cmake lib/ -B build -DCMAKE_INSTALL_PREFIX=./install
cmake --build build
cmake --install build/
cmake example/ -B build2 -DCMAKE_PREFIX_PATH=./install
cmake --build build2

You will get error:
/usr/bin/ld: cannot find PNG::PNG: No such file or directory

If you look at the contents of installed mylibrary.pc file you will see something like:

libdir=/home/pf/Downloads/repos/cmake_bug/install/lib
includedir=/home/pf/Downloads/repos/cmake_bug/install/include

Name: mylibrary
Description: PNG reader
Version: 
Libs: -L${libdir} -lmylibrary PNG::PNG
Cflags: -I${includedir} $<BUILD_INTERFACE:/home/pf/Downloads/repos/cmake_bug/lib>

This line is the culprit:

Libs: -L${libdir} -lmylibrary PNG::PNG

make sense now?

I dit understand you the whole time, but you expect that CMake is doing the work for you!

It is an usage error, not a CMake error

bash-5.2$ git diff *.in
diff --git a/lib/mylibrary.pc.in b/lib/mylibrary.pc.in
index d073438..e7fa2e2 100644
--- a/lib/mylibrary.pc.in
+++ b/lib/mylibrary.pc.in
@@ -1,8 +1,11 @@
-libdir=@CMAKE_INSTALL_FULL_LIBDIR@
-includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+# libdir=@CMAKE_INSTALL_FULL_LIBDIR@
+# includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
+libdir=${pcfiledir}/../@CMAKE_INSTALL_LIBDIR@
+includedir=${pcfiledir}/../../@CMAKE_INSTALL_INCLUDEDIR@
 
 Name: @PROJECT_NAME@
 Description: PNG reader
-Version: @VERSION@
-Libs: -L${libdir} -lmylibrary @MYLIBRARY_LINK_LIBRARIES@
-Cflags: -I${includedir} @MYLIBRARY_INCLUDE_DIRECTORIES@
+Version: @PROJECT_VERSION@
+Requires: libpng >= 1.6
+Libs: -L${libdir} -lmylibrary -lpng16 # XXX @MYLIBRARY_LINK_LIBRARIES@
+Cflags: -I${includedir} -I@PNG_INCLUDE_DIRECTORIES@ # -I/usr/local/opt/libpng/include/libpng16 # XXX @MYLIBRARY_INCLUDE_DIRECTORIES@

This works fine :slightly_smiling_face:

see too https://bugs.freedesktop.org/show_bug.cgi?id=62018
and How to generate .pc (pkg-config) file supporting --prefix of the cmake --install? - #6 by craig.scott

What if libpng is an optional dependency in my library controlled by a cmake option ? That’s my use case. So you can’t guarantee it’s always a dependency.