[kde-doc-english] [kservice] /: KSycoca: change DB filename to include language and sha1 of the dirs it's built from.

David Faure faure at kde.org
Sun Sep 20 17:15:50 UTC 2015


Git commit f1bdf30501b87cb79806362a68c7515ba755275a by David Faure.
Committed on 20/09/2015 at 10:26.
Pushed by dfaure into branch 'master'.

KSycoca: change DB filename to include language and sha1 of the dirs it's built from.

This will prevent sycoca-rebuild ping-pong if two apps with different settings
would share the same file (and keep finding that it's wrong for them),
and it fixes Albert's bug that "LANG=de kcmshell5 --list" doesn't show German
translations for the strings coming from desktop files.

To avoid migration issues, the old filename ksycoca5 is still provided, as a hardlink
so that mmap works.

REVIEW: 125279
BUGS: 340731, 335780
FIXED-IN: 5.15

M  +18   -7    autotests/ksycocatest.cpp
M  +1    -1    docs/kbuildsycoca5/man-kbuildsycoca5.8.docbook
M  +16   -0    src/sycoca/kbuildsycoca.cpp
M  +15   -8    src/sycoca/ksycoca.cpp
M  +1    -0    src/sycoca/ksycoca.h

http://commits.kde.org/kservice/f1bdf30501b87cb79806362a68c7515ba755275a

diff --git a/autotests/ksycocatest.cpp b/autotests/ksycocatest.cpp
index 7c2d91e..56a202f 100644
--- a/autotests/ksycocatest.cpp
+++ b/autotests/ksycocatest.cpp
@@ -18,6 +18,7 @@
 */
 
 #include <ksycoca.h>
+#include <ksycoca_p.h>
 #include <QTemporaryDir>
 #include <QTest>
 #include <QDebug>
@@ -27,6 +28,7 @@
 #include <QSignalSpy>
 #include <QProcess>
 #include <kservice.h>
+#include <kservicefactory_p.h>
 
 class KSycocaTest : public QObject
 {
@@ -42,7 +44,11 @@ private Q_SLOTS:
         // we don't need the services dir -> ensure there isn't one, so we can check allResourceDirs below.
         QDir(servicesDir()).removeRecursively();
 
+        QSignalSpy spy(KSycoca::self(), SIGNAL(databaseChanged(QStringList)));
         runKBuildSycoca(QProcessEnvironment::systemEnvironment());
+        qDebug() << "waiting for signal";
+        QVERIFY(spy.wait(10000));
+        qDebug() << "got signal";
     }
     void testAllResourceDirs();
     void testOtherAppDir();
@@ -60,7 +66,6 @@ QTEST_MAIN(KSycocaTest)
 
 void KSycocaTest::runKBuildSycoca(const QProcessEnvironment &environment)
 {
-    QSignalSpy spy(KSycoca::self(), SIGNAL(databaseChanged(QStringList)));
     QProcess proc;
     const QString kbuildsycoca = QStandardPaths::findExecutable(KBUILDSYCOCA_EXENAME);
     QVERIFY(!kbuildsycoca.isEmpty());
@@ -72,15 +77,11 @@ void KSycocaTest::runKBuildSycoca(const QProcessEnvironment &environment)
 
     proc.waitForFinished();
     QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
-
-    qDebug() << "waiting for signal";
-    QVERIFY(spy.wait(10000));
-    qDebug() << "got signal";
 }
 
 void KSycocaTest::testAllResourceDirs()
 {
-    // Dirs that exist and dirs that don't exist, should both in allResourceDirs().
+    // Dirs that exist and dirs that don't exist, should both be in allResourceDirs().
     const QStringList dirs = KSycoca::self()->allResourceDirs();
     QVERIFY2(dirs.contains(servicesDir()), qPrintable(dirs.join(',')));
     QVERIFY2(dirs.contains(serviceTypesDir()), qPrintable(dirs.join(',')));
@@ -126,7 +127,17 @@ void KSycocaTest::testOtherAppDir()
     }
 #endif
 
-    QVERIFY(KService::serviceByStorageId("test_app_other.desktop"));
+    // This is still NOT available. kbuildsycoca created a different DB file, the one we read from hasn't changed.
+    // Changing XDG_DATA_DIRS at runtime isn't supported, so this test isn't doing what apps would do.
+    // The point however is that another app using different dirs cannot mess up our DB.
+    QVERIFY(!KService::serviceByStorageId("test_app_other.desktop"));
+
+    // Check here what the other app would see, by creating another sycoca instance.
+    KSycoca otherAppSycoca;
+    // do what serviceByStorageId does:
+    otherAppSycoca.ensureCacheValid();
+    QVERIFY(otherAppSycoca.d->serviceFactory()->findServiceByStorageId("test_app_other.desktop"));
+    QVERIFY(otherAppSycoca.d->m_databasePath != KSycoca::self()->d->m_databasePath); // check that they use a different filename
 }
 
 #include "ksycocatest.moc"
diff --git a/docs/kbuildsycoca5/man-kbuildsycoca5.8.docbook b/docs/kbuildsycoca5/man-kbuildsycoca5.8.docbook
index d7f8581..03add7a 100644
--- a/docs/kbuildsycoca5/man-kbuildsycoca5.8.docbook
+++ b/docs/kbuildsycoca5/man-kbuildsycoca5.8.docbook
@@ -166,7 +166,7 @@ Show version information.
 <title>Files</title>
 <variablelist>
 <varlistentry>
-<term><filename><varname>cachedir</varname>/ksycoca5</filename></term>
+<term><filename><varname>cachedir</varname>/ksycoca5_[lang]_[sha1-of-dirs]</filename></term>
 <listitem>
 <para>The KService cache generated by <command>kbuildsycoca5</command>.  On Unix systems, <varname>cachedir</varname> 
 is typically <filename class="directory"><envar>XDG_CONFIG_HOME</envar></filename></para>
diff --git a/src/sycoca/kbuildsycoca.cpp b/src/sycoca/kbuildsycoca.cpp
index d3328f0..ccb9350 100644
--- a/src/sycoca/kbuildsycoca.cpp
+++ b/src/sycoca/kbuildsycoca.cpp
@@ -448,6 +448,21 @@ bool KBuildSycoca::recreate(bool incremental)
             qCWarning(SYCOCA) << "ERROR writing database" << database.fileName() << ". Disk full?";
             return false;
         }
+
+        if (!m_globalDatabase) {
+            // Compatibility code for KF < 5.15: provide a ksycoca5 symlink after the filename change, for old apps to keep working during the upgrade
+            const QString oldSycoca = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/ksycoca5");
+            if (QFile::exists(oldSycoca)) {
+                QFile::remove(oldSycoca);
+#ifdef Q_OS_UNIX
+                if (::link(QFile::encodeName(path).constData(), QFile::encodeName(oldSycoca).constData()) != 0) {
+                    QFile::copy(path, oldSycoca);
+                }
+#else
+                QFile::copy(path, oldSycoca);
+#endif
+            }
+        }
     } else {
         delete str;
         str = 0;
@@ -615,6 +630,7 @@ quint32 KBuildSycoca::calcResourceHash(const QString &resourceSubDir, const QStr
 
 bool KBuildSycoca::checkGlobalHeader()
 {
+    // Since it's part of the filename, we are 99% sure that the locale and prefixes will match.
     const QString current_language = QLocale().bcp47Name();
     const quint32 current_update_sig = KBuildSycoca::calcResourceHash(QStringLiteral("kservices5"), QStringLiteral("update_ksycoca"));
     const QString current_prefixes = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(QString(QLatin1Char(':')));
diff --git a/src/sycoca/ksycoca.cpp b/src/sycoca/ksycoca.cpp
index a2b7040..0035f97 100644
--- a/src/sycoca/ksycoca.cpp
+++ b/src/sycoca/ksycoca.cpp
@@ -43,6 +43,7 @@
 #include <kservicetypefactory_p.h>
 #include <kservicegroupfactory_p.h>
 #include <kservicefactory_p.h>
+#include <QCryptographicHash>
 
 #include "kbuildsycoca_p.h"
 #include "ksycocadevices_p.h"
@@ -54,11 +55,6 @@
  */
 #define KSYCOCA_VERSION 302
 
-/**
- * Sycoca file name, used internally (by kbuildsycoca)
- */
-#define KSYCOCA_FILENAME "ksycoca5"
-
 #if HAVE_MADVISE || HAVE_MMAP
 #include <sys/mman.h> // This #include was checked when looking for posix_madvise
 #endif
@@ -684,17 +680,28 @@ quint32 KSycoca::updateSignature()
 
 QString KSycoca::absoluteFilePath(DatabaseType type)
 {
+    const QString paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(QString(QLatin1Char(':')));
+    QString suffix = QLatin1Char('_') + QLocale().bcp47Name();
+
     if (type == GlobalDatabase) {
-        QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString::fromLatin1("kservices5/" KSYCOCA_FILENAME));
+        const QString fileName = QStringLiteral("ksycoca5") + suffix;
+        QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5/") + fileName);
         if (path.isEmpty()) {
-            return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString::fromLatin1("/kservices5/" KSYCOCA_FILENAME);
+            return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kservices5/") + fileName;
         }
         return path;
     }
 
     const QByteArray ksycoca_env = qgetenv("KDESYCOCA");
     if (ksycoca_env.isEmpty()) {
-        return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + QString::fromLatin1(KSYCOCA_FILENAME);
+        const QByteArray pathHash = QCryptographicHash::hash(paths.toUtf8(), QCryptographicHash::Sha1);
+        suffix += QLatin1Char('_') + QString::fromLatin1(pathHash.toBase64());
+        suffix.replace('/', '_');
+#ifdef Q_OS_WIN
+        suffix.replace(':', '_');
+#endif
+        const QString fileName = QStringLiteral("ksycoca5") + suffix;
+        return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1Char('/') + fileName;
     } else {
         return QFile::decodeName(ksycoca_env);
     }
diff --git a/src/sycoca/ksycoca.h b/src/sycoca/ksycoca.h
index 34a2f63..c0c3502 100644
--- a/src/sycoca/ksycoca.h
+++ b/src/sycoca/ksycoca.h
@@ -244,6 +244,7 @@ private:
     friend class KBuildSycoca;
     friend class Kded;
     friend class KSycocaPrivate;
+    friend class KSycocaTest;
 
     Q_DISABLE_COPY(KSycoca)
     KSycocaPrivate *const d;


More information about the kde-doc-english mailing list