1diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp
2index 9dee91f713..9dec5cae67 100644
3--- a/src/qml/common/qv4compileddata.cpp
4+++ b/src/qml/common/qv4compileddata.cpp
5@@ -15,6 +15,8 @@
6 #include <QtCore/qscopeguard.h>
7 #include <QtCore/qstandardpaths.h>
8
9+#include <QtCore/qcoreapplication.h>
10+
11 static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
12
13 #if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
14@@ -26,6 +28,35 @@ __attribute__((section(".qml_compile_hash")))
15 const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
16 static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
17 "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
18+
19+bool nix__isNixApplication() {
20+ static const bool value = QCoreApplication::applicationFilePath().startsWith(QStringLiteral("@nixStore@"));
21+ return value;
22+}
23+
24+static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >
25+ /*sha1*/ 20 + /*NIX:*/ 4,
26+ "Nix compile hash length exceeds the reserved space in data "
27+ "structure. Please review the patch.");
28+
29+const QByteArray &nix__applicationHash() {
30+ static const QByteArray value = [](){
31+ QCryptographicHash applicationHash(QCryptographicHash::Sha1);
32+ applicationHash.addData(QByteArrayView(qml_compile_hash, QML_COMPILE_HASH_LENGTH));
33+
34+ // We only care about the package, not the specific file path.
35+ auto view = QCoreApplication::applicationFilePath().sliced(@nixStoreLength@);
36+ auto pkgEndIdx = view.indexOf(QStringLiteral("/"));
37+ if (pkgEndIdx != -1) view = view.sliced(0, pkgEndIdx);
38+
39+ applicationHash.addData(view.toUtf8());
40+
41+ return QByteArray("NIX:") + applicationHash.result();
42+ }();
43+
44+ return value;
45+}
46+
47 #else
48 # error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
49 #endif
50@@ -69,13 +100,29 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString)
51 }
52
53 #if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
54- if (qstrncmp(qml_compile_hash, libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
55+ const bool nixUnit = qstrncmp("NIX:", this->libraryVersionHash, 4) == 0;
56+
57+ if (nixUnit && !nix__isNixApplication()) {
58+ *errorString = QStringLiteral("QML compile hash is for a nix store application.");
59+ return false;
60+ }
61+
62+ const char *targetHash = qml_compile_hash;
63+ size_t targetHashLength = QML_COMPILE_HASH_LENGTH;
64+
65+ if (nixUnit) {
66+ const auto &applicationHash = nix__applicationHash();
67+ targetHash = applicationHash.constData();
68+ targetHashLength = applicationHash.length();
69+ }
70+
71+ if (qstrncmp(targetHash, this->libraryVersionHash, targetHashLength) != 0) {
72 *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
73 .arg(QString::fromLatin1(
74- QByteArray(libraryVersionHash, QML_COMPILE_HASH_LENGTH)
75+ QByteArray(this->libraryVersionHash, targetHashLength)
76 .toPercentEncoding()),
77 QString::fromLatin1(
78- QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
79+ QByteArray(targetHash, targetHashLength)
80 .toPercentEncoding()));
81 return false;
82 }
83@@ -213,6 +260,29 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
84 return false;
85 }
86
87+#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
88+ if (nix__isNixApplication() && unitUrl.scheme() == QStringLiteral("qrc")) {
89+ // If the application is running from the nix store, we can safely save
90+ // bytecode for its embedded QML files as long as we hash the
91+ // application path into the version. This will invalidate the caches
92+ // when the store path changes.
93+ const auto &applicationHash = nix__applicationHash();
94+
95+ memcpy(const_cast<char *>(unitData()->libraryVersionHash),
96+ applicationHash.constData(), applicationHash.length());
97+ } else if (unitUrl.path().startsWith(QStringLiteral("@nixStore@"))) {
98+ // We don't store bytecode for bare QML files in the nix store as the
99+ // paths will change every time the application updates, filling caches
100+ // endlessly with junk.
101+ *errorString = QStringLiteral("Refusing to save bytecode for bare @nixStore@ path.");
102+ return false;
103+ } else {
104+ // If the QML file is loaded from a normal file path it doesn't matter
105+ // if the application itself is running from a nix path, so we fall back
106+ // to the default Qt behavior.
107+ }
108+#endif
109+
110 return SaveableUnitPointer(unitData()).saveToDisk<char>(
111 [&unitUrl, errorString](const char *data, quint32 size) {
112 const QString cachePath = localCacheFilePath(unitUrl);