Autogen: Pass explicit predefines header to moc if possible

Qt is relying on whoever calls moc to include a file with the predefined
values that will be used by the compiler, otherwise moc takes wrong
paths and weird things happen.
Instead, generate an include file and feed it to all mocs to make sure
it's generating correct code.

Co-Author: Sebastian Holtermann <sebholt@xwmw.org>
Fixes: #16640
This commit is contained in:
Aleix Pol 2017-04-06 04:14:35 +02:00 committed by Brad King
parent 135611176c
commit 0903531964
6 changed files with 111 additions and 5 deletions

View File

@ -21,6 +21,7 @@ set(AM_MOC_INCLUDES @_moc_incs@)
set(AM_MOC_OPTIONS @_moc_options@)
set(AM_MOC_RELAXED_MODE @_moc_relaxed_mode@)
set(AM_MOC_DEPEND_FILTERS @_moc_depend_filters@)
set(AM_MOC_PREDEFS_CMD @_moc_predefs_cmd@)
# UIC settings
set(AM_UIC_SKIP @_uic_skip@)
set(AM_UIC_TARGET_OPTIONS @_uic_target_options@)

View File

@ -22,5 +22,7 @@ else()
string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os")
string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3")
string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
set(CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "${CMAKE_${lang}_COMPILER}" "-QdM" "-P" "-Za" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
endmacro()
endif()

View File

@ -12,4 +12,5 @@ macro(__linux_compiler_gnu lang)
# We pass this for historical reasons. Projects may have
# executables that use dlopen but do not set ENABLE_EXPORTS.
set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic")
set(CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "${CMAKE_${lang}_COMPILER}" "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
endmacro()

View File

@ -96,6 +96,41 @@ static std::string GetQtMajorVersion(cmGeneratorTarget const* target)
return qtMajorVersion;
}
static std::string GetQtMinorVersion(cmGeneratorTarget const* target,
const std::string& qtMajorVersion)
{
cmMakefile* makefile = target->Target->GetMakefile();
std::string qtMinorVersion;
if (qtMajorVersion == "5") {
qtMinorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR");
}
if (qtMinorVersion.empty()) {
qtMinorVersion = makefile->GetSafeDefinition("QT_VERSION_MINOR");
}
const char* targetQtVersion =
target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", "");
if (targetQtVersion != CM_NULLPTR) {
qtMinorVersion = targetQtVersion;
}
return qtMinorVersion;
}
static bool QtVersionGreaterOrEqual(const std::string& major,
const std::string& minor,
unsigned long requestMajor,
unsigned long requestMinor)
{
unsigned long majorUL(0);
unsigned long minorUL(0);
if (cmSystemTools::StringToULong(major.c_str(), &majorUL) &&
cmSystemTools::StringToULong(minor.c_str(), &minorUL)) {
return (majorUL > requestMajor) ||
(majorUL == requestMajor && minorUL >= requestMinor);
}
return false;
}
static void GetCompileDefinitionsAndDirectories(
cmGeneratorTarget const* target, const std::string& config,
std::string& incs, std::string& defs)
@ -258,6 +293,12 @@ static void MocSetupAutoTarget(
AddDefinitionEscaped(makefile, "_moc_depend_filters",
GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS"));
if (QtVersionGreaterOrEqual(
qtMajorVersion, GetQtMinorVersion(target, qtMajorVersion), 5, 8)) {
AddDefinitionEscaped(
makefile, "_moc_predefs_cmd",
makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"));
}
// Moc includes and compile definitions
{
std::string _moc_incs;

View File

@ -360,6 +360,8 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
InfoGet(makefile, "AM_QT_MOC_EXECUTABLE", this->MocExecutable);
InfoGet(makefile, "AM_QT_UIC_EXECUTABLE", this->UicExecutable);
InfoGet(makefile, "AM_QT_RCC_EXECUTABLE", this->RccExecutable);
InfoGet(makefile, "AM_MOC_PREDEFS_CMD", this->MocPredefsCmd);
// Check Qt version
if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) {
this->LogError("AutoGen: Error: Unsupported Qt version: " +
@ -579,6 +581,12 @@ void cmQtAutoGenerators::Init(cmMakefile* makefile)
this->MocCppFilenameAbs = this->CurrentBinaryDir + this->MocCppFilenameRel;
// Moc predefs file
if (!this->MocPredefsCmd.empty()) {
this->MocPredefsFileRel = this->AutogenBuildSubDir + "moc_predefs.h";
this->MocPredefsFileAbs = this->CurrentBinaryDir + this->MocPredefsFileRel;
}
// Init file path checksum generator
fpathCheckSum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir,
this->ProjectSourceDir,
@ -1142,6 +1150,50 @@ bool cmQtAutoGenerators::MocGenerateAll(
return true;
}
// Generate moc_predefs
if (!this->MocPredefsCmd.empty()) {
if (!this->MakeParentDirectory(this->MocPredefsFileAbs)) {
this->LogError("AutoMoc: Error creating directory for " +
this->MocPredefsFileRel);
return false;
}
this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel);
std::vector<std::string> cmd = this->MocPredefsCmd;
cmd.insert(cmd.end(), this->MocIncludes.begin(), this->MocIncludes.end());
for (std::vector<std::string>::const_iterator it =
this->MocDefinitions.begin();
it != this->MocDefinitions.end(); ++it) {
cmd.push_back("-D" + (*it));
}
cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end());
std::string output;
bool moc_predefsGenerated = this->RunCommand(cmd, output, false);
if (!moc_predefsGenerated) {
return false;
}
// actually write the file
cmsys::ofstream outfile;
outfile.open(this->MocPredefsFileAbs.c_str(), std::ios::trunc);
if (!outfile) {
moc_predefsGenerated = false;
this->LogError("AutoMoc: Error opening " + this->MocPredefsFileRel);
} else {
outfile << output;
// Check for write errors
if (!outfile.good()) {
moc_predefsGenerated = false;
this->LogError("AutoMoc: Error writing " + this->MocPredefsFileRel);
}
}
if (!moc_predefsGenerated) {
return false;
}
}
bool mocCompFileGenerated = false;
bool mocCompChanged = false;
@ -1305,6 +1357,10 @@ bool cmQtAutoGenerators::MocGenerateFile(
cmd.push_back("-D" + (*it));
}
cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end());
if (!this->MocPredefsFileAbs.empty()) {
cmd.push_back("--include");
cmd.push_back(this->MocPredefsFileAbs);
}
#ifdef _WIN32
cmd.push_back("-DWIN32");
#endif
@ -1805,7 +1861,7 @@ bool cmQtAutoGenerators::MakeParentDirectory(const std::string& filename) const
* @return True on success
*/
bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command,
std::string& output) const
std::string& output, bool verbose) const
{
// Log command
if (this->Verbose) {
@ -1813,8 +1869,9 @@ bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command,
}
// Execute command
int retVal = 0;
bool res =
cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
bool res = cmSystemTools::RunSingleCommand(
command, &output, &output, &retVal, CM_NULLPTR,
verbose ? cmSystemTools::OUTPUT_MERGE : cmSystemTools::OUTPUT_NONE);
return (res && (retVal == 0));
}

View File

@ -142,8 +142,8 @@ private:
const char* basePrefix,
const char* baseSuffix) const;
bool MakeParentDirectory(const std::string& filename) const;
bool RunCommand(const std::vector<std::string>& command,
std::string& output) const;
bool RunCommand(const std::vector<std::string>& command, std::string& output,
bool verbose = true) const;
bool FindHeader(std::string& header, const std::string& testBasePath) const;
@ -176,6 +176,8 @@ private:
// - Moc
std::string MocCppFilenameRel;
std::string MocCppFilenameAbs;
std::string MocPredefsFileRel;
std::string MocPredefsFileAbs;
std::vector<std::string> MocSkipList;
std::vector<std::string> MocIncludePaths;
std::vector<std::string> MocIncludes;
@ -197,6 +199,8 @@ private:
MacroFilter MacroFilters[2];
cmsys::RegularExpression RegExpMocInclude;
cmsys::RegularExpression RegExpUicInclude;
// - moc_predefs
std::vector<std::string> MocPredefsCmd;
// - Flags
bool IncludeProjectDirsBefore;
bool Verbose;