Skip to content

Commit

Permalink
qmake/vcxproj: Fix "CONFIG += combine" extra compilers
Browse files Browse the repository at this point in the history
Extra compilers with "CONFIG += combine" were broken for qmake's vcxproj
generator since forever.

Usually, extra compilers are handled by attaching the Custom Build Tool
to the input file.  This is not possible for combine extra compilers,
because they map multiple inputs to one output.  We cannot attach the
Custom Build Tool to the output either, because this would result in
circular dependency errors (output trying to create output itself).

To fix this, we create a custom build tool fake file (.cbt) for the
output and attach the Custom Build Tool there.  This is the same trick
we do for regular extra compilers that have C++ sources as
input (e.g. the one that generates moc_predefs.h).

Fixes: QTBUG-94806
Change-Id: Ib808a43fead737df91b89a1ac5e180aeae37efae
Reviewed-by: Alexandru Croitor <[email protected]>
(cherry picked from commit e022ff0)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
  • Loading branch information
jobor authored and Qt Cherry-pick Bot committed Oct 25, 2021
1 parent b9cdd2f commit f2b0d0c
Showing 1 changed file with 58 additions and 26 deletions.
84 changes: 58 additions & 26 deletions qmake/generators/win32/msvc_vcproj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include <qregexp.h>

#include <stdlib.h>
#include <tuple>
#include <utility>

//#define DEBUG_SOLUTION_GEN

Expand Down Expand Up @@ -814,33 +816,62 @@ void VcprojGenerator::init()
}
}

// Add all input files for a custom compiler into a map for uniqueness,
// unless the compiler is configure as a combined stage, then use the first one
// Helper function to create a fake file foo.cbt for the project view.
//
// This prevents VS from complaining about a circular dependency from "foo -> foo".
//
// The .cbt file is added as "source" of the Custom Build Tool. This means, in the project
// view, this is the file the Custom Build Tool property page is attached to.
//
// This function returns a pair with
// - the fully resolved output file path
// - the file path of the .cbt file
auto addExtraCompilerSourceWithCustomBuildToolFakeFile
= [this](const QString &compilerOutput, const ProString &extraCompiler,
const QStringList &inputs) -> std::pair<QString, QString>
{
QString realOut = replaceExtraCompilerVariables(compilerOutput, inputs, {}, NoShell);
QString out = realOut + customBuildToolFilterFileSuffix;
createCustomBuildToolFakeFile(out, realOut);
out = Option::fixPathToTargetOS(out, false);
extraCompilerSources[out] += extraCompiler.toQString();
return { realOut, out };
};

// Add all input files for a custom compiler into a map for uniqueness.
//
// Use .cbt files for the following cases:
// - CONFIG += combine
// - the input has a built-in compiler (e.g. C++ source file)
for (const ProString &quc : project->values("QMAKE_EXTRA_COMPILERS")) {
const ProStringList &invar = project->values(ProKey(quc + ".input"));
const QString compiler_out = project->first(ProKey(quc + ".output")).toQString();
for (ProStringList::ConstIterator iit = invar.constBegin(); iit != invar.constEnd(); ++iit) {
ProStringList fileList = project->values((*iit).toKey());
if (!fileList.isEmpty()) {
if (project->values(ProKey(quc + ".CONFIG")).indexOf("combine") != -1)
fileList.erase(fileList.begin() + 1, fileList.end());
for (ProStringList::ConstIterator fit = fileList.constBegin(); fit != fileList.constEnd(); ++fit) {
QString file = (*fit).toQString();
if (verifyExtraCompiler(quc, file)) {
if (!hasBuiltinCompiler(file)) {
extraCompilerSources[file] += quc.toQString();
} else {
// Create a fake file foo.moc.cbt for the project view.
// This prevents VS from complaining about a circular
// dependency from foo.moc -> foo.moc.
QString realOut = replaceExtraCompilerVariables(
compiler_out, file, QString(), NoShell);
QString out = realOut + customBuildToolFilterFileSuffix;
createCustomBuildToolFakeFile(out, realOut);
out = Option::fixPathToTargetOS(out, false);
extraCompilerSources[out] += quc.toQString();
extraCompilerOutputs[out] = file;
}

QStringList inputFiles;
for (auto it = invar.begin(); it != invar.end(); ++it)
inputFiles += project->values(it->toKey()).toQStringList();

if (project->values(ProKey(quc + ".CONFIG")).contains("combine")) {
// Handle "CONFIG += combine" extra compilers.
QString realOut;
QString out;
std::tie(realOut, out)
= addExtraCompilerSourceWithCustomBuildToolFakeFile(compiler_out, quc, inputFiles);
if (hasBuiltinCompiler(realOut))
extraCompilerOutputs[out] = realOut;
} else {
// Handle regular 1-to-1 extra compilers.
for (const QString &file : inputFiles) {
if (verifyExtraCompiler(quc, file)) {
if (!hasBuiltinCompiler(file)) {
extraCompilerSources[file] += quc.toQString();
} else {
QString out;
std::tie(std::ignore, out)
= addExtraCompilerSourceWithCustomBuildToolFakeFile(compiler_out,
quc,
QStringList(file));
extraCompilerOutputs[out] = file;
}
}
}
Expand Down Expand Up @@ -1586,9 +1617,10 @@ void VcprojGenerator::initExtraCompilerOutputs()
if (!outputs.isEmpty())
tmp_out = outputs.first().toQString();
if (project->values(ProKey(*it + ".CONFIG")).indexOf("combine") != -1) {
// Combined output, only one file result
// Combined output, only one file result. Use .cbt file.
extraCompile.addFile(Option::fixPathToTargetOS(
replaceExtraCompilerVariables(tmp_out, QString(), QString(), NoShell), false));
replaceExtraCompilerVariables(tmp_out + customBuildToolFilterFileSuffix,
QString(), QString(), NoShell), false));
} else if (!inputVars.isEmpty()) {
// One output file per input
const ProStringList &tmp_in = project->values(inputVars.first().toKey());
Expand Down

0 comments on commit f2b0d0c

Please sign in to comment.