qt5base-lts/cmake/QtPkgConfigHelpers.cmake
Martin Reboredo 5b07f14a4f CMake build: generate pkgconfig for public modules
After the update to the CMake based build system the ability to
generate pkgconfig files, like it was with QMake, was lost.

This patch adds pkgconfig generation again via a new internal command
named qt_internal_export_pkg_config_file.

The functionality of this command consists in checking if the target
is internal. Then gets the compile definitions. It performs a search
for dependencies that is somewhat similar to
qt_get_direct_module_dependencies, although it won't recurse down for
more deps. Each dependency is then again, checked if it's internal or
has a public interface. Later these deps get deduplicated and lastly
a pkgconfig file is filled.

The resulting pkgconfig files of many of the Qt6 packages were
validated via invocations of `pkg-config --validate` and
`pkg-config --simulate` commands and later used to build local
projects plus tests that use the pkg-config provided details at
compilation time.

Although it has some limitations, with qt_internal_add_qml_module if
it specifies non-public deps these won't be listed and with non-Qt
requirements, notably in static builds, not being appended to the
PkgConfig file.

Task-number: QTBUG-86080
Change-Id: I0690bb3ca729eec328500f227261db9b7e7628f6
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
2022-02-28 08:39:54 -03:00

143 lines
6.0 KiB
CMake

function(qt_internal_collect_direct_target_dependencies target targets_out_var)
__qt_internal_walk_libs("${target}" "${targets_out_var}" _rcc_objects
"qt_direct_targets_dict" "direct_targets")
set("${targets_out_var}" "${${targets_out_var}}" PARENT_SCOPE)
endfunction()
macro(qt_internal_set_pkg_config_cpp_flags var options flag)
set(tmpopts "${options}")
list(FILTER tmpopts EXCLUDE REGEX "\\$<BUILD_INTERFACE:[^,>]+>")
list(FILTER tmpopts EXCLUDE REGEX "\\$<TARGET_PROPERTY:[^,>]+>")
list(TRANSFORM tmpopts REPLACE "\\$<INSTALL_INTERFACE:([^,>]+)>" "\\1")
list(TRANSFORM tmpopts REPLACE ">" "$<ANGLE-R>")
list(TRANSFORM tmpopts REPLACE "," "$<COMMA>")
set(${var} "$<$<BOOL:${tmpopts}>:${flag}$<JOIN:$<REMOVE_DUPLICATES:${tmpopts}>, ${flag}>>")
unset(tmpopts)
endmacro()
# Create a Qt6*.pc file intended for pkg-config consumption.
function(qt_internal_generate_pkg_config_file module)
# TODO: PkgConfig is supported under MSVC with pkgconf (github.com/pkgconf/pkgconf)
if((NOT UNIX OR QT_FEATURE_framework) AND NOT MINGW OR CMAKE_VERSION VERSION_LESS "3.20")
return()
endif()
if(NOT BUILD_SHARED_LIBS)
return()
endif()
set(pkgconfig_file "${QT_CMAKE_EXPORT_NAMESPACE}${module}")
set(pkgconfig_name "${QT_CMAKE_EXPORT_NAMESPACE} ${module}")
set(pkgconfig_description "Qt ${module} module")
set(target "${QT_CMAKE_EXPORT_NAMESPACE}::${module}")
set(is_interface_library "$<STREQUAL:$<TARGET_PROPERTY:${target},TYPE>,INTERFACE_LIBRARY>")
# The flags macro expanded this variables so it's better to set them at
# their corresponding PkgConfig string.
set(includedir "\${includedir}")
set(mkspecsdir "\${mkspecsdir}")
get_target_property(is_internal_module ${target} _qt_is_internal_module)
if(is_internal_module)
return()
endif()
get_target_property(loose_link_options ${target} INTERFACE_LINK_OPTIONS)
get_target_property(loose_compile_defs ${target} INTERFACE_COMPILE_DEFINITIONS)
get_target_property(loose_include_dirs ${target} INTERFACE_INCLUDE_DIRECTORIES)
list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_INCLUDEDIR}" "\${includedir}")
list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_MKSPECSDIR}" "\${mkspecsdir}")
qt_internal_set_pkg_config_cpp_flags(link_options "${loose_link_options}" "")
qt_internal_set_pkg_config_cpp_flags(compile_defs "${loose_compile_defs}" -D)
qt_internal_set_pkg_config_cpp_flags(include_dirs "${loose_include_dirs}" -I)
if("${include_dirs}" MATCHES "\\${mkspecsdir}")
set(contains_mkspecs TRUE)
endif()
# TODO: Handle macOS framework builds
qt_internal_collect_direct_target_dependencies(${target} loose_target_requires)
foreach(dep IN LISTS loose_target_requires)
if(dep MATCHES "^Qt::")
string(REGEX REPLACE "Qt" "${QT_CMAKE_EXPORT_NAMESPACE}" dep ${dep})
else()
# TODO: Figure out a way to get non-Qt requirements PkgConfig files.
continue()
endif()
if(NOT TARGET ${dep})
continue()
endif()
get_target_property(is_internal_module ${dep} _qt_is_internal_module)
if(is_internal_module OR dep MATCHES ".*Platform.*Internal")
continue()
endif()
get_target_property(type ${dep} TYPE)
if(type STREQUAL "INTERFACE_LIBRARY")
if(dep MATCHES "(.*)Private")
set(dep "${CMAKE_MATCH_1}")
endif()
get_target_property(type ${dep} TYPE)
endif()
string(REGEX REPLACE "::" "" req ${dep})
if(type STREQUAL "STATIC_LIBRARY")
list(APPEND target_libs -l${req})
else()
list(APPEND target_requires ${req})
endif()
endforeach()
string(APPEND link_options " $<JOIN:$<REMOVE_DUPLICATES:${target_libs}>, >")
qt_path_join(path_suffix "${INSTALL_LIBDIR}" pkgconfig)
qt_path_join(build_dir "${QT_BUILD_DIR}" "${path_suffix}")
qt_path_join(install_dir "${QT_INSTALL_DIR}" "${path_suffix}")
set(step_prefix "preliminary_pc_for_${pkgconfig_file}")
qt_path_join(template_pc "${QT_CMAKE_DIR}" PkgConfigLibrary.pc.in)
qt_path_join(pc_step1_path "${build_dir}" ${step_prefix}_step1.pc)
qt_path_join(pc_step2_path "${build_dir}" ${step_prefix}_$<CONFIG>_step2.pc)
configure_file("${template_pc}" "${pc_step1_path}" @ONLY)
file(GENERATE OUTPUT "${pc_step2_path}" INPUT "${pc_step1_path}")
if(QT_GENERATOR_IS_MULTI_CONFIG)
set(configs ${CMAKE_CONFIGURATION_TYPES})
set(rels ${configs})
list(FILTER rels INCLUDE REGEX "(Release|RelWithDebInfo|MinSizeRel)")
if(rels)
list(GET rels 0 release)
endif()
else()
set(configs ${CMAKE_BUILD_TYPE})
set(release ${configs})
endif()
foreach(config ${configs})
if(config STREQUAL "Debug" AND CMAKE_DEBUG_POSTFIX)
set(postfix ${CMAKE_DEBUG_POSTFIX})
elseif(NOT config STREQUAL release)
string(TOLOWER "_${config}" postfix)
else()
set(postfix "")
endif()
qt_path_join(pc_step2_path "${build_dir}" ${step_prefix}_${config}_step2.pc)
qt_path_join(final_pc_path "${build_dir}" ${pkgconfig_file}${postfix}.pc)
add_custom_command(
OUTPUT "${final_pc_path}"
DEPENDS "${pc_step2_path}"
"${QT_CMAKE_DIR}/QtFinishPkgConfigFile.cmake"
COMMAND ${CMAKE_COMMAND}
"-DIN_FILE=${pc_step2_path}"
"-DOUT_FILE=${final_pc_path}"
"$<$<BOOL:${postfix}>:-DPOSTFIX=${postfix}>"
-P "${QT_CMAKE_DIR}/QtFinishPkgConfigFile.cmake"
VERBATIM
COMMENT "Generating pc file for target ${target}"
)
# This is inspired by https://gitlab.kitware.com/cmake/cmake/-/issues/20842
target_sources(${module} PRIVATE "${final_pc_path}")
qt_install(FILES "${final_pc_path}" DESTINATION "${install_dir}")
endforeach()
endfunction()