packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmQtAutomoc.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2004-2011 Kitware, Inc.
4   Copyright 2011 Alexander Neundorf (neundorf@kde.org)
5
6   Distributed under the OSI-approved BSD License (the "License");
7   see accompanying file Copyright.txt for details.
8
9   This software is distributed WITHOUT ANY WARRANTY; without even the
10   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11   See the License for more information.
12 ============================================================================*/
13
14 #include "cmGlobalGenerator.h"
15 #include "cmLocalGenerator.h"
16 #include "cmMakefile.h"
17 #include "cmSourceFile.h"
18 #include "cmSystemTools.h"
19
20 #if defined(_WIN32) && !defined(__CYGWIN__)
21 # include "cmLocalVisualStudioGenerator.h"
22 #endif
23
24 #include <cmsys/Terminal.h>
25 #include <cmsys/ios/sstream>
26
27 #include <string.h>
28 #if defined(__APPLE__)
29 #include <unistd.h>
30 #endif
31
32 #include "cmQtAutomoc.h"
33
34
35 static bool containsQ_OBJECT(const std::string& text)
36 {
37   // this simple check is much much faster than the regexp
38   if (strstr(text.c_str(), "Q_OBJECT") == NULL)
39     {
40     return false;
41     }
42
43   cmsys::RegularExpression qObjectRegExp("[\n][ \t]*Q_OBJECT[^a-zA-Z0-9_]");
44   return qObjectRegExp.find(text);
45 }
46
47
48 static std::string findMatchingHeader(const std::string& absPath,
49                                       const std::string& mocSubDir,
50                                       const std::string& basename,
51                               const std::vector<std::string>& headerExtensions)
52 {
53   std::string header;
54   for(std::vector<std::string>::const_iterator ext = headerExtensions.begin();
55       ext != headerExtensions.end();
56       ++ext)
57     {
58     std::string sourceFilePath = absPath + basename + "." + (*ext);
59     if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
60       {
61       header = sourceFilePath;
62       break;
63       }
64     if (!mocSubDir.empty())
65       {
66       sourceFilePath = mocSubDir + basename + "." + (*ext);
67       if (cmsys::SystemTools::FileExists(sourceFilePath.c_str()))
68         {
69         header = sourceFilePath;
70         break;
71         }
72       }
73     }
74
75   return header;
76 }
77
78
79 static std::string extractSubDir(const std::string& absPath,
80                                  const std::string& currentMoc)
81 {
82   std::string subDir;
83   if (currentMoc.find_first_of('/') != std::string::npos)
84     {
85     subDir = absPath
86                   + cmsys::SystemTools::GetFilenamePath(currentMoc) + '/';
87     }
88   return subDir;
89 }
90
91
92 static void copyTargetProperty(cmTarget* destinationTarget,
93                                cmTarget* sourceTarget,
94                                const char* propertyName)
95 {
96   const char* propertyValue = sourceTarget->GetProperty(propertyName);
97   if (propertyValue)
98     {
99     destinationTarget->SetProperty(propertyName, propertyValue);
100     }
101 }
102
103
104 cmQtAutomoc::cmQtAutomoc()
105 :Verbose(cmsys::SystemTools::GetEnv("VERBOSE") != 0)
106 ,ColorOutput(true)
107 ,RunMocFailed(false)
108 ,GenerateAll(false)
109 {
110
111   std::string colorEnv = "";
112   cmsys::SystemTools::GetEnv("COLOR", colorEnv);
113   if(!colorEnv.empty())
114     {
115     if(cmSystemTools::IsOn(colorEnv.c_str()))
116       {
117       this->ColorOutput = true;
118       }
119     else
120       {
121       this->ColorOutput = false;
122       }
123     }
124 }
125
126 bool cmQtAutomoc::InitializeMocSourceFile(cmTarget* target)
127 {
128   cmMakefile* makefile = target->GetMakefile();
129   // don't do anything if there is no Qt4 or Qt5Core (which contains moc):
130   std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
131   if (qtMajorVersion == "")
132     {
133     qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
134     }
135   if (qtMajorVersion != "4" && qtMajorVersion != "5")
136     {
137     return false;
138     }
139
140   std::string automocTargetName = target->GetName();
141   automocTargetName += "_automoc";
142   std::string mocCppFile = makefile->GetCurrentOutputDirectory();
143   mocCppFile += "/";
144   mocCppFile += automocTargetName;
145   mocCppFile += ".cpp";
146   cmSourceFile* mocCppSource = makefile->GetOrCreateSource(mocCppFile.c_str(),
147                                                          true);
148   makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES",
149                            mocCppFile.c_str(), false);
150
151   target->AddSourceFile(mocCppSource);
152   return true;
153 }
154
155 static void GetCompileDefinitionsAndDirectories(cmTarget *target,
156                                                 const char * config,
157                                                 std::string &incs,
158                                                 std::string &defs)
159 {
160   cmMakefile* makefile = target->GetMakefile();
161   cmLocalGenerator* localGen = makefile->GetLocalGenerator();
162   std::vector<std::string> includeDirs;
163   cmGeneratorTarget gtgt(target);
164   // Get the include dirs for this target, without stripping the implicit
165   // include dirs off, see http://public.kitware.com/Bug/view.php?id=13667
166   localGen->GetIncludeDirectories(includeDirs, &gtgt, "CXX", config, false);
167   const char* sep = "";
168   incs = "";
169   for(std::vector<std::string>::const_iterator incDirIt = includeDirs.begin();
170       incDirIt != includeDirs.end();
171       ++incDirIt)
172     {
173     incs += sep;
174     sep = ";";
175     incs += *incDirIt;
176     }
177
178   std::set<std::string> defines;
179   localGen->AddCompileDefinitions(defines, target, config);
180
181   sep = "";
182   for(std::set<std::string>::const_iterator defIt = defines.begin();
183       defIt != defines.end();
184       ++defIt)
185     {
186     defs += sep;
187     sep = ";";
188     defs += *defIt;
189     }
190 }
191
192 void cmQtAutomoc::SetupAutomocTarget(cmTarget* target)
193 {
194   cmMakefile* makefile = target->GetMakefile();
195   const char* targetName = target->GetName();
196
197   bool relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE");
198
199   // create a custom target for running automoc at buildtime:
200   std::string automocTargetName = targetName;
201   automocTargetName += "_automoc";
202
203   std::string targetDir = makefile->GetCurrentOutputDirectory();
204   targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
205   targetDir += "/";
206   targetDir += automocTargetName;
207   targetDir += ".dir/";
208
209   cmCustomCommandLine currentLine;
210   currentLine.push_back(makefile->GetSafeDefinition("CMAKE_COMMAND"));
211   currentLine.push_back("-E");
212   currentLine.push_back("cmake_automoc");
213   currentLine.push_back(targetDir);
214   currentLine.push_back("$<CONFIGURATION>");
215
216   cmCustomCommandLines commandLines;
217   commandLines.push_back(currentLine);
218
219   std::string workingDirectory = cmSystemTools::CollapseFullPath(
220                                     "", makefile->GetCurrentOutputDirectory());
221
222   std::vector<std::string> depends;
223   std::string automocComment = "Automoc for target ";
224   automocComment += targetName;
225
226 #if defined(_WIN32) && !defined(__CYGWIN__)
227   bool usePRE_BUILD = false;
228   cmLocalGenerator* localGen = makefile->GetLocalGenerator();
229   cmGlobalGenerator* gg = localGen->GetGlobalGenerator();
230   if(strstr(gg->GetName(), "Visual Studio"))
231     {
232     cmLocalVisualStudioGenerator* vslg =
233       static_cast<cmLocalVisualStudioGenerator*>(localGen);
234     // Under VS >= 7 use a PRE_BUILD event instead of a separate target to
235     // reduce the number of targets loaded into the IDE.
236     // This also works around a VS 11 bug that may skip updating the target:
237     //  https://connect.microsoft.com/VisualStudio/feedback/details/769495
238     usePRE_BUILD = vslg->GetVersion() >= cmLocalVisualStudioGenerator::VS7;
239     }
240   if(usePRE_BUILD)
241     {
242     // Add the pre-build command directly to bypass the OBJECT_LIBRARY
243     // rejection in cmMakefile::AddCustomCommandToTarget because we know
244     // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
245     std::vector<std::string> no_output;
246     cmCustomCommand cc(makefile, no_output, depends,
247                        commandLines, automocComment.c_str(),
248                        workingDirectory.c_str());
249     cc.SetEscapeOldStyle(false);
250     cc.SetEscapeAllowMakeVars(true);
251     target->GetPreBuildCommands().push_back(cc);
252     }
253   else
254 #endif
255     {
256     cmTarget* automocTarget = makefile->AddUtilityCommand(
257                                 automocTargetName.c_str(), true,
258                                 workingDirectory.c_str(), depends,
259                                 commandLines, false, automocComment.c_str());
260     // Set target folder
261     const char* automocFolder = makefile->GetCMakeInstance()->GetProperty(
262                                                      "AUTOMOC_TARGETS_FOLDER");
263     if (automocFolder && *automocFolder)
264       {
265       automocTarget->SetProperty("FOLDER", automocFolder);
266       }
267     else
268       {
269       // inherit FOLDER property from target (#13688)
270       copyTargetProperty(automocTarget, target, "FOLDER");
271       }
272
273     target->AddUtility(automocTargetName.c_str());
274     }
275
276   // configure a file to get all information to automoc at buildtime:
277   std::string _moc_files;
278   std::string _moc_headers;
279   const char* sepFiles = "";
280   const char* sepHeaders = "";
281
282   const std::vector<cmSourceFile*>& srcFiles = target->GetSourceFiles();
283
284   for(std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin();
285       fileIt != srcFiles.end();
286       ++fileIt)
287     {
288     cmSourceFile* sf = *fileIt;
289     std::string absFile = cmsys::SystemTools::GetRealPath(
290                                                     sf->GetFullPath().c_str());
291     bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC"));
292     bool generated = cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"));
293
294     if ((skip==false) && (generated == false))
295       {
296       std::string ext = sf->GetExtension();
297       cmSystemTools::FileFormat fileType = cmSystemTools::GetFileFormat(
298                                                                   ext.c_str());
299       if (fileType == cmSystemTools::CXX_FILE_FORMAT)
300         {
301         _moc_files += sepFiles;
302         _moc_files += absFile;
303         sepFiles = ";";
304         }
305       else if (fileType == cmSystemTools::HEADER_FILE_FORMAT)
306         {
307         _moc_headers += sepHeaders;
308         _moc_headers += absFile;
309         sepHeaders = ";";
310         }
311       }
312     }
313
314   const char* tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS");
315   std::string _moc_options = (tmp!=0 ? tmp : "");
316
317   // forget the variables added here afterwards again:
318   cmMakefile::ScopePushPop varScope(makefile);
319   static_cast<void>(varScope);
320
321   makefile->AddDefinition("_moc_target_name",
322           cmLocalGenerator::EscapeForCMake(automocTargetName.c_str()).c_str());
323   makefile->AddDefinition("_moc_options",
324           cmLocalGenerator::EscapeForCMake(_moc_options.c_str()).c_str());
325   makefile->AddDefinition("_moc_files",
326           cmLocalGenerator::EscapeForCMake(_moc_files.c_str()).c_str());
327   makefile->AddDefinition("_moc_headers",
328           cmLocalGenerator::EscapeForCMake(_moc_headers.c_str()).c_str());
329   makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE");
330
331   std::string _moc_incs;
332   std::string _moc_compile_defs;
333   std::vector<std::string> configs;
334   const char *config = makefile->GetConfigurations(configs);
335   GetCompileDefinitionsAndDirectories(target, config,
336                                       _moc_incs, _moc_compile_defs);
337
338   makefile->AddDefinition("_moc_incs",
339           cmLocalGenerator::EscapeForCMake(_moc_incs.c_str()).c_str());
340   makefile->AddDefinition("_moc_compile_defs",
341           cmLocalGenerator::EscapeForCMake(_moc_compile_defs.c_str()).c_str());
342
343   std::map<std::string, std::string> configIncludes;
344   std::map<std::string, std::string> configDefines;
345
346   for (std::vector<std::string>::const_iterator li = configs.begin();
347        li != configs.end(); ++li)
348     {
349     std::string config_moc_incs;
350     std::string config_moc_compile_defs;
351     GetCompileDefinitionsAndDirectories(target, li->c_str(),
352                                         config_moc_incs,
353                                         config_moc_compile_defs);
354     if (config_moc_incs != _moc_incs)
355       {
356       configIncludes["_moc_incs_" + *li] =
357                     cmLocalGenerator::EscapeForCMake(config_moc_incs.c_str());
358       if(_moc_incs.empty())
359         {
360         _moc_incs = config_moc_incs;
361         }
362       }
363     if (config_moc_compile_defs != _moc_compile_defs)
364       {
365       configDefines["_moc_compile_defs_" + *li] =
366             cmLocalGenerator::EscapeForCMake(config_moc_compile_defs.c_str());
367       if(_moc_compile_defs.empty())
368         {
369         _moc_compile_defs = config_moc_compile_defs;
370         }
371       }
372     }
373
374   const char *qtVersion = makefile->GetDefinition("Qt5Core_VERSION_MAJOR");
375   if (!qtVersion)
376     {
377     qtVersion = makefile->GetDefinition("QT_VERSION_MAJOR");
378     }
379   if (const char *targetQtVersion =
380       target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", 0))
381     {
382     qtVersion = targetQtVersion;
383     }
384   if (qtVersion)
385     {
386     makefile->AddDefinition("_target_qt_version", qtVersion);
387     }
388
389   {
390   const char *qtMoc = makefile->GetSafeDefinition("QT_MOC_EXECUTABLE");
391   makefile->AddDefinition("_qt_moc_executable", qtMoc);
392   }
393
394   if (strcmp(qtVersion, "5") == 0)
395     {
396     cmTarget *qt5Moc = makefile->FindTargetToUse("Qt5::moc");
397     if (!qt5Moc)
398       {
399       cmSystemTools::Error("Qt5::moc target not found ",
400                           automocTargetName.c_str());
401       return;
402       }
403     makefile->AddDefinition("_qt_moc_executable", qt5Moc->GetLocation(0));
404     }
405   else
406     {
407     if (strcmp(qtVersion, "4") != 0)
408       {
409       cmSystemTools::Error("The CMAKE_AUTOMOC feature supports only Qt 4 and "
410                           "Qt 5 ", automocTargetName.c_str());
411       }
412     }
413
414   const char* cmakeRoot = makefile->GetSafeDefinition("CMAKE_ROOT");
415   std::string inputFile = cmakeRoot;
416   inputFile += "/Modules/AutomocInfo.cmake.in";
417   std::string outputFile = targetDir;
418   outputFile += "/AutomocInfo.cmake";
419   makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(),
420                           false, true, false);
421
422   if (!configDefines.empty() || !configIncludes.empty())
423     {
424     std::ofstream infoFile(outputFile.c_str(), std::ios::app);
425     if ( !infoFile )
426       {
427       std::string error = "Internal CMake error when trying to open file: ";
428       error += outputFile.c_str();
429       error += " for writing.";
430       cmSystemTools::Error(error.c_str());
431       return;
432       }
433     if (!configDefines.empty())
434       {
435       for (std::map<std::string, std::string>::iterator
436             it = configDefines.begin(), end = configDefines.end();
437             it != end; ++it)
438         {
439         infoFile << "SET(AM_MOC_COMPILE_DEFINITIONS_" << it->first <<
440           " " << it->second << ")\n";
441         }
442       }
443     if (!configIncludes.empty())
444       {
445       for (std::map<std::string, std::string>::iterator
446             it = configIncludes.begin(), end = configIncludes.end();
447             it != end; ++it)
448         {
449         infoFile << "SET(AM_MOC_INCLUDES_" << it->first <<
450           " " << it->second << ")\n";
451         }
452       }
453     }
454 }
455
456
457 bool cmQtAutomoc::Run(const char* targetDirectory, const char *config)
458 {
459   bool success = true;
460   cmake cm;
461   cmGlobalGenerator* gg = this->CreateGlobalGenerator(&cm, targetDirectory);
462   cmMakefile* makefile = gg->GetCurrentLocalGenerator()->GetMakefile();
463
464   this->ReadAutomocInfoFile(makefile, targetDirectory, config);
465   this->ReadOldMocDefinitionsFile(makefile, targetDirectory);
466
467   this->Init();
468
469   if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5")
470     {
471     success = this->RunAutomoc(makefile);
472     }
473
474   this->WriteOldMocDefinitionsFile(targetDirectory);
475
476   delete gg;
477   gg = NULL;
478   makefile = NULL;
479   return success;
480 }
481
482
483 cmGlobalGenerator* cmQtAutomoc::CreateGlobalGenerator(cmake* cm,
484                                                   const char* targetDirectory)
485 {
486   cmGlobalGenerator* gg = new cmGlobalGenerator();
487   gg->SetCMakeInstance(cm);
488
489   cmLocalGenerator* lg = gg->CreateLocalGenerator();
490   lg->GetMakefile()->SetHomeOutputDirectory(targetDirectory);
491   lg->GetMakefile()->SetStartOutputDirectory(targetDirectory);
492   lg->GetMakefile()->SetHomeDirectory(targetDirectory);
493   lg->GetMakefile()->SetStartDirectory(targetDirectory);
494   gg->SetCurrentLocalGenerator(lg);
495
496   return gg;
497 }
498
499
500 bool cmQtAutomoc::ReadAutomocInfoFile(cmMakefile* makefile,
501                                       const char* targetDirectory,
502                                       const char *config)
503 {
504   std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
505   cmSystemTools::ConvertToUnixSlashes(filename);
506   filename += "/AutomocInfo.cmake";
507
508   if (!makefile->ReadListFile(0, filename.c_str()))
509     {
510     cmSystemTools::Error("Error processing file: ", filename.c_str());
511     return false;
512     }
513
514   this->QtMajorVersion = makefile->GetSafeDefinition("AM_QT_VERSION_MAJOR");
515   if (this->QtMajorVersion == "")
516     {
517     this->QtMajorVersion = makefile->GetSafeDefinition(
518                                      "AM_Qt5Core_VERSION_MAJOR");
519     }
520   this->Sources = makefile->GetSafeDefinition("AM_SOURCES");
521   this->Headers = makefile->GetSafeDefinition("AM_HEADERS");
522   this->IncludeProjectDirsBefore = makefile->IsOn(
523                                 "AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
524   this->Srcdir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR");
525   this->Builddir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR");
526   this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE");
527   std::string compileDefsPropOrig = "AM_MOC_COMPILE_DEFINITIONS";
528   std::string compileDefsProp = compileDefsPropOrig;
529   if(config)
530     {
531     compileDefsProp += "_";
532     compileDefsProp += config;
533     }
534   const char *compileDefs = makefile->GetDefinition(compileDefsProp.c_str());
535   this->MocCompileDefinitionsStr = compileDefs ? compileDefs
536                   : makefile->GetSafeDefinition(compileDefsPropOrig.c_str());
537   std::string includesPropOrig = "AM_MOC_INCLUDES";
538   std::string includesProp = includesPropOrig;
539   if(config)
540     {
541     includesProp += "_";
542     includesProp += config;
543     }
544   const char *includes = makefile->GetDefinition(includesProp.c_str());
545   this->MocIncludesStr = includes ? includes
546                       : makefile->GetSafeDefinition(includesPropOrig.c_str());
547   this->MocOptionsStr = makefile->GetSafeDefinition("AM_MOC_OPTIONS");
548   this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR");
549   this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR");
550   this->TargetName = makefile->GetSafeDefinition("AM_TARGET_NAME");
551
552   this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile);
553
554   this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE");
555
556   return true;
557 }
558
559
560 std::string cmQtAutomoc::MakeCompileSettingsString(cmMakefile* makefile)
561 {
562   std::string s;
563   s += makefile->GetSafeDefinition("AM_MOC_COMPILE_DEFINITIONS");
564   s += " ~~~ ";
565   s += makefile->GetSafeDefinition("AM_MOC_INCLUDES");
566   s += " ~~~ ";
567   s += makefile->GetSafeDefinition("AM_MOC_OPTIONS");
568   s += " ~~~ ";
569   s += makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE") ? "TRUE"
570                                                                      : "FALSE";
571   s += " ~~~ ";
572
573   return s;
574 }
575
576
577 bool cmQtAutomoc::ReadOldMocDefinitionsFile(cmMakefile* makefile,
578                                             const char* targetDirectory)
579 {
580   std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
581   cmSystemTools::ConvertToUnixSlashes(filename);
582   filename += "/AutomocOldMocDefinitions.cmake";
583
584   if (makefile->ReadListFile(0, filename.c_str()))
585     {
586     this->OldCompileSettingsStr =
587                         makefile->GetSafeDefinition("AM_OLD_COMPILE_SETTINGS");
588     }
589   return true;
590 }
591
592
593 void cmQtAutomoc::WriteOldMocDefinitionsFile(const char* targetDirectory)
594 {
595   std::string filename(cmSystemTools::CollapseFullPath(targetDirectory));
596   cmSystemTools::ConvertToUnixSlashes(filename);
597   filename += "/AutomocOldMocDefinitions.cmake";
598
599   std::fstream outfile;
600   outfile.open(filename.c_str(),
601                std::ios::out | std::ios::trunc);
602   outfile << "set(AM_OLD_COMPILE_SETTINGS "
603               << cmLocalGenerator::EscapeForCMake(
604                  this->CurrentCompileSettingsStr.c_str()) << ")\n";
605
606   outfile.close();
607 }
608
609
610 void cmQtAutomoc::Init()
611 {
612   this->OutMocCppFilename = this->Builddir;
613   this->OutMocCppFilename += this->TargetName;
614   this->OutMocCppFilename += ".cpp";
615
616   std::vector<std::string> cdefList;
617   cmSystemTools::ExpandListArgument(this->MocCompileDefinitionsStr, cdefList);
618   for(std::vector<std::string>::const_iterator it = cdefList.begin();
619       it != cdefList.end();
620       ++it)
621     {
622     this->MocDefinitions.push_back("-D" + (*it));
623     }
624
625   cmSystemTools::ExpandListArgument(this->MocOptionsStr, this->MocOptions);
626
627   std::vector<std::string> incPaths;
628   cmSystemTools::ExpandListArgument(this->MocIncludesStr, incPaths);
629
630   std::set<std::string> frameworkPaths;
631   for(std::vector<std::string>::const_iterator it = incPaths.begin();
632       it != incPaths.end();
633       ++it)
634     {
635     const std::string &path = *it;
636     this->MocIncludes.push_back("-I" + path);
637     if (this->EndsWith(path, ".framework/Headers"))
638       {
639       // Go up twice to get to the framework root
640       std::vector<std::string> pathComponents;
641       cmsys::SystemTools::SplitPath(path.c_str(), pathComponents);
642       std::string frameworkPath =cmsys::SystemTools::JoinPath(
643                              pathComponents.begin(), pathComponents.end() - 2);
644       frameworkPaths.insert(frameworkPath);
645       }
646     }
647
648   for (std::set<std::string>::const_iterator it = frameworkPaths.begin();
649          it != frameworkPaths.end(); ++it)
650     {
651     this->MocIncludes.push_back("-F");
652     this->MocIncludes.push_back(*it);
653     }
654
655
656     if (this->IncludeProjectDirsBefore)
657       {
658       const std::string &binDir = "-I" + this->ProjectBinaryDir;
659
660       const std::string srcDir = "-I" + this->ProjectSourceDir;
661
662       std::list<std::string> sortedMocIncludes;
663       std::list<std::string>::iterator it = this->MocIncludes.begin();
664       while (it != this->MocIncludes.end())
665         {
666         if (this->StartsWith(*it, binDir))
667           {
668           sortedMocIncludes.push_back(*it);
669           it = this->MocIncludes.erase(it);
670           }
671         else
672           {
673           ++it;
674           }
675         }
676       it = this->MocIncludes.begin();
677       while (it != this->MocIncludes.end())
678         {
679         if (this->StartsWith(*it, srcDir))
680           {
681           sortedMocIncludes.push_back(*it);
682           it = this->MocIncludes.erase(it);
683           }
684         else
685           {
686           ++it;
687           }
688         }
689       sortedMocIncludes.insert(sortedMocIncludes.end(),
690                            this->MocIncludes.begin(), this->MocIncludes.end());
691       this->MocIncludes = sortedMocIncludes;
692     }
693
694 }
695
696
697 bool cmQtAutomoc::RunAutomoc(cmMakefile* makefile)
698 {
699   if (!cmsys::SystemTools::FileExists(this->OutMocCppFilename.c_str())
700     || (this->OldCompileSettingsStr != this->CurrentCompileSettingsStr))
701     {
702     this->GenerateAll = true;
703     }
704
705   // the program goes through all .cpp files to see which moc files are
706   // included. It is not really interesting how the moc file is named, but
707   // what file the moc is created from. Once a moc is included the same moc
708   // may not be included in the _automoc.cpp file anymore. OTOH if there's a
709   // header containing Q_OBJECT where no corresponding moc file is included
710   // anywhere a moc_<filename>.cpp file is created and included in
711   // the _automoc.cpp file.
712
713   // key = moc source filepath, value = moc output filepath
714   std::map<std::string, std::string> includedMocs;
715   // collect all headers which may need to be mocced
716   std::set<std::string> headerFiles;
717
718   std::vector<std::string> sourceFiles;
719   cmSystemTools::ExpandListArgument(this->Sources, sourceFiles);
720
721   const std::vector<std::string>& headerExtensions =
722                                                makefile->GetHeaderExtensions();
723
724   for (std::vector<std::string>::const_iterator it = sourceFiles.begin();
725        it != sourceFiles.end();
726        ++it)
727     {
728     const std::string &absFilename = *it;
729     if (this->Verbose)
730       {
731       std::cout << "AUTOMOC: Checking " << absFilename << std::endl;
732       }
733     if (this->RelaxedMode)
734       {
735       this->ParseCppFile(absFilename, headerExtensions, includedMocs);
736       }
737     else
738       {
739       this->StrictParseCppFile(absFilename, headerExtensions, includedMocs);
740       }
741     this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles);
742     }
743
744   std::vector<std::string> headerFilesVec;
745   cmSystemTools::ExpandListArgument(this->Headers, headerFilesVec);
746   for (std::vector<std::string>::const_iterator it = headerFilesVec.begin();
747        it != headerFilesVec.end();
748        ++it)
749     {
750     headerFiles.insert(*it);
751     }
752
753   // key = moc source filepath, value = moc output filename
754   std::map<std::string, std::string> notIncludedMocs;
755   this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs);
756
757   // run moc on all the moc's that are #included in source files
758   for(std::map<std::string, std::string>::const_iterator
759                                                      it = includedMocs.begin();
760       it != includedMocs.end();
761       ++it)
762     {
763     this->GenerateMoc(it->first, it->second);
764     }
765
766   cmsys_ios::stringstream outStream;
767   outStream << "/* This file is autogenerated, do not edit*/\n";
768
769   bool automocCppChanged = false;
770   if (notIncludedMocs.empty())
771     {
772     outStream << "enum some_compilers { need_more_than_nothing };\n";
773     }
774   else
775     {
776     // run moc on the remaining headers and include them in
777     // the _automoc.cpp file
778     for(std::map<std::string, std::string>::const_iterator
779                                                   it = notIncludedMocs.begin();
780         it != notIncludedMocs.end();
781         ++it)
782       {
783       bool mocSuccess = this->GenerateMoc(it->first, it->second);
784       if (mocSuccess)
785         {
786         automocCppChanged = true;
787         }
788       outStream << "#include \"" << it->second << "\"\n";
789       }
790     }
791
792   if (this->RunMocFailed)
793     {
794     std::cerr << "moc failed..."<< std::endl;
795     return false;
796     }
797   outStream.flush();
798   std::string automocSource = outStream.str();
799   if (!automocCppChanged)
800     {
801     // compare contents of the _automoc.cpp file
802     const std::string oldContents = this->ReadAll(this->OutMocCppFilename);
803     if (oldContents == automocSource)
804       {
805       // nothing changed: don't touch the _automoc.cpp file
806       return true;
807       }
808     }
809
810   // source file that includes all remaining moc files (_automoc.cpp file)
811   std::fstream outfile;
812   outfile.open(this->OutMocCppFilename.c_str(),
813                std::ios::out | std::ios::trunc);
814   outfile << automocSource;
815   outfile.close();
816
817   return true;
818 }
819
820
821 void cmQtAutomoc::ParseCppFile(const std::string& absFilename,
822                               const std::vector<std::string>& headerExtensions,
823                               std::map<std::string, std::string>& includedMocs)
824 {
825   cmsys::RegularExpression mocIncludeRegExp(
826               "[\n][ \t]*#[ \t]*include[ \t]+"
827               "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
828
829   const std::string contentsString = this->ReadAll(absFilename);
830   if (contentsString.empty())
831     {
832     std::cerr << "AUTOMOC: warning: " << absFilename << ": file is empty\n"
833               << std::endl;
834     return;
835     }
836   const std::string absPath = cmsys::SystemTools::GetFilenamePath(
837                    cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
838   const std::string scannedFileBasename = cmsys::SystemTools::
839                                   GetFilenameWithoutLastExtension(absFilename);
840   const bool cppContainsQ_OBJECT = containsQ_OBJECT(contentsString);
841   bool dotMocIncluded = false;
842   bool mocUnderscoreIncluded = false;
843   std::string ownMocUnderscoreFile;
844   std::string ownDotMocFile;
845   std::string ownMocHeaderFile;
846
847   std::string::size_type matchOffset = 0;
848   // first a simple string check for "moc" is *much* faster than the regexp,
849   // and if the string search already fails, we don't have to try the
850   // expensive regexp
851   if ((strstr(contentsString.c_str(), "moc") != NULL)
852                                     && (mocIncludeRegExp.find(contentsString)))
853     {
854     // for every moc include in the file
855     do
856       {
857       const std::string currentMoc = mocIncludeRegExp.match(1);
858       //std::cout << "found moc include: " << currentMoc << std::endl;
859
860       std::string basename = cmsys::SystemTools::
861                                    GetFilenameWithoutLastExtension(currentMoc);
862       const bool moc_style = this->StartsWith(basename, "moc_");
863
864       // If the moc include is of the moc_foo.cpp style we expect
865       // the Q_OBJECT class declaration in a header file.
866       // If the moc include is of the foo.moc style we need to look for
867       // a Q_OBJECT macro in the current source file, if it contains the
868       // macro we generate the moc file from the source file.
869       // Q_OBJECT
870       if (moc_style)
871         {
872         // basename should be the part of the moc filename used for
873         // finding the correct header, so we need to remove the moc_ part
874         basename = basename.substr(4);
875         std::string mocSubDir = extractSubDir(absPath, currentMoc);
876         std::string headerToMoc = findMatchingHeader(
877                                absPath, mocSubDir, basename, headerExtensions);
878
879         if (!headerToMoc.empty())
880           {
881           includedMocs[headerToMoc] = currentMoc;
882           if (basename == scannedFileBasename)
883             {
884             mocUnderscoreIncluded = true;
885             ownMocUnderscoreFile = currentMoc;
886             ownMocHeaderFile = headerToMoc;
887             }
888           }
889         else
890           {
891           std::cerr << "AUTOMOC: error: " << absFilename << " The file "
892                     << "includes the moc file \"" << currentMoc << "\", "
893                     << "but could not find header \"" << basename
894                     << '{' << this->Join(headerExtensions, ',') << "}\" ";
895           if (mocSubDir.empty())
896             {
897             std::cerr << "in " << absPath << "\n" << std::endl;
898             }
899           else
900             {
901             std::cerr << "neither in " << absPath
902                       << " nor in " << mocSubDir << "\n" << std::endl;
903             }
904
905           ::exit(EXIT_FAILURE);
906           }
907         }
908       else
909         {
910         std::string fileToMoc = absFilename;
911         if ((basename != scannedFileBasename) || (cppContainsQ_OBJECT==false))
912           {
913           std::string mocSubDir = extractSubDir(absPath, currentMoc);
914           std::string headerToMoc = findMatchingHeader(
915                               absPath, mocSubDir, basename, headerExtensions);
916           if (!headerToMoc.empty())
917             {
918             // this is for KDE4 compatibility:
919             fileToMoc = headerToMoc;
920             if ((cppContainsQ_OBJECT==false) &&(basename==scannedFileBasename))
921               {
922               std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
923                             "includes the moc file \"" << currentMoc <<
924                             "\", but does not contain a Q_OBJECT macro. "
925                             "Running moc on "
926                         << "\"" << headerToMoc << "\" ! Include \"moc_"
927                         << basename << ".cpp\" for a compatiblity with "
928                            "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
929                         << std::endl;
930               }
931             else
932               {
933               std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
934                             "includes the moc file \"" << currentMoc <<
935                             "\" instead of \"moc_" << basename << ".cpp\". "
936                             "Running moc on "
937                         << "\"" << headerToMoc << "\" ! Include \"moc_"
938                         << basename << ".cpp\" for compatiblity with "
939                            "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
940                         << std::endl;
941               }
942             }
943           else
944             {
945             std::cerr <<"AUTOMOC: error: " << absFilename << ": The file "
946                         "includes the moc file \"" << currentMoc <<
947                         "\", which seems to be the moc file from a different "
948                         "source file. CMake also could not find a matching "
949                         "header.\n" << std::endl;
950             ::exit(EXIT_FAILURE);
951             }
952           }
953         else
954           {
955           dotMocIncluded = true;
956           ownDotMocFile = currentMoc;
957           }
958         includedMocs[fileToMoc] = currentMoc;
959         }
960       matchOffset += mocIncludeRegExp.end();
961       } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset));
962     }
963
964   // In this case, check whether the scanned file itself contains a Q_OBJECT.
965   // If this is the case, the moc_foo.cpp should probably be generated from
966   // foo.cpp instead of foo.h, because otherwise it won't build.
967   // But warn, since this is not how it is supposed to be used.
968   if ((dotMocIncluded == false) && (cppContainsQ_OBJECT == true))
969     {
970     if (mocUnderscoreIncluded == true)
971       {
972       // this is for KDE4 compatibility:
973       std::cerr << "AUTOMOC: warning: " << absFilename << ": The file "
974                 << "contains a Q_OBJECT macro, but does not include "
975                 << "\"" << scannedFileBasename << ".moc\", but instead "
976                    "includes "
977                 << "\"" << ownMocUnderscoreFile  << "\". Running moc on "
978                 << "\"" << absFilename << "\" ! Better include \""
979                 << scannedFileBasename << ".moc\" for compatiblity with "
980                    "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n"
981                 << std::endl;
982       includedMocs[absFilename] = ownMocUnderscoreFile;
983       includedMocs.erase(ownMocHeaderFile);
984       }
985     else
986       {
987       // otherwise always error out since it will not compile:
988       std::cerr << "AUTOMOC: error: " << absFilename << ": The file "
989                 << "contains a Q_OBJECT macro, but does not include "
990                 << "\"" << scannedFileBasename << ".moc\" !\n"
991                 << std::endl;
992       ::exit(EXIT_FAILURE);
993       }
994     }
995
996 }
997
998
999 void cmQtAutomoc::StrictParseCppFile(const std::string& absFilename,
1000                               const std::vector<std::string>& headerExtensions,
1001                               std::map<std::string, std::string>& includedMocs)
1002 {
1003   cmsys::RegularExpression mocIncludeRegExp(
1004               "[\n][ \t]*#[ \t]*include[ \t]+"
1005               "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
1006
1007   const std::string contentsString = this->ReadAll(absFilename);
1008   if (contentsString.empty())
1009     {
1010     std::cerr << "AUTOMOC: warning: " << absFilename << ": file is empty\n"
1011               << std::endl;
1012     return;
1013     }
1014   const std::string absPath = cmsys::SystemTools::GetFilenamePath(
1015                    cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
1016   const std::string scannedFileBasename = cmsys::SystemTools::
1017                                   GetFilenameWithoutLastExtension(absFilename);
1018
1019   bool dotMocIncluded = false;
1020
1021   std::string::size_type matchOffset = 0;
1022   // first a simple string check for "moc" is *much* faster than the regexp,
1023   // and if the string search already fails, we don't have to try the
1024   // expensive regexp
1025   if ((strstr(contentsString.c_str(), "moc") != NULL)
1026                                     && (mocIncludeRegExp.find(contentsString)))
1027     {
1028     // for every moc include in the file
1029     do
1030       {
1031       const std::string currentMoc = mocIncludeRegExp.match(1);
1032
1033       std::string basename = cmsys::SystemTools::
1034                                    GetFilenameWithoutLastExtension(currentMoc);
1035       const bool mocUnderscoreStyle = this->StartsWith(basename, "moc_");
1036
1037       // If the moc include is of the moc_foo.cpp style we expect
1038       // the Q_OBJECT class declaration in a header file.
1039       // If the moc include is of the foo.moc style we need to look for
1040       // a Q_OBJECT macro in the current source file, if it contains the
1041       // macro we generate the moc file from the source file.
1042       if (mocUnderscoreStyle)
1043         {
1044         // basename should be the part of the moc filename used for
1045         // finding the correct header, so we need to remove the moc_ part
1046         basename = basename.substr(4);
1047         std::string mocSubDir = extractSubDir(absPath, currentMoc);
1048         std::string headerToMoc = findMatchingHeader(
1049                                absPath, mocSubDir, basename, headerExtensions);
1050
1051         if (!headerToMoc.empty())
1052           {
1053           includedMocs[headerToMoc] = currentMoc;
1054           }
1055         else
1056           {
1057           std::cerr << "AUTOMOC: error: " << absFilename << " The file "
1058                     << "includes the moc file \"" << currentMoc << "\", "
1059                     << "but could not find header \"" << basename
1060                     << '{' << this->Join(headerExtensions, ',') << "}\" ";
1061           if (mocSubDir.empty())
1062             {
1063             std::cerr << "in " << absPath << "\n" << std::endl;
1064             }
1065           else
1066             {
1067             std::cerr << "neither in " << absPath
1068                       << " nor in " << mocSubDir << "\n" << std::endl;
1069             }
1070
1071           ::exit(EXIT_FAILURE);
1072           }
1073         }
1074       else
1075         {
1076         if (basename != scannedFileBasename)
1077           {
1078           std::cerr <<"AUTOMOC: error: " << absFilename << ": The file "
1079                       "includes the moc file \"" << currentMoc <<
1080                       "\", which seems to be the moc file from a different "
1081                       "source file. This is not supported. "
1082                       "Include \"" << scannedFileBasename << ".moc\" to run "
1083                       "moc on this source file.\n" << std::endl;
1084           ::exit(EXIT_FAILURE);
1085           }
1086         dotMocIncluded = true;
1087         includedMocs[absFilename] = currentMoc;
1088         }
1089       matchOffset += mocIncludeRegExp.end();
1090       } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset));
1091     }
1092
1093   // In this case, check whether the scanned file itself contains a Q_OBJECT.
1094   // If this is the case, the moc_foo.cpp should probably be generated from
1095   // foo.cpp instead of foo.h, because otherwise it won't build.
1096   // But warn, since this is not how it is supposed to be used.
1097   if ((dotMocIncluded == false) && (containsQ_OBJECT(contentsString)))
1098     {
1099     // otherwise always error out since it will not compile:
1100     std::cerr << "AUTOMOC: error: " << absFilename << ": The file "
1101               << "contains a Q_OBJECT macro, but does not include "
1102               << "\"" << scannedFileBasename << ".moc\" !\n"
1103               << std::endl;
1104     ::exit(EXIT_FAILURE);
1105     }
1106
1107 }
1108
1109
1110 void cmQtAutomoc::SearchHeadersForCppFile(const std::string& absFilename,
1111                               const std::vector<std::string>& headerExtensions,
1112                               std::set<std::string>& absHeaders)
1113 {
1114   // search for header files and private header files we may need to moc:
1115   const std::string basename =
1116               cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
1117   const std::string absPath = cmsys::SystemTools::GetFilenamePath(
1118                    cmsys::SystemTools::GetRealPath(absFilename.c_str())) + '/';
1119
1120   for(std::vector<std::string>::const_iterator ext = headerExtensions.begin();
1121       ext != headerExtensions.end();
1122       ++ext)
1123     {
1124     const std::string headerName = absPath + basename + "." + (*ext);
1125     if (cmsys::SystemTools::FileExists(headerName.c_str()))
1126       {
1127       absHeaders.insert(headerName);
1128       break;
1129       }
1130     }
1131   for(std::vector<std::string>::const_iterator ext = headerExtensions.begin();
1132       ext != headerExtensions.end();
1133       ++ext)
1134     {
1135     const std::string privateHeaderName = absPath+basename+"_p."+(*ext);
1136     if (cmsys::SystemTools::FileExists(privateHeaderName.c_str()))
1137       {
1138       absHeaders.insert(privateHeaderName);
1139       break;
1140       }
1141     }
1142
1143 }
1144
1145
1146 void cmQtAutomoc::ParseHeaders(const std::set<std::string>& absHeaders,
1147                         const std::map<std::string, std::string>& includedMocs,
1148                         std::map<std::string, std::string>& notIncludedMocs)
1149 {
1150   for(std::set<std::string>::const_iterator hIt=absHeaders.begin();
1151       hIt!=absHeaders.end();
1152       ++hIt)
1153     {
1154     const std::string& headerName = *hIt;
1155
1156     if (includedMocs.find(headerName) == includedMocs.end())
1157       {
1158       if (this->Verbose)
1159         {
1160         std::cout << "AUTOMOC: Checking " << headerName << std::endl;
1161         }
1162
1163       const std::string basename = cmsys::SystemTools::
1164                                    GetFilenameWithoutLastExtension(headerName);
1165
1166       const std::string currentMoc = "moc_" + basename + ".cpp";
1167       const std::string contents = this->ReadAll(headerName);
1168       if (containsQ_OBJECT(contents))
1169         {
1170         //std::cout << "header contains Q_OBJECT macro";
1171         notIncludedMocs[headerName] = currentMoc;
1172         }
1173       }
1174     }
1175
1176 }
1177
1178
1179 bool cmQtAutomoc::GenerateMoc(const std::string& sourceFile,
1180                               const std::string& mocFileName)
1181 {
1182   const std::string mocFilePath = this->Builddir + mocFileName;
1183   int sourceNewerThanMoc = 0;
1184   bool success = cmsys::SystemTools::FileTimeCompare(sourceFile.c_str(),
1185                                                      mocFilePath.c_str(),
1186                                                      &sourceNewerThanMoc);
1187   if (this->GenerateAll || !success || sourceNewerThanMoc >= 0)
1188     {
1189     // make sure the directory for the resulting moc file exists
1190     std::string mocDir = mocFilePath.substr(0, mocFilePath.rfind('/'));
1191     if (!cmsys::SystemTools::FileExists(mocDir.c_str(), false))
1192       {
1193       cmsys::SystemTools::MakeDirectory(mocDir.c_str());
1194       }
1195
1196     std::string msg = "Generating ";
1197     msg += mocFileName;
1198     cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue
1199                                            |cmsysTerminal_Color_ForegroundBold,
1200                                      msg.c_str(), true, this->ColorOutput);
1201
1202     std::vector<cmStdString> command;
1203     command.push_back(this->MocExecutable);
1204     for (std::list<std::string>::const_iterator it = this->MocIncludes.begin();
1205          it != this->MocIncludes.end();
1206          ++it)
1207       {
1208       command.push_back(*it);
1209       }
1210     for(std::list<std::string>::const_iterator it=this->MocDefinitions.begin();
1211         it != this->MocDefinitions.end();
1212         ++it)
1213       {
1214       command.push_back(*it);
1215       }
1216     for(std::vector<std::string>::const_iterator it=this->MocOptions.begin();
1217         it != this->MocOptions.end();
1218         ++it)
1219       {
1220       command.push_back(*it);
1221       }
1222 #ifdef _WIN32
1223     command.push_back("-DWIN32");
1224 #endif
1225     command.push_back("-o");
1226     command.push_back(mocFilePath);
1227     command.push_back(sourceFile);
1228
1229     if (this->Verbose)
1230       {
1231       for(std::vector<cmStdString>::const_iterator cmdIt = command.begin();
1232           cmdIt != command.end();
1233           ++cmdIt)
1234         {
1235         std::cout << *cmdIt << " ";
1236         }
1237       std::cout << std::endl;
1238       }
1239
1240     std::string output;
1241     int retVal = 0;
1242     bool result = cmSystemTools::RunSingleCommand(command, &output, &retVal);
1243     if (!result || retVal)
1244       {
1245       std::cerr << "AUTOMOC: error: process for " << mocFilePath <<" failed:\n"
1246                 << output << std::endl;
1247       this->RunMocFailed = true;
1248       cmSystemTools::RemoveFile(mocFilePath.c_str());
1249       }
1250     return true;
1251     }
1252   return false;
1253 }
1254
1255
1256 std::string cmQtAutomoc::Join(const std::vector<std::string>& lst,
1257                               char separator)
1258 {
1259     if (lst.empty())
1260       {
1261       return "";
1262       }
1263
1264     std::string result;
1265     for (std::vector<std::string>::const_iterator it = lst.begin();
1266          it != lst.end();
1267          ++it)
1268       {
1269       result += "." + (*it) + separator;
1270       }
1271     result.erase(result.end() - 1);
1272     return result;
1273 }
1274
1275
1276 bool cmQtAutomoc::StartsWith(const std::string& str, const std::string& with)
1277 {
1278   return (str.substr(0, with.length()) == with);
1279 }
1280
1281
1282 bool cmQtAutomoc::EndsWith(const std::string& str, const std::string& with)
1283 {
1284   if (with.length() > (str.length()))
1285     {
1286     return false;
1287     }
1288   return (str.substr(str.length() - with.length(), with.length()) == with);
1289 }
1290
1291
1292 std::string cmQtAutomoc::ReadAll(const std::string& filename)
1293 {
1294   std::ifstream file(filename.c_str());
1295   cmsys_ios::stringstream stream;
1296   stream << file.rdbuf();
1297   file.close();
1298   return stream.str();
1299 }