summaryrefslogtreecommitdiffstats
path: root/Source/cmQtAutomoc.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmQtAutomoc.cxx')
-rw-r--r--Source/cmQtAutomoc.cxx410
1 files changed, 339 insertions, 71 deletions
diff --git a/Source/cmQtAutomoc.cxx b/Source/cmQtAutomoc.cxx
index ae63701..65c7952 100644
--- a/Source/cmQtAutomoc.cxx
+++ b/Source/cmQtAutomoc.cxx
@@ -17,11 +17,70 @@
#include "cmSourceFile.h"
#include "cmSystemTools.h"
-# include <cmsys/Terminal.h>
+#include <cmsys/Terminal.h>
+
+#include <string.h>
#include "cmQtAutomoc.h"
+static bool containsQ_OBJECT(const std::string& text)
+{
+ // this simple check is much much faster than the regexp
+ if (strstr(text.c_str(), "Q_OBJECT") == NULL)
+ {
+ return false;
+ }
+
+ cmsys::RegularExpression qObjectRegExp("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]");
+ return qObjectRegExp.find(text);
+}
+
+
+static std::string findMatchingHeader(const std::string& absPath,
+ const std::string& mocSubDir,
+ const std::string& basename,
+ const std::list<std::string>& headerExtensions)
+{
+ std::string header;
+ for(std::list<std::string>::const_iterator ext = headerExtensions.begin();
+ ext != headerExtensions.end();
+ ++ext)
+ {
+ std::string sourceFilePath = absPath + basename + (*ext);
+ if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
+ {
+ header = sourceFilePath;
+ break;
+ }
+ if (!mocSubDir.empty())
+ {
+ sourceFilePath = mocSubDir + basename + (*ext);
+ if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
+ {
+ header = sourceFilePath;
+ break;
+ }
+ }
+ }
+
+ return header;
+}
+
+
+static std::string extractSubDir(const std::string& absPath,
+ const std::string& currentMoc)
+{
+ std::string subDir;
+ if (currentMoc.find_first_of('/') != std::string::npos)
+ {
+ subDir = absPath
+ + cmsys::SystemTools::GetFilenamePath(currentMoc) + '/';
+ }
+ return subDir;
+}
+
+
cmQtAutomoc::cmQtAutomoc()
:Verbose(cmsys::SystemTools::GetEnv("VERBOSE") != 0)
,ColorOutput(true)
@@ -49,13 +108,23 @@ void cmQtAutomoc::SetupAutomocTarget(cmTarget* target)
{
cmMakefile* makefile = target->GetMakefile();
const char* targetName = target->GetName();
- // don't do anything if there is no Qt4:
+ // don't do anything if there is no Qt4 or Qt5Core (which contains moc):
std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
+ if (qtMajorVersion == "")
+ {
+ qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
+ }
if (qtMajorVersion != "4" && qtMajorVersion != "5")
{
return;
}
+ bool strictMode = (qtMajorVersion == "5");
+ if (makefile->IsDefinitionSet("CMAKE_AUTOMOC_STRICT_MODE"))
+ {
+ strictMode = makefile->IsOn("CMAKE_AUTOMOC_STRICT_MODE");
+ }
+
// create a custom target for running automoc at buildtime:
std::string automocTargetName = targetName;
automocTargetName += "_automoc";
@@ -144,6 +213,7 @@ void cmQtAutomoc::SetupAutomocTarget(cmTarget* target)
makefile->AddDefinition("_moc_options", _moc_options.c_str());
makefile->AddDefinition("_moc_files", _moc_files.c_str());
makefile->AddDefinition("_moc_headers", _moc_headers.c_str());
+ makefile->AddDefinition("_moc_strict_mode", strictMode ? "TRUE" : "FALSE");
const char* cmakeRoot = makefile->GetSafeDefinition("CMAKE_ROOT");
std::string inputFile = cmakeRoot;
@@ -179,7 +249,7 @@ bool cmQtAutomoc::Run(const char* targetDirectory)
if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5")
{
- this->RunAutomocQt4();
+ this->RunAutomoc();
}
this->WriteOldMocDefinitionsFile(targetDirectory);
@@ -222,6 +292,11 @@ bool cmQtAutomoc::ReadAutomocInfoFile(cmMakefile* makefile,
}
this->QtMajorVersion = makefile->GetSafeDefinition("AM_QT_VERSION_MAJOR");
+ if (this->QtMajorVersion == "")
+ {
+ this->QtMajorVersion = makefile->GetSafeDefinition(
+ "AM_Qt5Core_VERSION_MAJOR");
+ }
this->Sources = makefile->GetSafeDefinition("AM_SOURCES");
this->Headers = makefile->GetSafeDefinition("AM_HEADERS");
this->IncludeProjectDirsBefore = makefile->IsOn(
@@ -238,6 +313,8 @@ bool cmQtAutomoc::ReadAutomocInfoFile(cmMakefile* makefile,
this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR");
this->TargetName = makefile->GetSafeDefinition("AM_TARGET_NAME");
+ this->StrictMode = makefile->IsOn("AM_STRICT_MODE");
+
return true;
}
@@ -382,7 +459,7 @@ void cmQtAutomoc::Init()
}
-bool cmQtAutomoc::RunAutomocQt4()
+bool cmQtAutomoc::RunAutomoc()
{
if (!cmsys::SystemTools::FileExists(this->OutMocCppFilename.c_str())
|| (this->OldMocDefinitionsStr != this->Join(this->MocDefinitions, ' ')))
@@ -406,6 +483,23 @@ bool cmQtAutomoc::RunAutomocQt4()
std::vector<std::string> sourceFiles;
cmSystemTools::ExpandListArgument(this->Sources, sourceFiles);
+ std::list<std::string> headerExtensions;
+ headerExtensions.push_back(".h");
+ headerExtensions.push_back(".hpp");
+ headerExtensions.push_back(".hxx");
+#if defined(_WIN32)
+ // not case sensitive, don't add ".H"
+#elif defined(__APPLE__)
+ // detect case-sensitive filesystem
+ long caseSensitive = pathconf(this->Srcdir.c_str(), _PC_CASE_SENSITIVE);
+ if (caseSensitive == 1)
+ {
+ headerExtensions.push_back(".H");
+ }
+#else
+ headerExtensions.push_back(".H");
+#endif
+
for (std::vector<std::string>::const_iterator it = sourceFiles.begin();
it != sourceFiles.end();
++it)
@@ -415,7 +509,15 @@ bool cmQtAutomoc::RunAutomocQt4()
{
std::cout << "AUTOMOC: Checking " << absFilename << std::endl;
}
- this->ParseCppFile(absFilename, includedMocs, headerFiles);
+ if (this->StrictMode == false)
+ {
+ this->ParseCppFile(absFilename, headerExtensions, includedMocs);
+ }
+ else
+ {
+ this->StrictParseCppFile(absFilename, headerExtensions, includedMocs);
+ }
+ this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles);
}
std::vector<std::string> headerFilesVec;
@@ -496,40 +598,37 @@ bool cmQtAutomoc::RunAutomocQt4()
void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
- std::map<std::string, std::string>& includedMocs,
- std::set<std::string>& absHeaders)
+ const std::list<std::string>& headerExtensions,
+ std::map<std::string, std::string>& includedMocs)
{
cmsys::RegularExpression mocIncludeRegExp(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
- std::list<std::string> headerExtensions;
- headerExtensions.push_back(".h");
- headerExtensions.push_back(".hpp");
- headerExtensions.push_back(".hxx");
-#if defined(_WIN32)
- // not case sensitive, don't add ".H"
-#elif defined(__APPLE__)
- // detect case-sensitive filesystem
- long caseSensitive = pathconf(this->Srcdir.c_str(), _PC_CASE_SENSITIVE);
- if (caseSensitive == 1)
- {
- headerExtensions.push_back(".H");
- }
-#else
- headerExtensions.push_back(".H");
-#endif
const std::string contentsString = this->ReadAll(absFilename);
if (contentsString.empty())
{
- std::cerr << "AUTOMOC: empty source file: " << absFilename << std::endl;
+ std::cerr << "AUTOMOC: warning: " << absFilename << ": file is empty\n"
+ << std::endl;
return;
}
const std::string absPath = cmsys::SystemTools::GetFilenamePath(
cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
+ const std::string scannedFileBasename = cmsys::SystemTools::
+ GetFilenameWithoutLastExtension(absFilename);
+ const bool cppContainsQ_OBJECT = containsQ_OBJECT(contentsString);
+ bool dotMocIncluded = false;
+ bool mocUnderscoreIncluded = false;
+ std::string ownMocUnderscoreFile;
+ std::string ownDotMocFile;
+ std::string ownMocHeaderFile;
std::string::size_type matchOffset = 0;
- if (mocIncludeRegExp.find(contentsString.c_str()))
+ // first a simply string check for "moc" is *much* faster than the regexp,
+ // and if the string search already fails, we don't have to try the
+ // expensive regexp
+ if ((strstr(contentsString.c_str(), "moc") != NULL)
+ && (mocIncludeRegExp.find(contentsString)))
{
// for every moc include in the file
do
@@ -552,78 +651,248 @@ void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
// basename should be the part of the moc filename used for
// finding the correct header, so we need to remove the moc_ part
basename = basename.substr(4);
+ std::string mocSubDir = extractSubDir(absPath, currentMoc);
+ std::string headerToMoc = findMatchingHeader(
+ absPath, mocSubDir, basename, headerExtensions);
- bool headerFound = false;
- for(std::list<std::string>::const_iterator ext =
- headerExtensions.begin();
- ext != headerExtensions.end();
- ++ext)
+ if (!headerToMoc.empty())
+ {
+ includedMocs[headerToMoc] = currentMoc;
+ if (basename == scannedFileBasename)
+ {
+ mocUnderscoreIncluded = true;
+ ownMocUnderscoreFile = currentMoc;
+ ownMocHeaderFile = headerToMoc;
+ }
+ }
+ else
{
- const std::string &sourceFilePath = absPath + basename + (*ext);
- if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
+ std::cerr << "AUTOMOC: error: " << absFilename << " The file "
+ << "includes the moc file \"" << currentMoc << "\", "
+ << "but could not find header \"" << basename
+ << '{' << this->Join(headerExtensions, ',') << "}\" ";
+ if (mocSubDir.empty())
{
- headerFound = true;
- includedMocs[sourceFilePath] = currentMoc;
- break;
+ std::cerr << "in " << absPath << "\n" << std::endl;
}
+ else
+ {
+ std::cerr << "neither in " << absPath
+ << " nor in " << mocSubDir << "\n" << std::endl;
+ }
+
+ ::exit(EXIT_FAILURE);
}
- if (!headerFound)
+ }
+ else
+ {
+ std::string fileToMoc = absFilename;
+ if ((basename != scannedFileBasename) || (cppContainsQ_OBJECT==false))
{
- // the moc file is in a subdir => look for the header in the
- // same subdir
- if (currentMoc.find_first_of('/') != std::string::npos)
+ std::string mocSubDir = extractSubDir(absPath, currentMoc);
+ std::string headerToMoc = findMatchingHeader(
+ absPath, mocSubDir, basename, headerExtensions);
+ if (!headerToMoc.empty())
{
- const std::string &filepath = absPath
- + cmsys::SystemTools::GetFilenamePath(currentMoc)
- + '/' + basename;
-
- for(std::list<std::string>::const_iterator ext =
- headerExtensions.begin();
- ext != headerExtensions.end();
- ++ext)
+ // this is for KDE4 compatibility:
+ fileToMoc = headerToMoc;
+ if ((cppContainsQ_OBJECT==false) &&(basename==scannedFileBasename))
{
- const std::string &sourceFilePath = filepath + (*ext);
- if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
- {
- headerFound = true;
- includedMocs[sourceFilePath] = currentMoc;
- break;
- }
+ std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
+ "includes the moc file \"" << currentMoc <<
+ "\", but does not contain a Q_OBJECT macro. "
+ "Running moc on "
+ << "\"" << headerToMoc << "\" ! Better include \"moc_"
+ << basename << ".cpp\" for a robust build.\n"
+ << std::endl;
}
- if (!headerFound)
+ else
{
- std::cerr << "AUTOMOC: The file \"" << absFilename
- << "\" includes the moc file \"" << currentMoc
- << "\", but neither \"" << absPath << basename
- << '{' << this->Join(headerExtensions, ',')
- << "}\" nor \"" << filepath << '{'
- << this->Join(headerExtensions, ',') << '}'
- << "\" exist." << std::endl;
- ::exit(EXIT_FAILURE);
+ std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
+ "includes the moc file \"" << currentMoc <<
+ "\" instead of \"moc_" << basename << ".cpp\". "
+ "Running moc on "
+ << "\"" << headerToMoc << "\" ! Better include \"moc_"
+ << basename << ".cpp\" for a robust build.\n"
+ << std::endl;
}
}
else
{
- std::cerr << "AUTOMOC: The file \"" << absFilename
- << "\" includes the moc file \"" << currentMoc
- << "\", but \"" << absPath << basename << '{'
- << this->Join(headerExtensions, ',') << '}'
- << "\" does not exist." << std::endl;
+ std::cerr <<"AUTOMOC: error: " << absFilename << ": The file "
+ "includes the moc file \"" << currentMoc <<
+ "\", which seems to be the moc file from a different "
+ "source file. CMake also could not find a matching "
+ "header.\n" << std::endl;
::exit(EXIT_FAILURE);
}
}
+ else
+ {
+ dotMocIncluded = true;
+ ownDotMocFile = currentMoc;
+ }
+ includedMocs[fileToMoc] = currentMoc;
+ }
+ matchOffset += mocIncludeRegExp.end();
+ } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset));
+ }
+
+ // In this case, check whether the scanned file itself contains a Q_OBJECT.
+ // If this is the case, the moc_foo.cpp should probably be generated from
+ // foo.cpp instead of foo.h, because otherwise it won't build.
+ // But warn, since this is not how it is supposed to be used.
+ if ((dotMocIncluded == false) && (cppContainsQ_OBJECT == true))
+ {
+ if (mocUnderscoreIncluded == true)
+ {
+ // this is for KDE4 compatibility:
+ std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
+ << "contains a Q_OBJECT macro, but does not include "
+ << "\"" << scannedFileBasename << ".moc\", but instead "
+ "includes "
+ << "\"" << ownMocUnderscoreFile << "\". Running moc on "
+ << "\"" << absFilename << "\" ! Better include \""
+ << scannedFileBasename << ".moc\" for a robust build.\n"
+ << std::endl;
+ includedMocs[absFilename] = ownMocUnderscoreFile;
+ includedMocs.erase(ownMocHeaderFile);
+ }
+ else
+ {
+ // otherwise always error out since it will not compile:
+ std::cerr << "AUTOMOC: error: " << absFilename << ": The file "
+ << "contains a Q_OBJECT macro, but does not include "
+ << "\"" << scannedFileBasename << ".moc\" !\n"
+ << std::endl;
+ ::exit(EXIT_FAILURE);
+ }
+ }
+
+}
+
+
+void cmQtAutomoc::StrictParseCppFile(const std::string& absFilename,
+ const std::list<std::string>& headerExtensions,
+ std::map<std::string, std::string>& includedMocs)
+{
+ cmsys::RegularExpression mocIncludeRegExp(
+ "[\n][ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+
+ const std::string contentsString = this->ReadAll(absFilename);
+ if (contentsString.empty())
+ {
+ std::cerr << "AUTOMOC: warning: " << absFilename << ": file is empty\n"
+ << std::endl;
+ return;
+ }
+ const std::string absPath = cmsys::SystemTools::GetFilenamePath(
+ cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
+ const std::string scannedFileBasename = cmsys::SystemTools::
+ GetFilenameWithoutLastExtension(absFilename);
+
+ bool dotMocIncluded = false;
+
+ std::string::size_type matchOffset = 0;
+ // first a simply string check for "moc" is *much* faster than the regexp,
+ // and if the string search already fails, we don't have to try the
+ // expensive regexp
+ if ((strstr(contentsString.c_str(), "moc") != NULL)
+ && (mocIncludeRegExp.find(contentsString)))
+ {
+ // for every moc include in the file
+ do
+ {
+ const std::string currentMoc = mocIncludeRegExp.match(1);
+
+ std::string basename = cmsys::SystemTools::
+ GetFilenameWithoutLastExtension(currentMoc);
+ const bool mocUnderscoreStyle = this->StartsWith(basename, "moc_");
+
+ // If the moc include is of the moc_foo.cpp style we expect
+ // the Q_OBJECT class declaration in a header file.
+ // If the moc include is of the foo.moc style we need to look for
+ // a Q_OBJECT macro in the current source file, if it contains the
+ // macro we generate the moc file from the source file.
+ if (mocUnderscoreStyle)
+ {
+ // basename should be the part of the moc filename used for
+ // finding the correct header, so we need to remove the moc_ part
+ basename = basename.substr(4);
+ std::string mocSubDir = extractSubDir(absPath, currentMoc);
+ std::string headerToMoc = findMatchingHeader(
+ absPath, mocSubDir, basename, headerExtensions);
+
+ if (!headerToMoc.empty())
+ {
+ includedMocs[headerToMoc] = currentMoc;
+ }
+ else
+ {
+ std::cerr << "AUTOMOC: error: " << absFilename << " The file "
+ << "includes the moc file \"" << currentMoc << "\", "
+ << "but could not find header \"" << basename
+ << '{' << this->Join(headerExtensions, ',') << "}\" ";
+ if (mocSubDir.empty())
+ {
+ std::cerr << "in " << absPath << "\n" << std::endl;
+ }
+ else
+ {
+ std::cerr << "neither in " << absPath
+ << " nor in " << mocSubDir << "\n" << std::endl;
+ }
+
+ ::exit(EXIT_FAILURE);
+ }
}
else
{
+ if (basename != scannedFileBasename)
+ {
+ std::cerr <<"AUTOMOC: error: " << absFilename << ": The file "
+ "includes the moc file \"" << currentMoc <<
+ "\", which seems to be the moc file from a different "
+ "source file. This is not supported. "
+ "Include \"" << scannedFileBasename << ".moc\" to run "
+ "moc on this source file.\n" << std::endl;
+ ::exit(EXIT_FAILURE);
+ }
+ dotMocIncluded = true;
includedMocs[absFilename] = currentMoc;
}
matchOffset += mocIncludeRegExp.end();
} while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset));
}
+ // In this case, check whether the scanned file itself contains a Q_OBJECT.
+ // If this is the case, the moc_foo.cpp should probably be generated from
+ // foo.cpp instead of foo.h, because otherwise it won't build.
+ // But warn, since this is not how it is supposed to be used.
+ if ((dotMocIncluded == false) && (containsQ_OBJECT(contentsString)))
+ {
+ // otherwise always error out since it will not compile:
+ std::cerr << "AUTOMOC: error: " << absFilename << ": The file "
+ << "contains a Q_OBJECT macro, but does not include "
+ << "\"" << scannedFileBasename << ".moc\" !\n"
+ << std::endl;
+ ::exit(EXIT_FAILURE);
+ }
+
+}
+
+
+void cmQtAutomoc::SearchHeadersForCppFile(const std::string& absFilename,
+ const std::list<std::string>& headerExtensions,
+ std::set<std::string>& absHeaders)
+{
// search for header files and private header files we may need to moc:
const std::string basename =
cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
+ const std::string absPath = cmsys::SystemTools::GetFilenamePath(
+ cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
+
for(std::list<std::string>::const_iterator ext = headerExtensions.begin();
ext != headerExtensions.end();
++ext)
@@ -654,7 +923,6 @@ void cmQtAutomoc::ParseHeaders(const std::set<std::string>& absHeaders,
const std::map<std::string, std::string>& includedMocs,
std::map<std::string, std::string>& notIncludedMocs)
{
- cmsys::RegularExpression qObjectRegExp("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]");
for(std::set<std::string>::const_iterator hIt=absHeaders.begin();
hIt!=absHeaders.end();
++hIt)
@@ -673,7 +941,7 @@ void cmQtAutomoc::ParseHeaders(const std::set<std::string>& absHeaders,
const std::string currentMoc = "moc_" + basename + ".cpp";
const std::string contents = this->ReadAll(headerName);
- if (qObjectRegExp.find(contents))
+ if (containsQ_OBJECT(contents))
{
//std::cout << "header contains Q_OBJECT macro";
notIncludedMocs[headerName] = currentMoc;
@@ -750,7 +1018,7 @@ bool cmQtAutomoc::GenerateMoc(const std::string& sourceFile,
bool result = cmSystemTools::RunSingleCommand(command, &output, &retVal);
if (!result || retVal)
{
- std::cerr << "AUTOMOC: process for " << mocFilePath << " failed:\n"
+ std::cerr << "AUTOMOC: error: process for " << mocFilePath <<" failed:\n"
<< output << std::endl;
this->RunMocFailed = true;
cmSystemTools::RemoveFile(mocFilePath.c_str());