CMake: Generate qmake .prl files
This commit also adds a qt_finalize_module function that is called for every Qt module after all link dependencies have been added. Change-Id: I489d188d05e368208a8a62828bb12fb395df54bc Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
parent
6f5c91e985
commit
bfcf36d459
@ -287,6 +287,7 @@ qt_copy_or_install(FILES
|
||||
cmake/QtCompilerFlags.cmake
|
||||
cmake/QtCompilerOptimization.cmake
|
||||
cmake/QtFeature.cmake
|
||||
cmake/QtFinishPrlFile.cmake
|
||||
cmake/QtFindWrapHelper.cmake
|
||||
cmake/QtFindWrapConfigExtra.cmake.in
|
||||
cmake/QtFileConfigure.txt.in
|
||||
|
@ -661,7 +661,6 @@ QT_VERSION = ${PROJECT_VERSION}
|
||||
QT_MAJOR_VERSION = ${PROJECT_VERSION_MAJOR}
|
||||
QT_MINOR_VERSION = ${PROJECT_VERSION_MINOR}
|
||||
QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH}
|
||||
CONFIG -= link_prl # we do not create prl files right now
|
||||
CONFIG += ${config_entries}
|
||||
"
|
||||
)
|
||||
@ -1558,6 +1557,89 @@ function(qt_handle_multi_config_output_dirs target)
|
||||
qt_clone_property_for_configs(${target} ARCHIVE_OUTPUT_DIRECTORY "${possible_configs}")
|
||||
endfunction()
|
||||
|
||||
# Add a finalizer function for the current CMake list file.
|
||||
#
|
||||
# You may add up to nine arguments that are passed to the finalizer.
|
||||
# A finalizer that is registered with qt_add_list_file_finalizer(foo bar baz)
|
||||
# will be called with nine arguments: foo(bar baz IGNORE IGNORE IGNORE...),
|
||||
# because CMake's handling of empty list elements is a cruel joke.
|
||||
#
|
||||
# For CMake < 3.18 the function qt_watch_current_list_dir must know about the finalizer.
|
||||
function(qt_add_list_file_finalizer func)
|
||||
set_property(GLOBAL APPEND
|
||||
PROPERTY QT_LIST_FILE_FINALIZER_FILES "${CMAKE_CURRENT_LIST_FILE}")
|
||||
set_property(GLOBAL APPEND
|
||||
PROPERTY QT_LIST_FILE_FINALIZER_FUNCS ${func})
|
||||
foreach(i RANGE 1 9)
|
||||
set(arg "${ARGV${i}}")
|
||||
if(i GREATER_EQUAL ARGC)
|
||||
set(arg "IGNORE")
|
||||
endif()
|
||||
set_property(GLOBAL APPEND
|
||||
PROPERTY QT_LIST_FILE_FINALIZER_ARGV${i} "${arg}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Watcher function for the variable CMAKE_CURRENT_LIST_DIR.
|
||||
# This is the driver of the finalizer facility.
|
||||
function(qt_watch_current_list_dir variable access value current_list_file stack)
|
||||
if(NOT access STREQUAL "MODIFIED_ACCESS")
|
||||
# We are only interested in modifications of CMAKE_CURRENT_LIST_DIR.
|
||||
return()
|
||||
endif()
|
||||
list(GET stack -1 stack_top)
|
||||
if(stack_top STREQUAL current_list_file)
|
||||
# If the top of the stack equals the current list file then
|
||||
# we're entering a file. We're not interested in this case.
|
||||
return()
|
||||
endif()
|
||||
get_property(files GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_FILES)
|
||||
if(NOT files)
|
||||
return()
|
||||
endif()
|
||||
get_property(funcs GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_FUNCS)
|
||||
foreach(i RANGE 1 9)
|
||||
get_property(args${i} GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_ARGV${i})
|
||||
endforeach()
|
||||
list(LENGTH files n)
|
||||
set(i 0)
|
||||
while(i LESS n)
|
||||
list(GET files ${i} file)
|
||||
if(file STREQUAL stack_top)
|
||||
list(GET funcs ${i} func)
|
||||
foreach(k RANGE 1 9)
|
||||
list(GET args${k} ${i} a${k})
|
||||
endforeach()
|
||||
# We've found a file we're looking for. Call the finalizer.
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.18.0")
|
||||
# Make finalizer known functions here:
|
||||
if(func STREQUAL "qt_generate_prl_file")
|
||||
qt_generate_prl_file(${a1} ${a2} ${a3} ${a4} ${a5} ${a6} ${a7} ${a8} ${a9})
|
||||
else()
|
||||
message(FATAL_ERROR "qt_watch_current_list_dir doesn't know about ${func}. Consider adding it.")
|
||||
endif()
|
||||
else()
|
||||
cmake_command(INVOKE ${func} ${a1} ${a2} ${a3} ${a4} ${a5} ${a6} ${a7} ${a8} ${a9})
|
||||
endif()
|
||||
list(REMOVE_AT files ${i})
|
||||
list(REMOVE_AT funcs ${i})
|
||||
foreach(k RANGE 1 9)
|
||||
list(REMOVE_AT args${k} ${i})
|
||||
endforeach()
|
||||
math(EXPR n "${n} - 1")
|
||||
else()
|
||||
math(EXPR i "${i} + 1")
|
||||
endif()
|
||||
endwhile()
|
||||
set_property(GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_FILES ${files})
|
||||
set_property(GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_FUNCS ${funcs})
|
||||
foreach(i RANGE 1 9)
|
||||
set_property(GLOBAL PROPERTY QT_LIST_FILE_FINALIZER_ARGV${i} "${args${i}}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
variable_watch(CMAKE_CURRENT_LIST_DIR qt_watch_current_list_dir)
|
||||
|
||||
# This is the main entry function for creating a Qt module, that typically
|
||||
# consists of a library, public header files, private header files and configurable
|
||||
# features.
|
||||
@ -2059,7 +2141,159 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})")
|
||||
endif()
|
||||
|
||||
qt_describe_module(${target})
|
||||
qt_add_list_file_finalizer(qt_generate_prl_file ${target})
|
||||
endfunction()
|
||||
|
||||
# Add libraries to variable ${out_libs_var} in a way that duplicates
|
||||
# are added at the end. This ensures the library order needed for the
|
||||
# linker.
|
||||
function(qt_merge_libs out_libs_var)
|
||||
foreach(dep ${ARGN})
|
||||
list(REMOVE_ITEM ${out_libs_var} ${dep})
|
||||
list(APPEND ${out_libs_var} ${dep})
|
||||
endforeach()
|
||||
set(${out_libs_var} ${${out_libs_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Collects the library dependencies of a target.
|
||||
# This takes into account transitive usage requirements.
|
||||
function(qt_collect_libs target out_var)
|
||||
set(collected ${ARGN})
|
||||
if(target IN_LIST collected)
|
||||
return()
|
||||
endif()
|
||||
list(APPEND collected ${target})
|
||||
if(NOT TARGET qt_collect_libs_dict)
|
||||
add_library(qt_collect_libs_dict INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
get_target_property(libs qt_collect_libs_dict INTERFACE_${target})
|
||||
if(NOT libs)
|
||||
unset(libs)
|
||||
get_target_property(target_libs ${target} INTERFACE_LINK_LIBRARIES)
|
||||
if(NOT target_libs)
|
||||
unset(target_libs)
|
||||
endif()
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
if(target_type STREQUAL "STATIC_LIBRARY")
|
||||
get_target_property(link_libs ${target} LINK_LIBRARIES)
|
||||
if(link_libs)
|
||||
list(APPEND target_libs ${link_libs})
|
||||
endif()
|
||||
endif()
|
||||
foreach(lib ${target_libs})
|
||||
# Cannot use $<TARGET_POLICY:...> in add_custom_command.
|
||||
# Check the policy now, and replace the generator expression with the value.
|
||||
while(lib MATCHES "\\$<TARGET_POLICY:([^>]+)>")
|
||||
cmake_policy(GET ${CMAKE_MATCH_1} value)
|
||||
if(value STREQUAL "NEW")
|
||||
set(value "TRUE")
|
||||
else()
|
||||
set(value "FALSE")
|
||||
endif()
|
||||
string(REPLACE "${CMAKE_MATCH_0}" "${value}" lib "${lib}")
|
||||
endwhile()
|
||||
|
||||
# Fix up $<TARGET_PROPERTY:FOO> expressions that refer to the "current" target.
|
||||
# Those cannot be used with add_custom_command.
|
||||
while(lib MATCHES "\\$<TARGET_PROPERTY:([^,>]+)>")
|
||||
string(REPLACE "${CMAKE_MATCH_0}" "$<TARGET_PROPERTY:${target},${CMAKE_MATCH_1}>"
|
||||
lib "${lib}")
|
||||
endwhile()
|
||||
|
||||
if(lib MATCHES "^\\$<TARGET_OBJECTS:")
|
||||
# Skip object files.
|
||||
continue()
|
||||
elseif(lib MATCHES "^\\$<LINK_ONLY:(.*)>$")
|
||||
set(lib_target ${CMAKE_MATCH_1})
|
||||
else()
|
||||
set(lib_target ${lib})
|
||||
endif()
|
||||
|
||||
if(TARGET ${lib_target})
|
||||
if ("${lib_target}" MATCHES "^Qt::(.*)")
|
||||
# If both, Qt::Foo and Foo targets exist, prefer the target name without
|
||||
# namespace. Which one is preferred doesn't really matter. This code exists to
|
||||
# avoid ending up with both, Qt::Foo and Foo in our dependencies.
|
||||
set(namespaceless_lib_target "${CMAKE_MATCH_1}")
|
||||
if(TARGET namespaceless_lib_target)
|
||||
set(lib_target ${namespaceless_lib_target})
|
||||
endif()
|
||||
endif()
|
||||
get_target_property(lib_target_type ${lib_target} TYPE)
|
||||
if(lib_target_type STREQUAL "INTERFACE_LIBRARY")
|
||||
qt_collect_libs(${lib_target} lib_libs ${collected})
|
||||
if(lib_libs)
|
||||
qt_merge_libs(libs ${lib_libs})
|
||||
set(is_module 0)
|
||||
endif()
|
||||
else()
|
||||
qt_merge_libs(libs "$<TARGET_FILE:${lib_target}>")
|
||||
qt_collect_libs(${lib_target} lib_libs ${collected})
|
||||
if(lib_libs)
|
||||
qt_merge_libs(libs ${lib_libs})
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
qt_merge_libs(libs "${lib_target}")
|
||||
endif()
|
||||
endforeach()
|
||||
set_target_properties(qt_collect_libs_dict PROPERTIES INTERFACE_${target} "${libs}")
|
||||
endif()
|
||||
set(${out_var} ${libs} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Generate a qmake .prl file for the given target
|
||||
function(qt_generate_prl_file target)
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
if(target_type STREQUAL "INTERFACE_LIBRARY")
|
||||
return()
|
||||
endif()
|
||||
|
||||
unset(prl_libs)
|
||||
qt_collect_libs(${target} prl_libs)
|
||||
|
||||
unset(prl_config)
|
||||
if(target_type STREQUAL "STATIC_LIBRARY")
|
||||
list(APPEND prl_config static)
|
||||
elseif(target_type STREQUAL "SHARED_LIBRARY")
|
||||
list(APPEND prl_config shared)
|
||||
endif()
|
||||
if (NOT target_type STREQUAL "INTERFACE_LIBRARY")
|
||||
get_target_property(is_fw ${target} FRAMEWORK)
|
||||
if(is_fw)
|
||||
list(APPEND prl_config lib_bundle)
|
||||
endif()
|
||||
endif()
|
||||
list(JOIN prl_config " " prl_config)
|
||||
|
||||
# Generate a preliminary .prl file that contains absolute paths to all libraries
|
||||
set(prl_file_name "$<TARGET_FILE_PREFIX:${target}>$<TARGET_FILE_BASE_NAME:${target}>.prl")
|
||||
file(GENERATE
|
||||
OUTPUT "${prl_file_name}"
|
||||
CONTENT
|
||||
"QMAKE_PRL_BUILD_DIR = ${CMAKE_CURRENT_BINARY_DIR}
|
||||
QMAKE_PRL_TARGET = $<TARGET_FILE_NAME:${target}>
|
||||
QMAKE_PRL_CONFIG = ${prl_config}
|
||||
QMAKE_PRL_VERSION = ${PROJECT_VERSION}
|
||||
QMAKE_PRL_LIBS_FOR_CMAKE = ${prl_libs}
|
||||
"
|
||||
)
|
||||
|
||||
# Add a custom command that prepares the .prl file for installation
|
||||
qt_path_join(qt_build_libdir ${QT_BUILD_DIR} ${INSTALL_LIBDIR})
|
||||
qt_path_join(prl_file_path "${qt_build_libdir}" "${prl_file_name}")
|
||||
set(library_suffixes ${CMAKE_SHARED_LIBRARY_SUFFIX} ${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
add_custom_command(
|
||||
TARGET ${target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -DIN_FILE=${prl_file_name} -DOUT_FILE=${prl_file_path}
|
||||
"-DLIBRARY_SUFFIXES=${library_suffixes}"
|
||||
-DQT_BUILD_LIBDIR=${qt_build_libdir}
|
||||
-P "${QT_CMAKE_DIR}/QtFinishPrlFile.cmake"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Installation of the .prl file happens globally elsewhere,
|
||||
# because we have no clue here what the actual file name is.
|
||||
endfunction()
|
||||
|
||||
function(qt_export_tools module_name)
|
||||
|
57
cmake/QtFinishPrlFile.cmake
Normal file
57
cmake/QtFinishPrlFile.cmake
Normal file
@ -0,0 +1,57 @@
|
||||
# Finish a preliminary .prl file.
|
||||
#
|
||||
# - Replaces occurrences of the build libdir with $$[QT_INSTALL_LIBDIR/get].
|
||||
# - Strips version number suffixes from absolute paths, because qmake's lflag
|
||||
# merging does not handle them correctly.
|
||||
#
|
||||
# This file is to be used in CMake script mode with the following variables set:
|
||||
# IN_FILE: path to the preliminary .prl file
|
||||
# OUT_FILE: path to the final .prl file that's going to be installed
|
||||
# QT_BUILD_LIBDIR: path to Qt's libdir when building (those paths get replaced)
|
||||
# LIBRARY_SUFFIXES: list of known library extensions, e.g. .so;.a on Linux
|
||||
|
||||
function(strip_library_version_suffix out_var file_path)
|
||||
get_filename_component(dir "${file_path}" DIRECTORY)
|
||||
get_filename_component(basename "${file_path}" NAME_WE)
|
||||
get_filename_component(ext "${file_path}" EXT)
|
||||
foreach(libsuffix ${LIBRARY_SUFFIXES})
|
||||
if(ext MATCHES "^${libsuffix}(\\.[0-9]+)+")
|
||||
set(ext ${libsuffix})
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
set(${out_var} "${dir}/${basename}${ext}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
file(STRINGS "${IN_FILE}" lines)
|
||||
set(content "")
|
||||
foreach(line ${lines})
|
||||
if(line MATCHES "^QMAKE_PRL_LIBS_FOR_CMAKE = (.*)")
|
||||
unset(adjusted_libs)
|
||||
foreach(lib ${CMAKE_MATCH_1})
|
||||
if("${lib}" STREQUAL "")
|
||||
continue()
|
||||
endif()
|
||||
if(IS_ABSOLUTE "${lib}")
|
||||
strip_library_version_suffix(lib "${lib}")
|
||||
file(RELATIVE_PATH relative_lib "${QT_BUILD_LIBDIR}" "${lib}")
|
||||
if(IS_ABSOLUTE "${relative_lib}" OR (relative_lib MATCHES "^\\.\\."))
|
||||
list(APPEND adjusted_libs "${lib}")
|
||||
else()
|
||||
list(APPEND adjusted_libs "$$[QT_INSTALL_LIBS/get]/${relative_lib}")
|
||||
endif()
|
||||
else()
|
||||
if(NOT lib MATCHES "^-l")
|
||||
string(PREPEND lib "-l")
|
||||
endif()
|
||||
list(APPEND adjusted_libs "${lib}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(JOIN adjusted_libs " " adjusted_libs_for_qmake)
|
||||
string(APPEND content "QMAKE_PRL_LIBS = ${adjusted_libs_for_qmake}\n")
|
||||
string(APPEND content "QMAKE_PRL_LIBS_FOR_CMAKE = ${adjusted_libs}\n")
|
||||
else()
|
||||
string(APPEND content "${line}\n")
|
||||
endif()
|
||||
endforeach()
|
||||
file(WRITE "${OUT_FILE}" "${content}")
|
@ -470,3 +470,9 @@ qt_create_tools_config_files()
|
||||
if (ANDROID)
|
||||
qt_modules_process_android_dependencies()
|
||||
endif()
|
||||
|
||||
# Install prl files
|
||||
qt_install(DIRECTORY "${PROJECT_BINARY_DIR}/${INSTALL_LIBDIR}/"
|
||||
DESTINATION ${INSTALL_LIBDIR}
|
||||
FILES_MATCHING PATTERN "*.prl"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user