Files
qt-creator/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
Christian Kandeler a34125ae11 CMake/qmake: Fix target triple for iOS targets
Neither cmake nor qmake know the full iOS compiler command line, so we
have to construct the target triple for the code model ourselves.

Fixes: QTCREATORBUG-28278
Change-Id: I6cac06f340e9388de5c86509a8df4ac00eef87cd
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
2022-10-13 13:28:52 +00:00

2111 lines
79 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "qmakeparsernodes.h"
#include "qmakeproject.h"
#include "qmakeprojectmanagerconstants.h"
#include "qmakeprojectmanagertr.h"
#include <android/androidconstants.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/iversioncontrol.h>
#include <coreplugin/vcsmanager.h>
#include <cppeditor/cppeditorconstants.h>
#include <projectexplorer/editorconfiguration.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <qtsupport/profilereader.h>
#include <texteditor/icodestylepreferences.h>
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
#include <utils/QtConcurrentTools>
#include <utils/algorithm.h>
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
#include <utils/mimeutils.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/stringutils.h>
#include <utils/temporarydirectory.h>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QTextCodec>
#include <QXmlStreamWriter>
using namespace Core;
using namespace ProjectExplorer;
using namespace QmakeProjectManager;
using namespace QmakeProjectManager::Internal;
using namespace QMakeInternal;
using namespace Utils;
namespace QmakeProjectManager {
static Q_LOGGING_CATEGORY(qmakeParse, "qtc.qmake.parsing", QtWarningMsg);
size_t qHash(Variable key, uint seed) { return ::qHash(static_cast<int>(key), seed); }
size_t qHash(FileOrigin fo) { return ::qHash(int(fo)); }
namespace Internal {
Q_LOGGING_CATEGORY(qmakeNodesLog, "qtc.qmake.nodes", QtWarningMsg)
class QmakeEvalInput
{
public:
QString projectDir;
FilePath projectFilePath;
FilePath buildDirectory;
FilePath sysroot;
QtSupport::ProFileReader *readerExact;
QtSupport::ProFileReader *readerCumulative;
QMakeGlobals *qmakeGlobals;
QMakeVfs *qmakeVfs;
QSet<FilePath> parentFilePaths;
bool includedInExcactParse;
};
class QmakePriFileEvalResult
{
public:
QSet<FilePath> folders;
QSet<FilePath> recursiveEnumerateFiles;
QMap<FileType, QSet<FilePath>> foundFilesExact;
QMap<FileType, QSet<FilePath>> foundFilesCumulative;
};
class QmakeIncludedPriFile
{
public:
ProFile *proFile;
FilePath name;
QmakePriFileEvalResult result;
QMap<FilePath, QmakeIncludedPriFile *> children;
~QmakeIncludedPriFile()
{
qDeleteAll(children);
}
};
class QmakeEvalResult
{
public:
~QmakeEvalResult() { qDeleteAll(directChildren); }
enum EvalResultState { EvalAbort, EvalFail, EvalPartial, EvalOk };
EvalResultState state;
ProjectType projectType;
QStringList subProjectsNotToDeploy;
QSet<FilePath> exactSubdirs;
QmakeIncludedPriFile includedFiles;
TargetInformation targetInformation;
InstallsList installsList;
QHash<Variable, QStringList> newVarValues;
QStringList errors;
QSet<QString> directoriesWithWildcards;
QList<QmakePriFile *> directChildren;
QList<QPair<QmakePriFile *, QmakePriFileEvalResult>> priFiles;
QList<QmakeProFile *> proFiles;
};
} // namespace Internal
QmakePriFile::QmakePriFile(QmakeBuildSystem *buildSystem, QmakeProFile *qmakeProFile,
const FilePath &filePath) : m_filePath(filePath)
{
finishInitialization(buildSystem, qmakeProFile);
}
QmakePriFile::QmakePriFile(const FilePath &filePath) : m_filePath(filePath) { }
void QmakePriFile::finishInitialization(QmakeBuildSystem *buildSystem, QmakeProFile *qmakeProFile)
{
QTC_ASSERT(buildSystem, return);
m_buildSystem = buildSystem;
m_qmakeProFile = qmakeProFile;
}
FilePath QmakePriFile::filePath() const
{
return m_filePath;
}
FilePath QmakePriFile::directoryPath() const
{
return filePath().parentDir();
}
QString QmakePriFile::displayName() const
{
return filePath().completeBaseName();
}
QmakePriFile *QmakePriFile::parent() const
{
return m_parent;
}
QmakeProject *QmakePriFile::project() const
{
return static_cast<QmakeProject *>(m_buildSystem->project());
}
const QVector<QmakePriFile *> QmakePriFile::children() const
{
return m_children;
}
QmakePriFile *QmakePriFile::findPriFile(const FilePath &fileName)
{
if (fileName == filePath())
return this;
for (QmakePriFile *n : std::as_const(m_children)) {
if (QmakePriFile *result = n->findPriFile(fileName))
return result;
}
return nullptr;
}
const QmakePriFile *QmakePriFile::findPriFile(const FilePath &fileName) const
{
if (fileName == filePath())
return this;
for (const QmakePriFile *n : std::as_const(m_children)) {
if (const QmakePriFile *result = n->findPriFile(fileName))
return result;
}
return nullptr;
}
void QmakePriFile::makeEmpty()
{
qDeleteAll(m_children);
m_children.clear();
}
SourceFiles QmakePriFile::files(const FileType &type) const
{
return m_files.value(type);
}
const QSet<FilePath> QmakePriFile::collectFiles(const FileType &type) const
{
QSet<FilePath> allFiles = transform(files(type),
[](const SourceFile &sf) { return sf.first; });
for (const QmakePriFile * const priFile : std::as_const(m_children)) {
if (!dynamic_cast<const QmakeProFile *>(priFile))
allFiles.unite(priFile->collectFiles(type));
}
return allFiles;
}
QmakePriFile::~QmakePriFile()
{
watchFolders( {} );
qDeleteAll(m_children);
}
void QmakePriFile::scheduleUpdate()
{
QTC_ASSERT(m_buildSystem, return);
QtSupport::ProFileCacheManager::instance()->discardFile(
filePath().toString(), m_buildSystem->qmakeVfs());
m_qmakeProFile->scheduleUpdate(QmakeProFile::ParseLater);
}
QStringList QmakePriFile::baseVPaths(QtSupport::ProFileReader *reader, const QString &projectDir, const QString &buildDir)
{
QStringList result;
if (!reader)
return result;
result += reader->absolutePathValues(QLatin1String("VPATH"), projectDir);
result << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
result << buildDir;
result.removeDuplicates();
return result;
}
QStringList QmakePriFile::fullVPaths(const QStringList &baseVPaths, QtSupport::ProFileReader *reader,
const QString &qmakeVariable, const QString &projectDir)
{
QStringList vPaths;
if (!reader)
return vPaths;
vPaths = reader->absolutePathValues(QLatin1String("VPATH_") + qmakeVariable, projectDir);
vPaths += baseVPaths;
vPaths.removeDuplicates();
return vPaths;
}
QSet<FilePath> QmakePriFile::recursiveEnumerate(const QString &folder)
{
QSet<FilePath> result;
QDir dir(folder);
dir.setFilter(dir.filter() | QDir::NoDotAndDotDot);
const QFileInfoList entries = dir.entryInfoList();
for (const QFileInfo &file : entries) {
if (file.isDir() && !file.isSymLink())
result += recursiveEnumerate(file.absoluteFilePath());
else if (!Core::EditorManager::isAutoSaveFile(file.fileName()))
result += FilePath::fromFileInfo(file);
}
return result;
}
static QStringList fileListForVar(
const QHash<QString, QVector<ProFileEvaluator::SourceFile>> &sourceFiles,
const QString &varName)
{
const QVector<ProFileEvaluator::SourceFile> &sources = sourceFiles[varName];
QStringList result;
result.reserve(sources.size());
for (const ProFileEvaluator::SourceFile &sf : sources)
result << sf.fileName;
return result;
}
void QmakePriFile::extractSources(
QHash<int, QmakePriFileEvalResult *> proToResult, QmakePriFileEvalResult *fallback,
QVector<ProFileEvaluator::SourceFile> sourceFiles, FileType type, bool cumulative)
{
for (const ProFileEvaluator::SourceFile &source : std::as_const(sourceFiles)) {
auto *result = proToResult.value(source.proFileId);
if (!result)
result = fallback;
auto &foundFiles = cumulative ? result->foundFilesCumulative : result->foundFilesExact;
foundFiles[type].insert(FilePath::fromString(source.fileName));
}
}
void QmakePriFile::extractInstalls(
QHash<int, QmakePriFileEvalResult *> proToResult, QmakePriFileEvalResult *fallback,
const InstallsList &installList)
{
for (const InstallsItem &item : installList.items) {
for (const ProFileEvaluator::SourceFile &source : item.files) {
auto *result = proToResult.value(source.proFileId);
if (!result)
result = fallback;
result->folders.insert(FilePath::fromString(source.fileName));
}
}
}
void QmakePriFile::processValues(QmakePriFileEvalResult &result)
{
// Remove non existing items and non folders
auto it = result.folders.begin();
while (it != result.folders.end()) {
QFileInfo fi((*it).toFileInfo());
if (fi.exists()) {
if (fi.isDir()) {
result.recursiveEnumerateFiles += recursiveEnumerate((*it).toString());
// keep directories
++it;
} else {
// move files directly to recursiveEnumerateFiles
result.recursiveEnumerateFiles += (*it);
it = result.folders.erase(it);
}
} else {
// do remove non exsting stuff
it = result.folders.erase(it);
}
}
for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) {
auto type = static_cast<FileType>(i);
for (QSet<FilePath> * const foundFiles
: {&result.foundFilesExact[type], &result.foundFilesCumulative[type]}) {
result.recursiveEnumerateFiles.subtract(*foundFiles);
QSet<FilePath> newFilePaths = filterFilesProVariables(type, *foundFiles);
newFilePaths += filterFilesRecursiveEnumerata(type, result.recursiveEnumerateFiles);
*foundFiles = newFilePaths;
}
}
}
void QmakePriFile::update(const Internal::QmakePriFileEvalResult &result)
{
m_recursiveEnumerateFiles = result.recursiveEnumerateFiles;
watchFolders(result.folders);
for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) {
const auto type = static_cast<FileType>(i);
SourceFiles &files = m_files[type];
files.clear();
const QSet<FilePath> exactFps = result.foundFilesExact.value(type);
for (const FilePath &exactFp : exactFps)
files.insert({exactFp, FileOrigin::ExactParse});
for (const FilePath &cumulativeFp : result.foundFilesCumulative.value(type)) {
if (!exactFps.contains(cumulativeFp))
files.insert({cumulativeFp, FileOrigin::CumulativeParse});
}
}
}
void QmakePriFile::watchFolders(const QSet<FilePath> &folders)
{
const QSet<QString> folderStrings = Utils::transform(folders, &FilePath::toString);
QSet<QString> toUnwatch = m_watchedFolders;
toUnwatch.subtract(folderStrings);
QSet<QString> toWatch = folderStrings;
toWatch.subtract(m_watchedFolders);
if (m_buildSystem) {
// Check needed on early exit of QmakeProFile::applyEvaluate?
m_buildSystem->unwatchFolders(Utils::toList(toUnwatch), this);
m_buildSystem->watchFolders(Utils::toList(toWatch), this);
}
m_watchedFolders = folderStrings;
}
QString QmakePriFile::continuationIndent() const
{
const EditorConfiguration *editorConf = project()->editorConfiguration();
const TextEditor::TabSettings &tabSettings = editorConf->useGlobalSettings()
? TextEditor::TextEditorSettings::codeStyle()->tabSettings()
: editorConf->codeStyle()->tabSettings();
if (tabSettings.m_continuationAlignBehavior == TextEditor::TabSettings::ContinuationAlignWithIndent
&& tabSettings.m_tabPolicy == TextEditor::TabSettings::TabsOnlyTabPolicy) {
return QString("\t");
}
return QString(tabSettings.m_indentSize, ' ');
}
QmakeBuildSystem *QmakePriFile::buildSystem() const
{
return m_buildSystem;
}
bool QmakePriFile::knowsFile(const FilePath &filePath) const
{
return m_recursiveEnumerateFiles.contains(filePath);
}
bool QmakePriFile::folderChanged(const QString &changedFolder, const QSet<FilePath> &newFiles)
{
qCDebug(qmakeParse()) << "QmakePriFile::folderChanged";
const QSet<FilePath> addedFiles = newFiles - m_recursiveEnumerateFiles;
const QSet<FilePath> removedFiles = Utils::filtered(m_recursiveEnumerateFiles - newFiles,
[changedFolder](const FilePath &file) {
return file.isChildOf(FilePath::fromString(changedFolder));
});
if (addedFiles.isEmpty() && removedFiles.isEmpty())
return false;
m_recursiveEnumerateFiles = newFiles;
// Apply the differences per file type
for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) {
auto type = static_cast<FileType>(i);
const QSet<FilePath> add = filterFilesRecursiveEnumerata(type, addedFiles);
const QSet<FilePath> remove = filterFilesRecursiveEnumerata(type, removedFiles);
if (!add.isEmpty() || !remove.isEmpty()) {
qCDebug(qmakeParse()) << "For type" << static_cast<int>(type) <<"\n"
<< "added files" << add << "\n"
<< "removed files" << remove;
SourceFiles &currentFiles = m_files[type];
for (const FilePath &fp : add) {
if (!contains(currentFiles, [&fp](const SourceFile &sf) { return sf.first == fp; }))
currentFiles.insert({fp, FileOrigin::ExactParse});
}
for (const FilePath &fp : remove) {
const auto it = std::find_if(currentFiles.begin(), currentFiles.end(),
[&fp](const SourceFile &sf) {
return sf.first == fp; });
if (it != currentFiles.end())
currentFiles.erase(it);
}
}
}
return true;
}
bool QmakePriFile::deploysFolder(const QString &folder) const
{
QString f = folder;
const QChar slash = QLatin1Char('/');
if (!f.endsWith(slash))
f.append(slash);
for (const QString &wf : std::as_const(m_watchedFolders)) {
if (f.startsWith(wf)
&& (wf.endsWith(slash)
|| (wf.length() < f.length() && f.at(wf.length()) == slash)))
return true;
}
return false;
}
QVector<QmakePriFile *> QmakePriFile::subPriFilesExact() const
{
return Utils::filtered(m_children, &QmakePriFile::includedInExactParse);
}
QmakeProFile *QmakePriFile::proFile() const
{
return m_qmakeProFile;
}
bool QmakePriFile::includedInExactParse() const
{
return m_includedInExactParse;
}
void QmakePriFile::setIncludedInExactParse(bool b)
{
m_includedInExactParse = b;
}
bool QmakePriFile::canAddSubProject(const FilePath &proFilePath) const
{
return proFilePath.suffix() == "pro" || proFilePath.suffix() == "pri";
}
static FilePath simplifyProFilePath(const FilePath &proFilePath)
{
// if proFilePath is like: _path_/projectName/projectName.pro
// we simplify it to: _path_/projectName
QFileInfo fi = proFilePath.toFileInfo(); // FIXME
const QString parentPath = fi.absolutePath();
QFileInfo parentFi(parentPath);
if (parentFi.fileName() == fi.completeBaseName())
return FilePath::fromString(parentPath);
return proFilePath;
}
bool QmakePriFile::addSubProject(const FilePath &proFile)
{
FilePaths uniqueProFilePaths;
if (!m_recursiveEnumerateFiles.contains(proFile))
uniqueProFilePaths.append(simplifyProFilePath(proFile));
FilePaths failedFiles;
changeFiles(QLatin1String(Constants::PROFILE_MIMETYPE), uniqueProFilePaths, &failedFiles, AddToProFile);
return failedFiles.isEmpty();
}
bool QmakePriFile::removeSubProjects(const FilePath &proFilePath)
{
FilePaths failedOriginalFiles;
changeFiles(QLatin1String(Constants::PROFILE_MIMETYPE), {proFilePath}, &failedOriginalFiles, RemoveFromProFile);
FilePaths simplifiedProFiles = Utils::transform(failedOriginalFiles, &simplifyProFilePath);
FilePaths failedSimplifiedFiles;
changeFiles(QLatin1String(Constants::PROFILE_MIMETYPE), simplifiedProFiles, &failedSimplifiedFiles, RemoveFromProFile);
return failedSimplifiedFiles.isEmpty();
}
bool QmakePriFile::addFiles(const FilePaths &filePaths, FilePaths *notAdded)
{
// If a file is already referenced in the .pro file then we don't add them.
// That ignores scopes and which variable was used to reference the file
// So it's obviously a bit limited, but in those cases you need to edit the
// project files manually anyway.
using TypeFileMap = QMap<QString, FilePaths>;
// Split into lists by file type and bulk-add them.
TypeFileMap typeFileMap;
for (const FilePath &file : filePaths) {
const MimeType mt = Utils::mimeTypeForFile(file);
typeFileMap[mt.name()] << file;
}
FilePaths failedFiles;
for (auto it = typeFileMap.constBegin(); it != typeFileMap.constEnd(); ++it) {
const FilePaths &typeFiles = *it;
FilePaths qrcFiles; // the list of qrc files referenced from ui files
if (it.key() == QLatin1String(ProjectExplorer::Constants::RESOURCE_MIMETYPE)) {
for (const FilePath &formFile : typeFiles) {
const FilePaths resourceFiles = formResources(formFile);
for (const FilePath &resourceFile : resourceFiles)
if (!qrcFiles.contains(resourceFile))
qrcFiles.append(resourceFile);
}
}
FilePaths uniqueQrcFiles;
for (const FilePath &file : std::as_const(qrcFiles)) {
if (!m_recursiveEnumerateFiles.contains(file))
uniqueQrcFiles.append(file);
}
FilePaths uniqueFilePaths;
for (const FilePath &file : typeFiles) {
if (!m_recursiveEnumerateFiles.contains(file))
uniqueFilePaths.append(file);
}
FilePath::sort(uniqueFilePaths);
changeFiles(it.key(), uniqueFilePaths, &failedFiles, AddToProFile);
if (notAdded)
*notAdded += failedFiles;
changeFiles(QLatin1String(ProjectExplorer::Constants::RESOURCE_MIMETYPE), uniqueQrcFiles, &failedFiles, AddToProFile);
if (notAdded)
*notAdded += failedFiles;
}
return failedFiles.isEmpty();
}
bool QmakePriFile::removeFiles(const FilePaths &filePaths, FilePaths *notRemoved)
{
FilePaths failedFiles;
using TypeFileMap = QMap<QString, FilePaths>;
// Split into lists by file type and bulk-add them.
TypeFileMap typeFileMap;
for (const FilePath &file : filePaths) {
const MimeType mt = Utils::mimeTypeForFile(file);
typeFileMap[mt.name()] << file;
}
const QStringList types = typeFileMap.keys();
for (const QString &type : types) {
const FilePaths typeFiles = typeFileMap.value(type);
changeFiles(type, typeFiles, &failedFiles, RemoveFromProFile);
if (notRemoved)
*notRemoved = failedFiles;
}
return failedFiles.isEmpty();
}
bool QmakePriFile::deleteFiles(const FilePaths &filePaths)
{
removeFiles(filePaths);
return true;
}
bool QmakePriFile::canRenameFile(const FilePath &oldFilePath, const FilePath &newFilePath)
{
if (newFilePath.isEmpty())
return false;
bool changeProFileOptional = deploysFolder(oldFilePath.absolutePath().toString());
if (changeProFileOptional)
return true;
return renameFile(oldFilePath, newFilePath, Change::TestOnly);
}
bool QmakePriFile::renameFile(const FilePath &oldFilePath, const FilePath &newFilePath)
{
if (newFilePath.isEmpty())
return false;
bool changeProFileOptional = deploysFolder(oldFilePath.absolutePath().toString());
if (renameFile(oldFilePath, newFilePath, Change::Save))
return true;
return changeProFileOptional;
}
bool QmakePriFile::addDependencies(const QStringList &dependencies)
{
if (dependencies.isEmpty())
return true;
if (!prepareForChange())
return false;
QStringList qtDependencies = filtered(dependencies, [](const QString &dep) {
return dep.length() > 3 && dep.startsWith("Qt.");
});
qtDependencies = transform(qtDependencies, [](const QString &dep) {
return dep.mid(3);
});
qtDependencies.removeOne("core");
if (qtDependencies.isEmpty())
return true;
const QPair<ProFile *, QStringList> pair = readProFile();
ProFile * const includeFile = pair.first;
if (!includeFile)
return false;
QStringList lines = pair.second;
const QString indent = continuationIndent();
const ProWriter::PutFlags appendFlags(ProWriter::AppendValues | ProWriter::AppendOperator);
if (!proFile()->variableValue(Variable::Config).contains("qt")) {
if (lines.removeAll("CONFIG -= qt") == 0) {
ProWriter::putVarValues(includeFile, &lines, {"qt"}, "CONFIG", appendFlags,
QString(), indent);
}
}
const QStringList currentQtDependencies = proFile()->variableValue(Variable::Qt);
qtDependencies = filtered(qtDependencies, [currentQtDependencies](const QString &dep) {
return !currentQtDependencies.contains(dep);
});
if (!qtDependencies.isEmpty()) {
ProWriter::putVarValues(includeFile, &lines, qtDependencies, "QT", appendFlags,
QString(), indent);
}
save(lines);
includeFile->deref();
return true;
}
bool QmakePriFile::saveModifiedEditors()
{
Core::IDocument *document = Core::DocumentModel::documentForFilePath(filePath());
if (!document || !document->isModified())
return true;
if (!Core::DocumentManager::saveDocument(document))
return false;
// force instant reload of ourselves
QtSupport::ProFileCacheManager::instance()->discardFile(
filePath().toString(), m_buildSystem->qmakeVfs());
m_buildSystem->notifyChanged(filePath());
return true;
}
FilePaths QmakePriFile::formResources(const FilePath &formFile) const
{
QStringList resourceFiles;
QFile file(formFile.toString());
if (!file.open(QIODevice::ReadOnly))
return {};
QXmlStreamReader reader(&file);
QFileInfo fi(formFile.toString());
QDir formDir = fi.absoluteDir();
while (!reader.atEnd()) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == QLatin1String("iconset")) {
const QXmlStreamAttributes attributes = reader.attributes();
if (attributes.hasAttribute(QLatin1String("resource")))
resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
attributes.value(QLatin1String("resource")).toString())));
} else if (reader.name() == QLatin1String("include")) {
const QXmlStreamAttributes attributes = reader.attributes();
if (attributes.hasAttribute(QLatin1String("location")))
resourceFiles.append(QDir::cleanPath(formDir.absoluteFilePath(
attributes.value(QLatin1String("location")).toString())));
}
}
}
if (reader.hasError())
qWarning() << "Could not read form file:" << formFile;
return FileUtils::toFilePathList(resourceFiles);
}
bool QmakePriFile::ensureWriteableProFile(const QString &file)
{
// Ensure that the file is not read only
QFileInfo fi(file);
if (!fi.isWritable()) {
// Try via vcs manager
Core::IVersionControl *versionControl =
Core::VcsManager::findVersionControlForDirectory(FilePath::fromString(fi.absolutePath()));
if (!versionControl || !versionControl->vcsOpen(FilePath::fromString(file))) {
bool makeWritable = QFile::setPermissions(file, fi.permissions() | QFile::WriteUser);
if (!makeWritable) {
QMessageBox::warning(Core::ICore::dialogParent(),
Tr::tr("Failed"),
Tr::tr("Could not write project file %1.").arg(file));
return false;
}
}
}
return true;
}
QPair<ProFile *, QStringList> QmakePriFile::readProFile()
{
QStringList lines;
ProFile *includeFile = nullptr;
{
QString contents;
{
QString errorMsg;
if (TextFileFormat::readFile(filePath(),
Core::EditorManager::defaultTextCodec(),
&contents,
&m_textFormat,
&errorMsg)
!= TextFileFormat::ReadSuccess) {
QmakeBuildSystem::proFileParseError(errorMsg, filePath());
return {includeFile, lines};
}
lines = contents.split('\n');
}
QMakeVfs vfs;
QtSupport::ProMessageHandler handler;
QMakeParser parser(nullptr, &vfs, &handler);
includeFile = parser.parsedProBlock(QStringView(contents),
0,
filePath().toString(),
1);
}
return {includeFile, lines};
}
bool QmakePriFile::prepareForChange()
{
return saveModifiedEditors() && ensureWriteableProFile(filePath().toString());
}
bool QmakePriFile::renameFile(const FilePath &oldFilePath, const FilePath &newFilePath, Change mode)
{
if (!prepareForChange())
return false;
QPair<ProFile *, QStringList> pair = readProFile();
ProFile *includeFile = pair.first;
QStringList lines = pair.second;
if (!includeFile)
return false;
QDir priFileDir = QDir(m_qmakeProFile->directoryPath().toString());
ProWriter::VarLocations removedLocations;
const QStringList notChanged = ProWriter::removeFiles(includeFile,
&lines,
priFileDir,
{oldFilePath.toString()},
varNamesForRemoving(),
&removedLocations);
includeFile->deref();
if (!notChanged.isEmpty())
return false;
QTC_ASSERT(!removedLocations.isEmpty(), return false);
int endLine = lines.count();
reverseForeach(removedLocations,
[this, &newFilePath, &lines, &endLine](const ProWriter::VarLocation &loc) {
QStringList currentLines = lines.mid(loc.second, endLine - loc.second);
const QString currentContents = currentLines.join('\n');
// Reparse necessary due to changed contents.
QMakeParser parser(nullptr, nullptr, nullptr);
ProFile *const proFile = parser.parsedProBlock(QStringView(currentContents),
0,
filePath().toString(),
1,
QMakeParser::FullGrammar);
QTC_ASSERT(proFile, return); // The file should still be valid after what we did.
ProWriter::addFiles(proFile,
&currentLines,
{newFilePath.toString()},
loc.first,
continuationIndent());
lines = lines.mid(0, loc.second) + currentLines + lines.mid(endLine);
endLine = loc.second;
proFile->deref();
});
if (mode == Change::Save)
save(lines);
return true;
}
void QmakePriFile::changeFiles(const QString &mimeType,
const FilePaths &filePaths,
FilePaths *notChanged,
ChangeType change, Change mode)
{
if (filePaths.isEmpty())
return;
*notChanged = filePaths;
// Check for modified editors
if (!prepareForChange())
return;
QPair<ProFile *, QStringList> pair = readProFile();
ProFile *includeFile = pair.first;
QStringList lines = pair.second;
if (!includeFile)
return;
qCDebug(qmakeNodesLog) << Q_FUNC_INFO << "mime type:" << mimeType << "file paths:"
<< filePaths << "change type:" << int(change) << "mode:" << int(mode);
if (change == AddToProFile) {
// Use the first variable for adding.
ProWriter::addFiles(includeFile, &lines,
Utils::transform(filePaths, &FilePath::toString),
varNameForAdding(mimeType),
continuationIndent());
notChanged->clear();
} else { // RemoveFromProFile
QDir priFileDir = QDir(m_qmakeProFile->directoryPath().toString());
*notChanged = FileUtils::toFilePathList(
ProWriter::removeFiles(includeFile,
&lines,
priFileDir,
Utils::transform(filePaths, &FilePath::toString),
varNamesForRemoving()));
}
// save file
if (mode == Change::Save)
save(lines);
includeFile->deref();
}
void QmakePriFile::addChild(QmakePriFile *pf)
{
QTC_ASSERT(!m_children.contains(pf), return);
QTC_ASSERT(!pf->parent(), return);
m_children.append(pf);
pf->setParent(this);
}
void QmakePriFile::setParent(QmakePriFile *p)
{
QTC_ASSERT(!m_parent, return);
m_parent = p;
}
bool QmakePriFile::setProVariable(const QString &var, const QStringList &values, const QString &scope, int flags)
{
if (!prepareForChange())
return false;
QPair<ProFile *, QStringList> pair = readProFile();
ProFile *includeFile = pair.first;
QStringList lines = pair.second;
if (!includeFile)
return false;
ProWriter::putVarValues(includeFile, &lines, values, var,
ProWriter::PutFlags(flags),
scope, continuationIndent());
save(lines);
includeFile->deref();
return true;
}
void QmakePriFile::save(const QStringList &lines)
{
{
QTC_ASSERT(m_textFormat.codec, return);
FileChangeBlocker changeGuard(filePath());
QString errorMsg;
if (!m_textFormat.writeFile(filePath(), lines.join('\n'), &errorMsg)) {
QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("File Error"), errorMsg);
}
}
// This is a hack.
// We are saving twice in a very short timeframe, once the editor and once the ProFile.
// So the modification time might not change between those two saves.
// We manually tell each editor to reload it's file.
// (The .pro files are notified by the file system watcher.)
QStringList errorStrings;
Core::IDocument *document = Core::DocumentModel::documentForFilePath(filePath());
if (document) {
QString errorString;
if (!document->reload(&errorString, Core::IDocument::FlagReload, Core::IDocument::TypeContents))
errorStrings << errorString;
}
if (!errorStrings.isEmpty())
QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("File Error"),
errorStrings.join(QLatin1Char('\n')));
}
QStringList QmakePriFile::varNames(FileType type, QtSupport::ProFileReader *readerExact)
{
QStringList vars;
switch (type) {
case FileType::Header:
vars << "HEADERS" << "OBJECTIVE_HEADERS" << "PRECOMPILED_HEADER";
break;
case FileType::Source: {
vars << QLatin1String("SOURCES");
const QStringList listOfExtraCompilers = readerExact->values("QMAKE_EXTRA_COMPILERS");
for (const QString &var : listOfExtraCompilers) {
const QStringList inputs = readerExact->values(var + QLatin1String(".input"));
for (const QString &input : inputs)
// FORMS, RESOURCES, and STATECHARTS are handled below, HEADERS and SOURCES above
if (input != "FORMS"
&& input != "STATECHARTS"
&& input != "RESOURCES"
&& input != "SOURCES"
&& input != "HEADERS"
&& input != "OBJECTIVE_HEADERS"
&& input != "PRECOMPILED_HEADER") {
vars << input;
}
}
break;
}
case FileType::Resource:
vars << QLatin1String("RESOURCES");
break;
case FileType::Form:
vars << QLatin1String("FORMS");
break;
case FileType::StateChart:
vars << QLatin1String("STATECHARTS");
break;
case FileType::Project:
vars << QLatin1String("SUBDIRS");
break;
case FileType::QML:
vars << QLatin1String("OTHER_FILES");
vars << QLatin1String("DISTFILES");
break;
default:
vars << "DISTFILES" << "ICON" << "OTHER_FILES" << "QMAKE_INFO_PLIST" << "TRANSLATIONS";
break;
}
return vars;
}
//!
//! \brief QmakePriFile::varNames
//! \param mimeType
//! \return the qmake variable name for the mime type
//! Note: Only used for adding.
//!
QString QmakePriFile::varNameForAdding(const QString &mimeType)
{
if (mimeType == QLatin1String(ProjectExplorer::Constants::CPP_HEADER_MIMETYPE)
|| mimeType == QLatin1String(ProjectExplorer::Constants::C_HEADER_MIMETYPE)) {
return QLatin1String("HEADERS");
}
if (mimeType == QLatin1String(ProjectExplorer::Constants::CPP_SOURCE_MIMETYPE)
|| mimeType == QLatin1String(CppEditor::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
|| mimeType == QLatin1String(ProjectExplorer::Constants::C_SOURCE_MIMETYPE)) {
return QLatin1String("SOURCES");
}
if (mimeType == QLatin1String(ProjectExplorer::Constants::RESOURCE_MIMETYPE))
return QLatin1String("RESOURCES");
if (mimeType == QLatin1String(ProjectExplorer::Constants::FORM_MIMETYPE))
return QLatin1String("FORMS");
if (mimeType == QLatin1String(ProjectExplorer::Constants::QML_MIMETYPE)
|| mimeType == QLatin1String(ProjectExplorer::Constants::QMLUI_MIMETYPE)) {
return QLatin1String("DISTFILES");
}
if (mimeType == QLatin1String(ProjectExplorer::Constants::SCXML_MIMETYPE))
return QLatin1String("STATECHARTS");
if (mimeType == QLatin1String(Constants::PROFILE_MIMETYPE))
return QLatin1String("SUBDIRS");
return QLatin1String("DISTFILES");
}
//!
//! \brief QmakePriFile::varNamesForRemoving
//! \return all qmake variables which are displayed in the project tree
//! Note: Only used for removing.
//!
QStringList QmakePriFile::varNamesForRemoving()
{
QStringList vars;
vars << QLatin1String("HEADERS");
vars << QLatin1String("OBJECTIVE_HEADERS");
vars << QLatin1String("PRECOMPILED_HEADER");
vars << QLatin1String("SOURCES");
vars << QLatin1String("OBJECTIVE_SOURCES");
vars << QLatin1String("RESOURCES");
vars << QLatin1String("FORMS");
vars << QLatin1String("OTHER_FILES");
vars << QLatin1String("SUBDIRS");
vars << QLatin1String("DISTFILES");
vars << QLatin1String("ICON");
vars << QLatin1String("QMAKE_INFO_PLIST");
vars << QLatin1String("STATECHARTS");
return vars;
}
QSet<FilePath> QmakePriFile::filterFilesProVariables(FileType fileType, const QSet<FilePath> &files)
{
if (fileType != FileType::QML && fileType != FileType::Unknown)
return files;
QSet<FilePath> result;
if (fileType == FileType::QML) {
for (const FilePath &file : files)
if (file.toString().endsWith(QLatin1String(".qml")))
result << file;
} else {
for (const FilePath &file : files)
if (!file.toString().endsWith(QLatin1String(".qml")))
result << file;
}
return result;
}
QSet<FilePath> QmakePriFile::filterFilesRecursiveEnumerata(FileType fileType, const QSet<FilePath> &files)
{
QSet<FilePath> result;
if (fileType != FileType::QML && fileType != FileType::Unknown)
return result;
if (fileType == FileType::QML) {
for (const FilePath &file : files)
if (file.toString().endsWith(QLatin1String(".qml")))
result << file;
} else {
for (const FilePath &file : files)
if (!file.toString().endsWith(QLatin1String(".qml")))
result << file;
}
return result;
}
} // namespace QmakeProjectManager
static ProjectType proFileTemplateTypeToProjectType(ProFileEvaluator::TemplateType type)
{
switch (type) {
case ProFileEvaluator::TT_Unknown:
case ProFileEvaluator::TT_Application:
return ProjectType::ApplicationTemplate;
case ProFileEvaluator::TT_StaticLibrary:
return ProjectType::StaticLibraryTemplate;
case ProFileEvaluator::TT_SharedLibrary:
return ProjectType::SharedLibraryTemplate;
case ProFileEvaluator::TT_Script:
return ProjectType::ScriptTemplate;
case ProFileEvaluator::TT_Aux:
return ProjectType::AuxTemplate;
case ProFileEvaluator::TT_Subdirs:
return ProjectType::SubDirsTemplate;
default:
return ProjectType::Invalid;
}
}
QmakeProFile *QmakeProFile::findProFile(const FilePath &fileName)
{
return static_cast<QmakeProFile *>(findPriFile(fileName));
}
const QmakeProFile *QmakeProFile::findProFile(const FilePath &fileName) const
{
return static_cast<const QmakeProFile *>(findPriFile(fileName));
}
QByteArray QmakeProFile::cxxDefines() const
{
QByteArray result;
const QStringList defs = variableValue(Variable::Defines);
for (const QString &def : defs) {
// 'def' is shell input, so interpret it.
ProcessArgs::SplitError error = ProcessArgs::SplitOk;
const QStringList args = ProcessArgs::splitArgs(def, HostOsInfo::hostOs(), false, &error);
if (error != ProcessArgs::SplitOk || args.size() == 0)
continue;
result += "#define ";
const QString defInterpreted = args.first();
const int index = defInterpreted.indexOf(QLatin1Char('='));
if (index == -1) {
result += defInterpreted.toLatin1();
result += " 1\n";
} else {
const QString name = defInterpreted.left(index);
const QString value = defInterpreted.mid(index + 1);
result += name.toLatin1();
result += ' ';
result += value.toLocal8Bit();
result += '\n';
}
}
return result;
}
/*!
\class QmakeProFile
Implements abstract ProjectNode class
*/
QmakeProFile::QmakeProFile(QmakeBuildSystem *buildSystem, const FilePath &filePath) :
QmakePriFile(buildSystem, this, filePath)
{
}
QmakeProFile::QmakeProFile(const FilePath &filePath) : QmakePriFile(filePath) { }
QmakeProFile::~QmakeProFile()
{
qDeleteAll(m_extraCompilers);
cleanupFutureWatcher();
cleanupProFileReaders();
}
void QmakeProFile::cleanupFutureWatcher()
{
if (!m_parseFutureWatcher)
return;
m_parseFutureWatcher->disconnect();
m_parseFutureWatcher->cancel();
m_parseFutureWatcher->waitForFinished();
m_parseFutureWatcher->deleteLater();
m_parseFutureWatcher = nullptr;
m_buildSystem->decrementPendingEvaluateFutures();
}
void QmakeProFile::setupFutureWatcher()
{
QTC_ASSERT(!m_parseFutureWatcher, return);
m_parseFutureWatcher = new QFutureWatcher<Internal::QmakeEvalResultPtr>;
QObject::connect(m_parseFutureWatcher, &QFutureWatcherBase::finished, [this]() {
applyEvaluate(m_parseFutureWatcher->result());
cleanupFutureWatcher();
});
m_buildSystem->incrementPendingEvaluateFutures();
}
bool QmakeProFile::isParent(QmakeProFile *node)
{
while ((node = dynamic_cast<QmakeProFile *>(node->parent()))) {
if (node == this)
return true;
}
return false;
}
QString QmakeProFile::displayName() const
{
if (!m_displayName.isEmpty())
return m_displayName;
return QmakePriFile::displayName();
}
QList<QmakeProFile *> QmakeProFile::allProFiles()
{
QList<QmakeProFile *> result = { this };
for (QmakePriFile *c : std::as_const(m_children)) {
auto proC = dynamic_cast<QmakeProFile *>(c);
if (proC)
result.append(proC->allProFiles());
}
return result;
}
ProjectType QmakeProFile::projectType() const
{
return m_projectType;
}
QStringList QmakeProFile::variableValue(const Variable var) const
{
return m_varValues.value(var);
}
QString QmakeProFile::singleVariableValue(const Variable var) const
{
const QStringList &values = variableValue(var);
return values.isEmpty() ? QString() : values.first();
}
void QmakeProFile::setParseInProgressRecursive(bool b)
{
setParseInProgress(b);
for (QmakePriFile *c : children()) {
if (auto node = dynamic_cast<QmakeProFile *>(c))
node->setParseInProgressRecursive(b);
}
}
void QmakeProFile::setParseInProgress(bool b)
{
m_parseInProgress = b;
}
// Do note the absence of signal emission, always set validParse
// before setParseInProgress, as that will emit the signals
void QmakeProFile::setValidParseRecursive(bool b)
{
m_validParse = b;
for (QmakePriFile *c : children()) {
if (auto *node = dynamic_cast<QmakeProFile *>(c))
node->setValidParseRecursive(b);
}
}
bool QmakeProFile::validParse() const
{
return m_validParse;
}
bool QmakeProFile::parseInProgress() const
{
return m_parseInProgress;
}
void QmakeProFile::scheduleUpdate(QmakeProFile::AsyncUpdateDelay delay)
{
setParseInProgressRecursive(true);
m_buildSystem->scheduleAsyncUpdateFile(this, delay);
}
void QmakeProFile::asyncUpdate()
{
cleanupFutureWatcher();
setupFutureWatcher();
setupReader();
if (!includedInExactParse())
m_readerExact->setExact(false);
QmakeEvalInput input = evalInput();
QFuture<QmakeEvalResultPtr> future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
QThread::LowestPriority,
&QmakeProFile::asyncEvaluate, this, input);
m_parseFutureWatcher->setFuture(future);
}
bool QmakeProFile::isFileFromWildcard(const QString &filePath) const
{
const QFileInfo fileInfo(filePath);
const auto directoryIterator = m_wildcardDirectoryContents.constFind(fileInfo.path());
return (directoryIterator != m_wildcardDirectoryContents.end()
&& directoryIterator.value().contains(fileInfo.fileName()));
}
QmakeEvalInput QmakeProFile::evalInput() const
{
QmakeEvalInput input;
input.projectDir = directoryPath().toString();
input.projectFilePath = filePath();
input.buildDirectory = m_buildSystem->buildDir(m_filePath);
input.sysroot = FilePath::fromString(m_buildSystem->qmakeSysroot());
input.readerExact = m_readerExact;
input.readerCumulative = m_readerCumulative;
input.qmakeGlobals = m_buildSystem->qmakeGlobals();
input.qmakeVfs = m_buildSystem->qmakeVfs();
input.includedInExcactParse = includedInExactParse();
for (const QmakePriFile *pri = this; pri; pri = pri->parent())
input.parentFilePaths.insert(pri->filePath());
return input;
}
void QmakeProFile::setupReader()
{
Q_ASSERT(!m_readerExact);
Q_ASSERT(!m_readerCumulative);
m_readerExact = m_buildSystem->createProFileReader(this);
m_readerCumulative = m_buildSystem->createProFileReader(this);
m_readerCumulative->setCumulative(true);
}
static bool evaluateOne(const QmakeEvalInput &input, ProFile *pro,
QtSupport::ProFileReader *reader, bool cumulative,
QtSupport::ProFileReader **buildPassReader)
{
if (!reader->accept(pro, QMakeEvaluator::LoadAll))
return false;
QStringList builds = reader->values(QLatin1String("BUILDS"));
if (builds.isEmpty()) {
*buildPassReader = reader;
} else {
QString build = builds.first();
QHash<QString, QStringList> basevars;
QStringList basecfgs = reader->values(build + QLatin1String(".CONFIG"));
basecfgs += build;
basecfgs += QLatin1String("build_pass");
basecfgs += "qtc_run";
basevars[QLatin1String("BUILD_PASS")] = QStringList(build);
QStringList buildname = reader->values(build + QLatin1String(".name"));
basevars[QLatin1String("BUILD_NAME")] = (buildname.isEmpty() ? QStringList(build) : buildname);
// We don't increase/decrease m_qmakeGlobalsRefCnt here, because the outer profilereaders keep m_qmakeGlobals alive anyway
auto bpReader = new QtSupport::ProFileReader(input.qmakeGlobals, input.qmakeVfs); // needs to access m_qmakeGlobals, m_qmakeVfs
// FIXME: Currently intentional.
// Core parts of the ProParser hard-assert on non-local items.
bpReader->setOutputDir(input.buildDirectory.path());
bpReader->setCumulative(cumulative);
bpReader->setExtraVars(basevars);
bpReader->setExtraConfigs(basecfgs);
if (bpReader->accept(pro, QMakeEvaluator::LoadAll))
*buildPassReader = bpReader;
else
delete bpReader;
}
return true;
}
QmakeEvalResultPtr QmakeProFile::evaluate(const QmakeEvalInput &input)
{
QmakeEvalResultPtr result(new QmakeEvalResult);
QtSupport::ProFileReader *exactBuildPassReader = nullptr;
QtSupport::ProFileReader *cumulativeBuildPassReader = nullptr;
ProFile *pro;
if ((pro = input.readerExact->parsedProFile(input.projectFilePath.toString()))) {
bool exactOk = evaluateOne(input, pro, input.readerExact, false, &exactBuildPassReader);
bool cumulOk = evaluateOne(input, pro, input.readerCumulative, true, &cumulativeBuildPassReader);
pro->deref();
result->state = exactOk ? QmakeEvalResult::EvalOk
: cumulOk ? QmakeEvalResult::EvalPartial : QmakeEvalResult::EvalFail;
} else {
result->state = QmakeEvalResult::EvalFail;
}
if (result->state == QmakeEvalResult::EvalFail)
return result;
result->includedFiles.proFile = pro;
result->includedFiles.name = input.projectFilePath;
QHash<int, QmakePriFileEvalResult *> proToResult;
result->projectType
= proFileTemplateTypeToProjectType(
(result->state == QmakeEvalResult::EvalOk ? input.readerExact
: input.readerCumulative)->templateType());
if (result->state == QmakeEvalResult::EvalOk) {
if (result->projectType == ProjectType::SubDirsTemplate) {
QStringList errors;
const FilePaths subDirs = subDirsPaths(input.readerExact, input.projectDir,
&result->subProjectsNotToDeploy, &errors);
result->errors.append(errors);
for (const FilePath &subDirName : subDirs) {
auto subDir = new QmakeIncludedPriFile;
subDir->proFile = nullptr;
subDir->name = subDirName;
result->includedFiles.children.insert(subDirName, subDir);
}
result->exactSubdirs = Utils::toSet(subDirs);
}
// Convert ProFileReader::includeFiles to IncludedPriFile structure
QHash<ProFile *, QVector<ProFile *>> includeFiles = input.readerExact->includeFiles();
QList<QmakeIncludedPriFile *> toBuild = {&result->includedFiles};
while (!toBuild.isEmpty()) {
QmakeIncludedPriFile *current = toBuild.takeFirst();
if (!current->proFile)
continue; // Don't attempt to map subdirs here
const QVector<ProFile *> children = includeFiles.value(current->proFile);
for (ProFile *child : children) {
const FilePath childName = FilePath::fromString(child->fileName());
auto it = current->children.find(childName);
if (it == current->children.end()) {
auto childTree = new QmakeIncludedPriFile;
childTree->proFile = child;
childTree->name = childName;
current->children.insert(childName, childTree);
proToResult[child->id()] = &childTree->result;
}
}
toBuild.append(current->children.values());
}
}
if (result->projectType == ProjectType::SubDirsTemplate) {
const FilePaths subDirs = subDirsPaths(input.readerCumulative, input.projectDir, nullptr, nullptr);
for (const FilePath &subDirName : subDirs) {
auto it = result->includedFiles.children.find(subDirName);
if (it == result->includedFiles.children.end()) {
auto subDir = new QmakeIncludedPriFile;
subDir->proFile = nullptr;
subDir->name = subDirName;
result->includedFiles.children.insert(subDirName, subDir);
}
}
}
// Add ProFileReader::includeFiles information from cumulative parse to IncludedPriFile structure
QHash<ProFile *, QVector<ProFile *>> includeFiles = input.readerCumulative->includeFiles();
QList<QmakeIncludedPriFile *> toBuild = {&result->includedFiles};
while (!toBuild.isEmpty()) {
QmakeIncludedPriFile *current = toBuild.takeFirst();
if (!current->proFile)
continue; // Don't attempt to map subdirs here
const QVector<ProFile *> children = includeFiles.value(current->proFile);
for (ProFile *child : children) {
const FilePath childName = FilePath::fromString(child->fileName());
auto it = current->children.find(childName);
if (it == current->children.end()) {
auto childTree = new QmakeIncludedPriFile;
childTree->proFile = child;
childTree->name = childName;
current->children.insert(childName, childTree);
proToResult[child->id()] = &childTree->result;
}
}
toBuild.append(current->children.values());
}
auto exactReader = exactBuildPassReader ? exactBuildPassReader : input.readerExact;
auto cumulativeReader = cumulativeBuildPassReader ? cumulativeBuildPassReader : input.readerCumulative;
QHash<QString, QVector<ProFileEvaluator::SourceFile>> exactSourceFiles;
QHash<QString, QVector<ProFileEvaluator::SourceFile>> cumulativeSourceFiles;
const QStringList baseVPathsExact
= baseVPaths(exactReader, input.projectDir, input.buildDirectory.toString());
const QStringList baseVPathsCumulative
= baseVPaths(cumulativeReader, input.projectDir, input.buildDirectory.toString());
for (int i = 0; i < static_cast<int>(FileType::FileTypeSize); ++i) {
const auto type = static_cast<FileType>(i);
const QStringList qmakeVariables = varNames(type, exactReader);
for (const QString &qmakeVariable : qmakeVariables) {
QHash<ProString, bool> handled;
if (result->state == QmakeEvalResult::EvalOk) {
const QStringList vPathsExact = fullVPaths(
baseVPathsExact, exactReader, qmakeVariable, input.projectDir);
auto sourceFiles = exactReader->absoluteFileValues(
qmakeVariable, input.projectDir, vPathsExact, &handled, result->directoriesWithWildcards);
exactSourceFiles[qmakeVariable] = sourceFiles;
extractSources(proToResult, &result->includedFiles.result, sourceFiles, type, false);
}
const QStringList vPathsCumulative = fullVPaths(
baseVPathsCumulative, cumulativeReader, qmakeVariable, input.projectDir);
auto sourceFiles = cumulativeReader->absoluteFileValues(
qmakeVariable, input.projectDir, vPathsCumulative, &handled, result->directoriesWithWildcards);
cumulativeSourceFiles[qmakeVariable] = sourceFiles;
extractSources(proToResult, &result->includedFiles.result, sourceFiles, type, true);
}
}
// This is used for two things:
// - Actual deployment, in which case we need exact values.
// - The project tree, in which case we also want exact values to avoid recursively
// watching bogus paths. However, we accept the values even if the evaluation
// failed, to at least have a best-effort result.
result->installsList = installsList(exactBuildPassReader, input.projectFilePath.toString(),
input.projectDir, input.buildDirectory.toString());
extractInstalls(proToResult, &result->includedFiles.result, result->installsList);
if (result->state == QmakeEvalResult::EvalOk) {
result->targetInformation = targetInformation(input.readerExact, exactBuildPassReader,
input.buildDirectory, input.projectFilePath);
// update other variables
result->newVarValues[Variable::Defines] = exactReader->values(QLatin1String("DEFINES"));
result->newVarValues[Variable::IncludePath] = includePaths(exactReader, input.sysroot,
input.buildDirectory, input.projectDir);
result->newVarValues[Variable::CppFlags] = exactReader->values(QLatin1String("QMAKE_CXXFLAGS"));
result->newVarValues[Variable::CFlags] = exactReader->values(QLatin1String("QMAKE_CFLAGS"));
result->newVarValues[Variable::ExactSource] =
fileListForVar(exactSourceFiles, QLatin1String("SOURCES")) +
fileListForVar(exactSourceFiles, QLatin1String("HEADERS")) +
fileListForVar(exactSourceFiles, QLatin1String("OBJECTIVE_HEADERS"));
result->newVarValues[Variable::CumulativeSource] =
fileListForVar(cumulativeSourceFiles, QLatin1String("SOURCES")) +
fileListForVar(cumulativeSourceFiles, QLatin1String("HEADERS")) +
fileListForVar(cumulativeSourceFiles, QLatin1String("OBJECTIVE_HEADERS"));
result->newVarValues[Variable::UiDir] = QStringList() << uiDirPath(exactReader, input.buildDirectory);
result->newVarValues[Variable::HeaderExtension] = QStringList() << exactReader->value(QLatin1String("QMAKE_EXT_H"));
result->newVarValues[Variable::CppExtension] = QStringList() << exactReader->value(QLatin1String("QMAKE_EXT_CPP"));
result->newVarValues[Variable::MocDir] = QStringList() << mocDirPath(exactReader, input.buildDirectory);
result->newVarValues[Variable::ExactResource] = fileListForVar(exactSourceFiles, QLatin1String("RESOURCES"));
result->newVarValues[Variable::CumulativeResource] = fileListForVar(cumulativeSourceFiles, QLatin1String("RESOURCES"));
result->newVarValues[Variable::PkgConfig] = exactReader->values(QLatin1String("PKGCONFIG"));
result->newVarValues[Variable::PrecompiledHeader] = ProFileEvaluator::sourcesToFiles(exactReader->fixifiedValues(
QLatin1String("PRECOMPILED_HEADER"), input.projectDir, input.buildDirectory.toString(), false));
result->newVarValues[Variable::LibDirectories] = libDirectories(exactReader);
result->newVarValues[Variable::Config] = exactReader->values(QLatin1String("CONFIG"));
result->newVarValues[Variable::QmlImportPath] = exactReader->absolutePathValues(
QLatin1String("QML_IMPORT_PATH"), input.projectDir);
result->newVarValues[Variable::QmlDesignerImportPath] = exactReader->absolutePathValues(
QLatin1String("QML_DESIGNER_IMPORT_PATH"), input.projectDir);
result->newVarValues[Variable::Makefile] = exactReader->values(QLatin1String("MAKEFILE"));
result->newVarValues[Variable::Qt] = exactReader->values(QLatin1String("QT"));
result->newVarValues[Variable::ObjectExt] = exactReader->values(QLatin1String("QMAKE_EXT_OBJ"));
result->newVarValues[Variable::ObjectsDir] = exactReader->values(QLatin1String("OBJECTS_DIR"));
result->newVarValues[Variable::Version] = exactReader->values(QLatin1String("VERSION"));
result->newVarValues[Variable::TargetExt] = exactReader->values(QLatin1String("TARGET_EXT"));
result->newVarValues[Variable::TargetVersionExt]
= exactReader->values(QLatin1String("TARGET_VERSION_EXT"));
result->newVarValues[Variable::StaticLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_STATICLIB"));
result->newVarValues[Variable::ShLibExtension] = exactReader->values(QLatin1String("QMAKE_EXTENSION_SHLIB"));
result->newVarValues[Variable::AndroidAbi] = exactReader->values(QLatin1String(Android::Constants::ANDROID_TARGET_ARCH));
result->newVarValues[Variable::AndroidDeploySettingsFile] = exactReader->values(QLatin1String(Android::Constants::ANDROID_DEPLOYMENT_SETTINGS_FILE));
result->newVarValues[Variable::AndroidPackageSourceDir] = exactReader->values(QLatin1String(Android::Constants::ANDROID_PACKAGE_SOURCE_DIR));
result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String(Android::Constants::ANDROID_ABIS));
result->newVarValues[Variable::AndroidApplicationArgs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS));
result->newVarValues[Variable::AndroidExtraLibs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS));
result->newVarValues[Variable::IosDeploymentTarget] = exactReader->values("QMAKE_IOS_DEPLOYMENT_TARGET");
result->newVarValues[Variable::AppmanPackageDir] = exactReader->values(QLatin1String("AM_PACKAGE_DIR"));
result->newVarValues[Variable::AppmanManifest] = exactReader->values(QLatin1String("AM_MANIFEST"));
result->newVarValues[Variable::IsoIcons] = exactReader->values(QLatin1String("ISO_ICONS"));
result->newVarValues[Variable::QmakeProjectName] = exactReader->values(QLatin1String("QMAKE_PROJECT_NAME"));
result->newVarValues[Variable::QmakeCc] = exactReader->values("QMAKE_CC");
result->newVarValues[Variable::QmakeCxx] = exactReader->values("QMAKE_CXX");
}
if (result->state == QmakeEvalResult::EvalOk || result->state == QmakeEvalResult::EvalPartial) {
QList<QmakeIncludedPriFile *> toExtract = {&result->includedFiles};
while (!toExtract.isEmpty()) {
QmakeIncludedPriFile *current = toExtract.takeFirst();
processValues(current->result);
toExtract.append(current->children.values());
}
}
if (exactBuildPassReader && exactBuildPassReader != input.readerExact)
delete exactBuildPassReader;
if (cumulativeBuildPassReader && cumulativeBuildPassReader != input.readerCumulative)
delete cumulativeBuildPassReader;
QList<QPair<QmakePriFile *, QmakeIncludedPriFile *>>
toCompare{{nullptr, &result->includedFiles}};
while (!toCompare.isEmpty()) {
QmakePriFile *pn = toCompare.first().first;
QmakeIncludedPriFile *tree = toCompare.first().second;
toCompare.pop_front();
// Loop prevention: Make sure that exact same node is not in our parent chain
for (QmakeIncludedPriFile *priFile : std::as_const(tree->children)) {
bool loop = input.parentFilePaths.contains(priFile->name);
for (const QmakePriFile *n = pn; n && !loop; n = n->parent()) {
if (n->filePath() == priFile->name)
loop = true;
}
if (loop)
continue; // Do nothing
if (priFile->proFile) {
auto *qmakePriFileNode = new QmakePriFile(priFile->name);
if (pn)
pn->addChild(qmakePriFileNode);
else
result->directChildren << qmakePriFileNode;
qmakePriFileNode->setIncludedInExactParse(input.includedInExcactParse
&& result->state == QmakeEvalResult::EvalOk);
result->priFiles.push_back({qmakePriFileNode, priFile->result});
toCompare.push_back({qmakePriFileNode, priFile});
} else {
auto *qmakeProFileNode = new QmakeProFile(priFile->name);
if (pn)
pn->addChild(qmakeProFileNode);
else
result->directChildren << qmakeProFileNode;
qmakeProFileNode->setIncludedInExactParse(input.includedInExcactParse
&& result->exactSubdirs.contains(qmakeProFileNode->filePath()));
qmakeProFileNode->setParseInProgress(true);
result->proFiles << qmakeProFileNode;
}
}
}
return result;
}
void QmakeProFile::asyncEvaluate(QFutureInterface<QmakeEvalResultPtr> &fi, QmakeEvalInput input)
{
fi.reportResult(evaluate(input));
}
bool sortByParserNodes(Node *a, Node *b)
{
return a->filePath() < b->filePath();
}
void QmakeProFile::applyEvaluate(const QmakeEvalResultPtr &result)
{
if (!m_readerExact)
return;
if (m_buildSystem->asyncUpdateState() == QmakeBuildSystem::ShuttingDown) {
cleanupProFileReaders();
return;
}
for (const QString &error : std::as_const(result->errors))
QmakeBuildSystem::proFileParseError(error, filePath());
// we are changing what is executed in that case
if (result->state == QmakeEvalResult::EvalFail || m_buildSystem->wasEvaluateCanceled()) {
m_validParse = false;
cleanupProFileReaders();
setValidParseRecursive(false);
setParseInProgressRecursive(false);
if (result->state == QmakeEvalResult::EvalFail) {
QmakeBuildSystem::proFileParseError(Tr::tr("Error while parsing file %1. Giving up.")
.arg(filePath().toUserOutput()), filePath());
if (m_projectType == ProjectType::Invalid)
return;
makeEmpty();
m_projectType = ProjectType::Invalid;
}
return;
}
qCDebug(qmakeParse()) << "QmakeProFile - updating files for file " << filePath();
if (result->projectType != m_projectType) {
// probably all subfiles/projects have changed anyway
// delete files && folders && projects
for (QmakePriFile *c : children()) {
if (auto qmakeProFile = dynamic_cast<QmakeProFile *>(c)) {
qmakeProFile->setValidParseRecursive(false);
qmakeProFile->setParseInProgressRecursive(false);
}
}
makeEmpty();
m_projectType = result->projectType;
}
//
// Add/Remove pri files, sub projects
//
FilePath buildDirectory = m_buildSystem->buildDir(m_filePath);
makeEmpty();
for (QmakePriFile * const toAdd : std::as_const(result->directChildren))
addChild(toAdd);
result->directChildren.clear();
for (const auto &priFiles : std::as_const(result->priFiles)) {
priFiles.first->finishInitialization(m_buildSystem, this);
priFiles.first->update(priFiles.second);
}
for (QmakeProFile * const proFile : std::as_const(result->proFiles)) {
proFile->finishInitialization(m_buildSystem, proFile);
proFile->asyncUpdate();
}
QmakePriFile::update(result->includedFiles.result);
m_validParse = (result->state == QmakeEvalResult::EvalOk);
if (m_validParse) {
// update TargetInformation
m_qmakeTargetInformation = result->targetInformation;
m_subProjectsNotToDeploy
= Utils::transform(result->subProjectsNotToDeploy,
[](const QString &s) { return FilePath::fromString(s); });
m_installsList = result->installsList;
if (m_varValues != result->newVarValues)
m_varValues = result->newVarValues;
m_displayName = singleVariableValue(Variable::QmakeProjectName);
m_featureRoots = m_readerExact->featureRoots();
} // result == EvalOk
if (!result->directoriesWithWildcards.isEmpty()) {
if (!m_wildcardWatcher) {
m_wildcardWatcher = std::make_unique<FileSystemWatcher>();
QObject::connect(
m_wildcardWatcher.get(), &FileSystemWatcher::directoryChanged,
[this](QString path) {
QStringList directoryContents = QDir(path).entryList();
if (m_wildcardDirectoryContents.value(path) != directoryContents) {
m_wildcardDirectoryContents.insert(path, directoryContents);
scheduleUpdate();
}
});
}
const QStringList directoriesToAdd = Utils::filtered<QStringList>(
Utils::toList(result->directoriesWithWildcards),
[this](const QString &path) {
return !m_wildcardWatcher->watchesDirectory(path);
});
for (const QString &path : directoriesToAdd)
m_wildcardDirectoryContents.insert(path, QDir(path).entryList());
m_wildcardWatcher->addDirectories(directoriesToAdd, FileSystemWatcher::WatchModifiedDate);
}
if (m_wildcardWatcher) {
if (result->directoriesWithWildcards.isEmpty()) {
m_wildcardWatcher.reset();
m_wildcardDirectoryContents.clear();
} else {
const QStringList directoriesToRemove =
Utils::filtered<QStringList>(
m_wildcardWatcher->directories(),
[&result](const QString &path) {
return !result->directoriesWithWildcards.contains(path);
});
m_wildcardWatcher->removeDirectories(directoriesToRemove);
for (const QString &path : directoriesToRemove)
m_wildcardDirectoryContents.remove(path);
}
}
setParseInProgress(false);
updateGeneratedFiles(buildDirectory);
cleanupProFileReaders();
}
void QmakeProFile::cleanupProFileReaders()
{
if (m_readerExact)
m_buildSystem->destroyProFileReader(m_readerExact);
if (m_readerCumulative)
m_buildSystem->destroyProFileReader(m_readerCumulative);
m_readerExact = nullptr;
m_readerCumulative = nullptr;
}
QString QmakeProFile::uiDirPath(QtSupport::ProFileReader *reader, const FilePath &buildDir)
{
QString path = reader->value(QLatin1String("UI_DIR"));
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir.toString() + QLatin1Char('/') + path);
return path;
}
QString QmakeProFile::mocDirPath(QtSupport::ProFileReader *reader, const FilePath &buildDir)
{
QString path = reader->value(QLatin1String("MOC_DIR"));
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir.toString() + QLatin1Char('/') + path);
return path;
}
QString QmakeProFile::sysrootify(const QString &path, const QString &sysroot,
const QString &baseDir, const QString &outputDir)
{
#ifdef Q_OS_WIN
Qt::CaseSensitivity cs = Qt::CaseInsensitive;
#else
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif
if (sysroot.isEmpty() || path.startsWith(sysroot, cs)
|| path.startsWith(baseDir, cs) || path.startsWith(outputDir, cs)) {
return path;
}
QString sysrooted = QDir::cleanPath(sysroot + path);
return !IoUtils::exists(sysrooted) ? path : sysrooted;
}
QStringList QmakeProFile::includePaths(QtSupport::ProFileReader *reader, const FilePath &sysroot,
const FilePath &buildDir, const QString &projectDir)
{
QStringList paths;
bool nextIsAnIncludePath = false;
const QStringList flagList = reader->values(QLatin1String("QMAKE_CXXFLAGS"));
for (const QString &cxxflags : flagList) {
if (nextIsAnIncludePath) {
nextIsAnIncludePath = false;
paths.append(cxxflags);
} else if (cxxflags.startsWith(QLatin1String("-I"))) {
paths.append(cxxflags.mid(2));
} else if (cxxflags.startsWith(QLatin1String("-isystem"))) {
nextIsAnIncludePath = true;
}
}
bool tryUnfixified = false;
// These paths should not be checked for existence, to ensure consistent include path lists
// before and after building.
const QString mocDir = mocDirPath(reader, buildDir);
const QString uiDir = uiDirPath(reader, buildDir);
const QVector<ProFileEvaluator::SourceFile> elList = reader->fixifiedValues(
QLatin1String("INCLUDEPATH"), projectDir, buildDir.toString(), false);
for (const ProFileEvaluator::SourceFile &el : elList) {
const QString sysrootifiedPath = sysrootify(el.fileName, sysroot.toString(), projectDir,
buildDir.toString());
if (IoUtils::isAbsolutePath(sysrootifiedPath)
&& (IoUtils::exists(sysrootifiedPath) || sysrootifiedPath == mocDir
|| sysrootifiedPath == uiDir)) {
paths << sysrootifiedPath;
} else {
tryUnfixified = true;
}
}
// If sysrootifying a fixified path does not yield a valid path, try again with the
// unfixified value. This can be necessary for cross-building; see QTCREATORBUG-21164.
if (tryUnfixified) {
const QStringList rawValues = reader->values("INCLUDEPATH");
for (const QString &p : rawValues) {
const QString sysrootifiedPath = sysrootify(QDir::cleanPath(p), sysroot.toString(),
projectDir, buildDir.toString());
if (IoUtils::isAbsolutePath(sysrootifiedPath) && IoUtils::exists(sysrootifiedPath))
paths << sysrootifiedPath;
}
}
paths.removeDuplicates();
return paths;
}
QStringList QmakeProFile::libDirectories(QtSupport::ProFileReader *reader)
{
QStringList result;
const QStringList values = reader->values(QLatin1String("LIBS"));
for (const QString &str : values) {
if (str.startsWith(QLatin1String("-L")))
result.append(str.mid(2));
}
return result;
}
FilePaths QmakeProFile::subDirsPaths(QtSupport::ProFileReader *reader,
const QString &projectDir,
QStringList *subProjectsNotToDeploy,
QStringList *errors)
{
FilePaths subProjectPaths;
const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
for (const QString &subDirVar : subDirVars) {
// Special case were subdir is just an identifier:
// "SUBDIR = subid
// subid.subdir = realdir"
// or
// "SUBDIR = subid
// subid.file = realdir/realfile.pro"
QString realDir;
const QString subDirKey = subDirVar + QLatin1String(".subdir");
const QString subDirFileKey = subDirVar + QLatin1String(".file");
if (reader->contains(subDirKey))
realDir = reader->value(subDirKey);
else if (reader->contains(subDirFileKey))
realDir = reader->value(subDirFileKey);
else
realDir = subDirVar;
QFileInfo info(realDir);
if (!info.isAbsolute())
info.setFile(projectDir + QLatin1Char('/') + realDir);
realDir = info.filePath();
QString realFile;
if (info.isDir())
realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
else
realFile = realDir;
if (QFile::exists(realFile)) {
realFile = QDir::cleanPath(realFile);
subProjectPaths << FilePath::fromString(realFile);
if (subProjectsNotToDeploy && !subProjectsNotToDeploy->contains(realFile)
&& reader->values(subDirVar + QLatin1String(".CONFIG"))
.contains(QLatin1String("no_default_target"))) {
subProjectsNotToDeploy->append(realFile);
}
} else {
if (errors)
errors->append(Tr::tr("Could not find .pro file for subdirectory \"%1\" in \"%2\".")
.arg(subDirVar).arg(realDir));
}
}
return Utils::filteredUnique(subProjectPaths);
}
TargetInformation QmakeProFile::targetInformation(QtSupport::ProFileReader *reader,
QtSupport::ProFileReader *readerBuildPass,
const FilePath &buildDir,
const FilePath &projectFilePath)
{
TargetInformation result;
if (!reader || !readerBuildPass)
return result;
QStringList builds = reader->values(QLatin1String("BUILDS"));
if (!builds.isEmpty()) {
QString build = builds.first();
result.buildTarget = reader->value(build + QLatin1String(".target"));
}
// BUILD DIR
result.buildDir = buildDir;
if (readerBuildPass->contains(QLatin1String("DESTDIR")))
result.destDir = FilePath::fromString(readerBuildPass->value(QLatin1String("DESTDIR")));
// Target
result.target = readerBuildPass->value(QLatin1String("TARGET"));
if (result.target.isEmpty())
result.target = projectFilePath.baseName();
result.valid = true;
return result;
}
TargetInformation QmakeProFile::targetInformation() const
{
return m_qmakeTargetInformation;
}
InstallsList QmakeProFile::installsList(const QtSupport::ProFileReader *reader, const QString &projectFilePath,
const QString &projectDir, const QString &buildDir)
{
InstallsList result;
if (!reader)
return result;
const QStringList &itemList = reader->values(QLatin1String("INSTALLS"));
if (itemList.isEmpty())
return result;
const QStringList installPrefixVars{"QT_INSTALL_PREFIX", "QT_INSTALL_EXAMPLES"};
QList<QPair<QString, QString>> installPrefixValues;
for (const QString &installPrefix : installPrefixVars) {
installPrefixValues.push_back({reader->propertyValue(installPrefix),
reader->propertyValue(installPrefix + "/dev")});
}
for (const QString &item : itemList) {
const QStringList config = reader->values(item + ".CONFIG");
const bool active = !config.contains("no_default_install");
const bool executable = config.contains("executable");
const QString pathVar = item + QLatin1String(".path");
const QStringList &itemPaths = reader->values(pathVar);
if (itemPaths.count() != 1) {
qDebug("Invalid RHS: Variable '%s' has %d values.",
qPrintable(pathVar), int(itemPaths.count()));
if (itemPaths.isEmpty()) {
qDebug("%s: Ignoring INSTALLS item '%s', because it has no path.",
qPrintable(projectFilePath), qPrintable(item));
continue;
}
}
QString itemPath = itemPaths.last();
for (const auto &prefixValuePair : std::as_const(installPrefixValues)) {
if (prefixValuePair.first == prefixValuePair.second
|| !itemPath.startsWith(prefixValuePair.first)) {
continue;
}
// This is a hack for projects which install into $$[QT_INSTALL_*],
// in particular Qt itself, examples being most relevant.
// Projects which implement their own install path policy must
// parametrize their INSTALLS themselves depending on the intended
// installation/deployment mode.
itemPath.replace(0, prefixValuePair.first.length(), prefixValuePair.second);
break;
}
if (item == QLatin1String("target")) {
if (active)
result.targetPath = itemPath;
} else {
const auto &itemFiles = reader->fixifiedValues(
item + QLatin1String(".files"), projectDir, buildDir, true);
result.items << InstallsItem(itemPath, itemFiles, active, executable);
}
}
return result;
}
InstallsList QmakeProFile::installsList() const
{
return m_installsList;
}
FilePath QmakeProFile::sourceDir() const
{
return directoryPath();
}
FilePaths QmakeProFile::generatedFiles(const FilePath &buildDir,
const FilePath &sourceFile,
const FileType &sourceFileType) const
{
// The mechanism for finding the file names is rather crude, but as we
// cannot parse QMAKE_EXTRA_COMPILERS and qmake has facilities to put
// ui_*.h files into a special directory, or even change the .h suffix, we
// cannot help doing this here.
if (sourceFileType == FileType::Form) {
FilePath location;
auto it = m_varValues.constFind(Variable::UiDir);
if (it != m_varValues.constEnd() && !it.value().isEmpty())
location = FilePath::fromString(it.value().front());
else
location = buildDir;
if (location.isEmpty())
return { };
location = location.pathAppended("ui_"
+ sourceFile.completeBaseName()
+ singleVariableValue(Variable::HeaderExtension));
return { FilePath::fromString(QDir::cleanPath(location.toString())) };
} else if (sourceFileType == FileType::StateChart) {
if (buildDir.isEmpty())
return { };
const FilePath location = buildDir.pathAppended(sourceFile.completeBaseName());
return {
location.stringAppended(singleVariableValue(Variable::HeaderExtension)),
location.stringAppended(singleVariableValue(Variable::CppExtension))
};
}
return { };
}
QList<ExtraCompiler *> QmakeProFile::extraCompilers() const
{
return m_extraCompilers;
}
void QmakeProFile::setupExtraCompiler(const FilePath &buildDir,
const FileType &fileType, ExtraCompilerFactory *factory)
{
for (const FilePath &fn : collectFiles(fileType)) {
const FilePaths generated = generatedFiles(buildDir, fn, fileType);
if (!generated.isEmpty())
m_extraCompilers.append(factory->create(m_buildSystem->project(), fn, generated));
}
}
void QmakeProFile::updateGeneratedFiles(const FilePath &buildDir)
{
// We can do this because other plugins are not supposed to keep the compilers around.
qDeleteAll(m_extraCompilers);
m_extraCompilers.clear();
// Only those project types can have generated files for us
if (m_projectType != ProjectType::ApplicationTemplate
&& m_projectType != ProjectType::SharedLibraryTemplate
&& m_projectType != ProjectType::StaticLibraryTemplate) {
return;
}
const QList<ExtraCompilerFactory *> factories =
ProjectExplorer::ExtraCompilerFactory::extraCompilerFactories();
ExtraCompilerFactory *formFactory
= Utils::findOrDefault(factories, Utils::equal(&ExtraCompilerFactory::sourceType, FileType::Form));
if (formFactory)
setupExtraCompiler(buildDir, FileType::Form, formFactory);
ExtraCompilerFactory *scxmlFactory
= Utils::findOrDefault(factories, Utils::equal(&ExtraCompilerFactory::sourceType, FileType::StateChart));
if (scxmlFactory)
setupExtraCompiler(buildDir, FileType::StateChart, scxmlFactory);
}