Newer
Older
default:
return InvalidProject;
}
}
void Qt4ProFileNode::applyEvaluate(bool parseResult, bool async)
{
if (!m_readerExact)
return;
if (!parseResult || m_project->wasEvaluateCanceled()) {
m_project->destroyProFileReader(m_readerExact);
if (m_readerCumulative)
m_project->destroyProFileReader(m_readerCumulative);
m_readerExact = m_readerCumulative = 0;
if (!parseResult) // Invalidate
invalidate();
return;
}
if (debug)
qDebug() << "Qt4ProFileNode - updating files for file " << m_projectFilePath;
Qt4ProjectType projectType = InvalidProject;
// Check that both are the same if we have both
if (m_readerExact->templateType() != m_readerCumulative->templateType()) {
// Now what. The only thing which could be reasonable is that someone
// changes between template app and library.
// Well, we are conservative here for now.
// Let's wait until someone complains and look at what they are doing.
m_project->destroyProFileReader(m_readerCumulative);
m_readerCumulative = 0;
projectType = proFileTemplateTypeToProjectType(m_readerExact->templateType());
if (projectType != m_projectType) {
Qt4ProjectType oldType = m_projectType;
// probably all subfiles/projects have changed anyway ...
clear();
m_projectType = projectType;
// really emit here? or at the end? Noone is connected to this signal at the moment
// so we kind of can ignore that question for now
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->projectTypeChanged(this, oldType, projectType);
}
//
// Add/Remove pri files, sub projects
//
QList<ProjectNode*> existingProjectNodes = subProjectNodes();
QStringList newProjectFilesExact;
QHash<QString, ProFile*> includeFilesExact;
ProFile *fileForCurrentProjectExact = 0;
if (m_projectType == SubDirsTemplate)
newProjectFilesExact = subDirsPaths(m_readerExact);
foreach (ProFile *includeFile, m_readerExact->includeFiles()) {
if (includeFile->fileName() == m_projectFilePath) { // this file
fileForCurrentProjectExact = includeFile;
} else {
newProjectFilesExact << includeFile->fileName();
includeFilesExact.insert(includeFile->fileName(), includeFile);
QStringList newProjectFilesCumlative;
QHash<QString, ProFile*> includeFilesCumlative;
ProFile *fileForCurrentProjectCumlative = 0;
if (m_readerCumulative) {
if (m_projectType == SubDirsTemplate)
newProjectFilesCumlative = subDirsPaths(m_readerCumulative);
foreach (ProFile *includeFile, m_readerCumulative->includeFiles()) {
if (includeFile->fileName() == m_projectFilePath) {
fileForCurrentProjectCumlative = includeFile;
newProjectFilesCumlative << includeFile->fileName();
includeFilesCumlative.insert(includeFile->fileName(), includeFile);
}
}
}
qSort(existingProjectNodes.begin(), existingProjectNodes.end(),
sortNodesByPath);
qSort(newProjectFilesExact);
qSort(newProjectFilesCumlative);
QList<ProjectNode*> toAdd;
QList<ProjectNode*> toRemove;
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
QList<ProjectNode*>::const_iterator existingIt = existingProjectNodes.constBegin();
QStringList::const_iterator newExactIt = newProjectFilesExact.constBegin();
QStringList::const_iterator newCumlativeIt = newProjectFilesCumlative.constBegin();
forever {
bool existingAtEnd = (existingIt == existingProjectNodes.constEnd());
bool newExactAtEnd = (newExactIt == newProjectFilesExact.constEnd());
bool newCumlativeAtEnd = (newCumlativeIt == newProjectFilesCumlative.constEnd());
if (existingAtEnd && newExactAtEnd && newCumlativeAtEnd)
break; // we are done, hurray!
// So this is one giant loop comparing 3 lists at once and sorting the comparision
// into mainly 2 buckets: toAdd and toRemove
// We need to distinguish between nodes that came from exact and cumalative
// parsing, since the update call is diffrent for them
// I believe this code to be correct, be careful in changing it
QString nodeToAdd;
if (! existingAtEnd
&& (newExactAtEnd || (*existingIt)->path() < *newExactIt)
&& (newCumlativeAtEnd || (*existingIt)->path() < *newCumlativeIt)) {
// Remove case
toRemove << *existingIt;
++existingIt;
} else if(! newExactAtEnd
&& (existingAtEnd || *newExactIt < (*existingIt)->path())
&& (newCumlativeAtEnd || *newExactIt < *newCumlativeIt)) {
// Mark node from exact for adding
nodeToAdd = *newExactIt;
++newExactIt;
} else if (! newCumlativeAtEnd
&& (existingAtEnd || *newCumlativeIt < (*existingIt)->path())
&& (newExactAtEnd || *newCumlativeIt < *newExactIt)) {
// Mark node from cumalative for adding
nodeToAdd = *newCumlativeIt;
++newCumlativeIt;
} else if (!newExactAtEnd
&& !newCumlativeAtEnd
&& (existingAtEnd || *newExactIt < (*existingIt)->path())
&& (existingAtEnd || *newCumlativeIt < (*existingIt)->path())) {
// Mark node from both for adding
nodeToAdd = *newExactIt;
++newExactIt;
++newCumlativeIt;
} else {
Q_ASSERT(!newExactAtEnd || !newCumlativeAtEnd);
// update case, figure out which case exactly
if (newExactAtEnd) {
++newCumlativeIt;
} else if (newCumlativeAtEnd) {
++newExactIt;
} else if(*newExactIt < *newCumlativeIt) {
++newExactIt;
} else if (*newCumlativeIt < *newExactIt) {
++newCumlativeIt;
// Update existingNodeIte
ProFile *fileExact = includeFilesCumlative.value((*existingIt)->path());
ProFile *fileCumlative = includeFilesCumlative.value((*existingIt)->path());
if (fileExact || fileCumlative) {
static_cast<Qt4PriFileNode *>(*existingIt)->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
} else {
// We always parse exactly, because we later when async parsing don't know whether
// the .pro file is included in this .pro file
// So to compare that later parse with the sync one
if (async)
static_cast<Qt4ProFileNode *>(*existingIt)->asyncUpdate();
else
static_cast<Qt4ProFileNode *>(*existingIt)->update();
++existingIt;
// newCumalativeIt and newExactIt are already incremented
// If we found something to add do it
if (!nodeToAdd.isEmpty()) {
ProFile *fileExact = includeFilesCumlative.value(nodeToAdd);
ProFile *fileCumlative = includeFilesCumlative.value(nodeToAdd);
if (fileExact || fileCumlative) {
Qt4PriFileNode *qt4PriFileNode = new Qt4PriFileNode(m_project, this, nodeToAdd);
qt4PriFileNode->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
toAdd << qt4PriFileNode;
} else {
Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
if (async)
qt4ProFileNode->asyncUpdate();
else
qt4ProFileNode->update();
toAdd << qt4ProFileNode;
}
if (!toRemove.isEmpty())
removeProjectNodes(toRemove);
if (!toAdd.isEmpty())
addProjectNodes(toAdd);
Qt4PriFileNode::update(fileForCurrentProjectExact, m_readerExact, fileForCurrentProjectCumlative, m_readerCumulative);
// update TargetInformation
m_qt4targetInformation = targetInformation(m_readerExact);
// update other variables
QHash<Qt4Variable, QStringList> newVarValues;
newVarValues[DefinesVar] = m_readerExact->values(QLatin1String("DEFINES"));
newVarValues[IncludePathVar] = includePaths(m_readerExact);
newVarValues[UiDirVar] = QStringList() << uiDirPath(m_readerExact);
newVarValues[MocDirVar] = QStringList() << mocDirPath(m_readerExact);
newVarValues[PkgConfigVar] = m_readerExact->values(QLatin1String("PKGCONFIG"));
newVarValues[PrecompiledHeaderVar] =
m_readerExact->absoluteFileValues(QLatin1String("PRECOMPILED_HEADER"),
m_projectDir,
QStringList() << m_projectDir,
0);
newVarValues[LibDirectoriesVar] = libDirectories(m_readerExact);
if (m_varValues != newVarValues) {
m_varValues = newVarValues;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->variablesChanged(this, m_varValues, newVarValues);
}
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->proFileUpdated(this);
m_project->destroyProFileReader(m_readerExact);
if (m_readerCumulative)
m_project->destroyProFileReader(m_readerCumulative);
m_readerExact = 0;
m_readerCumulative = 0;
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
}
namespace {
// find all ui files in project
class FindUiFileNodesVisitor : public ProjectExplorer::NodesVisitor {
public:
void visitProjectNode(ProjectNode *projectNode)
{
visitFolderNode(projectNode);
}
void visitFolderNode(FolderNode *folderNode)
{
foreach (FileNode *fileNode, folderNode->fileNodes()) {
if (fileNode->fileType() == ProjectExplorer::FormType)
uiFileNodes << fileNode;
}
}
QList<FileNode*> uiFileNodes;
};
}
// This function is triggered after a build, and updates the state ui files
// It does so by storing a modification time for each ui file we know about.
// TODO this function should also be called if the build directory is changed
// Only those two project types can have ui files for us
if (m_projectType != ApplicationTemplate
&& m_projectType != LibraryTemplate)
FindUiFileNodesVisitor uiFilesVisitor;
this->accept(&uiFilesVisitor);
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one
QString uiDir = buildDir();
QStringList tmp = m_varValues[UiDirVar];
if (tmp.size() != 0)
uiDir = tmp.first();
// Collect all existing generated files
QList<FileNode*> existingFileNodes;
foreach (FileNode *file, fileNodes()) {
if (file->isGenerated())
existingFileNodes << file;
}
// Convert uiFile to uiHeaderFilePath, find all headers that correspond
foreach (FileNode *uiFile, uiFiles) {
const QString uiHeaderFilePath
= QString("%1/ui_%2.h").arg(uiDir, QFileInfo(uiFile->path()).completeBaseName());
if (QFileInfo(uiHeaderFilePath).exists())
newFilePaths << uiHeaderFilePath;
// The list of files for which we call updateSourceFile
QStringList toUpdate;
qSort(newFilePaths);
qSort(existingFileNodes.begin(), existingFileNodes.end(), ProjectNode::sortNodesByPath);
QList<FileNode*>::const_iterator existingNodeIter = existingFileNodes.constBegin();
QList<QString>::const_iterator newPathIter = newFilePaths.constBegin();
while (existingNodeIter != existingFileNodes.constEnd()
&& newPathIter != newFilePaths.constEnd()) {
if ((*existingNodeIter)->path() < *newPathIter) {
toRemove << *existingNodeIter;
++existingNodeIter;
} else if ((*existingNodeIter)->path() > *newPathIter) {
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
++newPathIter;
} else { // *existingNodeIter->path() == *newPathIter
QString fileName = (*existingNodeIter)->path();
QMap<QString, QDateTime>::const_iterator it = m_uitimestamps.find(fileName);
QDateTime lastModified = QFileInfo(fileName).lastModified();
if (it == m_uitimestamps.constEnd() || it.value() < lastModified) {
toUpdate << fileName;
m_uitimestamps[fileName] = lastModified;
}
++existingNodeIter;
++newPathIter;
}
}
while (existingNodeIter != existingFileNodes.constEnd()) {
toRemove << *existingNodeIter;
++existingNodeIter;
}
while (newPathIter != newFilePaths.constEnd()) {
toAdd << new FileNode(*newPathIter, ProjectExplorer::HeaderType, true);
++newPathIter;
}
if (!toRemove.isEmpty()) {
foreach (FileNode *file, toRemove)
CppTools::CppModelManagerInterface *modelManager =
ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
foreach (FileNode *file, toAdd) {
m_uitimestamps.insert(file->path(), QFileInfo(file->path()).lastModified());
toUpdate << file->path();
// Also adding files depending on that
// We only need to do that for files that were newly created
QString fileName = QFileInfo(file->path()).fileName();
foreach (CPlusPlus::Document::Ptr doc, modelManager->snapshot()) {
if (doc->includedFiles().contains(fileName)) {
if (!toUpdate.contains(doc->fileName()))
toUpdate << doc->fileName();
}
}
}
QString Qt4ProFileNode::uiDirPath(ProFileReader *reader) const
QString path = reader->value("UI_DIR");
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir() + "/" + path);
return path;
QString Qt4ProFileNode::mocDirPath(ProFileReader *reader) const
QString path = reader->value("MOC_DIR");
if (QFileInfo(path).isRelative())
path = QDir::cleanPath(buildDir() + "/" + path);
return path;
}
QStringList Qt4ProFileNode::includePaths(ProFileReader *reader) const
{
QStringList paths;
foreach (const QString &cxxflags, m_readerExact->values("QMAKE_CXXFLAGS")) {
if (cxxflags.startsWith("-I"))
paths.append(cxxflags.mid(2));
}
paths.append(reader->absolutePathValues(QLatin1String("INCLUDEPATH"), m_projectDir));
// paths already contains moc dir and ui dir, due to corrrectly parsing uic.prf and moc.prf
// except if those directories don't exist at the time of parsing
// thus we add those directories manually (without checking for existance)
paths << mocDirPath(reader) << uiDirPath(reader);
QStringList Qt4ProFileNode::libDirectories(ProFileReader *reader) const
{
QStringList result;
foreach (const QString &str, reader->values(QLatin1String("LIBS"))) {
if (str.startsWith("-L")) {
result.append(str.mid(2));
}
}
return result;
}
QStringList Qt4ProFileNode::subDirsPaths(ProFileReader *reader) const
{
QStringList subProjectPaths;
const QStringList subDirVars = reader->values(QLatin1String("SUBDIRS"));
foreach (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");
realDir = QFileInfo(reader->value(subDirKey)).filePath();
else if (reader->contains(subDirFileKey))
realDir = QFileInfo(reader->value(subDirFileKey)).filePath();
else
info.setFile(m_projectDir + QLatin1Char('/') + realDir);
realDir = m_projectDir + QLatin1Char('/') + realDir;
QString realFile;
if (info.isDir()) {
realFile = QString::fromLatin1("%1/%2.pro").arg(realDir, info.fileName());
if (QFile::exists(realFile)) {
if (!subProjectPaths.contains(realFile))
subProjectPaths << realFile;
} else {
m_project->proFileParseError(tr("Could not find .pro file for sub dir '%1' in '%2'")
.arg(subDirVar).arg(realDir));
}
TargetInformation Qt4ProFileNode::targetInformation(ProFileReader *reader) const
{
TargetInformation result;
if (!reader)
return result;
result.buildDir = buildDir();
const QString baseDir = result.buildDir;
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
// qDebug() << "base build dir is:"<<baseDir;
// Working Directory
if (reader->contains("DESTDIR")) {
//qDebug() << "reader contains destdir:" << reader->value("DESTDIR");
result.workingDir = reader->value("DESTDIR");
if (QDir::isRelativePath(result.workingDir)) {
result.workingDir = baseDir + QLatin1Char('/') + result.workingDir;
//qDebug() << "was relative and expanded to" << result.workingDir;
}
} else {
//qDebug() << "reader didn't contain DESTDIR, setting to " << baseDir;
result.workingDir = baseDir;
}
result.target = reader->value("TARGET");
if (result.target.isEmpty())
result.target = QFileInfo(m_projectFilePath).baseName();
#if defined (Q_OS_MAC)
if (reader->values("CONFIG").contains("app_bundle")) {
result.workingDir += QLatin1Char('/')
+ result.target
+ QLatin1String(".app/Contents/MacOS");
}
#endif
result.workingDir = QDir::cleanPath(result.workingDir);
QString wd = result.workingDir;
if (!reader->contains("DESTDIR")
&& reader->values("CONFIG").contains("debug_and_release")
&& reader->values("CONFIG").contains("debug_and_release_target")) {
// If we don't have a destdir and debug and release is set
// then the executable is in a debug/release folder
//qDebug() << "reader has debug_and_release_target";
// Hmm can we find out whether it's debug or release in a saner way?
// Theoretically it's in CONFIG
QString qmakeBuildConfig = "release";
if (m_project->activeTarget()->activeBuildConfiguration()->qmakeBuildConfiguration() & QtVersion::DebugBuild)
qmakeBuildConfig = "debug";
wd += QLatin1Char('/') + qmakeBuildConfig;
}
result.executable = QDir::cleanPath(wd + QLatin1Char('/') + result.target);
//qDebug() << "##### updateTarget sets:" << result.workingDir << result.executable;
#if defined (Q_OS_WIN)
result.executable += QLatin1String(".exe");
#endif
result.valid = true;
return result;
}
TargetInformation Qt4ProFileNode::targetInformation()
{
return m_qt4targetInformation;
}
QString Qt4ProFileNode::buildDir() const
{
const QDir srcDirRoot = QFileInfo(m_project->rootProjectNode()->path()).absoluteDir();
const QString relativeDir = srcDirRoot.relativeFilePath(m_projectDir);
return QDir(m_project->activeTarget()->activeBuildConfiguration()->buildDirectory()).absoluteFilePath(relativeDir);
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
}
/*
Sets project type to InvalidProject & deletes all subprojects/files/virtual folders
*/
void Qt4ProFileNode::invalidate()
{
if (m_projectType == InvalidProject)
return;
clear();
// change project type
Qt4ProjectType oldType = m_projectType;
m_projectType = InvalidProject;
foreach (NodesWatcher *watcher, watchers())
if (Qt4NodesWatcher *qt4Watcher = qobject_cast<Qt4NodesWatcher*>(watcher))
emit qt4Watcher->projectTypeChanged(this, oldType, InvalidProject);
}
void Qt4ProFileNode::updateCodeModelSupportFromBuild(const QStringList &files)
{
foreach (const QString &file, files) {
QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
end = m_uiCodeModelSupport.constEnd();
for (it = m_uiCodeModelSupport.constBegin(); it != end; ++it) {
if (it.value()->fileName() == file)
it.value()->updateFromBuild();
}
}
}
void Qt4ProFileNode::updateCodeModelSupportFromEditor(const QString &uiFileName,
const QString &contents)
const QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it =
m_uiCodeModelSupport.constFind(uiFileName);
if (it != m_uiCodeModelSupport.constEnd())
it.value()->updateFromEditor(contents);
foreach (ProjectExplorer::ProjectNode *pro, subProjectNodes())
if (Qt4ProFileNode *qt4proFileNode = qobject_cast<Qt4ProFileNode *>(pro))
qt4proFileNode->updateCodeModelSupportFromEditor(uiFileName, contents);
QString Qt4ProFileNode::uiDirectory() const
{
const Qt4VariablesHash::const_iterator it = m_varValues.constFind(UiDirVar);
if (it != m_varValues.constEnd() && !it.value().isEmpty())
return it.value().front();
return buildDir();
}
QString Qt4ProFileNode::uiHeaderFile(const QString &uiDir, const QString &formFile)
{
QString uiHeaderFilePath = uiDir;
uiHeaderFilePath += QLatin1String("/ui_");
uiHeaderFilePath += QFileInfo(formFile).completeBaseName();
uiHeaderFilePath += QLatin1String(".h");
return QDir::cleanPath(uiHeaderFilePath);
}
void Qt4ProFileNode::createUiCodeModelSupport()
{
CppTools::CppModelManagerInterface *modelManager
= ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
// First move all to
QMap<QString, Qt4UiCodeModelSupport *> oldCodeModelSupport;
oldCodeModelSupport = m_uiCodeModelSupport;
m_uiCodeModelSupport.clear();
// Only those two project types can have ui files for us
if (m_projectType == ApplicationTemplate || m_projectType == LibraryTemplate) {
// Find all ui files
FindUiFileNodesVisitor uiFilesVisitor;
this->accept(&uiFilesVisitor);
const QList<FileNode*> uiFiles = uiFilesVisitor.uiFileNodes;
// Find the UiDir, there can only ever be one
const QString uiDir = uiDirectory();
foreach (const FileNode *uiFile, uiFiles) {
const QString uiHeaderFilePath = uiHeaderFile(uiDir, uiFile->path());
// qDebug()<<"code model support for "<<uiFile->path()<<" "<<uiHeaderFilePath;
QMap<QString, Qt4UiCodeModelSupport *>::iterator it = oldCodeModelSupport.find(uiFile->path());
if (it != oldCodeModelSupport.end()) {
Qt4UiCodeModelSupport *cms = it.value();
cms->setFileName(uiHeaderFilePath);
m_uiCodeModelSupport.insert(it.key(), cms);
oldCodeModelSupport.erase(it);
} else {
Qt4UiCodeModelSupport *cms = new Qt4UiCodeModelSupport(modelManager, m_project, uiFile->path(), uiHeaderFilePath);
m_uiCodeModelSupport.insert(uiFile->path(), cms);
modelManager->addEditorSupport(cms);
}
}
}
// Remove old
QMap<QString, Qt4UiCodeModelSupport *>::const_iterator it, end;
end = oldCodeModelSupport.constEnd();
for (it = oldCodeModelSupport.constBegin(); it!=end; ++it) {
modelManager->removeEditorSupport(it.value());
delete it.value();
}
}
Qt4NodesWatcher::Qt4NodesWatcher(QObject *parent)
: NodesWatcher(parent)
{
}
} // namespace Internal
} // namespace Qt4ProjectManager