CMake: Fix building debug apps with qmake on Windows
Our CMake build system only generated working .prl files for
the Release configuration in debug_and_release.
This caused a linking failure when building a Widgets example that
links against qtmain, specifically
qtmaind.lib(qtmain_win.cpp.obj) : error LNK2019: unresolved external
symbol __imp_CommandLineToArgvW referenced in function WinMain
The symbol is located in shell32.dll, which was not linked in, because
there was no qtmaind.prl file.
The fix to generate per config prl files is a bit complicated, because
add_custom_command does not support generator expressions in OUTPUT
and DEPENDS.
Instead we pre-generate 2 files per config, one with the preliminary
prl file content and another file that contains the final prl file
path (via generator expression).
Then we iterate over all configurations and create custom commands
with well known paths, and the final prl file is created by the script
called by the command.
Amends 06557312d2
Task-number: QTBUG-85240
Change-Id: I413b705bc69732b0cbe1ee8cd089a1aef19365db
Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
parent
7b9904849f
commit
9d55d6b45b
@ -3313,21 +3313,50 @@ function(qt_generate_prl_file target install_dir)
|
||||
# Generate a preliminary .prl file that contains absolute paths to all libraries
|
||||
if(MINGW)
|
||||
# For MinGW, qmake doesn't have a lib prefix in prl files.
|
||||
set(prefix_for_prl_name "")
|
||||
set(prefix_for_final_prl_name "")
|
||||
else()
|
||||
set(prefix_for_prl_name "$<TARGET_FILE_PREFIX:${target}>")
|
||||
set(prefix_for_final_prl_name "$<TARGET_FILE_PREFIX:${target}>")
|
||||
endif()
|
||||
|
||||
# For frameworks, the prl file should be placed under the Resources subdir.
|
||||
get_target_property(is_framework ${target} FRAMEWORK)
|
||||
if(is_framework)
|
||||
get_target_property(fw_version ${target} FRAMEWORK_VERSION)
|
||||
string(APPEND prefix_for_prl_name "Versions/${fw_version}/Resources/")
|
||||
string(APPEND prefix_for_final_prl_name "Versions/${fw_version}/Resources/")
|
||||
endif()
|
||||
|
||||
set(prl_file_name "${prefix_for_prl_name}$<TARGET_FILE_BASE_NAME:${target}>.prl")
|
||||
# What follows is a complicated setup for generating configuration-specific
|
||||
# prl files. It has to be this way, because add_custom_command doesn't support
|
||||
# generator expressions in OUTPUT or DEPENDS.
|
||||
# To circumvent that, we create well known file names with file(GENERATE)
|
||||
# with configuration specific content, which are then fed to add_custom_command
|
||||
# that uses these genex-less file names. The actual command will extract the info
|
||||
# from the configuration-specific files, and create a properly named final prl file.
|
||||
|
||||
# The file is named according to a pattern, that is then used in the
|
||||
# add_custom_command.
|
||||
set(prl_step1_name_prefix "preliminary_prl_for_${target}_step1_")
|
||||
set(prl_step1_name_suffix ".prl" )
|
||||
qt_path_join(prl_step1_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${prl_step1_name_prefix}$<CONFIG>${prl_step1_name_suffix}")
|
||||
|
||||
# Same, except instead of containing the prl contents, it will contain the final prl file
|
||||
# name computed via a generator expression.
|
||||
set(prl_meta_info_name_prefix "preliminary_prl_meta_info_for_${target}_")
|
||||
set(prl_meta_info_name_suffix ".txt")
|
||||
qt_path_join(prl_meta_info_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${prl_meta_info_name_prefix}$<CONFIG>${prl_meta_info_name_suffix}")
|
||||
|
||||
# The final prl file name that will be embedded in the file above.
|
||||
set(final_prl_file_name "${prefix_for_final_prl_name}$<TARGET_FILE_BASE_NAME:${target}>.prl")
|
||||
qt_path_join(final_prl_file_path "${QT_BUILD_DIR}/${install_dir}" "${final_prl_file_name}")
|
||||
|
||||
# Generate the prl content and its final file name into configuration specific files
|
||||
# whose names we know, and can be used in add_custom_command.
|
||||
file(GENERATE
|
||||
OUTPUT "${prl_file_name}"
|
||||
OUTPUT "${prl_step1_path}"
|
||||
CONTENT
|
||||
"RCC_OBJECTS = ${rcc_objects}
|
||||
QMAKE_PRL_BUILD_DIR = ${CMAKE_CURRENT_BINARY_DIR}
|
||||
@ -3335,38 +3364,57 @@ QMAKE_PRL_TARGET = $<TARGET_FILE_NAME:${target}>
|
||||
QMAKE_PRL_CONFIG = ${prl_config}
|
||||
QMAKE_PRL_VERSION = ${PROJECT_VERSION}
|
||||
QMAKE_PRL_LIBS_FOR_CMAKE = ${prl_libs}
|
||||
"
|
||||
)
|
||||
")
|
||||
file(GENERATE
|
||||
OUTPUT "${prl_meta_info_path}"
|
||||
CONTENT
|
||||
"FINAL_PRL_FILE_PATH = ${final_prl_file_path}")
|
||||
|
||||
# Add a custom command that prepares the .prl file for installation
|
||||
qt_path_join(final_prl_file_path "${QT_BUILD_DIR}/${install_dir}" "${prl_file_name}")
|
||||
qt_path_join(fake_dependency_prl_file_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}" "fake_dep_prl_for_${target}.prl")
|
||||
set(library_suffixes ${CMAKE_SHARED_LIBRARY_SUFFIX} ${CMAKE_STATIC_LIBRARY_SUFFIX})
|
||||
add_custom_command(
|
||||
OUTPUT "${fake_dependency_prl_file_path}"
|
||||
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${prl_file_name}"
|
||||
"${QT_CMAKE_DIR}/QtFinishPrlFile.cmake"
|
||||
COMMAND ${CMAKE_COMMAND} "-DIN_FILE=${prl_file_name}"
|
||||
"-DOUT_FILE=${fake_dependency_prl_file_path}"
|
||||
"-DLIBRARY_SUFFIXES=${library_suffixes}"
|
||||
"-DQT_BUILD_LIBDIR=${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
|
||||
-P "${QT_CMAKE_DIR}/QtFinishPrlFile.cmake"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${fake_dependency_prl_file_path}"
|
||||
"${final_prl_file_path}"
|
||||
VERBATIM
|
||||
COMMENT "Generating prl file for target ${target}"
|
||||
)
|
||||
|
||||
# Tell the target to depend on the fake dependency prl file, to ensure the custom command
|
||||
# is executed. As a side-effect, this will also create the final_prl_file_path that uses
|
||||
# generator expressions. It should not be specified as a BYPRODUCT.
|
||||
# This allows proper per-file dependency tracking, without having to resort on a POST_BUILD
|
||||
# step, which means that relinking would happen as well as transitive rebuilding of any
|
||||
# dependees.
|
||||
# This is inspired by https://gitlab.kitware.com/cmake/cmake/-/issues/20842
|
||||
target_sources(${target} PRIVATE "${fake_dependency_prl_file_path}")
|
||||
if(QT_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(configs ${CMAKE_CONFIGURATION_TYPES})
|
||||
else()
|
||||
set(configs ${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
|
||||
foreach(config ${configs})
|
||||
# Output file for dependency tracking, and which will contain the final content.
|
||||
qt_path_join(prl_step2_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}" "preliminary_prl_for_${target}_step2_${config}.prl")
|
||||
|
||||
# Input dependency names that are constructed for each config manually
|
||||
# (no genexes allowed).
|
||||
qt_path_join(prl_step1_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${prl_step1_name_prefix}${config}${prl_step1_name_suffix}")
|
||||
qt_path_join(prl_meta_info_path
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${prl_meta_info_name_prefix}${config}${prl_meta_info_name_suffix}")
|
||||
add_custom_command(
|
||||
OUTPUT "${prl_step2_path}"
|
||||
DEPENDS "${prl_step1_path}" "${prl_meta_info_path}"
|
||||
"${QT_CMAKE_DIR}/QtFinishPrlFile.cmake"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-DIN_FILE=${prl_step1_path}"
|
||||
"-DIN_META_FILE=${prl_meta_info_path}"
|
||||
"-DOUT_FILE=${prl_step2_path}"
|
||||
"-DLIBRARY_SUFFIXES=${library_suffixes}"
|
||||
"-DQT_BUILD_LIBDIR=${QT_BUILD_DIR}/${INSTALL_LIBDIR}"
|
||||
-P "${QT_CMAKE_DIR}/QtFinishPrlFile.cmake"
|
||||
VERBATIM
|
||||
COMMENT "Generating prl file for target ${target}"
|
||||
)
|
||||
|
||||
# Tell the target to depend on the preliminary prl file, to ensure the custom command
|
||||
# is executed. As a side-effect, this will also create the final prl file that
|
||||
# is named appropriately. It should not be specified as a BYPRODUCT.
|
||||
# This allows proper per-file dependency tracking, without having to resort on a POST_BUILD
|
||||
# step, which means that relinking would happen as well as transitive rebuilding of any
|
||||
# dependees.
|
||||
# This is inspired by https://gitlab.kitware.com/cmake/cmake/-/issues/20842
|
||||
target_sources(${target} PRIVATE "${prl_step2_path}")
|
||||
endforeach()
|
||||
|
||||
# Installation of the .prl file happens globally elsewhere,
|
||||
# because we have no clue here what the actual file name is.
|
||||
|
@ -60,3 +60,18 @@ foreach(line ${lines})
|
||||
endif()
|
||||
endforeach()
|
||||
file(WRITE "${OUT_FILE}" "${content}")
|
||||
|
||||
# Read the prl meta file to find out where should the final prl file be placed,
|
||||
# Copy it there, if the contents hasn't changed.
|
||||
file(STRINGS "${IN_META_FILE}" lines)
|
||||
|
||||
foreach(line ${lines})
|
||||
if(line MATCHES "^FINAL_PRL_FILE_PATH = (.*)")
|
||||
set(final_prl_file_path "${CMAKE_MATCH_1}")
|
||||
configure_file(
|
||||
"${OUT_FILE}"
|
||||
"${final_prl_file_path}"
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
Loading…
Reference in New Issue
Block a user