-
Notifications
You must be signed in to change notification settings - Fork 455
Cleanup pkg-config #1326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleanup pkg-config #1326
Changes from all commits
3896bd6
e421311
8383802
195d91a
c4a4008
163a21f
626b615
f333f71
15f08cc
353fb76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,298 @@ | ||
include_guard(GLOBAL) | ||
|
||
include(GNUInstallDirs) | ||
|
||
define_property( | ||
TARGET PROPERTY pkg_config_REQUIRES INHERITED | ||
BRIEF_DOCS "pkg-config 'Requires:' items" | ||
FULL_DOCS "Specify 'Requires:' items for the targets' pkg-config file" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_NAME INHERITED | ||
BRIEF_DOCS "The 'Name' for pkg-config" | ||
FULL_DOCS "The 'Name' of the pkg-config target" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_DESCRIPTION INHERITED | ||
BRIEF_DOCS "The 'Description' pkg-config property" | ||
FULL_DOCS "The 'Description' property to add to a target's pkg-config file" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_VERSION INHERITED | ||
BRIEF_DOCS "The 'Version' pkg-config property" | ||
FULL_DOCS "The 'Version' property to add to a target's pkg-config file" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_CFLAGS INHERITED | ||
BRIEF_DOCS "The 'Cflags' pkg-config property" | ||
FULL_DOCS "Set a list of options to add to a target's pkg-config file 'Cflags' field" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_INCLUDE_DIRECTORIES INHERITED | ||
BRIEF_DOCS "Add -I options to the 'Cflags' pkg-config property" | ||
FULL_DOCS "Set a list of directories that will be added using -I for the 'Cflags' pkg-config field" | ||
) | ||
define_property( | ||
TARGET PROPERTY pkg_config_LIBS INHERITED | ||
BRIEF_DOCS "Add linker options to the 'Libs' pkg-config field" | ||
FULL_DOCS "Set a list of linker options that will joined in a string for the 'Libs' pkg-config field" | ||
) | ||
|
||
# Given a string, escape any generator-expression-special characters | ||
function(_genex_escape out in) | ||
# Escape '>' | ||
string(REPLACE ">" "$<ANGLE-R>" str "${in}") | ||
# Escape "$" | ||
string(REPLACE "$" "$<1:$>" str "${str}") | ||
# Undo the escaping of the dollar for $<ANGLE-R> | ||
string(REPLACE "$<1:$><ANGLE-R>" "$<ANGLE-R>" str "${str}") | ||
# Escape "," | ||
string(REPLACE "," "$<COMMA>" str "${str}") | ||
# Escape ";" | ||
string(REPLACE ";" "$<SEMICOLON>" str "${str}") | ||
set("${out}" "${str}" PARENT_SCOPE) | ||
endfunction() | ||
|
||
# Create a generator expression that ensures the given input generator expression | ||
# is evaluated within the context of the named target. | ||
function(_bind_genex_to_target out target genex) | ||
_genex_escape(escaped "${genex}") | ||
set("${out}" $<TARGET_GENEX_EVAL:${target},${escaped}> PARENT_SCOPE) | ||
endfunction() | ||
|
||
#[==[ | ||
Generate a pkg-config .pc file for the Given CMake target, and optionally a | ||
rule to install it:: | ||
|
||
generate_pkg_config( | ||
<target> | ||
[FILENAME <filename>] | ||
[LIBDIR <libdir>] | ||
[INSTALL [DESTINATION <dir>] [RENAME <filename>]] | ||
[CONDITION <cond>] | ||
) | ||
|
||
The `<target>` must name an existing target. The following options are accepted: | ||
|
||
FILENAME <filename> | ||
- The generated .pc file will have the given `<filename>`. This name *must* | ||
be only the filename, and not a qualified path. If omitted, the default | ||
filename is generated based on the target name. If using a multi-config | ||
generator, the default filename will include the name of the configuration | ||
for which it was generated. | ||
|
||
LIBDIR <libdir> | ||
- Specify the subdirectory of the install prefix in which the target binary | ||
will live. If unspecified, uses `CMAKE_INSTALL_LIBDIR`, which comes from | ||
the GNUInstallDirs module, which has a default of `lib`. | ||
|
||
INSTALL [DESTINATION <dir>] [RENAME <filename>] | ||
- Generate a rule to install the generated pkg-config file. This is better | ||
than using a `file(INSTALL)` on the generated file directly, since it | ||
ensures that the installed .pc file will have the correct install prefix | ||
value. The following additional arguments are also accepted: | ||
|
||
DESTINATION <dir> | ||
- If provided, specify the *directory* (relative to the install-prefix) | ||
in which the generated file will be installed. If unspecified, the | ||
default destination is `<libdir>/pkgconfig` | ||
|
||
RENAME <filename> | ||
- If provided, set the filename of the installed pkg-config file. If | ||
unspecified, the top-level `<filename>` will be used. (Note that the | ||
default top-level `<filename>` will include the configuration type | ||
when built/installed using a multi-config generator!) | ||
|
||
CONDITION <cond> | ||
- The file will only be generated/installed if the condition `<cond>` | ||
results in the string "1" after evaluating generator expressions. | ||
|
||
All named parameters accept generator expressions. | ||
|
||
]==] | ||
function(mongo_generate_pkg_config target) | ||
# Collect some target properties: | ||
# The name: | ||
_genex_escape(proj_name "${PROJECT_NAME}") | ||
_genex_escape(proj_desc "${PROJECT_DESCRIPTION}") | ||
set(tgt_name $<TARGET_PROPERTY:pkg_config_NAME>) | ||
set(tgt_version $<TARGET_PROPERTY:pkg_config_VERSION>) | ||
set(tgt_desc $<TARGET_PROPERTY:pkg_config_DESCRIPTION>) | ||
string(CONCAT gx_name | ||
$<IF:$<STREQUAL:,${tgt_name}>, | ||
${proj_name}, | ||
${tgt_name}>) | ||
# Version: | ||
string(CONCAT gx_version | ||
$<IF:$<STREQUAL:,${tgt_version}>, | ||
${PROJECT_VERSION}, | ||
${tgt_version}>) | ||
# Description: | ||
string(CONCAT gx_desc | ||
$<IF:$<STREQUAL:,${tgt_desc}>, | ||
${proj_desc}, | ||
${tgt_desc}>) | ||
|
||
# Parse and validate arguments: | ||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "FILENAME;LIBDIR;CONDITION" "INSTALL") | ||
|
||
# Compute the default FILENAME | ||
if(NOT DEFINED ARG_FILENAME) | ||
# No filename given. Pick a default: | ||
if(DEFINED CMAKE_CONFIGURATION_TYPES) | ||
# Multi-conf: We may want to generate more than one, so qualify the | ||
# filename with the configuration type: | ||
set(ARG_FILENAME "$<TARGET_FILE_BASE_NAME:${target}>-$<LOWER_CASE:$<CONFIG>>.pc") | ||
else() | ||
# Just generate a file based on the basename of the target: | ||
set(ARG_FILENAME "$<TARGET_FILE_BASE_NAME:${target}>.pc") | ||
endif() | ||
endif() | ||
|
||
# The defalut CONDITION is just "1" (true) | ||
if(NOT DEFINED ARG_CONDITION) | ||
set(ARG_CONDITION 1) | ||
endif() | ||
_bind_genex_to_target(gx_cond ${target} "${ARG_CONDITION}") | ||
|
||
# The default LIBDIR comes from GNUInstallDirs.cmake | ||
if(NOT ARG_LIBDIR) | ||
set(ARG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") | ||
endif() | ||
_bind_genex_to_target(gx_libdir ${target} "${ARG_LIBDIR}") | ||
|
||
# Evaluate the filename genex in the context of the target: | ||
_bind_genex_to_target(gx_filename ${target} "${ARG_FILENAME}") | ||
if(IS_ABSOLUTE "${ARG_FILENAME}") | ||
set(gx_output "${gx_filename}") | ||
else() | ||
get_filename_component(gx_output "${CMAKE_CURRENT_BINARY_DIR}/${gx_filename}" ABSOLUTE) | ||
endif() | ||
|
||
# Generate the content of the file: | ||
_generate_pkg_config_content(content | ||
NAME "${gx_name}" | ||
VERSION "${gx_version}" | ||
DESCRIPTION "${gx_desc}" | ||
PREFIX "%INSTALL_PLACEHOLDER%" | ||
LIBDIR "${gx_libdir}" | ||
GENEX_TARGET "${target}" | ||
) | ||
_bind_genex_to_target(gx_content ${target} "${content}") | ||
string(REPLACE "%INSTALL_PLACEHOLDER%" "${CMAKE_INSTALL_PREFIX}" gx_with_prefix "${gx_content}") | ||
# Now, generate the file: | ||
file(GENERATE | ||
OUTPUT "${gx_output}" | ||
CONTENT "${gx_with_prefix}" | ||
CONDITION "${gx_cond}") | ||
if(NOT "INSTALL" IN_LIST ARGN) | ||
# Nothing more to do here. | ||
return() | ||
endif() | ||
|
||
# Installation handling: | ||
# Use file(GENERATE) to generate a temporary file to be picked up at install-time. | ||
# (For some reason, injecting the content directly into install(CODE) fails in corner cases) | ||
set(gx_tmpfile "${CMAKE_CURRENT_BINARY_DIR}/_pkgconfig/${target}-$<LOWER_CASE:$<CONFIG>>-for-install.txt") | ||
file(GENERATE OUTPUT "${gx_tmpfile}" | ||
CONTENT "${gx_content}" | ||
CONDITION "${gx_cond}") | ||
# Parse the install args that we will inspect: | ||
cmake_parse_arguments(inst "" "DESTINATION;RENAME" "" ${ARG_INSTALL}) | ||
if(NOT DEFINED inst_DESTINATION) | ||
# Install based on the libdir: | ||
set(inst_DESTINATION "${gx_libdir}/pkgconfig") | ||
endif() | ||
if(NOT DEFINED inst_RENAME) | ||
set(inst_RENAME "${ARG_FILENAME}") | ||
endif() | ||
# install(CODE) will write a simple temporary file: | ||
set(inst_tmp "${CMAKE_CURRENT_BINARY_DIR}/${target}-pkg-config-tmp.txt") | ||
_genex_escape(esc_cond "${ARG_CONDITION}") | ||
string(CONFIGURE [==[ | ||
$<@gx_cond@: | ||
# Installation of pkg-config for target @target@ | ||
message(STATUS "Generating pkg-config file: @inst_RENAME@") | ||
file(READ [[@gx_tmpfile@]] content) | ||
# Insert the install prefix: | ||
string(REPLACE "%INSTALL_PLACEHOLDER%" "${CMAKE_INSTALL_PREFIX}" content "${content}") | ||
# Write it before installing again: | ||
file(WRITE [[@inst_tmp@]] "${content}") | ||
> | ||
$<$<NOT:@gx_cond@>: | ||
# Installation was disabled for this generation. | ||
message(STATUS "Skipping install of file [@inst_RENAME@]: Disabled by CONDITION “@esc_cond@”") | ||
> | ||
]==] code @ONLY) | ||
install(CODE "${code}") | ||
_bind_genex_to_target(gx_dest ${target} "${inst_DESTINATION}") | ||
_bind_genex_to_target(gx_rename ${target} "${inst_RENAME}") | ||
# Wrap the filename to install with the same condition used to generate it. If the condition | ||
# is not met, then the FILES list will be empty, and nothing will be installed. | ||
install(FILES "$<${gx_cond}:${inst_tmp}>" | ||
DESTINATION ${gx_dest} | ||
RENAME ${gx_rename} | ||
${inst_UNPARSED_ARGUMENTS}) | ||
endfunction() | ||
|
||
# Generates the actual content of a .pc file. | ||
function(_generate_pkg_config_content out) | ||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "PREFIX;NAME;VERSION;DESCRIPTION;GENEX_TARGET;LIBDIR" "") | ||
if(ARG_UNPARSED_ARGUMENTS) | ||
message(FATAL_ERROR "Unknown arguments: ${ARG_UNPARSED_ARGUMENTS}") | ||
endif() | ||
set(content) | ||
string(APPEND content | ||
"# pkg-config .pc file generated by CMake ${CMAKE_VERSION} for ${ARG_NAME}-${ARG_VERSION}. DO NOT EDIT!\n" | ||
"prefix=${ARG_PREFIX}\n" | ||
"exec_prefix=\${prefix}\n" | ||
"libdir=\${exec_prefix}/${gx_libdir}\n" | ||
"\n" | ||
"Name: ${ARG_NAME}\n" | ||
"Description: ${ARG_DESCRIPTION}\n" | ||
"Version: ${ARG_VERSION}" | ||
) | ||
# Add Requires: | ||
set(requires_joiner "\nRequires: ") | ||
set(gx_requires $<GENEX_EVAL:$<TARGET_PROPERTY:pkg_config_REQUIRES>>) | ||
set(has_requires $<NOT:$<STREQUAL:,${gx_requires}>>) | ||
string(APPEND content "$<${has_requires}:${requires_joiner}$<JOIN:${gx_requires},${requires_joiner}>>\n") | ||
string(APPEND content "\n") | ||
# Add "Libs:" | ||
set(libs) | ||
# Link options: | ||
set(gx_libs | ||
"-L\${libdir}" | ||
"-l$<TARGET_PROPERTY:OUTPUT_NAME>" | ||
$<GENEX_EVAL:$<TARGET_PROPERTY:pkg_config_LIBS>> | ||
$<TARGET_PROPERTY:INTERFACE_LINK_OPTIONS> | ||
eramongodb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
string(APPEND libs "$<JOIN:${gx_libs};${gx_linkopts}, >") | ||
|
||
# Cflags: | ||
set(cflags) | ||
set(gx_flags | ||
$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:pkg_config_CFLAGS>>> | ||
$<REMOVE_DUPLICATES:$<TARGET_PROPERTY:INTERFACE_COMPILE_OPTIONS>> | ||
) | ||
string(APPEND cflags "$<JOIN:${gx_flags}, >") | ||
# Definitions: | ||
set(gx_defs $<REMOVE_DUPLICATES:$<TARGET_PROPERTY:INTERFACE_COMPILE_DEFINITIONS>>) | ||
set(has_defs $<NOT:$<STREQUAL:,${gx_defs}>>) | ||
set(def_joiner " -D") | ||
string(APPEND cflags $<${has_defs}:${def_joiner}$<JOIN:${gx_defs},${def_joiner}>>) | ||
# Includes: | ||
set(gx_inc $<GENEX_EVAL:$<TARGET_PROPERTY:pkg_config_INCLUDE_DIRECTORIES>>) | ||
set(gx_inc "$<REMOVE_DUPLICATES:${gx_inc}>") | ||
set(gx_abs_inc "$<FILTER:${gx_inc},INCLUDE,^/>") | ||
set(gx_rel_inc "$<FILTER:${gx_inc},EXCLUDE,^/>") | ||
set(has_abs_inc $<NOT:$<STREQUAL:,${gx_abs_inc}>>) | ||
set(has_rel_inc $<NOT:$<STREQUAL:,${gx_rel_inc}>>) | ||
string(APPEND cflags $<${has_rel_inc}: " -I\${prefix}/" | ||
$<JOIN:${gx_rel_inc}, " -I\${prefix}/" >> | ||
$<${has_abs_inc}: " -I" | ||
$<JOIN:${gx_abs_inc}, " -I" >>) | ||
string(APPEND content "Libs: ${libs}\n") | ||
string(APPEND content "Cflags: ${cflags}\n") | ||
set("${out}" "${content}" PARENT_SCOPE) | ||
endfunction() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
cmake_minimum_required (VERSION 3.1) | ||
cmake_minimum_required (VERSION 3.15) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a significant change. I suggest at least creating a Side question: is there a compelling reason we should continue to support a per-library CMake Project configuration instead of using only the root CMake project for all targets defined in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're already requiring this at the top level, so this line could likely actually just be removed. It's actually probably bad that we had There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
AFAIK there is no expectation to support I expect C++ driver can be included with This PR is for the C driver (not C++). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Had my repos mixed up. Sorry for the confusion. |
||
|
||
project (libbson C) | ||
|
||
|
@@ -237,6 +237,7 @@ target_include_directories ( | |
) | ||
set_target_properties (bson_shared PROPERTIES VERSION 0.0.0 SOVERSION 0) | ||
set_target_properties (bson_shared PROPERTIES OUTPUT_NAME "${BSON_OUTPUT_BASENAME}-${BSON_API_VERSION}") | ||
mongo_generate_pkg_config (bson_shared FILENAME libbson-1.0.pc INSTALL) | ||
|
||
if (ENABLE_APPLE_FRAMEWORK) | ||
set_target_properties(bson_shared PROPERTIES | ||
|
@@ -334,6 +335,9 @@ if (MONGOC_ENABLE_STATIC_BUILD) | |
# gethostbyname | ||
target_link_libraries (bson_static ws2_32) | ||
endif () | ||
if(MONGOC_ENABLE_STATIC_INSTALL) | ||
mongo_generate_pkg_config(bson_static FILENAME libbson-static-1.0.pc INSTALL) | ||
endif() | ||
endif () | ||
|
||
function (add_example bin src) | ||
|
@@ -378,6 +382,12 @@ install ( | |
INCLUDES DESTINATION ${BSON_HEADER_INSTALL_DIR} | ||
FRAMEWORK DESTINATION ${CMAKE_INSTALL_BINDIR} | ||
) | ||
set_target_properties(${TARGETS_TO_INSTALL} PROPERTIES | ||
pkg_config_NAME libbson | ||
pkg_config_DESCRIPTION "The libbson BSON serialization library." | ||
pkg_config_VERSION "${BSON_VERSION}" | ||
) | ||
set_property(TARGET ${TARGETS_TO_INSTALL} APPEND PROPERTY pkg_config_INCLUDE_DIRECTORIES "${BSON_HEADER_INSTALL_DIR}") | ||
|
||
install ( | ||
FILES ${HEADERS} | ||
|
@@ -408,35 +418,6 @@ foreach (_lib ${BSON_SYSTEM_LIBRARIES}) | |
set (LIBBSON_LIBRARIES "${LIBBSON_LIBRARIES} ${_lib}") | ||
endforeach () | ||
|
||
set (VERSION "${BSON_VERSION}") | ||
set (prefix "${CMAKE_INSTALL_PREFIX}") | ||
set (libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}") | ||
configure_file ( | ||
${CMAKE_CURRENT_SOURCE_DIR}/src/libbson-1.0.pc.in | ||
${CMAKE_CURRENT_BINARY_DIR}/src/libbson-1.0.pc | ||
@ONLY) | ||
|
||
install ( | ||
FILES | ||
${CMAKE_CURRENT_BINARY_DIR}/src/libbson-1.0.pc | ||
DESTINATION | ||
${CMAKE_INSTALL_LIBDIR}/pkgconfig | ||
) | ||
|
||
if (MONGOC_ENABLE_STATIC_INSTALL) | ||
configure_file ( | ||
${CMAKE_CURRENT_SOURCE_DIR}/src/libbson-static-1.0.pc.in | ||
${CMAKE_CURRENT_BINARY_DIR}/src/libbson-static-1.0.pc | ||
@ONLY) | ||
|
||
install ( | ||
FILES | ||
${CMAKE_CURRENT_BINARY_DIR}/src/libbson-static-1.0.pc | ||
DESTINATION | ||
${CMAKE_INSTALL_LIBDIR}/pkgconfig | ||
) | ||
endif () | ||
|
||
include (CMakePackageConfigHelpers) | ||
set (INCLUDE_INSTALL_DIRS "${BSON_HEADER_INSTALL_DIR}") | ||
set (LIBRARY_INSTALL_DIRS ${CMAKE_INSTALL_LIBDIR}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It baffles me that CMake doesn't provide any utilities to support smart target-based pkg-config file generation. 😞