aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/buildgraph/automoc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/buildgraph/automoc.cpp')
-rw-r--r--src/lib/buildgraph/automoc.cpp273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/lib/buildgraph/automoc.cpp b/src/lib/buildgraph/automoc.cpp
new file mode 100644
index 000000000..3a7f03ffa
--- /dev/null
+++ b/src/lib/buildgraph/automoc.cpp
@@ -0,0 +1,273 @@
+/**************************************************************************
+**
+** This file is part of the Qt Build Suite
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file.
+** Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**************************************************************************/
+
+#include "automoc.h"
+#include "scanresultcache.h"
+#include <buildgraph/artifact.h>
+#include <tools/error.h>
+#include <tools/logger.h>
+#include <tools/scannerpluginmanager.h>
+
+namespace qbs {
+
+AutoMoc::AutoMoc()
+ : m_scanResultCache(0)
+{
+}
+
+void AutoMoc::setScanResultCache(ScanResultCache *scanResultCache)
+{
+ m_scanResultCache = scanResultCache;
+}
+
+void AutoMoc::apply(BuildProduct::Ptr product)
+{
+ if (scanners().isEmpty())
+ throw Error("C++ scanner cannot be loaded.");
+
+ QList<QPair<Artifact *, FileType> > artifactsToMoc;
+ QSet<QString> includedMocCppFiles;
+ QHash<QString, Artifact *>::const_iterator it = product->artifacts.begin();
+ for (; it != product->artifacts.end(); ++it) {
+ Artifact *artifact = it.value();
+ FileType fileType = UnknownFileType;
+ if (artifact->fileTags.contains("hpp"))
+ fileType = HppFileType;
+ if (artifact->fileTags.contains("cpp"))
+ fileType = CppFileType;
+ if (fileType == UnknownFileType)
+ continue;
+ QString mocFileTag;
+ bool alreadyMocced = isVictimOfMoc(artifact, fileType, mocFileTag);
+ bool hasQObjectMacro;
+ apply(artifact, hasQObjectMacro, includedMocCppFiles);
+ if (hasQObjectMacro && !alreadyMocced) {
+ artifactsToMoc += qMakePair(artifact, fileType);
+ } else if (!hasQObjectMacro && alreadyMocced) {
+ unmoc(artifact, mocFileTag);
+ }
+ }
+
+ QMap<QString, QSet<Artifact *> > artifactsPerFileTag;
+ for (int i = artifactsToMoc.count(); --i >= 0;) {
+ const QPair<Artifact *, FileType> &p = artifactsToMoc.at(i);
+ Artifact * const artifact = p.first;
+ FileType fileType = p.second;
+ QString fileTag;
+ if (fileType == CppFileType) {
+ fileTag = "moc_cpp";
+ } else if (fileType == HppFileType) {
+ QString mocFileName = generateMocFileName(artifact, fileType);
+ if (includedMocCppFiles.contains(mocFileName))
+ fileTag = "moc_hpp_inc";
+ else
+ fileTag = "moc_hpp";
+ }
+ artifactsPerFileTag[fileTag].insert(artifact);
+ }
+
+ BuildGraph *buildGraph = product->project->buildGraph();
+ if (!artifactsPerFileTag.isEmpty()) {
+ qbsInfo() << DontPrintLogLevel << "Applying moc rules for '" << product->rProduct->name << "'.";
+ buildGraph->applyRules(product.data(), artifactsPerFileTag);
+ }
+ buildGraph->updateNodesThatMustGetNewTransformer();
+}
+
+QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType)
+{
+ QString mocFileName;
+ switch (fileType) {
+ case UnknownFileType:
+ break;
+ case HppFileType:
+ mocFileName = "moc_" + FileInfo::baseName(artifact->fileName) + ".cpp";
+ break;
+ case CppFileType:
+ mocFileName = FileInfo::baseName(artifact->fileName) + ".moc";
+ break;
+ }
+ return mocFileName;
+}
+
+void AutoMoc::apply(Artifact *artifact, bool &hasQObjectMacro, QSet<QString> &includedMocCppFiles)
+{
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] checks " << fileName(artifact);
+
+ hasQObjectMacro = false;
+ const int numFileTags = artifact->fileTags.count();
+ char **cFileTags = createCFileTags(artifact->fileTags);
+
+ foreach (ScannerPlugin *scanner, scanners()) {
+ void *opaq = scanner->open(artifact->fileName.utf16(), cFileTags, numFileTags);
+ if (!opaq || !scanner->additionalFileTags)
+ continue;
+
+ // HACK: misuse the file dependency scanner as provider for file tags
+ int length = 0;
+ const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
+ if (szFileTagsFromScanner && length > 0) {
+ for (int i=length; --i >= 0;) {
+ const QString fileTagFromScanner = QString::fromLocal8Bit(szFileTagsFromScanner[i]);
+ artifact->fileTags.insert(fileTagFromScanner);
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] finds Q_OBJECT macro";
+ if (fileTagFromScanner.startsWith("moc"))
+ hasQObjectMacro = true;
+ }
+ }
+
+ scanner->close(opaq);
+
+ ScanResultCache::Result scanResult;
+ if (m_scanResultCache)
+ scanResult = m_scanResultCache->value(artifact->fileName);
+ if (!scanResult.visited) {
+ scanResult.visited = true;
+ opaq = scanner->open(artifact->fileName.utf16(), 0, 0);
+ if (!opaq)
+ continue;
+
+ forever {
+ int flags = 0;
+ const char *szOutFilePath = scanner->next(opaq, &length, &flags);
+ if (szOutFilePath == 0)
+ break;
+ QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
+ if (includedFilePath.isEmpty())
+ continue;
+ bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
+ scanResult.deps.insert(includedFilePath, isLocalInclude);
+ }
+
+ scanner->close(opaq);
+ if (m_scanResultCache)
+ m_scanResultCache->insert(artifact->fileName, scanResult);
+ }
+
+ for (QHash<QString, bool>::const_iterator it = scanResult.deps.constBegin(); it != scanResult.deps.constEnd(); ++it) {
+ const QString &includedFilePath = it.key();
+ if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] finds included file: " << includedFilePath;
+ includedMocCppFiles += includedFilePath;
+ }
+ }
+ }
+
+ freeCFileTags(cFileTags, numFileTags);
+}
+
+bool AutoMoc::isVictimOfMoc(Artifact *artifact, FileType fileType, QString &foundMocFileTag)
+{
+ foundMocFileTag.clear();
+ switch (fileType) {
+ case UnknownFileType:
+ break;
+ case HppFileType:
+ if (artifact->fileTags.contains("moc_hpp"))
+ foundMocFileTag = "moc_hpp";
+ else if (artifact->fileTags.contains("moc_hpp_inc"))
+ foundMocFileTag = "moc_hpp_inc";
+ break;
+ case CppFileType:
+ if (artifact->fileTags.contains("moc_cpp"))
+ foundMocFileTag = "moc_cpp";
+ break;
+ }
+ return !foundMocFileTag.isEmpty();
+}
+
+void AutoMoc::unmoc(Artifact *artifact, const QString &mocFileTag)
+{
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] unmoc'ing " << fileName(artifact);
+
+ artifact->fileTags.remove(mocFileTag);
+
+ Artifact *generatedMocArtifact = 0;
+ foreach (Artifact *parent, artifact->parents) {
+ foreach (const QString &fileTag, parent->fileTags) {
+ if (fileTag == "hpp" || fileTag == "cpp") {
+ generatedMocArtifact = parent;
+ break;
+ }
+ }
+ }
+
+ if (!generatedMocArtifact) {
+ qbsTrace() << "[AUTOMOC] generated moc artifact could not be found";
+ return;
+ }
+
+ BuildGraph *buildGraph = artifact->project->buildGraph();
+ if (mocFileTag == "moc_hpp") {
+ Artifact *mocObjArtifact = 0;
+ foreach (Artifact *parent, generatedMocArtifact->parents) {
+ foreach (const QString &fileTag, parent->fileTags) {
+ if (fileTag == "obj" || fileTag == "fpicobj") {
+ mocObjArtifact = parent;
+ break;
+ }
+ }
+ }
+
+ if (!mocObjArtifact) {
+ qbsTrace() << "[AUTOMOC] generated moc obj artifact could not be found";
+ } else {
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] removing moc obj artifact " << fileName(mocObjArtifact);
+ buildGraph->remove(mocObjArtifact);
+ }
+ }
+
+ if (qbsLogLevel(LoggerTrace))
+ qbsTrace() << "[AUTOMOC] removing generated artifact " << fileName(generatedMocArtifact);
+ buildGraph->remove(generatedMocArtifact);
+ delete generatedMocArtifact;
+}
+
+QList<ScannerPlugin *> AutoMoc::scanners() const
+{
+ if (m_scanners.isEmpty())
+ m_scanners = ScannerPluginManager::scannersForFileTag("hpp");
+
+ return m_scanners;
+}
+
+} // namespace qbs