summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@qt.io>2018-01-25 14:22:59 +0100
committerChristian Kandeler <christian.kandeler@qt.io>2018-01-30 15:28:53 +0000
commitd61c5f66ff507fb496aeafbae424e5f789afa26a (patch)
tree81aafd82464f4e31a47493e080f05e4eda18e314
parentdaba827d0c1e9f69bc62260b24aa1691bdb9ff52 (diff)
downloadqbs-d61c5f66ff507fb496aeafbae424e5f789afa26a.tar.gz
Clean up the list of file dependencies before building
After adding an external file dependency, we would never remove it from the global list again, meaning the look-up table would contain stale entries if an external header file was moved around. Because we still checked the status of the artifact-local list of dependencies, the result was that in such a situation, the files including such a header would get recompiled on every build. [ChangeLog] Fixed constant rebuilding after moving an external header file. Task-number: QBS-1285 Change-Id: Id764da7485dd540f2ff64bcd890e43723f18e6cd Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--changelogs/changes-1.10.1.md1
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp57
-rw-r--r--src/lib/corelib/buildgraph/executor.h2
-rw-r--r--tests/auto/blackbox/testdata/moved-file-dependency/main.cpp3
-rw-r--r--tests/auto/blackbox/testdata/moved-file-dependency/moved-file-dependency.qbs6
-rw-r--r--tests/auto/blackbox/testdata/moved-file-dependency/subdir1/theheader.h0
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp27
-rw-r--r--tests/auto/blackbox/tst_blackbox.h1
8 files changed, 88 insertions, 9 deletions
diff --git a/changelogs/changes-1.10.1.md b/changelogs/changes-1.10.1.md
index 45b0cfdb3..d3be09e65 100644
--- a/changelogs/changes-1.10.1.md
+++ b/changelogs/changes-1.10.1.md
@@ -2,6 +2,7 @@
* Fix assertion on project loading (QBS-1275).
* Fix crash when the "original" value is misused (QBS-1255).
* Fix qtquickcompiler support for qml files in subdirectories (QBS-1261).
+* Fix constant rebuilding after moving an external header file (QBS-1285).
* Fix GCC support for "bare metal" systems (QBS-1263, QBS-1265).
* Fix using ids in Depends items (QBS-1264).
* Fix access to module instances in dependency parameters (QBS-1253).
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index 61533c4f0..b2cf22325 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -147,7 +147,7 @@ void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const
void Executor::build()
{
try {
- m_partialBuild = m_productsToBuild.count() != m_project->allProducts().count();
+ m_partialBuild = m_productsToBuild.count() != m_allProducts.count();
doBuild();
} catch (const ErrorInfo &e) {
handleError(e);
@@ -157,6 +157,7 @@ void Executor::build()
void Executor::setProject(const TopLevelProjectPtr &project)
{
m_project = project;
+ m_allProducts = project->allProducts();
}
void Executor::setProducts(const QList<ResolvedProductPtr> &productsToBuild)
@@ -166,23 +167,22 @@ void Executor::setProducts(const QList<ResolvedProductPtr> &productsToBuild)
class ProductPrioritySetter
{
- const TopLevelProject *m_topLevelProject;
+ const QList<ResolvedProductPtr> &m_allProducts;
unsigned int m_priority;
Set<ResolvedProductPtr> m_seenProducts;
public:
- ProductPrioritySetter(const TopLevelProject *tlp)
- : m_topLevelProject(tlp)
+ ProductPrioritySetter(const QList<ResolvedProductPtr> &allProducts) // TODO: Use only products to build?
+ : m_allProducts(allProducts)
{
}
void apply()
{
- const QList<ResolvedProductPtr> &allProducts = m_topLevelProject->allProducts();
Set<ResolvedProductPtr> allDependencies;
- for (const ResolvedProductPtr &product : allProducts)
+ for (const ResolvedProductPtr &product : m_allProducts)
allDependencies += product->dependencies;
const Set<ResolvedProductPtr> rootProducts
- = Set<ResolvedProductPtr>::fromList(allProducts) - allDependencies;
+ = Set<ResolvedProductPtr>::fromList(m_allProducts) - allDependencies;
m_priority = UINT_MAX;
m_seenProducts.clear();
for (const ResolvedProductPtr &rootProduct : rootProducts)
@@ -266,6 +266,7 @@ void Executor::doBuild()
m_productInstaller->removeInstallRoot();
addExecutorJobs();
+ syncFileDependencies();
prepareAllNodes();
prepareProducts();
setupRootNodes();
@@ -1095,7 +1096,7 @@ bool Executor::visit(RuleNode *ruleNode)
*/
void Executor::prepareAllNodes()
{
- for (const ResolvedProductPtr &product : m_project->allProducts()) {
+ for (const ResolvedProductPtr &product : m_allProducts) {
if (product->enabled) {
QBS_CHECK(product->buildData);
for (BuildGraphNode * const node : qAsConst(product->buildData->nodes))
@@ -1109,6 +1110,41 @@ void Executor::prepareAllNodes()
}
}
+void Executor::syncFileDependencies()
+{
+ Set<FileDependency *> &globalFileDepList = m_project->buildData->fileDependencies;
+ for (auto it = globalFileDepList.begin(); it != globalFileDepList.end(); ) {
+ FileDependency * const dep = *it;
+ if (FileInfo(dep->filePath()).exists()) {
+ ++it;
+ continue;
+ }
+ qCDebug(lcBuildGraph()) << "file dependency" << dep->filePath() << "no longer exists; "
+ "removing from lookup table";
+ m_project->buildData->removeFromLookupTable(dep);
+ bool isReferencedByArtifact = false;
+ for (const ResolvedProductConstPtr &product : m_allProducts) {
+ if (!product->buildData)
+ continue;
+ const auto artifactList = filterByType<Artifact>(product->buildData->nodes);
+ isReferencedByArtifact = std::any_of(artifactList.begin(), artifactList.end(),
+ [dep](const Artifact *a) { return a->fileDependencies.contains(dep); });
+ // TODO: Would it be safe to mark the artifact as "not up to date" here and clear
+ // its list of file dependencies, rather than doing the check again in
+ // isUpToDate()?
+ if (isReferencedByArtifact)
+ break;
+ }
+ if (!isReferencedByArtifact) {
+ qCDebug(lcBuildGraph()) << "dependency is not referenced by any artifact, deleting";
+ it = globalFileDepList.erase(it);
+ delete dep;
+ } else {
+ ++it;
+ }
+ }
+}
+
void Executor::prepareArtifact(Artifact *artifact)
{
artifact->inputsScanned = false;
@@ -1123,6 +1159,9 @@ void Executor::prepareArtifact(Artifact *artifact)
}
// Timestamps of file dependencies must be invalid for every build.
+ // TODO: These should be a subset of ProjectBuildData::fileDependencies, so clear the
+ // timestamps in syncFileDepencencies() instead.
+ // TODO: Verify this assumption in the sanity checks.
for (FileDependency * const fileDependency : qAsConst(artifact->fileDependencies))
fileDependency->clearTimestamp();
}
@@ -1173,7 +1212,7 @@ void Executor::prepareReachableNodes_impl(BuildGraphNode *node)
void Executor::prepareProducts()
{
- ProductPrioritySetter prioritySetter(m_project.get());
+ ProductPrioritySetter prioritySetter(m_allProducts);
prioritySetter.apply();
for (const ResolvedProductPtr &product : qAsConst(m_productsToBuild))
product->setupBuildEnvironment(m_evalContext->engine(), m_project->environment);
diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h
index 849920aee..9be8558bb 100644
--- a/src/lib/corelib/buildgraph/executor.h
+++ b/src/lib/corelib/buildgraph/executor.h
@@ -111,6 +111,7 @@ private:
void doBuild();
void prepareAllNodes();
+ void syncFileDependencies();
void prepareArtifact(Artifact *artifact);
void setupForBuildingSelectedFiles(const BuildGraphNode *node);
void prepareReachableNodes();
@@ -162,6 +163,7 @@ private:
ExecutorState m_state;
TopLevelProjectPtr m_project;
QList<ResolvedProductPtr> m_productsToBuild;
+ QList<ResolvedProductPtr> m_allProducts;
NodeSet m_roots;
Leaves m_leaves;
QList<Artifact *> m_changedSourceArtifacts;
diff --git a/tests/auto/blackbox/testdata/moved-file-dependency/main.cpp b/tests/auto/blackbox/testdata/moved-file-dependency/main.cpp
new file mode 100644
index 000000000..3e89e9f26
--- /dev/null
+++ b/tests/auto/blackbox/testdata/moved-file-dependency/main.cpp
@@ -0,0 +1,3 @@
+#include <theheader.h>
+
+int main() {}
diff --git a/tests/auto/blackbox/testdata/moved-file-dependency/moved-file-dependency.qbs b/tests/auto/blackbox/testdata/moved-file-dependency/moved-file-dependency.qbs
new file mode 100644
index 000000000..6252f98ed
--- /dev/null
+++ b/tests/auto/blackbox/testdata/moved-file-dependency/moved-file-dependency.qbs
@@ -0,0 +1,6 @@
+import qbs
+
+CppApplication {
+ cpp.includePaths: ["subdir1", "subdir2"]
+ files: ["main.cpp"]
+}
diff --git a/tests/auto/blackbox/testdata/moved-file-dependency/subdir1/theheader.h b/tests/auto/blackbox/testdata/moved-file-dependency/subdir1/theheader.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/moved-file-dependency/subdir1/theheader.h
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index 954c5cd6a..ddba191cb 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -5291,6 +5291,33 @@ void TestBlackbox::missingOverridePrefix()
m_qbsStderr.constData());
}
+void TestBlackbox::movedFileDependency()
+{
+ QDir::setCurrent(testDataDir + "/moved-file-dependency");
+ const QString subdir2 = QDir::currentPath() + "/subdir2";
+ QVERIFY(QDir::current().mkdir(subdir2));
+ const QString oldHeaderFilePath = QDir::currentPath() + "/subdir1/theheader.h";
+ const QString newHeaderFilePath = subdir2 + "/theheader.h";
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+
+ QFile f(oldHeaderFilePath);
+ QVERIFY2(f.rename(newHeaderFilePath), qPrintable(f.errorString()));
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+
+ f.setFileName(newHeaderFilePath);
+ QVERIFY2(f.rename(oldHeaderFilePath), qPrintable(f.errorString()));
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+}
+
void TestBlackbox::badInterpreter()
{
if (!HostOsInfo::isAnyUnixHost())
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index f08e1ad25..d5d43afa7 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -142,6 +142,7 @@ private slots:
void missingDependency();
void missingProjectFile();
void missingOverridePrefix();
+ void movedFileDependency();
void multipleChanges();
void nestedGroups();
void nestedProperties();