From 73331eebf885ba8918447d26ba37bef2208bdb5e Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Tue, 23 Aug 2016 15:16:42 -0700 Subject: Enable precompiled headers on iOS, tvOS, watchOS The actual blocker for precompiled headers is not the iOS/tvOS/watchOS platforms, but the way qmake handled multiple-architecture builds on Apple platforms. This patch allows multi-arch builds to be performed while using precompiled headers. Since df91ef3d6c55692a0236f67b6c6b134a3bf84098 (April 2009), Clang has had support for PCH files in the driver, which allows to use the -include flag to automatically translate to -include-pch. We can then take advantage of the fact that the -include option is allowed to not be separate from its argument, which lets us take advantage of -Xarch to specify a per-architecture precompiled header file. This is done through some magic in the qmake Makefile generator which "multiplexes" the PCH creation rule across multiple architectures and replaces a series of tokens with the proper precompiled header paths and architecture flags at usage point. Change-Id: I76c8dc9cda7e218869c2919f023d9b04f311c6fd Reviewed-by: Oswald Buddenhagen --- configure.json | 2 +- mkspecs/features/mac/sdk.prf | 29 +++++++++++++--- mkspecs/features/uikit/default_post.prf | 2 ++ qmake/generators/unix/unixmake.cpp | 26 +++++++++++++-- qmake/generators/unix/unixmake2.cpp | 59 ++++++++++++++++++++++++--------- 5 files changed, 94 insertions(+), 24 deletions(-) diff --git a/configure.json b/configure.json index 294299d82f..ee5a229ffd 100644 --- a/configure.json +++ b/configure.json @@ -1484,7 +1484,7 @@ }, "precompile_header": { "description": "Using precompiled headers", - "condition": "config.msvc || (!config.uikit && tests.precompile_header)", + "condition": "config.msvc || tests.precompile_header", "output": [ "privateConfig", { "type": "varRemove", "negative": true, "name": "CONFIG", "value": "'precompile_header'" } diff --git a/mkspecs/features/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index 5abc741b90..c7395ea572 100644 --- a/mkspecs/features/mac/sdk.prf +++ b/mkspecs/features/mac/sdk.prf @@ -56,13 +56,32 @@ for(tool, $$list(QMAKE_CC QMAKE_CXX QMAKE_FIX_RPATH QMAKE_AR QMAKE_RANLIB QMAKE_ !equals(MAKEFILE_GENERATOR, XCODE) { uikit:!host_buildĀ { - simulator: \ + ios: os_var = IOS + tvos: os_var = TVOS + watchos: os_var = WATCHOS + + deployment_target = $$eval(QMAKE_$${os_var}_DEPLOYMENT_TARGET) + simulator { + archs = $$eval(QMAKE_$${os_var}_SIMULATOR_ARCHS) version_identifier = $$simulator.deployment_identifier - else: \ + } else { + archs = $$eval(QMAKE_$${os_var}_DEVICE_ARCHS) version_identifier = $$device.deployment_identifier - ios: deployment_target = $$QMAKE_IOS_DEPLOYMENT_TARGET - tvos: deployment_target = $$QMAKE_TVOS_DEPLOYMENT_TARGET - watchos: deployment_target = $$QMAKE_WATCHOS_DEPLOYMENT_TARGET + } + + single_arch: archs = $$first(archs) + + QMAKE_CFLAGS_USE_PRECOMPILE = + for(arch, archs) { + QMAKE_CFLAGS_USE_PRECOMPILE += \ + -Xarch_$${arch} \ + -include${QMAKE_PCH_OUTPUT_$${arch}} + } + QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + QMAKE_OBJCFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + QMAKE_OBJCXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + + QMAKE_PCH_OUTPUT_EXT = _${QMAKE_PCH_ARCH}$${QMAKE_PCH_OUTPUT_EXT} } else: osx { version_identifier = macosx deployment_target = $$QMAKE_MACOSX_DEPLOYMENT_TARGET diff --git a/mkspecs/features/uikit/default_post.prf b/mkspecs/features/uikit/default_post.prf index 52c9b1e8c8..199074eefe 100644 --- a/mkspecs/features/uikit/default_post.prf +++ b/mkspecs/features/uikit/default_post.prf @@ -114,4 +114,6 @@ macx-xcode { QMAKE_CFLAGS += $$arch_flags QMAKE_CXXFLAGS += $$arch_flags QMAKE_LFLAGS += $$arch_flags + + QMAKE_PCH_ARCHS = $$VALID_ARCHS } diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index b0f7593fbe..349dcd2f40 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -194,6 +194,18 @@ UnixMakefileGenerator::init() if (!language.isEmpty()) { pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"), escapeFilePath(pchBaseName + language + headerSuffix)); + const ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS"); + for (const ProString &arch : pchArchs) { + QString suffix = headerSuffix; + suffix.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString()); + if (project->isActiveConfig("clang_pch_style") + && (suffix.endsWith(QLatin1String(".pch")) + || suffix.endsWith(QLatin1String(".gch")))) { + suffix.chop(4); // must omit header suffix for -include to recognize the PCH + } + pchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT_") + arch + QLatin1Char('}'), + escapeFilePath(pchBaseName + language + suffix)); + } } } @@ -351,9 +363,17 @@ QStringList if (!file.endsWith(extension.toQString())) continue; - QString precompiledHeader = header_prefix + language + header_suffix; - if (!ret.contains(precompiledHeader)) - ret += precompiledHeader; + ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS"); + if (pchArchs.isEmpty()) + pchArchs << ProString(); // normal single-arch PCH + for (const ProString &arch : qAsConst(pchArchs)) { + QString suffix = header_suffix; + if (!arch.isEmpty()) + suffix.replace(QLatin1String("${QMAKE_PCH_ARCH}"), arch.toQString()); + QString precompiledHeader = header_prefix + language + suffix; + if (!ret.contains(precompiledHeader)) + ret += precompiledHeader; + } goto foundPrecompiledDependency; } diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index d437a0782b..6fa355390f 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -996,7 +996,15 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) if (language.isEmpty()) continue; - precomp_files += precomph_out_dir + header_prefix + language + header_suffix; + ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS"); + if (pchArchs.isEmpty()) + pchArchs << ProString(); // normal single-arch PCH + for (const ProString &arch : qAsConst(pchArchs)) { + auto suffix = header_suffix.toQString(); + if (!arch.isEmpty()) + suffix.replace(QStringLiteral("${QMAKE_PCH_ARCH}"), arch.toQString()); + precomp_files += precomph_out_dir + header_prefix + language + suffix; + } } } t << "-$(DEL_FILE) " << escapeFilePaths(precomp_files).join(' ') << "\n\t"; @@ -1052,6 +1060,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) QString pchInput = project->first("PRECOMPILED_HEADER").toQString(); t << "###### Precompiled headers\n"; for (const ProString &compiler : project->values("QMAKE_BUILTIN_COMPILERS")) { + QString pchOutputDir; QString pchFlags = var(ProKey("QMAKE_" + compiler + "FLAGS_PRECOMPILE")); if(pchFlags.isEmpty()) continue; @@ -1062,6 +1071,9 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) else cflags += " $(CXXFLAGS)"; + ProStringList pchArchs = project->values("QMAKE_PCH_ARCHS"); + if (pchArchs.isEmpty()) + pchArchs << ProString(); // normal single-arch PCH ProString pchBaseName = project->first("QMAKE_ORIG_TARGET"); ProString pchOutput; if(!project->isEmpty("PRECOMPILED_DIR")) @@ -1088,30 +1100,47 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) ProString header_suffix = project->isActiveConfig("clang_pch_style") ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; pchOutput += Option::dir_sep; - QString pchOutputDir = pchOutput.toQString(); + pchOutputDir = pchOutput.toQString(); QString language = project->first(ProKey("QMAKE_LANGUAGE_" + compiler)).toQString(); if (language.isEmpty()) continue; pchOutput += header_prefix + language + header_suffix; - - t << escapeDependencyPath(pchOutput) << ": " << escapeDependencyPath(pchInput) << ' ' - << escapeDependencyPaths(findDependencies(pchInput)).join(" \\\n\t\t") - << "\n\t" << mkdir_p_asstring(pchOutputDir); } pchFlags.replace(QLatin1String("${QMAKE_PCH_INPUT}"), escapeFilePath(pchInput)) - .replace(QLatin1String("${QMAKE_PCH_OUTPUT_BASE}"), escapeFilePath(pchBaseName.toQString())) - .replace(QLatin1String("${QMAKE_PCH_OUTPUT}"), escapeFilePath(pchOutput.toQString())); + .replace(QLatin1String("${QMAKE_PCH_OUTPUT_BASE}"), escapeFilePath(pchBaseName.toQString())); + for (const ProString &arch : qAsConst(pchArchs)) { + auto pchArchOutput = pchOutput.toQString(); + if (!arch.isEmpty()) + pchArchOutput.replace(QStringLiteral("${QMAKE_PCH_ARCH}"), arch.toQString()); + + if (!project->isActiveConfig("icc_pch_style")) { + const auto pchFilePath_d = escapeDependencyPath(pchArchOutput); + if (!arch.isEmpty()) { + t << pchFilePath_d << ": " << "EXPORT_ARCH_ARGS = -arch " << arch << "\n\n"; + t << pchFilePath_d << ": " + << "EXPORT_QMAKE_XARCH_CFLAGS = $(EXPORT_QMAKE_XARCH_CFLAGS_" << arch << ")" << "\n\n"; + t << pchFilePath_d << ": " + << "EXPORT_QMAKE_XARCH_LFLAGS = $(EXPORT_QMAKE_XARCH_LFLAGS_" << arch << ")" << "\n\n"; + } + t << pchFilePath_d << ": " << escapeDependencyPath(pchInput) << ' ' + << escapeDependencyPaths(findDependencies(pchInput)).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(pchOutputDir); + } - QString compilerExecutable; - if (compiler == "C" || compiler == "OBJC") - compilerExecutable = "$(CC)"; - else - compilerExecutable = "$(CXX)"; + auto pchArchFlags = pchFlags; + pchArchFlags.replace(QLatin1String("${QMAKE_PCH_OUTPUT}"), escapeFilePath(pchArchOutput)); - // compile command - t << "\n\t" << compilerExecutable << cflags << " $(INCPATH) " << pchFlags << endl << endl; + QString compilerExecutable; + if (compiler == "C" || compiler == "OBJC") + compilerExecutable = "$(CC)"; + else + compilerExecutable = "$(CXX)"; + + // compile command + t << "\n\t" << compilerExecutable << cflags << " $(INCPATH) " << pchArchFlags << endl << endl; + } } } -- cgit v1.2.3