diff options
Diffstat (limited to 'Source/cmQtAutomoc.cxx')
-rw-r--r-- | Source/cmQtAutomoc.cxx | 1086 |
1 files changed, 1086 insertions, 0 deletions
diff --git a/Source/cmQtAutomoc.cxx b/Source/cmQtAutomoc.cxx new file mode 100644 index 000000000..65ecdf73f --- /dev/null +++ b/Source/cmQtAutomoc.cxx @@ -0,0 +1,1086 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2004-2011 Kitware, Inc. + Copyright 2011 Alexander Neundorf (neundorf@kde.org) + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmSystemTools.h" + +#include <cmsys/Terminal.h> +#include <cmsys/ios/sstream> + +#include <string.h> +#if defined(__APPLE__) +#include <unistd.h> +#endif + +#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) +,RunMocFailed(false) +,GenerateAll(false) +{ + + std::string colorEnv = ""; + cmsys::SystemTools::GetEnv("COLOR", colorEnv); + if(!colorEnv.empty()) + { + if(cmSystemTools::IsOn(colorEnv.c_str())) + { + this->ColorOutput = true; + } + else + { + this->ColorOutput = false; + } + } +} + + +void cmQtAutomoc::SetupAutomocTarget(cmTarget* target) +{ + cmMakefile* makefile = target->GetMakefile(); + const char* targetName = target->GetName(); + // 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 relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE"); + + // create a custom target for running automoc at buildtime: + std::string automocTargetName = targetName; + automocTargetName += "_automoc"; + + std::string targetDir = makefile->GetCurrentOutputDirectory(); + targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); + targetDir += "/"; + targetDir += automocTargetName; + targetDir += ".dir/"; + + cmCustomCommandLine currentLine; + currentLine.push_back(makefile->GetSafeDefinition("CMAKE_COMMAND")); + currentLine.push_back("-E"); + currentLine.push_back("cmake_automoc"); + currentLine.push_back(targetDir); + + cmCustomCommandLines commandLines; + commandLines.push_back(currentLine); + + std::string workingDirectory = cmSystemTools::CollapseFullPath( + "", makefile->GetCurrentOutputDirectory()); + + std::vector<std::string> depends; + std::string automocComment = "Automoc for target "; + automocComment += targetName; + + makefile->AddUtilityCommand(automocTargetName.c_str(), true, + workingDirectory.c_str(), depends, + commandLines, false, automocComment.c_str()); + target->AddUtility(automocTargetName.c_str()); + + // configure a file to get all information to automoc at buildtime: + std::string _moc_files; + std::string _moc_headers; + const char* sepFiles = ""; + const char* sepHeaders = ""; + + const std::vector<cmSourceFile*>& srcFiles = target->GetSourceFiles(); + + for(std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); + fileIt != srcFiles.end(); + ++fileIt) + { + cmSourceFile* sf = *fileIt; + std::string absFile = sf->GetFullPath(); + bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC")); + bool generated = cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED")); + + if ((skip==false) && (generated == false)) + { + std::string ext = sf->GetExtension(); + cmSystemTools::FileFormat fileType = cmSystemTools::GetFileFormat( + ext.c_str()); + if (fileType == cmSystemTools::CXX_FILE_FORMAT) + { + _moc_files += sepFiles; + _moc_files += absFile; + sepFiles = ";"; + } + else if (fileType == cmSystemTools::HEADER_FILE_FORMAT) + { + _moc_headers += sepHeaders; + _moc_headers += absFile; + sepHeaders = ";"; + } + } + } + + const char* tmp = makefile->GetProperty("INCLUDE_DIRECTORIES"); + std::string _moc_incs = (tmp!=0 ? tmp : ""); + tmp = makefile->GetProperty("DEFINITIONS"); + std::string _moc_defs = (tmp!=0 ? tmp : ""); + tmp = makefile->GetProperty("COMPILE_DEFINITIONS"); + std::string _moc_compile_defs = (tmp!=0 ? tmp : ""); + tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS"); + std::string _moc_options = (tmp!=0 ? tmp : ""); + + // forget the variables added here afterwards again: + cmMakefile::ScopePushPop varScope(makefile); + static_cast<void>(varScope); + + makefile->AddDefinition("_moc_target_name", + cmLocalGenerator::EscapeForCMake(automocTargetName.c_str()).c_str()); + makefile->AddDefinition("_moc_incs", + cmLocalGenerator::EscapeForCMake(_moc_incs.c_str()).c_str()); + makefile->AddDefinition("_moc_defs", + cmLocalGenerator::EscapeForCMake(_moc_defs.c_str()).c_str()); + makefile->AddDefinition("_moc_compile_defs", + cmLocalGenerator::EscapeForCMake(_moc_compile_defs.c_str()).c_str()); + makefile->AddDefinition("_moc_options", + cmLocalGenerator::EscapeForCMake(_moc_options.c_str()).c_str()); + makefile->AddDefinition("_moc_files", + cmLocalGenerator::EscapeForCMake(_moc_files.c_str()).c_str()); + makefile->AddDefinition("_moc_headers", + cmLocalGenerator::EscapeForCMake(_moc_headers.c_str()).c_str()); + makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE"); + + const char* cmakeRoot = makefile->GetSafeDefinition("CMAKE_ROOT"); + std::string inputFile = cmakeRoot; + inputFile += "/Modules/AutomocInfo.cmake.in"; + std::string outputFile = targetDir; + outputFile += "/AutomocInfo.cmake"; + makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), + false, true, false); + + std::string mocCppFile = makefile->GetCurrentOutputDirectory(); + mocCppFile += "/"; + mocCppFile += automocTargetName; + mocCppFile += ".cpp"; + cmSourceFile* mocCppSource = makefile->GetOrCreateSource(mocCppFile.c_str(), + true); + target->AddSourceFile(mocCppSource); + + makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", + mocCppFile.c_str(), false); +} + + +bool cmQtAutomoc::Run(const char* targetDirectory) +{ + bool success = true; + cmake cm; + cmGlobalGenerator* gg = this->CreateGlobalGenerator(&cm, targetDirectory); + cmMakefile* makefile = gg->GetCurrentLocalGenerator()->GetMakefile(); + + this->ReadAutomocInfoFile(makefile, targetDirectory); + this->ReadOldMocDefinitionsFile(makefile, targetDirectory); + + this->Init(); + + if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") + { + success = this->RunAutomoc(); + } + + this->WriteOldMocDefinitionsFile(targetDirectory); + + delete gg; + gg = NULL; + makefile = NULL; + return success; +} + + +cmGlobalGenerator* cmQtAutomoc::CreateGlobalGenerator(cmake* cm, + const char* targetDirectory) +{ + cmGlobalGenerator* gg = new cmGlobalGenerator(); + gg->SetCMakeInstance(cm); + + cmLocalGenerator* lg = gg->CreateLocalGenerator(); + lg->GetMakefile()->SetHomeOutputDirectory(targetDirectory); + lg->GetMakefile()->SetStartOutputDirectory(targetDirectory); + lg->GetMakefile()->SetHomeDirectory(targetDirectory); + lg->GetMakefile()->SetStartDirectory(targetDirectory); + gg->SetCurrentLocalGenerator(lg); + + return gg; +} + + +bool cmQtAutomoc::ReadAutomocInfoFile(cmMakefile* makefile, + const char* targetDirectory) +{ + std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); + cmSystemTools::ConvertToUnixSlashes(filename); + filename += "/AutomocInfo.cmake"; + + if (!makefile->ReadListFile(0, filename.c_str())) + { + cmSystemTools::Error("Error processing file:", filename.c_str()); + return false; + } + + 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( + "AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); + this->Srcdir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR"); + this->Builddir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR"); + this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE"); + this->MocCompileDefinitionsStr = makefile->GetSafeDefinition( + "AM_MOC_COMPILE_DEFINITIONS"); + this->MocDefinitionsStr = makefile->GetSafeDefinition("AM_MOC_DEFINITIONS"); + this->MocIncludesStr = makefile->GetSafeDefinition("AM_MOC_INCLUDES"); + this->MocOptionsStr = makefile->GetSafeDefinition("AM_MOC_OPTIONS"); + this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR"); + this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR"); + this->TargetName = makefile->GetSafeDefinition("AM_TARGET_NAME"); + + this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE"); + + return true; +} + + +bool cmQtAutomoc::ReadOldMocDefinitionsFile(cmMakefile* makefile, + const char* targetDirectory) +{ + std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); + cmSystemTools::ConvertToUnixSlashes(filename); + filename += "/AutomocOldMocDefinitions.cmake"; + + if (makefile->ReadListFile(0, filename.c_str())) + { + this->OldMocDefinitionsStr = + makefile->GetSafeDefinition("AM_OLD_MOC_DEFINITIONS"); + } + return true; +} + + +void cmQtAutomoc::WriteOldMocDefinitionsFile(const char* targetDirectory) +{ + std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); + cmSystemTools::ConvertToUnixSlashes(filename); + filename += "/AutomocOldMocDefinitions.cmake"; + + std::fstream outfile; + outfile.open(filename.c_str(), + std::ios::out | std::ios::trunc); + outfile << "set(AM_OLD_MOC_DEFINITIONS " + << cmLocalGenerator::EscapeForCMake( + this->Join(this->MocDefinitions, ' ').c_str()) << ")\n"; + + outfile.close(); +} + + +void cmQtAutomoc::Init() +{ + this->OutMocCppFilename = this->Builddir; + this->OutMocCppFilename += this->TargetName; + this->OutMocCppFilename += ".cpp"; + + std::vector<std::string> cdefList; + cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList); + if (!cdefList.empty()) + { + for(std::vector<std::string>::const_iterator it = cdefList.begin(); + it != cdefList.end(); + ++it) + { + this->MocDefinitions.push_back("-D" + (*it)); + } + } + else + { + std::string tmpMocDefs = this->MocDefinitionsStr; + cmSystemTools::ReplaceString(tmpMocDefs, " ", ";"); + + std::vector<std::string> defList; + cmSystemTools::ExpandListArgument(tmpMocDefs, defList); + + for(std::vector<std::string>::const_iterator it = defList.begin(); + it != defList.end(); + ++it) + { + if (this->StartsWith(*it, "-D")) + { + this->MocDefinitions.push_back(*it); + } + } + } + + cmSystemTools::ExpandListArgument(this->MocOptionsStr, this->MocOptions); + + std::vector<std::string> incPaths; + cmSystemTools::ExpandListArgument(this->MocIncludesStr, incPaths); + + std::set<std::string> frameworkPaths; + for(std::vector<std::string>::const_iterator it = incPaths.begin(); + it != incPaths.end(); + ++it) + { + const std::string &path = *it; + this->MocIncludes.push_back("-I" + path); + if (this->EndsWith(path, ".framework/Headers")) + { + // Go up twice to get to the framework root + std::vector<std::string> pathComponents; + cmsys::SystemTools::SplitPath(path.c_str(), pathComponents); + std::string frameworkPath =cmsys::SystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2); + frameworkPaths.insert(frameworkPath); + } + } + + for (std::set<std::string>::const_iterator it = frameworkPaths.begin(); + it != frameworkPaths.end(); ++it) + { + this->MocIncludes.push_back("-F"); + this->MocIncludes.push_back(*it); + } + + + if (this->IncludeProjectDirsBefore) + { + const std::string &binDir = "-I" + this->ProjectBinaryDir; + + const std::string srcDir = "-I" + this->ProjectSourceDir; + + std::list<std::string> sortedMocIncludes; + std::list<std::string>::iterator it = this->MocIncludes.begin(); + while (it != this->MocIncludes.end()) + { + if (this->StartsWith(*it, binDir)) + { + sortedMocIncludes.push_back(*it); + it = this->MocIncludes.erase(it); + } + else + { + ++it; + } + } + it = this->MocIncludes.begin(); + while (it != this->MocIncludes.end()) + { + if (this->StartsWith(*it, srcDir)) + { + sortedMocIncludes.push_back(*it); + it = this->MocIncludes.erase(it); + } + else + { + ++it; + } + } + sortedMocIncludes.insert(sortedMocIncludes.end(), + this->MocIncludes.begin(), this->MocIncludes.end()); + this->MocIncludes = sortedMocIncludes; + } + +} + + +bool cmQtAutomoc::RunAutomoc() +{ + if (!cmsys::SystemTools::FileExists(this->OutMocCppFilename.c_str()) + || (this->OldMocDefinitionsStr != this->Join(this->MocDefinitions, ' '))) + { + this->GenerateAll = true; + } + + // the program goes through all .cpp files to see which moc files are + // included. It is not really interesting how the moc file is named, but + // what file the moc is created from. Once a moc is included the same moc + // may not be included in the _automoc.cpp file anymore. OTOH if there's a + // header containing Q_OBJECT where no corresponding moc file is included + // anywhere a moc_<filename>.cpp file is created and included in + // the _automoc.cpp file. + + // key = moc source filepath, value = moc output filepath + std::map<std::string, std::string> includedMocs; + // collect all headers which may need to be mocced + std::set<std::string> headerFiles; + + 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) + { + const std::string &absFilename = *it; + if (this->Verbose) + { + std::cout << "AUTOMOC: Checking " << absFilename << std::endl; + } + if (this->RelaxedMode) + { + this->ParseCppFile(absFilename, headerExtensions, includedMocs); + } + else + { + this->StrictParseCppFile(absFilename, headerExtensions, includedMocs); + } + this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles); + } + + std::vector<std::string> headerFilesVec; + cmSystemTools::ExpandListArgument(this->Headers, headerFilesVec); + for (std::vector<std::string>::const_iterator it = headerFilesVec.begin(); + it != headerFilesVec.end(); + ++it) + { + headerFiles.insert(*it); + } + + // key = moc source filepath, value = moc output filename + std::map<std::string, std::string> notIncludedMocs; + this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs); + + // run moc on all the moc's that are #included in source files + for(std::map<std::string, std::string>::const_iterator + it = includedMocs.begin(); + it != includedMocs.end(); + ++it) + { + this->GenerateMoc(it->first, it->second); + } + + cmsys_ios::stringstream outStream; + outStream << "/* This file is autogenerated, do not edit*/\n"; + + bool automocCppChanged = false; + if (notIncludedMocs.empty()) + { + outStream << "enum some_compilers { need_more_than_nothing };\n"; + } + else + { + // run moc on the remaining headers and include them in + // the _automoc.cpp file + for(std::map<std::string, std::string>::const_iterator + it = notIncludedMocs.begin(); + it != notIncludedMocs.end(); + ++it) + { + bool mocSuccess = this->GenerateMoc(it->first, it->second); + if (mocSuccess) + { + automocCppChanged = true; + } + outStream << "#include \"" << it->second << "\"\n"; + } + } + + if (this->RunMocFailed) + { + std::cerr << "moc failed..."<< std::endl; + return false; + } + outStream.flush(); + std::string automocSource = outStream.str(); + if (!automocCppChanged) + { + // compare contents of the _automoc.cpp file + const std::string oldContents = this->ReadAll(this->OutMocCppFilename); + if (oldContents == automocSource) + { + // nothing changed: don't touch the _automoc.cpp file + return true; + } + } + + // source file that includes all remaining moc files (_automoc.cpp file) + std::fstream outfile; + outfile.open(this->OutMocCppFilename.c_str(), + std::ios::out | std::ios::trunc); + outfile << automocSource; + outfile.close(); + + return true; +} + + +void cmQtAutomoc::ParseCppFile(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); + 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; + // 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::cout << "found moc include: " << currentMoc << std::endl; + + std::string basename = cmsys::SystemTools:: + GetFilenameWithoutLastExtension(currentMoc); + const bool moc_style = 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. + // Q_OBJECT + if (moc_style) + { + // 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; + if (basename == scannedFileBasename) + { + mocUnderscoreIncluded = true; + ownMocUnderscoreFile = currentMoc; + ownMocHeaderFile = headerToMoc; + } + } + 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 + { + std::string fileToMoc = absFilename; + if ((basename != scannedFileBasename) || (cppContainsQ_OBJECT==false)) + { + std::string mocSubDir = extractSubDir(absPath, currentMoc); + std::string headerToMoc = findMatchingHeader( + absPath, mocSubDir, basename, headerExtensions); + if (!headerToMoc.empty()) + { + // this is for KDE4 compatibility: + fileToMoc = headerToMoc; + if ((cppContainsQ_OBJECT==false) &&(basename==scannedFileBasename)) + { + std::cerr << "AUTOMOC: warning: " << absFilename << ": The file " + "includes the moc file \"" << currentMoc << + "\", but does not contain a Q_OBJECT macro. " + "Running moc on " + << "\"" << headerToMoc << "\" ! Include \"moc_" + << basename << ".cpp\" for a compatiblity with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n" + << std::endl; + } + else + { + std::cerr << "AUTOMOC: warning: " << absFilename << ": The file " + "includes the moc file \"" << currentMoc << + "\" instead of \"moc_" << basename << ".cpp\". " + "Running moc on " + << "\"" << headerToMoc << "\" ! Include \"moc_" + << basename << ".cpp\" for compatiblity with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n" + << std::endl; + } + } + else + { + 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 compatiblity with " + "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\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) + { + const std::string headerName = absPath + basename + (*ext); + if (cmsys::SystemTools::FileExists(headerName.c_str())) + { + absHeaders.insert(headerName); + break; + } + } + for(std::list<std::string>::const_iterator ext = headerExtensions.begin(); + ext != headerExtensions.end(); + ++ext) + { + const std::string privateHeaderName = absPath+basename+"_p"+(*ext); + if (cmsys::SystemTools::FileExists(privateHeaderName.c_str())) + { + absHeaders.insert(privateHeaderName); + break; + } + } + +} + + +void cmQtAutomoc::ParseHeaders(const std::set<std::string>& absHeaders, + const std::map<std::string, std::string>& includedMocs, + std::map<std::string, std::string>& notIncludedMocs) +{ + for(std::set<std::string>::const_iterator hIt=absHeaders.begin(); + hIt!=absHeaders.end(); + ++hIt) + { + const std::string& headerName = *hIt; + + if (includedMocs.find(headerName) == includedMocs.end()) + { + if (this->Verbose) + { + std::cout << "AUTOMOC: Checking " << headerName << std::endl; + } + + const std::string basename = cmsys::SystemTools:: + GetFilenameWithoutLastExtension(headerName); + + const std::string currentMoc = "moc_" + basename + ".cpp"; + const std::string contents = this->ReadAll(headerName); + if (containsQ_OBJECT(contents)) + { + //std::cout << "header contains Q_OBJECT macro"; + notIncludedMocs[headerName] = currentMoc; + } + } + } + +} + + +bool cmQtAutomoc::GenerateMoc(const std::string& sourceFile, + const std::string& mocFileName) +{ + const std::string mocFilePath = this->Builddir + mocFileName; + int sourceNewerThanMoc = 0; + bool success = cmsys::SystemTools::FileTimeCompare(sourceFile.c_str(), + mocFilePath.c_str(), + &sourceNewerThanMoc); + if (this->GenerateAll || !success || sourceNewerThanMoc >= 0) + { + // make sure the directory for the resulting moc file exists + std::string mocDir = mocFilePath.substr(0, mocFilePath.rfind('/')); + if (!cmsys::SystemTools::FileExists(mocDir.c_str(), false)) + { + cmsys::SystemTools::MakeDirectory(mocDir.c_str()); + } + + std::string msg = "Generating "; + msg += mocFileName; + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue + |cmsysTerminal_Color_ForegroundBold, + msg.c_str(), true, this->ColorOutput); + + std::vector<cmStdString> command; + command.push_back(this->MocExecutable); + for (std::list<std::string>::const_iterator it = this->MocIncludes.begin(); + it != this->MocIncludes.end(); + ++it) + { + command.push_back(*it); + } + for(std::list<std::string>::const_iterator it=this->MocDefinitions.begin(); + it != this->MocDefinitions.end(); + ++it) + { + command.push_back(*it); + } + for(std::vector<std::string>::const_iterator it=this->MocOptions.begin(); + it != this->MocOptions.end(); + ++it) + { + command.push_back(*it); + } +#ifdef _WIN32 + command.push_back("-DWIN32"); +#endif + command.push_back("-o"); + command.push_back(mocFilePath); + command.push_back(sourceFile); + + if (this->Verbose) + { + for(std::vector<cmStdString>::const_iterator cmdIt = command.begin(); + cmdIt != command.end(); + ++cmdIt) + { + std::cout << *cmdIt << " "; + } + std::cout << std::endl; + } + + std::string output; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand(command, &output, &retVal); + if (!result || retVal) + { + std::cerr << "AUTOMOC: error: process for " << mocFilePath <<" failed:\n" + << output << std::endl; + this->RunMocFailed = true; + cmSystemTools::RemoveFile(mocFilePath.c_str()); + } + return true; + } + return false; +} + + +std::string cmQtAutomoc::Join(const std::list<std::string>& lst,char separator) +{ + if (lst.empty()) + { + return ""; + } + + std::string result; + for (std::list<std::string>::const_iterator it = lst.begin(); + it != lst.end(); + ++it) + { + result += (*it) + separator; + } + result.erase(result.end() - 1); + return result; +} + + +bool cmQtAutomoc::StartsWith(const std::string& str, const std::string& with) +{ + return (str.substr(0, with.length()) == with); +} + + +bool cmQtAutomoc::EndsWith(const std::string& str, const std::string& with) +{ + if (with.length() > (str.length())) + { + return false; + } + return (str.substr(str.length() - with.length(), with.length()) == with); +} + + +std::string cmQtAutomoc::ReadAll(const std::string& filename) +{ + std::ifstream file(filename.c_str()); + cmsys_ios::stringstream stream; + stream << file.rdbuf(); + file.close(); + return stream.str(); +} |