Ensure that per-ABI builds are sequential
The current approach doesn't work correctly because external project steps that we run on per-ABI build directories look as 'cmake --build <abi/build/dir> --target <target_name>'. When the project has a single APK this works perfectly fine. But when building more than one apk this leads to the simultanous build in the same 'abi/build/dir' which causes undefined behavior and concurrent access to the build artifacts. This is especially sensible when APK targets have the common dependencies. The solution is split for two usecases: - Ninja-like generators, that support job pools. - Other generator. For Ninja-like generators we now create job pools per-ABI with job number 1, this convinces ninja to run only one 'cmake --build' command in single ABI scope. For other generators the solution is not that good. We create the dependency chain between all APK targets and this leads to the build of the unwanted dependencies. For example if project has apkA and apkB targets, then apkB_x86 will depend on apkA_x86(assuming x86 is not the main ABI). This is the only way we may ensure that 'cmake --build' commands won't be running simultanously. Fixes: QTBUG-122838 Pick-to: 6.7 Change-Id: I6f48fae57047a29129836168c28e14cde4eaa958 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
parent
a92c195dd4
commit
31a91d2e93
@ -576,7 +576,7 @@ function(qt6_android_add_apk_target target)
|
||||
"$<TARGET_FILE:${target}>"
|
||||
"${androiddeployqt_output_path}/${target_file_copy_relative_path}"
|
||||
)
|
||||
if(has_depfile_support AND FALSE) # TODO: It breaks multi-abi builds. See QTBUG-122838
|
||||
if(has_depfile_support)
|
||||
set(deploy_android_deps_dir "${apk_final_dir}/${target}_deploy_android")
|
||||
set(timestamp_file "${deploy_android_deps_dir}/timestamp")
|
||||
set(dep_file "${deploy_android_deps_dir}/${target}.d")
|
||||
@ -1183,6 +1183,91 @@ function(_qt_internal_collect_default_android_abis)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Returns a path to the timestamp file for the specific step of the multi-ABI Android project
|
||||
function(_qt_internal_get_android_abi_step_stampfile out project abi step)
|
||||
get_target_property(build_dir ${project} _qt_android_build_directory)
|
||||
get_property(is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(is_multi)
|
||||
set(${out} "${build_dir}/$<CONFIG>/${project}_${step}_stamp" PARENT_SCOPE)
|
||||
else()
|
||||
set(${out} "${build_dir}/${project}_${step}_stamp" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Creates the multi-ABI Android projects and assigns the JOB_POOL to them if it's possible
|
||||
function(_qt_internal_add_android_abi_project project abi)
|
||||
add_custom_target(${project})
|
||||
|
||||
set(build_dir "${CMAKE_BINARY_DIR}/android_abi_builds/${abi}")
|
||||
set_target_properties(${project} PROPERTIES
|
||||
_qt_android_build_directory "${build_dir}"
|
||||
)
|
||||
|
||||
file(MAKE_DIRECTORY "${build_dir}")
|
||||
if(CMAKE_GENERATOR MATCHES "^Ninja")
|
||||
set_property(GLOBAL APPEND PROPERTY JOB_POOLS _qt_android_${project}_pool=1)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Adds the custom build step to the multi-ABI Android project
|
||||
function(_qt_internal_add_android_abi_step project abi step)
|
||||
cmake_parse_arguments(arg "" "" "COMMAND;DEPENDS" ${ARGV})
|
||||
|
||||
if(NOT arg_COMMAND)
|
||||
message(FATAL_ERROR "COMMAND is not set for ${project} step ${step} Android ABI ${abi}.")
|
||||
endif()
|
||||
|
||||
set(dep_stamps "")
|
||||
foreach(dep ${arg_DEPENDS})
|
||||
_qt_internal_get_android_abi_step_stampfile(stamp ${project} ${abi} ${dep})
|
||||
list(APPEND dep_stamps "${stamp}")
|
||||
endforeach()
|
||||
|
||||
get_target_property(build_dir ${project} _qt_android_build_directory)
|
||||
|
||||
if(CMAKE_GENERATOR MATCHES "^Ninja")
|
||||
set(add_to_pool JOB_POOL _qt_android_${project}_pool)
|
||||
else()
|
||||
set(add_to_pool "")
|
||||
endif()
|
||||
|
||||
_qt_internal_get_android_abi_step_stampfile(stamp ${project} ${abi} ${step})
|
||||
add_custom_command(OUTPUT "${stamp}"
|
||||
COMMAND ${arg_COMMAND}
|
||||
COMMAND "${CMAKE_COMMAND}" -E touch "${stamp}"
|
||||
${add_to_pool}
|
||||
DEPENDS
|
||||
${dep_stamps}
|
||||
WORKING_DIRECTORY
|
||||
"${build_dir}"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target("${project}_${step}" DEPENDS "${stamp}")
|
||||
|
||||
get_target_property(known_steps ${project} _qt_android_abi_steps)
|
||||
if(NOT CMAKE_GENERATOR MATCHES "^Ninja")
|
||||
if(NOT QT_NO_WARN_ANDROID_MULTI_ABI_GENERATOR)
|
||||
get_property(is_warned GLOBAL PROPERTY _qt_internal_warn_android_multi_abi_generator)
|
||||
if(NOT is_warned)
|
||||
set_property(GLOBAL PROPERTY _qt_internal_warn_android_multi_abi_generator TRUE)
|
||||
message(WARNING "Building Multi-ABI Qt projects with the '${CMAKE_GENERATOR}'"
|
||||
" generator has limitations. All targets from non-main ABI will be built"
|
||||
" unconditionally. Please use the 'Ninja' or 'Ninja Multi-config' generators"
|
||||
" with ninja build instead. Set QT_NO_WARN_ANDROID_MULTI_ABI_GENERATOR to"
|
||||
" 'TRUE' to suppress this warning."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if(known_steps)
|
||||
list(GET known_steps 0 first)
|
||||
add_dependencies(${first} ${project}_${step})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(PREPEND known_steps ${project}_${step})
|
||||
set_target_properties(${project} PROPERTIES _qt_android_abi_steps "${known_steps}")
|
||||
endfunction()
|
||||
|
||||
# The function configures external projects for ABIs that target packages need to build with.
|
||||
# Each target adds build step to the external project that is linked to the
|
||||
# qt_internal_android_${abi}-${target}_build target in the primary ABI build tree.
|
||||
@ -1292,7 +1377,6 @@ function(_qt_internal_configure_android_multiabi_target target)
|
||||
set(previous_copy_apk_dependencies_target ${target})
|
||||
# Create external projects for each android ABI except the main one.
|
||||
list(REMOVE_ITEM android_abis "${CMAKE_ANDROID_ARCH_ABI}")
|
||||
include(ExternalProject)
|
||||
foreach(abi IN ITEMS ${android_abis})
|
||||
if(NOT "${abi}" IN_LIST QT_DEFAULT_ANDROID_ABIS)
|
||||
list(APPEND missing_qt_abi_toolchains ${abi})
|
||||
@ -1300,16 +1384,17 @@ function(_qt_internal_configure_android_multiabi_target target)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
set(android_abi_build_dir "${CMAKE_BINARY_DIR}/android_abi_builds/${abi}")
|
||||
get_property(abi_external_projects GLOBAL
|
||||
PROPERTY _qt_internal_abi_external_projects)
|
||||
if(NOT abi_external_projects
|
||||
OR NOT "qt_internal_android_${abi}" IN_LIST abi_external_projects)
|
||||
_qt_internal_add_android_abi_project(qt_internal_android_${abi} ${abi})
|
||||
|
||||
get_target_property(android_abi_build_dir qt_internal_android_${abi}
|
||||
_qt_android_build_directory)
|
||||
_qt_internal_get_android_abi_toolchain_path(qt_abi_toolchain_path ${abi})
|
||||
ExternalProject_Add("qt_internal_android_${abi}"
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}"
|
||||
BINARY_DIR "${android_abi_build_dir}"
|
||||
CONFIGURE_COMMAND
|
||||
_qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi} configure
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
"-G${CMAKE_GENERATOR}"
|
||||
"-DCMAKE_TOOLCHAIN_FILE=${qt_abi_toolchain_path}"
|
||||
@ -1321,44 +1406,36 @@ function(_qt_internal_configure_android_multiabi_target target)
|
||||
"${user_cmake_args}"
|
||||
"-B" "${android_abi_build_dir}"
|
||||
"-S" "${CMAKE_SOURCE_DIR}"
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
BUILD_COMMAND "" # avoid top-level build of external project
|
||||
)
|
||||
set_property(GLOBAL APPEND PROPERTY
|
||||
_qt_internal_abi_external_projects "qt_internal_android_${abi}")
|
||||
endif()
|
||||
ExternalProject_Add_Step("qt_internal_android_${abi}"
|
||||
"${target}_build"
|
||||
DEPENDEES configure
|
||||
# TODO: Remove this when the step will depend on DEPFILE generated by
|
||||
# androiddeployqt for the ${target}.
|
||||
ALWAYS TRUE
|
||||
EXCLUDE_FROM_MAIN TRUE
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"--build" "${android_abi_build_dir}"
|
||||
"--config" "$<CONFIG>"
|
||||
"--target" "${target}"
|
||||
)
|
||||
ExternalProject_Add_StepTargets("qt_internal_android_${abi}"
|
||||
"${target}_build")
|
||||
add_dependencies(${target} "qt_internal_android_${abi}-${target}_build")
|
||||
|
||||
ExternalProject_Add_Step("qt_internal_android_${abi}"
|
||||
"${target}_copy_apk_dependencies"
|
||||
DEPENDEES "${target}_build"
|
||||
# TODO: Remove this when the step will depend on DEPFILE generated by
|
||||
# androiddeployqt for the ${target}.
|
||||
ALWAYS TRUE
|
||||
EXCLUDE_FROM_MAIN TRUE
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"--build" "${android_abi_build_dir}"
|
||||
"--config" "$<CONFIG>"
|
||||
"--target" "qt_internal_${target}_copy_apk_dependencies"
|
||||
get_target_property(android_abi_build_dir qt_internal_android_${abi}
|
||||
_qt_android_build_directory)
|
||||
_qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi} ${target}_build
|
||||
DEPENDS
|
||||
configure
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
--build "${android_abi_build_dir}"
|
||||
--config $<CONFIG>
|
||||
--target ${target}
|
||||
)
|
||||
add_dependencies(${target} "qt_internal_android_${abi}_${target}_build")
|
||||
|
||||
_qt_internal_add_android_abi_step(qt_internal_android_${abi} ${abi}
|
||||
${target}_copy_apk_dependencies
|
||||
DEPENDS
|
||||
${target}_build
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
--build "${android_abi_build_dir}"
|
||||
--config $<CONFIG>
|
||||
--target qt_internal_${target}_copy_apk_dependencies
|
||||
)
|
||||
ExternalProject_Add_StepTargets("qt_internal_android_${abi}"
|
||||
"${target}_copy_apk_dependencies")
|
||||
set(external_project_copy_target
|
||||
"qt_internal_android_${abi}-${target}_copy_apk_dependencies")
|
||||
"qt_internal_android_${abi}_${target}_copy_apk_dependencies")
|
||||
|
||||
# Need to build dependency chain between the
|
||||
# qt_internal_android_${abi}-${target}_copy_apk_dependencies targets for all ABI's, to
|
||||
|
Loading…
x
Reference in New Issue
Block a user