/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qbs. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtmocscanner.h" #include "artifact.h" #include "depscanner.h" #include "productbuilddata.h" #include "projectbuilddata.h" #include "rawscanresults.h" #include #include #include #include #include #include #include #include #include #include namespace qbs { namespace Internal { struct CommonFileTags { const FileTag cpp = "cpp"; const FileTag cppCombine = "cpp.combine"; const FileTag hpp = "hpp"; const FileTag moc_cpp = "moc_cpp"; const FileTag moc_cpp_plugin = "moc_cpp_plugin"; const FileTag moc_hpp_plugin = "moc_hpp_plugin"; const FileTag moc_hpp = "moc_hpp"; const FileTag objcpp = "objcpp"; const FileTag objcppCombine = "objcpp.combine"; }; Q_GLOBAL_STATIC(CommonFileTags, commonFileTags) class QtScanner : public DependencyScanner { public: QtScanner(const DependencyScanner &actualScanner) : m_id(QStringLiteral("qt") + actualScanner.id()) {} private: QStringList collectSearchPaths(Artifact *) override { return {}; } QStringList collectDependencies(Artifact *, FileResourceBase *, const char *) override { return {}; } bool recursive() const override { return false; } const void *key() const override { return nullptr; } QString createId() const override { return m_id; } bool areModulePropertiesCompatible(const PropertyMapConstPtr &, const PropertyMapConstPtr &) const override { return true; } bool cacheIsPerFile() const override { return false; } const QString m_id; }; static QString qtMocScannerJsName() { return QStringLiteral("QtMocScanner"); } QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue) : m_tags(*commonFileTags()) , m_product(product) , m_targetScriptValue(targetScriptValue) { const auto engine = static_cast(targetScriptValue.engine()); QScriptValue scannerObj = engine->newObject(); targetScriptValue.setProperty(qtMocScannerJsName(), scannerObj); QScriptValue applyFunction = engine->newFunction(&js_apply, this); scannerObj.setProperty(QStringLiteral("apply"), applyFunction); } QtMocScanner::~QtMocScanner() { m_targetScriptValue.setProperty(qtMocScannerJsName(), QScriptValue()); } static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact) { const QString &filepath = artifact->filePath(); QtScanner depScanner((PluginDependencyScanner(scanner))); RawScanResults &rawScanResults = artifact->product->topLevelProject()->buildData->rawScanResults; RawScanResults::ScanData &scanData = rawScanResults.findScanData(artifact, &depScanner, artifact->properties); if (scanData.lastScanTime < artifact->timestamp()) { FileTags tags = artifact->fileTags(); if (tags.contains(commonFileTags->cppCombine)) { tags.remove(commonFileTags->cppCombine); tags.insert(commonFileTags->cpp); } if (tags.contains(commonFileTags->objcppCombine)) { tags.remove(commonFileTags->objcppCombine); tags.insert(commonFileTags->objcpp); } const QByteArray tagsForScanner = tags.toStringList().join(QLatin1Char(',')).toLatin1(); void *opaq = scanner->open(filepath.utf16(), tagsForScanner.constData(), ScanForDependenciesFlag | ScanForFileTagsFlag); if (!opaq || !scanner->additionalFileTags) return scanData.rawScanResult; scanData.rawScanResult.additionalFileTags.clear(); scanData.rawScanResult.deps.clear(); int length = 0; const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length); if (szFileTagsFromScanner) { for (int i = length; --i >= 0;) scanData.rawScanResult.additionalFileTags += szFileTagsFromScanner[i]; } QString baseDirOfInFilePath = artifact->dirPath(); forever { int flags = 0; const char *szOutFilePath = scanner->next(opaq, &length, &flags); if (szOutFilePath == nullptr) break; QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length); if (includedFilePath.isEmpty()) continue; bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG); if (isLocalInclude) { QString localFilePath = FileInfo::resolvePath(baseDirOfInFilePath, includedFilePath); if (FileInfo::exists(localFilePath)) includedFilePath = localFilePath; } scanData.rawScanResult.deps.push_back(RawScannedDependency(includedFilePath)); } scanner->close(opaq); scanData.lastScanTime = FileTime::currentTime(); } return scanData.rawScanResult; } void QtMocScanner::findIncludedMocCppFiles() { if (!m_includedMocCppFiles.empty()) return; qCDebug(lcMocScan) << "looking for included moc_XXX.cpp files"; static const FileTags mocCppTags = {m_tags.cpp, m_tags.objcpp}; for (Artifact *artifact : m_product->lookupArtifactsByFileTags(mocCppTags)) { const RawScanResult scanResult = runScanner(m_cppScanner, artifact); for (const RawScannedDependency &dependency : scanResult.deps) { QString includedFileName = dependency.fileName(); if (includedFileName.startsWith(QLatin1String("moc_")) && includedFileName.endsWith(QLatin1String(".cpp"))) { qCDebug(lcMocScan) << artifact->fileName() << "includes" << includedFileName; includedFileName.remove(0, 4); includedFileName.chop(4); m_includedMocCppFiles.insert(includedFileName, artifact->fileName()); } } } } QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, QtMocScanner *that) { QScriptValue input = ctx->argument(0); return that->apply(engine, attachedPointer(input)); } static QScriptValue scannerCountError(QScriptEngine *engine, size_t scannerCount, const QString &fileTag) { return engine->currentContext()->throwError( Tr::tr("There are %1 scanners for the file tag %2. " "Expected is exactly one.").arg(scannerCount).arg(fileTag)); } QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact) { if (!m_cppScanner) { auto scanners = ScannerPluginManager::scannersForFileTag(m_tags.cpp); if (scanners.size() != 1) return scannerCountError(engine, scanners.size(), m_tags.cpp.toString()); m_cppScanner = scanners.front(); } qCDebug(lcMocScan).noquote() << "scanning" << artifact->toString(); bool hasQObjectMacro = false; bool mustCompile = false; bool hasPluginMetaDataMacro = false; const bool isHeaderFile = artifact->fileTags().contains(m_tags.hpp); RawScanResult scanResult = runScanner(m_cppScanner, artifact); if (scanResult.additionalFileTags.empty() && artifact->fileTags().contains("mocable")) { if (isHeaderFile) { scanResult.additionalFileTags.insert(m_tags.moc_hpp); } else if (artifact->fileTags().contains(m_tags.cpp) || artifact->fileTags().contains(m_tags.cppCombine) || artifact->fileTags().contains(m_tags.objcpp) || artifact->fileTags().contains(m_tags.objcppCombine)) { scanResult.additionalFileTags.insert(m_tags.moc_cpp); } } if (!scanResult.additionalFileTags.empty()) { if (isHeaderFile) { if (scanResult.additionalFileTags.contains(m_tags.moc_hpp)) hasQObjectMacro = true; if (scanResult.additionalFileTags.contains(m_tags.moc_hpp_plugin)) { hasQObjectMacro = true; hasPluginMetaDataMacro = true; } findIncludedMocCppFiles(); if (!m_includedMocCppFiles.contains(FileInfo::completeBaseName(artifact->fileName()))) mustCompile = true; } else { if (scanResult.additionalFileTags.contains(m_tags.moc_cpp)) hasQObjectMacro = true; if (scanResult.additionalFileTags.contains(m_tags.moc_cpp_plugin)) { hasQObjectMacro = true; hasPluginMetaDataMacro = true; } } } qCDebug(lcMocScan) << "hasQObjectMacro:" << hasQObjectMacro << "mustCompile:" << mustCompile << "hasPluginMetaDataMacro:" << hasPluginMetaDataMacro; QScriptValue obj = engine->newObject(); obj.setProperty(QStringLiteral("hasQObjectMacro"), hasQObjectMacro); obj.setProperty(QStringLiteral("mustCompile"), mustCompile); obj.setProperty(QStringLiteral("hasPluginMetaDataMacro"), hasPluginMetaDataMacro); static_cast(engine)->setUsesIo(); return obj; } } // namespace Internal } // namespace qbs