CMake/Source/cmQtAutoGenerators.cxx

1475 lines
51 KiB
C++
Raw Permalink Normal View History

Simplify CMake per-source license notices Per-source copyright/license notice headers that spell out copyright holder names and years are hard to maintain and often out-of-date or plain wrong. Precise contributor information is already maintained automatically by the version control tool. Ultimately it is the receiver of a file who is responsible for determining its licensing status, and per-source notices are merely a convenience. Therefore it is simpler and more accurate for each source to have a generic notice of the license name and references to more detailed information on copyright holders and full license terms. Our `Copyright.txt` file now contains a list of Contributors whose names appeared source-level copyright notices. It also references version control history for more precise information. Therefore we no longer need to spell out the list of Contributors in each source file notice. Replace CMake per-source copyright/license notice headers with a short description of the license and links to `Copyright.txt` and online information available from "https://cmake.org/licensing". The online URL also handles cases of modules being copied out of our source into other projects, so we can drop our notices about replacing links with full license text. Run the `Utilities/Scripts/filter-notices.bash` script to perform the majority of the replacements mechanically. Manually fix up shebang lines and trailing newlines in a few files. Manually update the notices in a few files that the script does not handle.
2016-09-27 22:01:08 +03:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmQtAutoGenerators.h"
#include "cmAlgorithms.h"
#include "cmFilePathUuid.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmOutputConverter.h"
#include "cmState.h"
#include "cmSystemTools.h"
#include "cm_auto_ptr.hxx"
#include "cmake.h"
#include <algorithm>
#include <assert.h>
#include <cmConfigure.h>
#include <cmsys/FStream.hxx>
#include <cmsys/RegularExpression.hxx>
#include <cmsys/Terminal.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <utility>
#if defined(__APPLE__)
#include <unistd.h>
#endif
2011-08-14 18:43:04 +04:00
static bool requiresMocing(const std::string& text, std::string& macroName)
{
// this simple check is much much faster than the regexp
2016-06-27 23:44:16 +03:00
if (strstr(text.c_str(), "Q_OBJECT") == CM_NULLPTR &&
strstr(text.c_str(), "Q_GADGET") == CM_NULLPTR) {
return false;
}
cmsys::RegularExpression qObjectRegExp("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]");
if (qObjectRegExp.find(text)) {
macroName = "Q_OBJECT";
return true;
}
cmsys::RegularExpression qGadgetRegExp("[\n][ \t]*Q_GADGET[^a-zA-Z0-9_]");
if (qGadgetRegExp.find(text)) {
macroName = "Q_GADGET";
return true;
}
return false;
}
static std::string findMatchingHeader(
const std::string& absPath, const std::string& mocSubDir,
const std::string& basename,
const std::vector<std::string>& headerExtensions)
{
std::string header;
for (std::vector<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;
}
static bool FileNameIsUnique(const std::string& filePath,
const std::map<std::string, std::string>& fileMap)
{
size_t count(0);
const std::string fileName = cmsys::SystemTools::GetFilenameName(filePath);
for (std::map<std::string, std::string>::const_iterator si = fileMap.begin();
si != fileMap.end(); ++si) {
if (cmsys::SystemTools::GetFilenameName(si->first) == fileName) {
++count;
if (count > 1) {
return false;
}
}
}
return true;
}
cmQtAutoGenerators::cmQtAutoGenerators()
: Verbose(cmsys::SystemTools::HasEnv("VERBOSE"))
, ColorOutput(true)
, RunMocFailed(false)
, RunUicFailed(false)
, RunRccFailed(false)
, GenerateAll(false)
{
2011-08-14 18:43:04 +04:00
std::string colorEnv = "";
cmsys::SystemTools::GetEnv("COLOR", colorEnv);
if (!colorEnv.empty()) {
if (cmSystemTools::IsOn(colorEnv.c_str())) {
2011-08-14 18:43:04 +04:00
this->ColorOutput = true;
} else {
2011-08-14 18:43:04 +04:00
this->ColorOutput = false;
}
}
}
void cmQtAutoGenerators::MergeUicOptions(
std::vector<std::string>& opts, const std::vector<std::string>& fileOpts,
bool isQt5)
{
static const char* valueOptions[] = { "tr", "translate",
"postfix", "generator",
"include", // Since Qt 5.3
"g" };
std::vector<std::string> extraOpts;
for (std::vector<std::string>::const_iterator it = fileOpts.begin();
it != fileOpts.end(); ++it) {
std::vector<std::string>::iterator existingIt =
std::find(opts.begin(), opts.end(), *it);
if (existingIt != opts.end()) {
const char* o = it->c_str();
if (*o == '-') {
++o;
}
if (isQt5 && *o == '-') {
++o;
}
if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions),
cmStrCmp(*it)) != cmArrayEnd(valueOptions)) {
assert(existingIt + 1 != opts.end());
*(existingIt + 1) = *(it + 1);
++it;
}
} else {
extraOpts.push_back(*it);
}
}
opts.insert(opts.end(), extraOpts.begin(), extraOpts.end());
}
bool cmQtAutoGenerators::Run(const std::string& targetDirectory,
const std::string& config)
{
bool success = true;
cmake cm;
cm.SetHomeOutputDirectory(targetDirectory);
cm.SetHomeDirectory(targetDirectory);
cm.GetCurrentSnapshot().SetDefaultDefinitions();
cmGlobalGenerator gg(&cm);
cmState::Snapshot snapshot = cm.GetCurrentSnapshot();
snapshot.GetDirectory().SetCurrentBinary(targetDirectory);
snapshot.GetDirectory().SetCurrentSource(targetDirectory);
CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, snapshot));
gg.SetCurrentMakefile(mf.get());
this->ReadAutogenInfoFile(mf.get(), targetDirectory, config);
this->ReadOldMocDefinitionsFile(mf.get(), targetDirectory);
this->Init();
if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") {
success = this->RunAutogen(mf.get());
}
this->WriteOldMocDefinitionsFile(targetDirectory);
return success;
}
bool cmQtAutoGenerators::ReadAutogenInfoFile(
cmMakefile* makefile, const std::string& targetDirectory,
const std::string& config)
{
std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
cmSystemTools::ConvertToUnixSlashes(filename);
filename += "/AutogenInfo.cmake";
if (!makefile->ReadListFile(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");
{
std::string rccSources = makefile->GetSafeDefinition("AM_RCC_SOURCES");
cmSystemTools::ExpandListArgument(rccSources, this->RccSources);
}
this->SkipMoc = makefile->GetSafeDefinition("AM_SKIP_MOC");
this->SkipUic = makefile->GetSafeDefinition("AM_SKIP_UIC");
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->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE");
this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE");
{
std::string compileDefsPropOrig = "AM_MOC_COMPILE_DEFINITIONS";
std::string compileDefsProp = compileDefsPropOrig;
if (!config.empty()) {
compileDefsProp += "_";
compileDefsProp += config;
}
const char* compileDefs = makefile->GetDefinition(compileDefsProp);
this->MocCompileDefinitionsStr = compileDefs
? compileDefs
: makefile->GetSafeDefinition(compileDefsPropOrig);
}
{
std::string includesPropOrig = "AM_MOC_INCLUDES";
std::string includesProp = includesPropOrig;
if (!config.empty()) {
includesProp += "_";
includesProp += config;
}
const char* includes = makefile->GetDefinition(includesProp);
this->MocIncludesStr =
includes ? includes : makefile->GetSafeDefinition(includesPropOrig);
}
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->OriginTargetName =
makefile->GetSafeDefinition("AM_ORIGIN_TARGET_NAME");
{
const char* uicOptionsFiles =
makefile->GetSafeDefinition("AM_UIC_OPTIONS_FILES");
std::string uicOptionsPropOrig = "AM_UIC_TARGET_OPTIONS";
std::string uicOptionsProp = uicOptionsPropOrig;
if (!config.empty()) {
uicOptionsProp += "_";
uicOptionsProp += config;
}
const char* uicTargetOptions = makefile->GetSafeDefinition(uicOptionsProp);
cmSystemTools::ExpandListArgument(
uicTargetOptions ? uicTargetOptions
: makefile->GetSafeDefinition(uicOptionsPropOrig),
this->UicTargetOptions);
const char* uicOptionsOptions =
makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS");
std::vector<std::string> uicFilesVec;
cmSystemTools::ExpandListArgument(uicOptionsFiles, uicFilesVec);
std::vector<std::string> uicOptionsVec;
cmSystemTools::ExpandListArgument(uicOptionsOptions, uicOptionsVec);
if (uicFilesVec.size() != uicOptionsVec.size()) {
return false;
}
for (std::vector<std::string>::iterator fileIt = uicFilesVec.begin(),
optionIt = uicOptionsVec.begin();
fileIt != uicFilesVec.end(); ++fileIt, ++optionIt) {
cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
this->UicOptions[*fileIt] = *optionIt;
}
}
{
const char* rccOptionsFiles =
makefile->GetSafeDefinition("AM_RCC_OPTIONS_FILES");
const char* rccOptionsOptions =
makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS");
std::vector<std::string> rccFilesVec;
cmSystemTools::ExpandListArgument(rccOptionsFiles, rccFilesVec);
std::vector<std::string> rccOptionsVec;
cmSystemTools::ExpandListArgument(rccOptionsOptions, rccOptionsVec);
if (rccFilesVec.size() != rccOptionsVec.size()) {
return false;
}
for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
optionIt = rccOptionsVec.begin();
fileIt != rccFilesVec.end(); ++fileIt, ++optionIt) {
cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
this->RccOptions[*fileIt] = *optionIt;
}
const char* rccInputs = makefile->GetSafeDefinition("AM_RCC_INPUTS");
std::vector<std::string> rccInputLists;
cmSystemTools::ExpandListArgument(rccInputs, rccInputLists);
if (this->RccSources.size() != rccInputLists.size()) {
cmSystemTools::Error("Error processing file: ", filename.c_str());
return false;
}
for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(),
inputIt = rccInputLists.begin();
fileIt != this->RccSources.end(); ++fileIt, ++inputIt) {
cmSystemTools::ReplaceString(*inputIt, "@list_sep@", ";");
std::vector<std::string> rccInputFiles;
cmSystemTools::ExpandListArgument(*inputIt, rccInputFiles);
this->RccInputs[*fileIt] = rccInputFiles;
}
}
this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile);
this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE");
return true;
}
std::string cmQtAutoGenerators::MakeCompileSettingsString(cmMakefile* makefile)
{
std::string s;
s += makefile->GetSafeDefinition("AM_MOC_COMPILE_DEFINITIONS");
s += " ~~~ ";
s += makefile->GetSafeDefinition("AM_MOC_INCLUDES");
s += " ~~~ ";
s += makefile->GetSafeDefinition("AM_MOC_OPTIONS");
s += " ~~~ ";
s += makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE") ? "TRUE"
: "FALSE";
s += " ~~~ ";
return s;
}
bool cmQtAutoGenerators::ReadOldMocDefinitionsFile(
cmMakefile* makefile, const std::string& targetDirectory)
{
std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
cmSystemTools::ConvertToUnixSlashes(filename);
filename += "/AutomocOldMocDefinitions.cmake";
if (makefile->ReadListFile(filename.c_str())) {
this->OldCompileSettingsStr =
makefile->GetSafeDefinition("AM_OLD_COMPILE_SETTINGS");
}
return true;
}
void cmQtAutoGenerators::WriteOldMocDefinitionsFile(
const std::string& targetDirectory)
{
std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
cmSystemTools::ConvertToUnixSlashes(filename);
filename += "/AutomocOldMocDefinitions.cmake";
cmsys::ofstream outfile;
outfile.open(filename.c_str(), std::ios::trunc);
outfile << "set(AM_OLD_COMPILE_SETTINGS "
<< cmOutputConverter::EscapeForCMake(this->CurrentCompileSettingsStr)
<< ")\n";
outfile.close();
}
void cmQtAutoGenerators::Init()
{
this->TargetBuildSubDir = this->TargetName;
this->TargetBuildSubDir += ".dir/";
this->OutMocCppFilenameRel = this->TargetName;
this->OutMocCppFilenameRel += ".cpp";
this->OutMocCppFilenameAbs = this->Builddir + this->OutMocCppFilenameRel;
std::vector<std::string> cdefList;
cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList);
for (std::vector<std::string>::const_iterator it = cdefList.begin();
it != cdefList.end(); ++it) {
this->MocDefinitions.push_back("-D" + (*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 (cmHasLiteralSuffix(path, ".framework/Headers")) {
// Go up twice to get to the framework root
std::vector<std::string> pathComponents;
cmsys::SystemTools::SplitPath(path, 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 (cmsys::SystemTools::StringStartsWith(*it, binDir.c_str())) {
sortedMocIncludes.push_back(*it);
it = this->MocIncludes.erase(it);
} else {
++it;
}
}
it = this->MocIncludes.begin();
while (it != this->MocIncludes.end()) {
if (cmsys::SystemTools::StringStartsWith(*it, srcDir.c_str())) {
sortedMocIncludes.push_back(*it);
it = this->MocIncludes.erase(it);
} else {
++it;
}
}
sortedMocIncludes.insert(sortedMocIncludes.end(),
this->MocIncludes.begin(),
this->MocIncludes.end());
this->MocIncludes = sortedMocIncludes;
}
}
static std::string ReadAll(const std::string& filename)
{
cmsys::ifstream file(filename.c_str());
std::ostringstream stream;
stream << file.rdbuf();
file.close();
return stream.str();
}
bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile)
{
// If settings changed everything needs to be re-generated.
if (this->OldCompileSettingsStr != this->CurrentCompileSettingsStr) {
this->GenerateAll = true;
}
2011-08-10 22:45:22 +04:00
// 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);
const std::vector<std::string>& headerExtensions =
makefile->GetCMakeInstance()->GetHeaderExtensions();
std::map<std::string, std::vector<std::string> > includedUis;
std::map<std::string, std::vector<std::string> > skippedUis;
std::vector<std::string> uicSkipped;
cmSystemTools::ExpandListArgument(this->SkipUic, uicSkipped);
for (std::vector<std::string>::const_iterator it = sourceFiles.begin();
it != sourceFiles.end(); ++it) {
const bool skipUic =
std::find(uicSkipped.begin(), uicSkipped.end(), *it) != uicSkipped.end();
std::map<std::string, std::vector<std::string> >& uiFiles =
skipUic ? skippedUis : includedUis;
const std::string& absFilename = *it;
if (this->Verbose) {
std::ostringstream err;
err << "AUTOGEN: Checking " << absFilename << std::endl;
this->LogInfo(err.str());
}
if (this->RelaxedMode) {
this->ParseCppFile(absFilename, headerExtensions, includedMocs, uiFiles);
} else {
this->StrictParseCppFile(absFilename, headerExtensions, includedMocs,
uiFiles);
}
this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles);
}
{
std::vector<std::string> mocSkipped;
cmSystemTools::ExpandListArgument(this->SkipMoc, mocSkipped);
for (std::vector<std::string>::const_iterator it = mocSkipped.begin();
it != mocSkipped.end(); ++it) {
if (std::find(uicSkipped.begin(), uicSkipped.end(), *it) !=
uicSkipped.end()) {
const std::string& absFilename = *it;
if (this->Verbose) {
std::ostringstream err;
err << "AUTOGEN: Checking " << absFilename << std::endl;
this->LogInfo(err.str());
}
this->ParseForUic(absFilename, includedUis);
}
}
}
std::vector<std::string> headerFilesVec;
cmSystemTools::ExpandListArgument(this->Headers, headerFilesVec);
headerFiles.insert(headerFilesVec.begin(), headerFilesVec.end());
// key = moc source filepath, value = moc output filename
std::map<std::string, std::string> notIncludedMocs;
this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs, includedUis);
if (!this->MocExecutable.empty()) {
this->GenerateMocFiles(includedMocs, notIncludedMocs);
}
if (!this->UicExecutable.empty()) {
this->GenerateUiFiles(includedUis);
}
if (!this->RccExecutable.empty()) {
this->GenerateQrcFiles();
}
if (this->RunMocFailed) {
std::ostringstream err;
err << "moc failed..." << std::endl;
this->LogError(err.str());
return false;
}
if (this->RunUicFailed) {
std::ostringstream err;
err << "uic failed..." << std::endl;
this->LogError(err.str());
return false;
}
if (this->RunRccFailed) {
std::ostringstream err;
err << "rcc failed..." << std::endl;
this->LogError(err.str());
return false;
}
return true;
}
void cmQtAutoGenerators::ParseCppFile(
const std::string& absFilename,
const std::vector<std::string>& headerExtensions,
std::map<std::string, std::string>& includedMocs,
std::map<std::string, std::vector<std::string> >& includedUis)
{
cmsys::RegularExpression mocIncludeRegExp(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
const std::string contentsString = ReadAll(absFilename);
if (contentsString.empty()) {
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename << ": file is empty\n"
<< std::endl;
this->LogError(err.str());
return;
}
this->ParseForUic(absFilename, contentsString, includedUis);
if (this->MocExecutable.empty()) {
return;
}
const std::string absPath = cmsys::SystemTools::GetFilenamePath(
cmsys::SystemTools::GetRealPath(absFilename)) +
'/';
const std::string scannedFileBasename =
cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
std::string macroName;
const bool requiresMoc = requiresMocing(contentsString, macroName);
bool dotMocIncluded = false;
bool mocUnderscoreIncluded = false;
std::string ownMocUnderscoreFile;
std::string ownDotMocFile;
std::string ownMocHeaderFile;
2011-08-19 22:16:53 +04:00
std::string::size_type matchOffset = 0;
2012-11-07 20:13:09 +04:00
// first a simple 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
2016-06-27 23:44:16 +03:00
if ((strstr(contentsString.c_str(), "moc") != CM_NULLPTR) &&
(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 moc_style = cmHasLiteralPrefix(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::ostringstream err;
err << "AUTOGEN: error: " << absFilename << ": The file "
<< "includes the moc file \"" << currentMoc << "\", "
<< "but could not find header \"" << basename << '{'
<< this->JoinExts(headerExtensions) << "}\" ";
if (mocSubDir.empty()) {
err << "in " << absPath << "\n" << std::endl;
} else {
err << "neither in " << absPath << " nor in " << mocSubDir << "\n"
<< std::endl;
}
this->LogError(err.str());
::exit(EXIT_FAILURE);
}
} else {
std::string fileToMoc = absFilename;
if (!requiresMoc || basename != scannedFileBasename) {
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 (!requiresMoc && basename == scannedFileBasename) {
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename
<< ": The file "
"includes the moc file \""
<< currentMoc << "\", but does not contain a " << macroName
<< " macro. Running moc on "
<< "\"" << headerToMoc << "\" ! Include \"moc_" << basename
<< ".cpp\" for a compatibility with "
"strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
<< std::endl;
this->LogError(err.str());
} else {
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename
<< ": The file "
"includes the moc file \""
<< currentMoc << "\" instead of \"moc_" << basename
<< ".cpp\". "
"Running moc on "
<< "\"" << headerToMoc << "\" ! Include \"moc_" << basename
<< ".cpp\" for compatibility with "
"strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
<< std::endl;
this->LogError(err.str());
}
} else {
std::ostringstream err;
err << "AUTOGEN: 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;
this->LogError(err.str());
::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 && requiresMoc) {
if (mocUnderscoreIncluded) {
// this is for KDE4 compatibility:
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename << ": The file "
<< "contains a " << macroName << " macro, but does not "
"include "
<< "\"" << scannedFileBasename << ".moc\", but instead "
"includes "
<< "\"" << ownMocUnderscoreFile << "\". Running moc on "
<< "\"" << absFilename << "\" ! Better include \""
<< scannedFileBasename
<< ".moc\" for compatibility with "
"strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
<< std::endl;
this->LogError(err.str());
includedMocs[absFilename] = ownMocUnderscoreFile;
includedMocs.erase(ownMocHeaderFile);
} else {
// otherwise always error out since it will not compile:
std::ostringstream err;
err << "AUTOGEN: error: " << absFilename << ": The file "
<< "contains a " << macroName << " macro, but does not "
"include "
<< "\"" << scannedFileBasename << ".moc\" !\n"
<< std::endl;
this->LogError(err.str());
::exit(EXIT_FAILURE);
}
}
}
void cmQtAutoGenerators::StrictParseCppFile(
const std::string& absFilename,
const std::vector<std::string>& headerExtensions,
std::map<std::string, std::string>& includedMocs,
std::map<std::string, std::vector<std::string> >& includedUis)
{
cmsys::RegularExpression mocIncludeRegExp(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
const std::string contentsString = ReadAll(absFilename);
if (contentsString.empty()) {
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename << ": file is empty\n"
<< std::endl;
this->LogError(err.str());
return;
}
this->ParseForUic(absFilename, contentsString, includedUis);
if (this->MocExecutable.empty()) {
return;
}
const std::string absPath = cmsys::SystemTools::GetFilenamePath(
cmsys::SystemTools::GetRealPath(absFilename)) +
'/';
const std::string scannedFileBasename =
cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
bool dotMocIncluded = false;
std::string::size_type matchOffset = 0;
2012-11-07 20:13:09 +04:00
// first a simple 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
2016-06-27 23:44:16 +03:00
if ((strstr(contentsString.c_str(), "moc") != CM_NULLPTR) &&
(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 = cmHasLiteralPrefix(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::ostringstream err;
err << "AUTOGEN: error: " << absFilename << " The file "
<< "includes the moc file \"" << currentMoc << "\", "
<< "but could not find header \"" << basename << '{'
<< this->JoinExts(headerExtensions) << "}\" ";
if (mocSubDir.empty()) {
err << "in " << absPath << "\n" << std::endl;
} else {
err << "neither in " << absPath << " nor in " << mocSubDir << "\n"
<< std::endl;
}
this->LogError(err.str());
::exit(EXIT_FAILURE);
}
} else {
if (basename != scannedFileBasename) {
std::ostringstream err;
err << "AUTOGEN: 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;
this->LogError(err.str());
::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.
std::string macroName;
if (!dotMocIncluded && requiresMocing(contentsString, macroName)) {
// otherwise always error out since it will not compile:
std::ostringstream err;
err << "AUTOGEN: error: " << absFilename << ": The file "
<< "contains a " << macroName << " macro, but does not include "
<< "\"" << scannedFileBasename << ".moc\" !\n"
<< std::endl;
this->LogError(err.str());
::exit(EXIT_FAILURE);
}
}
void cmQtAutoGenerators::ParseForUic(
const std::string& absFilename,
std::map<std::string, std::vector<std::string> >& includedUis)
{
if (this->UicExecutable.empty()) {
return;
}
const std::string contentsString = ReadAll(absFilename);
if (contentsString.empty()) {
std::ostringstream err;
err << "AUTOGEN: warning: " << absFilename << ": file is empty\n"
<< std::endl;
this->LogError(err.str());
return;
}
this->ParseForUic(absFilename, contentsString, includedUis);
}
void cmQtAutoGenerators::ParseForUic(
const std::string& absFilename, const std::string& contentsString,
std::map<std::string, std::vector<std::string> >& includedUis)
{
if (this->UicExecutable.empty()) {
return;
}
cmsys::RegularExpression uiIncludeRegExp(
"[\n][ \t]*#[ \t]*include[ \t]+"
"[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
std::string::size_type matchOffset = 0;
const std::string realName = cmsys::SystemTools::GetRealPath(absFilename);
matchOffset = 0;
2016-06-27 23:44:16 +03:00
if ((strstr(contentsString.c_str(), "ui_") != CM_NULLPTR) &&
(uiIncludeRegExp.find(contentsString))) {
do {
const std::string currentUi = uiIncludeRegExp.match(1);
std::string basename =
cmsys::SystemTools::GetFilenameWithoutLastExtension(currentUi);
// basename should be the part of the ui filename used for
// finding the correct header, so we need to remove the ui_ part
basename = basename.substr(3);
includedUis[realName].push_back(basename);
matchOffset += uiIncludeRegExp.end();
} while (uiIncludeRegExp.find(contentsString.c_str() + matchOffset));
}
}
void cmQtAutoGenerators::SearchHeadersForCppFile(
const std::string& absFilename,
const std::vector<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)) +
'/';
for (std::vector<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::vector<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 cmQtAutoGenerators::ParseHeaders(
const std::set<std::string>& absHeaders,
const std::map<std::string, std::string>& includedMocs,
std::map<std::string, std::string>& notIncludedMocs,
std::map<std::string, std::vector<std::string> >& includedUis)
{
cmFilePathUuid fpathUuid(this->Srcdir, this->Builddir,
this->ProjectSourceDir, this->ProjectBinaryDir);
for (std::set<std::string>::const_iterator hIt = absHeaders.begin();
hIt != absHeaders.end(); ++hIt) {
const std::string& headerName = *hIt;
const std::string contents = ReadAll(headerName);
if (!this->MocExecutable.empty() &&
includedMocs.find(headerName) == includedMocs.end()) {
if (this->Verbose) {
std::ostringstream err;
err << "AUTOGEN: Checking " << headerName << std::endl;
this->LogInfo(err.str());
}
std::string macroName;
if (requiresMocing(contents, macroName)) {
notIncludedMocs[headerName] =
this->TargetBuildSubDir + fpathUuid.get(headerName, "moc_", ".cpp");
}
}
this->ParseForUic(headerName, contents, includedUis);
}
}
bool cmQtAutoGenerators::GenerateMocFiles(
const std::map<std::string, std::string>& includedMocs,
const std::map<std::string, std::string>& notIncludedMocs)
{
// look for name collisions
{
std::multimap<std::string, std::string> collisions;
// Test merged map of included and notIncluded
std::map<std::string, std::string> mergedMocs(includedMocs);
mergedMocs.insert(notIncludedMocs.begin(), notIncludedMocs.end());
if (this->NameCollisionTest(mergedMocs, collisions)) {
std::ostringstream err;
err << "AUTOGEN: error: "
"The same moc file will be generated "
"from different sources."
<< std::endl
<< "To avoid this error either" << std::endl
<< "- rename the source files or" << std::endl
<< "- do not include the (moc_NAME.cpp|NAME.moc) file" << std::endl;
this->NameCollisionLog(err.str(), collisions);
::exit(EXIT_FAILURE);
}
}
// generate moc files that are included by source files.
for (std::map<std::string, std::string>::const_iterator it =
includedMocs.begin();
it != includedMocs.end(); ++it) {
if (!this->GenerateMoc(it->first, it->second)) {
if (this->RunMocFailed) {
return false;
}
}
}
// generate moc files that are _not_ included by source files.
bool automocCppChanged = false;
for (std::map<std::string, std::string>::const_iterator it =
notIncludedMocs.begin();
it != notIncludedMocs.end(); ++it) {
if (this->GenerateMoc(it->first, it->second)) {
automocCppChanged = true;
} else {
if (this->RunMocFailed) {
return false;
}
}
}
// compose _automoc.cpp content
std::string automocSource;
{
std::ostringstream outStream;
outStream << "/* This file is autogenerated, do not edit*/\n";
if (notIncludedMocs.empty()) {
outStream << "enum some_compilers { need_more_than_nothing };\n";
} else {
for (std::map<std::string, std::string>::const_iterator it =
notIncludedMocs.begin();
it != notIncludedMocs.end(); ++it) {
outStream << "#include \"" << it->second << "\"\n";
}
}
outStream.flush();
automocSource = outStream.str();
}
// check if we even need to update _automoc.cpp
if (!automocCppChanged) {
// compare contents of the _automoc.cpp file
const std::string oldContents = ReadAll(this->OutMocCppFilenameAbs);
if (oldContents == automocSource) {
// nothing changed: don't touch the _automoc.cpp file
if (this->Verbose) {
std::ostringstream err;
err << "AUTOGEN: " << this->OutMocCppFilenameRel << " still up to date"
<< std::endl;
this->LogInfo(err.str());
}
return true;
}
}
// actually write _automoc.cpp
{
std::string msg = "Generating moc compilation ";
msg += this->OutMocCppFilenameRel;
cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
cmsysTerminal_Color_ForegroundBold,
msg.c_str(), true, this->ColorOutput);
}
{
cmsys::ofstream outfile;
outfile.open(this->OutMocCppFilenameAbs.c_str(), std::ios::trunc);
outfile << automocSource;
outfile.close();
}
return true;
}
bool cmQtAutoGenerators::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, mocFilePath,
&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 moc source ";
2011-08-14 18:43:04 +04:00
msg += mocFileName;
cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
cmsysTerminal_Color_ForegroundBold,
2011-08-14 18:43:04 +04:00
msg.c_str(), true, this->ColorOutput);
std::vector<std::string> command;
command.push_back(this->MocExecutable);
command.insert(command.end(), this->MocIncludes.begin(),
this->MocIncludes.end());
command.insert(command.end(), this->MocDefinitions.begin(),
this->MocDefinitions.end());
command.insert(command.end(), this->MocOptions.begin(),
this->MocOptions.end());
#ifdef _WIN32
command.push_back("-DWIN32");
#endif
command.push_back("-o");
command.push_back(mocFilePath);
command.push_back(sourceFile);
if (this->Verbose) {
this->LogCommand(command);
}
std::string output;
int retVal = 0;
bool result =
cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
if (!result || retVal) {
std::ostringstream err;
err << "AUTOGEN: error: process for " << mocFilePath << " failed:\n"
<< output << std::endl;
this->LogError(err.str());
this->RunMocFailed = true;
cmSystemTools::RemoveFile(mocFilePath);
}
return true;
}
return false;
}
bool cmQtAutoGenerators::GenerateUiFiles(
const std::map<std::string, std::vector<std::string> >& includedUis)
{
// single map with input / output names
std::map<std::string, std::map<std::string, std::string> > uiGenMap;
std::map<std::string, std::string> testMap;
for (std::map<std::string, std::vector<std::string> >::const_iterator it =
includedUis.begin();
it != includedUis.end(); ++it) {
// source file path
std::string sourcePath = cmsys::SystemTools::GetFilenamePath(it->first);
sourcePath += '/';
// insert new map for source file an use new reference
uiGenMap[it->first] = std::map<std::string, std::string>();
std::map<std::string, std::string>& sourceMap = uiGenMap[it->first];
for (std::vector<std::string>::const_iterator sit = it->second.begin();
sit != it->second.end(); ++sit) {
const std::string& uiFileName = *sit;
const std::string uiInputFile = sourcePath + uiFileName + ".ui";
const std::string uiOutputFile = "ui_" + uiFileName + ".h";
sourceMap[uiInputFile] = uiOutputFile;
testMap[uiInputFile] = uiOutputFile;
}
}
// look for name collisions
{
std::multimap<std::string, std::string> collisions;
if (this->NameCollisionTest(testMap, collisions)) {
std::ostringstream err;
err << "AUTOGEN: error: The same ui_NAME.h file will be generated "
"from different sources."
<< std::endl
<< "To avoid this error rename the source files." << std::endl;
this->NameCollisionLog(err.str(), collisions);
::exit(EXIT_FAILURE);
}
}
testMap.clear();
// generate ui files
for (std::map<std::string,
std::map<std::string, std::string> >::const_iterator it =
uiGenMap.begin();
it != uiGenMap.end(); ++it) {
for (std::map<std::string, std::string>::const_iterator sit =
it->second.begin();
sit != it->second.end(); ++sit) {
if (!this->GenerateUi(it->first, sit->first, sit->second)) {
if (this->RunUicFailed) {
return false;
}
}
}
}
return true;
}
bool cmQtAutoGenerators::GenerateUi(const std::string& realName,
const std::string& uiInputFile,
const std::string& uiOutputFile)
{
if (!cmsys::SystemTools::FileExists(this->Builddir.c_str(), false)) {
cmsys::SystemTools::MakeDirectory(this->Builddir.c_str());
}
const std::string uiBuildFile = this->Builddir + uiOutputFile;
int sourceNewerThanUi = 0;
bool success = cmsys::SystemTools::FileTimeCompare(uiInputFile, uiBuildFile,
&sourceNewerThanUi);
if (this->GenerateAll || !success || sourceNewerThanUi >= 0) {
std::string msg = "Generating ui header ";
msg += uiOutputFile;
cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
cmsysTerminal_Color_ForegroundBold,
msg.c_str(), true, this->ColorOutput);
std::vector<std::string> command;
command.push_back(this->UicExecutable);
std::vector<std::string> opts = this->UicTargetOptions;
std::map<std::string, std::string>::const_iterator optionIt =
this->UicOptions.find(uiInputFile);
if (optionIt != this->UicOptions.end()) {
std::vector<std::string> fileOpts;
cmSystemTools::ExpandListArgument(optionIt->second, fileOpts);
2015-09-20 17:40:49 +03:00
cmQtAutoGenerators::MergeUicOptions(opts, fileOpts,
this->QtMajorVersion == "5");
}
command.insert(command.end(), opts.begin(), opts.end());
command.push_back("-o");
command.push_back(uiBuildFile);
command.push_back(uiInputFile);
if (this->Verbose) {
this->LogCommand(command);
}
std::string output;
int retVal = 0;
bool result =
cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
if (!result || retVal) {
std::ostringstream err;
err << "AUTOUIC: error: process for " << uiOutputFile
<< " needed by\n \"" << realName << "\"\nfailed:\n"
<< output << std::endl;
this->LogError(err.str());
this->RunUicFailed = true;
cmSystemTools::RemoveFile(uiOutputFile);
return false;
}
return true;
}
return false;
}
bool cmQtAutoGenerators::InputFilesNewerThanQrc(const std::string& qrcFile,
const std::string& rccOutput)
{
std::vector<std::string> const& files = this->RccInputs[qrcFile];
for (std::vector<std::string>::const_iterator it = files.begin();
it != files.end(); ++it) {
int inputNewerThanQrc = 0;
bool success =
cmsys::SystemTools::FileTimeCompare(*it, rccOutput, &inputNewerThanQrc);
if (!success || inputNewerThanQrc >= 0) {
return true;
}
}
return false;
}
bool cmQtAutoGenerators::GenerateQrcFiles()
{
// generate single map with input / output names
std::map<std::string, std::string> qrcGenMap;
{
cmFilePathUuid fpathUuid(this->Srcdir, this->Builddir,
this->ProjectSourceDir, this->ProjectBinaryDir);
for (std::vector<std::string>::const_iterator si =
this->RccSources.begin();
si != this->RccSources.end(); ++si) {
const std::string ext =
cmsys::SystemTools::GetFilenameLastExtension(*si);
if (ext == ".qrc") {
qrcGenMap[*si] =
(this->TargetBuildSubDir + fpathUuid.get(*si, "qrc_", ".cpp"));
}
}
}
// look for name collisions
{
std::multimap<std::string, std::string> collisions;
if (this->NameCollisionTest(qrcGenMap, collisions)) {
std::ostringstream err;
err << "AUTOGEN: error: The same qrc_NAME.cpp file"
" will be generated from different sources."
<< std::endl
<< "To avoid this error rename the source .qrc files." << std::endl;
this->NameCollisionLog(err.str(), collisions);
::exit(EXIT_FAILURE);
}
}
// generate qrc files
for (std::map<std::string, std::string>::const_iterator si =
qrcGenMap.begin();
si != qrcGenMap.end(); ++si) {
bool unique = FileNameIsUnique(si->first, qrcGenMap);
if (!this->GenerateQrc(si->first, si->second, unique)) {
if (this->RunRccFailed) {
return false;
}
}
}
return true;
}
bool cmQtAutoGenerators::GenerateQrc(const std::string& qrcInputFile,
const std::string& qrcOutputFile,
bool unique_n)
{
std::string symbolName;
if (unique_n) {
symbolName =
cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcInputFile);
} else {
symbolName =
cmsys::SystemTools::GetFilenameWithoutLastExtension(qrcOutputFile);
// Remove "qrc_" at string begin
symbolName.erase(0, 4);
}
// Replace '-' with '_'. The former is valid for
// file names but not for symbol names.
std::replace(symbolName.begin(), symbolName.end(), '-', '_');
const std::string qrcBuildFile = this->Builddir + qrcOutputFile;
int sourceNewerThanQrc = 0;
bool generateQrc = !cmsys::SystemTools::FileTimeCompare(
qrcInputFile, qrcBuildFile, &sourceNewerThanQrc);
generateQrc = generateQrc || (sourceNewerThanQrc >= 0);
generateQrc =
generateQrc || this->InputFilesNewerThanQrc(qrcInputFile, qrcBuildFile);
if (this->GenerateAll || generateQrc) {
std::string msg = "Generating qrc source ";
msg += qrcOutputFile;
cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue |
cmsysTerminal_Color_ForegroundBold,
msg.c_str(), true, this->ColorOutput);
std::vector<std::string> command;
command.push_back(this->RccExecutable);
std::map<std::string, std::string>::const_iterator optionIt =
this->RccOptions.find(qrcInputFile);
if (optionIt != this->RccOptions.end()) {
cmSystemTools::ExpandListArgument(optionIt->second, command);
}
command.push_back("-name");
command.push_back(symbolName);
command.push_back("-o");
command.push_back(qrcBuildFile);
command.push_back(qrcInputFile);
if (this->Verbose) {
this->LogCommand(command);
}
std::string output;
int retVal = 0;
bool result =
cmSystemTools::RunSingleCommand(command, &output, &output, &retVal);
if (!result || retVal) {
std::ostringstream err;
err << "AUTORCC: error: process for " << qrcOutputFile << " failed:\n"
<< output << std::endl;
this->LogError(err.str());
this->RunRccFailed = true;
cmSystemTools::RemoveFile(qrcBuildFile);
return false;
}
}
return true;
}
/**
* @brief Collects name collisions as output/input pairs
* @return True if there were collisions
*/
bool cmQtAutoGenerators::NameCollisionTest(
const std::map<std::string, std::string>& genFiles,
std::multimap<std::string, std::string>& collisions)
{
typedef std::map<std::string, std::string>::const_iterator Iter;
typedef std::map<std::string, std::string>::value_type VType;
for (Iter ait = genFiles.begin(); ait != genFiles.end(); ++ait) {
bool first_match(true);
for (Iter bit = (++Iter(ait)); bit != genFiles.end(); ++bit) {
if (ait->second == bit->second) {
if (first_match) {
if (collisions.find(ait->second) != collisions.end()) {
// We already know of this collision from before
break;
}
collisions.insert(VType(ait->second, ait->first));
first_match = false;
}
collisions.insert(VType(bit->second, bit->first));
}
}
}
return !collisions.empty();
}
void cmQtAutoGenerators::NameCollisionLog(
const std::string& message,
const std::multimap<std::string, std::string>& collisions)
{
typedef std::multimap<std::string, std::string>::const_iterator Iter;
std::ostringstream err;
// Add message
err << message;
// Append collision list
for (Iter it = collisions.begin(); it != collisions.end(); ++it) {
err << it->first << " : " << it->second << std::endl;
}
this->LogError(err.str());
}
void cmQtAutoGenerators::LogInfo(const std::string& message)
{
std::cout << message;
}
void cmQtAutoGenerators::LogError(const std::string& message)
{
std::cerr << message;
}
void cmQtAutoGenerators::LogCommand(const std::vector<std::string>& command)
{
std::ostringstream sbuf;
for (std::vector<std::string>::const_iterator cmdIt = command.begin();
cmdIt != command.end(); ++cmdIt) {
if (cmdIt != command.begin()) {
sbuf << " ";
}
sbuf << *cmdIt;
}
if (!sbuf.str().empty()) {
sbuf << std::endl;
this->LogInfo(sbuf.str());
}
}
std::string cmQtAutoGenerators::JoinExts(const std::vector<std::string>& lst)
{
if (lst.empty()) {
return "";
}
std::string result;
std::string separator = ",";
for (std::vector<std::string>::const_iterator it = lst.begin();
it != lst.end(); ++it) {
if (it != lst.begin()) {
result += separator;
}
result += '.' + (*it);
}
result.erase(result.end() - 1);
return result;
}