The typical approach
It seems to me that the common way of generating a .pc
(pkg-config
) file is to use configure_file
command. For example, something like this:
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/share/pkgconfig/${PROJECT_NAME}.pc.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
@ONLY
)
where the share/pkgconfig/${PROJECT_NAME}.pc.in
file might look like this:
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/@PROJECT_NAME@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Cflags: -I"${includedir}"
Then the .pc
file is installed for example like this:
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig
)
As a side note, I’m not sure if CMAKE_INSTALL_FULL_...
variables shouldn’t be used instead due to special cases mentioned by GNUInstallDirs
. Yet, while important in general, it is out of scope for this question.
Then, when you generate, build, and install like:
cmake -D "CMAKE_BUILD_TYPE=<config>" -D "CMAKE_INSTALL_PREFIX=<prefix>" -S "<source>" -B "<bin>"
cmake --build "<bin>" --config "<config>"
cmake --install "<bin>" --config "<config>"
it all works perfectly!
The problem
However, the problem with this approach is that it doesn’t work with setting prefix (by the --prefix
command-line argument) in the install step above. If I do something like:
cmake --instal "<bin>" --config "<config>" --prefix "<other-prefix>"
all seems to work fine with the exception that the .pc
file has a wrong value of the prefix
variable. It has the value provided (or defaulted by CMake itself) in the CMAKE_INSTALL_PREFIX
variable during the generate step. The value from the --prefix
command-line argument is not used since the .pc
file was already generated during the generate step!
A possible solution
The approach I found so far is to do configure_file
twice. The first run is done during generate step as before, however, it keeps “variable” for the prefix
. The second run is done with install(CODE)
where the CMAKE_INSTALL_PREFIX
has the new value.
So, instead of the configure_file
above we would have something like this:
set(DEFERED_CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@")
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/share/pkgconfig/${PROJECT_NAME}.pc.in.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc.in
@ONLY
)
install(
CODE "configure_file(\"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc.in\" \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc\" @ONLY)"
)
while the share/pkgconfig/${PROJECT_NAME}.pc.in.in
file is the same as before with the exception of the first line that now is:
prefix=@DEFERED_CMAKE_INSTALL_PREFIX@
(...)
For the record, a few important points on this solution:
DEFERED_CMAKE_INSTALL_PREFIX
is needed because it seemsconfigure_file
doesn’t have any way of escaping the@
. One could think that using@@CMAKE_INSTALL_PREFIX@@
will do the trick since the first run will change it to@CMAKE_INSTALL_PREFIX@
and the second run will finish off. However, this doesn’t work. Nor does\@
. I expect there is no way and we need a custom variable for this.- Two runs are needed since during the second run (in
install(CODE)
) all the other variables no longer exist. The second run is executed from thecmake_install.cmake
. - Quoting of arguments in the
CODE
argument is needed (unlike with “ordinary CMake”) since the values are replaced during the generate step and the code ending incmake_install.cmake
has just a raw string. Any space in that string breaks the argument from theconfigure_file
point of view.
Another approach
I haven’t tried it, but I expect we could keep the original approach and only add an install(CODE)
that would modify the ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
file by rewriting the prefix=
line. It could do so either in-place or by making a new file and installing it instead.
However, somehow I like this even less than the above approach. Especially with the in-place modification alternative.
Is there any better approach?
The above approaches are significantly more verbose and not so obvious - at least in my opinion. So, an obvious question is: is there any better solution?
There is CMakePackageConfigHelpers
with its configure_package_config_file
commnad. The documentation there says:
This has the effect that the resulting
FooConfig.cmake
file would work poorly under Windows and OSX, where users are used to choose the install location of a binary package at install time, independent from howCMAKE_INSTALL_PREFIX
was set at build/cmake time.
which is exactly what I’m trying to address here.
However, the configure_package_config_file
command is dedicated for creating package ...Config.cmake
files and doesn’t apply to any arbitrary files, like the .pc
file here.
So, is there anything else?
Or at least is there a “canonical guideline” on this? Since to my surprise. I didn’t find much on this. As if only I had this problem - so maybe in the end I’m just making this up? Maybe Windows users aren’t using pkg-config
that much (even though they could) while *nix users are used to pick the CMAKE_INSTALL_PREFIX
in generate step…