summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Buhr <andreas.buhr@qt.io>2021-06-18 15:09:58 +0200
committerAndreas Buhr <andreas.buhr@qt.io>2021-07-08 17:30:20 +0200
commit857be50b2e193b92de37c3e2bb5124d24d21a253 (patch)
treeb446bb5df0b8362dd807cb1bab287b4edfa5a156
parent85e25d93b3b99fbeae8541586252df2cb099081d (diff)
Let androiddeployqt write a dependency file
Let androiddeployqt write a dependency file so that the build system knows when to re-run it. Fixes: QTBUG-94567 Change-Id: I5985d707f257b22789013a74f0a6f7c4de6e5e88 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake61
-rw-r--r--src/tools/androiddeployqt/CMakeLists.txt2
-rw-r--r--src/tools/androiddeployqt/main.cpp52
-rw-r--r--src/tools/moc/CMakeLists.txt1
-rw-r--r--src/tools/moc/main.cpp45
-rw-r--r--src/tools/shared/depfile_shared.h80
6 files changed, 176 insertions, 65 deletions
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index b7d2490522..e37ce287bd 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -291,10 +291,13 @@ function(qt6_android_add_apk_target target)
set(apk_final_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>/android-build")
set(apk_intermediate_dir "${CMAKE_CURRENT_BINARY_DIR}/android-build")
set(apk_file_name "${target}.apk")
+ set(dep_file_name "${target}.d")
set(apk_final_file_path "${apk_final_dir}/${apk_file_name}")
set(apk_intermediate_file_path "${apk_intermediate_dir}/${apk_file_name}")
+ set(dep_intermediate_file_path "${apk_intermediate_dir}/${dep_file_name}")
- # This target is used by Qt Creator's Android support.
+ # This target is used by Qt Creator's Android support and by the ${target}_make_apk target
+ # in case DEPFILEs are not supported.
add_custom_target(${target}_prepare_apk_dir ALL
DEPENDS ${target}
COMMAND ${CMAKE_COMMAND}
@@ -303,26 +306,42 @@ function(qt6_android_add_apk_target target)
COMMENT "Copying ${target} binary to apk folder"
)
- # Add custom command that creates the apk in an intermediate location.
- # We need the intermediate location, because we cannot have target-dependent generator
- # expressions in OUTPUT.
- add_custom_command(OUTPUT "${apk_intermediate_file_path}"
- COMMAND ${CMAKE_COMMAND}
- -E copy "$<TARGET_FILE:${target}>"
- "${apk_intermediate_dir}/libs/${CMAKE_ANDROID_ARCH_ABI}/$<TARGET_FILE_NAME:${target}>"
- COMMAND "${deployment_tool}"
- --input "${deployment_file}"
- --output "${apk_intermediate_dir}"
- --apk "${apk_intermediate_file_path}"
- COMMENT "Creating APK for ${target}"
- DEPENDS "${target}" "${deployment_file}")
-
- # Create a ${target}_make_apk target to copy the apk from the intermediate to its final
- # location. If the final and intermediate locations are identical, this is a no-op.
- add_custom_target(${target}_make_apk
- COMMAND "${CMAKE_COMMAND}"
- -E copy_if_different "${apk_intermediate_file_path}" "${apk_final_file_path}"
- DEPENDS "${apk_intermediate_file_path}")
+ # The DEPFILE argument to add_custom_command is only available with Ninja or CMake>=3.20 and make.
+ if (CMAKE_GENERATOR MATCHES "Ninja" OR
+ (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles"))
+ # Add custom command that creates the apk in an intermediate location.
+ # We need the intermediate location, because we cannot have target-dependent generator
+ # expressions in OUTPUT.
+ add_custom_command(OUTPUT "${apk_intermediate_file_path}"
+ COMMAND ${CMAKE_COMMAND}
+ -E copy "$<TARGET_FILE:${target}>"
+ "${apk_intermediate_dir}/libs/${CMAKE_ANDROID_ARCH_ABI}/$<TARGET_FILE_NAME:${target}>"
+ COMMAND "${deployment_tool}"
+ --input "${deployment_file}"
+ --output "${apk_intermediate_dir}"
+ --apk "${apk_intermediate_file_path}"
+ --depfile "${dep_intermediate_file_path}"
+ --builddir "${CMAKE_BINARY_DIR}"
+ COMMENT "Creating APK for ${target}"
+ DEPENDS "${target}" "${deployment_file}"
+ DEPFILE "${dep_intermediate_file_path}")
+
+ # Create a ${target}_make_apk target to copy the apk from the intermediate to its final
+ # location. If the final and intermediate locations are identical, this is a no-op.
+ add_custom_target(${target}_make_apk
+ COMMAND "${CMAKE_COMMAND}"
+ -E copy_if_different "${apk_intermediate_file_path}" "${apk_final_file_path}"
+ DEPENDS "${apk_intermediate_file_path}")
+ else()
+ add_custom_target(${target}_make_apk
+ DEPENDS ${target}_prepare_apk_dir
+ COMMAND ${deployment_tool}
+ --input ${deployment_file}
+ --output ${apk_final_dir}
+ --apk ${apk_final_file_path}
+ COMMENT "Creating APK for ${target}"
+ )
+ endif()
endfunction()
function(_qt_internal_create_global_apk_target)
diff --git a/src/tools/androiddeployqt/CMakeLists.txt b/src/tools/androiddeployqt/CMakeLists.txt
index 4eec7b8164..941e160318 100644
--- a/src/tools/androiddeployqt/CMakeLists.txt
+++ b/src/tools/androiddeployqt/CMakeLists.txt
@@ -13,6 +13,8 @@ qt_internal_add_app(androiddeployqt
QT_NO_FOREACH
PUBLIC_LIBRARIES
Qt::Core # special case
+ INCLUDE_DIRECTORIES
+ ../shared
)
set_target_properties(androiddeployqt PROPERTIES
WIN32_EXECUTABLE FALSE
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp
index 7993a0cfb8..fcfb6d36f5 100644
--- a/src/tools/androiddeployqt/main.cpp
+++ b/src/tools/androiddeployqt/main.cpp
@@ -42,6 +42,8 @@
#include <QDirIterator>
#include <QRegularExpression>
+#include <depfile_shared.h>
+
#include <algorithm>
#if defined(Q_OS_WIN32)
@@ -73,6 +75,8 @@ public:
static const bool mustReadOutputAnyway = true; // pclose seems to return the wrong error code unless we read the output
+static QStringList dependenciesForDepfile;
+
void deleteRecursively(const QString &dirName)
{
QDir dir(dirName);
@@ -169,6 +173,8 @@ struct Options
QString applicationArguments;
QString rootPath;
QString rccBinaryPath;
+ QString depFilePath;
+ QString buildDirectory;
QStringList qmlImportPaths;
QStringList qrcFiles;
@@ -474,6 +480,16 @@ Options parseOptions()
options.helpRequested = true;
else
options.apkPath = arguments.at(++i);
+ } else if (argument.compare(QLatin1String("--depfile"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ options.helpRequested = true;
+ else
+ options.depFilePath = arguments.at(++i);
+ } else if (argument.compare(QLatin1String("--builddir"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ options.helpRequested = true;
+ else
+ options.buildDirectory = arguments.at(++i);
} else if (argument.compare(QLatin1String("--sign"), Qt::CaseInsensitive) == 0) {
if (i + 2 >= arguments.size()) {
const QString keyStore = qEnvironmentVariable("QT_ANDROID_KEYSTORE_PATH");
@@ -560,6 +576,9 @@ Options parseOptions()
}
}
+ if (options.buildDirectory.isEmpty() && !options.depFilePath.isEmpty())
+ options.helpRequested = true;
+
if (options.inputFileName.isEmpty())
options.inputFileName = QLatin1String("android-%1-deployment-settings.json").arg(QDir::current().dirName());
@@ -668,6 +687,11 @@ void printHelp()
" qmlimportscanner binary is located using the Qt directory\n"
" specified in the input file.\n"
"\n"
+ " --depfile <path/to/depfile>: Output a dependency file.\n"
+ "\n"
+ " --builddir <path/to/build/directory>: build directory. Necessary when\n"
+ " generating a depfile because ninja requires relative paths.\n"
+ "\n"
" --help: Displays this information.\n",
qPrintable(QCoreApplication::arguments().at(0))
);
@@ -703,6 +727,7 @@ bool copyFileIfNewer(const QString &sourceFileName,
const Options &options,
bool forceOverwrite = false)
{
+ dependenciesForDepfile << sourceFileName;
if (QFile::exists(destinationFileName)) {
QFileInfo destinationFileInfo(destinationFileName);
QFileInfo sourceFileInfo(sourceFileName);
@@ -840,6 +865,7 @@ bool readInputFile(Options *options)
fprintf(stderr, "Cannot read from input file: %s\n", qPrintable(options->inputFileName));
return false;
}
+ dependenciesForDepfile << options->inputFileName;
QJsonDocument jsonDocument = QJsonDocument::fromJson(file.readAll());
if (jsonDocument.isNull()) {
@@ -2950,6 +2976,29 @@ enum ErrorCode
CannotCreateRcc = 21
};
+bool writeDependencyFile(const Options &options)
+{
+ if (options.verbose)
+ fprintf(stdout, "Writing dependency file.\n");
+
+ QFile depFile(options.depFilePath);
+
+ QString relativeApkPath = QDir(options.buildDirectory).relativeFilePath(options.apkPath);
+
+ if (depFile.open(QIODevice::WriteOnly)) {
+ depFile.write(escapeAndEncodeDependencyPath(relativeApkPath));
+ depFile.write(u8": ");
+
+ for (const auto &file : dependenciesForDepfile) {
+ depFile.write(u8" \\\n ");
+ depFile.write(escapeAndEncodeDependencyPath(file));
+ }
+
+ depFile.write(u8"\n");
+ }
+ return true;
+}
+
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
@@ -3090,6 +3139,9 @@ int main(int argc, char *argv[])
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Installed APK\n", options.timer.elapsed());
+ if (!options.depFilePath.isEmpty())
+ writeDependencyFile(options);
+
fprintf(stdout, "Android package built successfully in %.3f ms.\n", options.timer.elapsed() / 1000.);
if (options.installApk)
diff --git a/src/tools/moc/CMakeLists.txt b/src/tools/moc/CMakeLists.txt
index 09c4927a53..88dce045f8 100644
--- a/src/tools/moc/CMakeLists.txt
+++ b/src/tools/moc/CMakeLists.txt
@@ -32,6 +32,7 @@ qt_internal_add_tool(${target_name}
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
../../3rdparty/tinycbor/src
+ ../shared
)
#### Keys ignored in scope 1:.:.:moc.pro:<TRUE>:
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp
index a2790fa1f7..3e98fbf2b8 100644
--- a/src/tools/moc/main.cpp
+++ b/src/tools/moc/main.cpp
@@ -27,6 +27,7 @@
**
****************************************************************************/
+#include <depfile_shared.h>
#include "preprocessor.h"
#include "moc.h"
#include "outputrevision.h"
@@ -177,50 +178,6 @@ static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments,
return allArguments;
}
-// Escape characters in given path. Dependency paths are Make-style, not NMake/Jom style.
-// The paths can also be consumed by Ninja.
-// "$" replaced by "$$"
-// "#" replaced by "\#"
-// " " replaced by "\ "
-// "\#" replaced by "\\#"
-// "\ " replaced by "\\\ "
-//
-// The escape rules are according to what clang / llvm escapes when generating a Make-style
-// dependency file.
-// Is a template function, because input param can be either a QString or a QByteArray.
-template <typename T> struct CharType;
-template <> struct CharType<QString> { using type = QLatin1Char; };
-template <> struct CharType<QByteArray> { using type = char; };
-template <typename StringType>
-StringType escapeDependencyPath(const StringType &path)
-{
- using CT = typename CharType<StringType>::type;
- StringType escapedPath;
- int size = path.size();
- escapedPath.reserve(size);
- for (int i = 0; i < size; ++i) {
- if (path[i] == CT('$')) {
- escapedPath.append(CT('$'));
- } else if (path[i] == CT('#')) {
- escapedPath.append(CT('\\'));
- } else if (path[i] == CT(' ')) {
- escapedPath.append(CT('\\'));
- int backwards_it = i - 1;
- while (backwards_it > 0 && path[backwards_it] == CT('\\')) {
- escapedPath.append(CT('\\'));
- --backwards_it;
- }
- }
- escapedPath.append(path[i]);
- }
- return escapedPath;
-}
-
-QByteArray escapeAndEncodeDependencyPath(const QString &path)
-{
- return QFile::encodeName(escapeDependencyPath(path));
-}
-
int runMoc(int argc, char **argv)
{
QCoreApplication app(argc, argv);
diff --git a/src/tools/shared/depfile_shared.h b/src/tools/shared/depfile_shared.h
new file mode 100644
index 0000000000..9fb9bbb863
--- /dev/null
+++ b/src/tools/shared/depfile_shared.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications 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$
+**
+****************************************************************************/
+
+#ifndef QTBASE_DEPFILE_SHARED_H
+#define QTBASE_DEPFILE_SHARED_H
+
+#include <QByteArray>
+#include <QString>
+#include <QFile>
+
+// Escape characters in given path. Dependency paths are Make-style, not NMake/Jom style.
+// The paths can also be consumed by Ninja.
+// "$" replaced by "$$"
+// "#" replaced by "\#"
+// " " replaced by "\ "
+// "\#" replaced by "\\#"
+// "\ " replaced by "\\\ "
+//
+// The escape rules are according to what clang / llvm escapes when generating a Make-style
+// dependency file.
+// Is a template function, because input param can be either a QString or a QByteArray.
+template <typename T> struct CharType;
+template <> struct CharType<QString> { using type = QLatin1Char; };
+template <> struct CharType<QByteArray> { using type = char; };
+template <typename StringType>
+StringType escapeDependencyPath(const StringType &path)
+{
+ using CT = typename CharType<StringType>::type;
+ StringType escapedPath;
+ int size = path.size();
+ escapedPath.reserve(size);
+ for (int i = 0; i < size; ++i) {
+ if (path[i] == CT('$')) {
+ escapedPath.append(CT('$'));
+ } else if (path[i] == CT('#')) {
+ escapedPath.append(CT('\\'));
+ } else if (path[i] == CT(' ')) {
+ escapedPath.append(CT('\\'));
+ int backwards_it = i - 1;
+ while (backwards_it > 0 && path[backwards_it] == CT('\\')) {
+ escapedPath.append(CT('\\'));
+ --backwards_it;
+ }
+ }
+ escapedPath.append(path[i]);
+ }
+ return escapedPath;
+}
+
+static inline QByteArray escapeAndEncodeDependencyPath(const QString &path)
+{
+ return QFile::encodeName(escapeDependencyPath(path));
+}
+
+#endif // QTBASE_DEPFILE_SHARED_H