diff options
author | Alexey Edelev <[email protected]> | 2021-12-29 18:00:52 +0100 |
---|---|---|
committer | Alexey Edelev <[email protected]> | 2022-02-10 02:31:05 +0100 |
commit | 2201934efa1b9889d474347e705784bf6925e120 (patch) | |
tree | 450a4b8dbb3777668be075790421de77733337f7 | |
parent | 06e7b5168e269f913f03ecb9d77fc82db4d9dfd6 (diff) |
Use 'copy' but not 'copy_if_different' on Windows platforms
Use custom script to copy big Android artifacts on Windows platforms.
The script uses 'copy' but not 'copy_if_different' when source file
size is bigger than 2GB. 'cmake -E copy_if_different' only compares
first 2GB of files because of cmake issue, so this step only
workaround the problem.
Pick-to: 6.2 6.3
Task-number: QTBUG-99491
Change-Id: Id076734700e334dfc3330da412462c2b53829b33
Reviewed-by: Alexandru Croitor <[email protected]>
Reviewed-by: Qt CI Bot <[email protected]>
-rw-r--r-- | cmake/QtBaseGlobalTargets.cmake | 2 | ||||
-rw-r--r-- | cmake/QtConfig.cmake.in | 1 | ||||
-rw-r--r-- | cmake/QtCopyFileIfDifferent.cmake | 15 | ||||
-rw-r--r-- | cmake/QtPublicCMakeHelpers.cmake | 20 | ||||
-rw-r--r-- | src/corelib/Qt6AndroidMacros.cmake | 20 | ||||
-rw-r--r-- | tests/manual/cmake/test_copy_file_if_different_command/CMakeLists.txt | 73 | ||||
-rw-r--r-- | tests/manual/cmake/test_copy_file_if_different_command/main.cpp | 61 |
7 files changed, 183 insertions, 9 deletions
diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index cae5ec8bf64..e9d93705103 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -218,6 +218,7 @@ qt_copy_or_install(FILES cmake/QtCompilerFlags.cmake cmake/QtCompilerOptimization.cmake cmake/QtConfigDependencies.cmake.in + cmake/QtCopyFileIfDifferent.cmake cmake/QtDeferredDependenciesHelpers.cmake cmake/QtDbusHelpers.cmake cmake/QtDocsHelpers.cmake @@ -299,6 +300,7 @@ qt_copy_or_install(DIRECTORY set(__public_cmake_helpers cmake/QtFeature.cmake cmake/QtFeatureCommon.cmake + cmake/QtPublicCMakeHelpers.cmake cmake/QtPublicCMakeVersionHelpers.cmake cmake/QtPublicFinalizerHelpers.cmake cmake/QtPublicPluginHelpers.cmake diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index dd39ff648e6..822224d2dbd 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -92,6 +92,7 @@ include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWalkLibsHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicFindPackageHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicDependencyHelpers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicToolHelpers.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtPublicCMakeHelpers.cmake") if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE) set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@) diff --git a/cmake/QtCopyFileIfDifferent.cmake b/cmake/QtCopyFileIfDifferent.cmake new file mode 100644 index 00000000000..4c64895b534 --- /dev/null +++ b/cmake/QtCopyFileIfDifferent.cmake @@ -0,0 +1,15 @@ +# copy_if_different works incorrect in Windows if file size if bigger than 2GB. +# See https://gitlab.kitware.com/cmake/cmake/-/issues/23052 and QTBUG-99491 for details. + +cmake_minimum_required(VERSION 3.16) + +set(copy_strategy "copy_if_different") +if(CMAKE_HOST_WIN32) + file(SIZE "${SRC_FILE_PATH}" size) + # If file size is bigger than 2GB copy it unconditionally + if(size GREATER_EQUAL 2147483648) + set(copy_strategy "copy") + endif() +endif() + +execute_process(COMMAND ${CMAKE_COMMAND} -E ${copy_strategy} "${SRC_FILE_PATH}" "${DST_FILE_PATH}") diff --git a/cmake/QtPublicCMakeHelpers.cmake b/cmake/QtPublicCMakeHelpers.cmake new file mode 100644 index 00000000000..86b8edacd49 --- /dev/null +++ b/cmake/QtPublicCMakeHelpers.cmake @@ -0,0 +1,20 @@ +# copy_if_different works incorrect in Windows if file size if bigger than 2GB. +# See https://gitlab.kitware.com/cmake/cmake/-/issues/23052 and QTBUG-99491 for details. +function(_qt_internal_copy_file_if_different_command out_var src_file dst_file) + # The CMake version higher than 3.23 doesn't contain the issue + if(CMAKE_HOST_WIN32 AND CMAKE_VERSION VERSION_LESS 3.23) + set(${out_var} "${CMAKE_COMMAND}" + "-DSRC_FILE_PATH=${src_file}" + "-DDST_FILE_PATH=${dst_file}" + -P "${_qt_6_config_cmake_dir}/QtCopyFileIfDifferent.cmake" + PARENT_SCOPE + ) + else() + set(${out_var} "${CMAKE_COMMAND}" + -E copy_if_different + "${src_file}" + "${dst_file}" + PARENT_SCOPE + ) + endif() +endfunction() diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake index 3313975022c..7b7f56041f1 100644 --- a/src/corelib/Qt6AndroidMacros.cmake +++ b/src/corelib/Qt6AndroidMacros.cmake @@ -381,11 +381,13 @@ function(qt6_android_add_apk_target target) # in case DEPFILEs are not supported. # Also the target is used to copy the library that belongs to ${target} when building multi-abi # apk to the abi-specific directory. + _qt_internal_copy_file_if_different_command(copy_command + "$<TARGET_FILE:${target}>" + "${apk_final_dir}/${target_file_copy_relative_path}" + ) add_custom_target(${target}_prepare_apk_dir ALL DEPENDS ${target} ${extra_deps} - COMMAND ${CMAKE_COMMAND} - -E copy_if_different $<TARGET_FILE:${target}> - "${apk_final_dir}/${target_file_copy_relative_path}" + COMMAND ${copy_command} COMMENT "Copying ${target} binary to apk folder" ) @@ -478,6 +480,10 @@ function(qt6_android_add_apk_target target) file(RELATIVE_PATH androiddeployqt_output_path "${CMAKE_BINARY_DIR}" "${apk_final_dir}") set(androiddeployqt_output_path "${QT_INTERNAL_ANDROID_MULTI_ABI_BINARY_DIR}/${androiddeployqt_output_path}") + _qt_internal_copy_file_if_different_command(copy_command + "$<TARGET_FILE:${target}>" + "${androiddeployqt_output_path}/${target_file_copy_relative_path}" + ) if(has_depfile_support) set(deploy_android_deps_dir "${apk_final_dir}/${target}_deploy_android") set(timestamp_file "${deploy_android_deps_dir}/timestamp") @@ -486,9 +492,7 @@ function(qt6_android_add_apk_target target) DEPENDS ${target} ${extra_deps} COMMAND ${CMAKE_COMMAND} -E make_directory "${deploy_android_deps_dir}" COMMAND ${CMAKE_COMMAND} -E touch "${timestamp_file}" - COMMAND ${CMAKE_COMMAND} - -E copy_if_different $<TARGET_FILE:${target}> - "${androiddeployqt_output_path}/${target_file_copy_relative_path}" + COMMAND ${copy_command} COMMAND ${deployment_tool} --input ${deployment_file} --output ${androiddeployqt_output_path} @@ -504,9 +508,7 @@ function(qt6_android_add_apk_target target) else() add_custom_target(qt_internal_${target}_copy_apk_dependencies DEPENDS ${target} ${extra_deps} - COMMAND ${CMAKE_COMMAND} - -E copy_if_different $<TARGET_FILE:${target}> - "${androiddeployqt_output_path}/${target_file_copy_relative_path}" + COMMAND ${copy_command} COMMAND ${deployment_tool} --input ${deployment_file} --output ${androiddeployqt_output_path} diff --git a/tests/manual/cmake/test_copy_file_if_different_command/CMakeLists.txt b/tests/manual/cmake/test_copy_file_if_different_command/CMakeLists.txt new file mode 100644 index 00000000000..c6486111267 --- /dev/null +++ b/tests/manual/cmake/test_copy_file_if_different_command/CMakeLists.txt @@ -0,0 +1,73 @@ +# How to run the test: +# 1. Create the build directory, e.g. /home/user/build_test_copy_file_if_different +# 2. cd /home/user/build_test_copy_file_if_different +# 3. /path/to/Qt/bin/qt-cmake /path/to/Qt/Sources/qtbase/tests/manual/cmake/test_copy_file_if_different_command +# 4. cmake --build . --parallel +# 5. ctest +cmake_minimum_required(VERSION 3.16) + +project(test_copy_file_if_different_command + LANGUAGES CXX +) + +if(NOT CMAKE_HOST_WIN32) + message("Test only applicable for WIN32 platform. Nothing to do.") + return() +endif() + +if(CMAKE_CROSSCOMPILING) + message("Test should only be run on host system. Crosscompiling is not supported.") + return() +endif() + +find_program(fsutil NAMES fsutil fsutil.exe) +if(NOT fsutil) + message(WARNING "Unable to find 'fsutil' executable. Skipping the test") + return() +endif() + +find_package(Qt6 REQUIRED COMPONENTS Core) + +function(test_copy_file_command output_file test_data_base_name size) + set(testdatasrc1 "${CMAKE_CURRENT_BINARY_DIR}/${test_data_base_name}1.bin") + set(testdatasrc2 "${CMAKE_CURRENT_BINARY_DIR}/${test_data_base_name}2.bin") + set(testdatadst "${CMAKE_CURRENT_BINARY_DIR}/${test_data_base_name}.bin") + + # Remove existing data first + file(REMOVE "${testdatasrc1}" "${testdatasrc2}" "${testdatadst}") + + file(TO_NATIVE_PATH "${testdatasrc1}" native_testdatasrc) + execute_process(COMMAND ${fsutil} file createNew "${native_testdatasrc}" ${size} + RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(FATAL_ERROR "Unable to allocate file ${native_testdatasrc}" + " of size ${size} for test" + ) + endif() + + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${testdatasrc1}" "${testdatasrc2}" + RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(FATAL_ERROR "Unable to copy test data from ${testdatasrc1} to ${testdatasrc2}") + endif() + + foreach(src_file_num RANGE 1 2) + set(src_file "${testdatasrc${src_file_num}}") + file(APPEND "${src_file}" "${src_file_num}") + _qt_internal_copy_file_if_different_command(copy_command "${src_file}" "${testdatadst}") + execute_process(COMMAND ${copy_command} RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(FATAL_ERROR "Unable to execute the copy command ${copy_command}") + endif() + endforeach() + + set(${output_file} "${testdatadst}" PARENT_SCOPE) +endfunction() + +test_copy_file_command(output_file1K testdata1K 1024) +test_copy_file_command(output_file2GB testdata2GB 2147483648) + +add_executable(test_copy_if_different_command main.cpp) + +enable_testing() +add_test(NAME "test_copy_if_different_command" COMMAND test_copy_if_different_command "${output_file1K}" "${output_file2GB}") diff --git a/tests/manual/cmake/test_copy_file_if_different_command/main.cpp b/tests/manual/cmake/test_copy_file_if_different_command/main.cpp new file mode 100644 index 00000000000..6248692efb0 --- /dev/null +++ b/tests/manual/cmake/test_copy_file_if_different_command/main.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <fstream> +#include <iostream> + +int checkFileLastByte(const std::string &filename, std::fstream::off_type offset) +{ + std::ifstream file(filename, std::ios_base::in); + if (!file.is_open()) { + std::cerr << "Unable to open test data file: " << filename << std::endl; + return 1; + } + file.seekg(offset, std::ios_base::beg); + char data = 0; + file.read(&data, sizeof(data)); + if (data != '2') { // We always expect it's the second copy of the file + std::cerr << "Invalid data inside the file: " << filename << std::endl; + return 2; + } + + return 0; +}; + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + std::cerr << "Test requires exact 2 arguments that point to the test data" << std::endl; + return 1; + } + + int result = checkFileLastByte(argv[1], 1024); + if (result != 0) + return result; + return checkFileLastByte(argv[2], 2147483648); +} |