diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,6 +106,7 @@
)
install(FILES
+ qrc.cmake
"${CMAKE_CURRENT_BINARY_DIR}/KF5PackageConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/KF5PackageConfigVersion.cmake"
"${CMAKE_CURRENT_SOURCE_DIR}/KF5PackageMacros.cmake"
diff --git a/KF5PackageMacros.cmake b/KF5PackageMacros.cmake
--- a/KF5PackageMacros.cmake
+++ b/KF5PackageMacros.cmake
@@ -21,28 +21,33 @@
# kpackage_install_package(declarativetoolbox org.kde.toolbox) # installs a generic package
#
+set(kpackagedir ${CMAKE_CURRENT_LIST_DIR})
function(kpackage_install_package dir component)
+ message(AUTHOR_WARNING "Deprecated: use kpackage_install_bundle_package")
set(root ${ARGV2})
set(install_dir ${ARGV3})
if(NOT root)
set(root packages)
endif()
if(NOT install_dir)
set(install_dir ${KPACKAGE_RELATIVE_DATA_INSTALL_DIR})
endif()
+
install(DIRECTORY ${dir}/ DESTINATION ${KDE_INSTALL_DATADIR}/${install_dir}/${root}/${component}
- PATTERN .svn EXCLUDE
- PATTERN *.qmlc EXCLUDE
- PATTERN CMakeLists.txt EXCLUDE
- PATTERN Messages.sh EXCLUDE
- PATTERN dummydata EXCLUDE)
+ PATTERN .svn EXCLUDE
+ PATTERN *.qmlc EXCLUDE
+ PATTERN CMakeLists.txt EXCLUDE
+ PATTERN Messages.sh EXCLUDE
+ PATTERN dummydata EXCLUDE)
+ set(metadatajson)
if(NOT EXISTS ${component}-${root}-metadata.json AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/metadata.desktop)
set(GENERATED_METADATA "${CMAKE_CURRENT_BINARY_DIR}/${component}-${root}-metadata.json")
add_custom_command(OUTPUT ${GENERATED_METADATA}
COMMAND KF5::desktoptojson -i ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/metadata.desktop -o ${GENERATED_METADATA})
add_custom_target(${component}-${root}-metadata-json ALL DEPENDS ${GENERATED_METADATA})
install(FILES ${GENERATED_METADATA} DESTINATION ${KDE_INSTALL_DATADIR}/${install_dir}/${root}/${component} RENAME metadata.json)
+ set(metadatajson ${GENERATED_METADATA})
endif()
@@ -77,7 +82,73 @@
set_directory_properties(PROPERTIES kpackageindex "${regenerateindex}")
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/regenerateindex.sh ${regenerateindex})
endif()
+
endfunction()
+#use this version instead: it compresses the contents directory
+#into a binary rcc file
+function(kpackage_install_bundled_package dir component)
+ set(root ${ARGV2})
+ set(install_dir ${ARGV3})
+ if(NOT root)
+ set(root packages)
+ endif()
+ if(NOT install_dir)
+ set(install_dir ${KPACKAGE_RELATIVE_DATA_INSTALL_DIR})
+ endif()
+
+ set(metadatajson)
+ if(NOT EXISTS ${component}-${root}-metadata.json AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/metadata.desktop)
+ set(GENERATED_METADATA "${CMAKE_CURRENT_BINARY_DIR}/${component}-${root}-metadata.json")
+ add_custom_command(OUTPUT ${GENERATED_METADATA}
+ COMMAND KF5::desktoptojson -i ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/metadata.desktop -o ${GENERATED_METADATA})
+ add_custom_target(${component}-${root}-metadata-json ALL DEPENDS ${GENERATED_METADATA})
+ install(FILES ${GENERATED_METADATA} DESTINATION ${KDE_INSTALL_DATADIR}/${install_dir}/${root}/${component} RENAME metadata.json)
+ set(metadatajson ${GENERATED_METADATA})
+ endif()
+ get_target_property(kpackagetool_cmd KF5::kpackagetool5 LOCATION)
+ if (${component} MATCHES "^.+\\..+\\.") #we make sure there's at least 2 dots
+ set(APPDATAFILE "${CMAKE_CURRENT_BINARY_DIR}/${component}.appdata.xml")
+
+ execute_process(
+ COMMAND ${kpackagetool_cmd} --appstream-metainfo ${CMAKE_CURRENT_SOURCE_DIR}/${dir} --appstream-metainfo-output ${APPDATAFILE}
+ ERROR_VARIABLE appstreamerror
+ RESULT_VARIABLE result)
+ if (NOT result EQUAL 0)
+ message(WARNING "couldn't generate metainfo for ${component}: ${appstreamerror}")
+ else()
+ if(appstreamerror)
+ message(WARNING "warnings during generation of metainfo for ${component}: ${appstreamerror}")
+ endif()
+
+ # OPTIONAL because desktop files can be NoDisplay so they render no XML.
+ install(FILES ${APPDATAFILE} DESTINATION ${KDE_INSTALL_METAINFODIR} OPTIONAL)
+ endif()
+ else()
+ message(WARNING "KPackage components should be specified in reverse domain notation. Appstream information won't be generated for ${component}.")
+ endif()
+
+ set(newentry "${kpackagetool_cmd} --generate-index -g -p ${CMAKE_INSTALL_PREFIX}/${KDE_INSTALL_DATADIR}/${install_dir}/${root}\n")
+ get_directory_property(currentindex kpackageindex)
+ string(FIND "${currentindex}" "${newentry}" alreadyin)
+ if (alreadyin LESS 0)
+ set(regenerateindex "${currentindex}${newentry}")
+
+ set_directory_properties(PROPERTIES kpackageindex "${regenerateindex}")
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/regenerateindex.sh ${regenerateindex})
+ endif()
+
+ set(kpkgqrc "${CMAKE_CURRENT_BINARY_DIR}/${component}.qrc")
+ set(kpkgrcc "${CMAKE_CURRENT_BINARY_DIR}/${component}.rcc")
+ add_custom_command(OUTPUT ${kpkgqrc} ${kpkgrcc}
+ DEPENDS ${metadatajson}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${dir}
+ COMMAND cmake "-Dmetadatajson=${metadatajson}" -Droot=${root} -Dinstall_dir=${install_dir} -DBINARYDIR=${CMAKE_CURRENT_BINARY_DIR} -DDIRECTORY="${CMAKE_CURRENT_SOURCE_DIR}/${dir}" -DOUTPUTFILE=${kpkgqrc} -DCOMPONENT=${component} -P ${kpackagedir}/qrc.cmake
+ COMMAND Qt5::rcc ${kpkgqrc} --binary -o ${kpkgrcc}
+ )
+ add_custom_target(${component}-${root}-qrc ALL DEPENDS ${kpkgqrc})
+ install(FILES ${kpkgrcc} DESTINATION ${KDE_INSTALL_DATADIR}/${install_dir}/${root}/${component}/ RENAME contents.rcc)
+
+endfunction()
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -22,6 +22,7 @@
fallbackpackagetest
packagestructuretest
plasmoidpackagetest
+ rccpackagetest
querytest
)
@@ -41,4 +42,13 @@
kpackagetool5test(${var})
endforeach()
+set(kpkgqrc "${CMAKE_CURRENT_SOURCE_DIR}/data/testpackage-rcc/resources.qrc")
+set(kpkgrcc "${CMAKE_CURRENT_BINARY_DIR}/testpackage-rcc/contents.rcc")
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/data/testpackage-rcc/metadata.json" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/testpackage-rcc)
+add_custom_command(OUTPUT ${kpkgrcc}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND Qt5::rcc ${kpkgqrc} --binary -o ${kpkgrcc})
+add_custom_target(testpackage-rcc ALL DEPENDS ${kpkgrcc})
+
+
add_subdirectory(mockdepresolver)
diff --git a/autotests/data/testpackage-rcc/contents/images/empty.png b/autotests/data/testpackage-rcc/contents/images/empty.png
new file mode 100644
diff --git a/autotests/data/testpackage-rcc/contents/ui/main.qml b/autotests/data/testpackage-rcc/contents/ui/main.qml
new file mode 100644
--- /dev/null
+++ b/autotests/data/testpackage-rcc/contents/ui/main.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ color: "darkblue"
+}
+
diff --git a/autotests/data/testpackage-rcc/contents/ui/otherfile.qml b/autotests/data/testpackage-rcc/contents/ui/otherfile.qml
new file mode 100644
--- /dev/null
+++ b/autotests/data/testpackage-rcc/contents/ui/otherfile.qml
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ color: "darkblue"
+}
+
diff --git a/autotests/data/testpackage-rcc/metadata.json b/autotests/data/testpackage-rcc/metadata.json
new file mode 100644
--- /dev/null
+++ b/autotests/data/testpackage-rcc/metadata.json
@@ -0,0 +1,24 @@
+{
+ "KPlugin": {
+ "Authors": [
+ {
+ "Email": "jblow@kde.org",
+ "Name": "Joe Blow"
+ }
+ ],
+ "Category": "",
+ "Description": "fancy shmancy summary",
+ "Icon": "plasma",
+ "Id": "org.kde.testpackagercc",
+ "License": "GPLv2+",
+ "Name": "Test QResource Package",
+ "ServiceTypes": [
+ "KPackage/Generic"
+ ],
+ "Version": "",
+ "Website": ""
+ },
+ "Keywords": "",
+ "X-KDE-ParentApp": "",
+ "X-Plasma-MainScript": "ui/main.qml"
+}
diff --git a/autotests/data/testpackage-rcc/resources.qrc b/autotests/data/testpackage-rcc/resources.qrc
new file mode 100644
--- /dev/null
+++ b/autotests/data/testpackage-rcc/resources.qrc
@@ -0,0 +1,8 @@
+
+
+ ./metadata.json
+ ./contents/ui/main.qml
+ ./contents/ui/otherfile.qml
+ ./contents/images/empty.png
+
+
diff --git a/autotests/rccpackagetest.h b/autotests/rccpackagetest.h
new file mode 100644
--- /dev/null
+++ b/autotests/rccpackagetest.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+* Copyright 2007 by Aaron Seigo *
+* Copyright 2014 Marco Martin *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Library General Public *
+* License as published by the Free Software Foundation; either *
+* version 2 of the License, or (at your option) any later version. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Library General Public License for more details. *
+* *
+* You should have received a copy of the GNU Library General Public License *
+* along with this library; see the file COPYING.LIB. If not, write to *
+* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
+* Boston, MA 02110-1301, USA. *
+*******************************************************************************/
+
+#ifndef RCCPACKAGETEST_H
+#define RCCPACKAGETEST_H
+
+#include
+
+#include "kpackage/package.h"
+
+class RccPackageTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+ void accessFiles();
+ void validate();
+ void testResourceRefCount();
+
+
+private:
+ KPackage::Package *m_pkg;
+ KPackage::Package *m_pkg2;
+ QString m_packagePath;
+};
+
+#endif
+
diff --git a/autotests/rccpackagetest.cpp b/autotests/rccpackagetest.cpp
new file mode 100644
--- /dev/null
+++ b/autotests/rccpackagetest.cpp
@@ -0,0 +1,105 @@
+/******************************************************************************
+* Copyright 2007 by Aaron Seigo *
+* Copyright 2014 Marco Martin *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Library General Public *
+* License as published by the Free Software Foundation; either *
+* version 2 of the License, or (at your option) any later version. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Library General Public License for more details. *
+* *
+* You should have received a copy of the GNU Library General Public License *
+* along with this library; see the file COPYING.LIB. If not, write to *
+* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
+* Boston, MA 02110-1301, USA. *
+*******************************************************************************/
+
+#include "rccpackagetest.h"
+
+
+#include
+
+#include
+#include "packagestructure.h"
+#include "plasmoidstructure.h"
+#include "packageloader.h"
+
+void RccPackageTest::initTestCase()
+{
+ QStandardPaths::enableTestMode(true);
+
+ QString destination = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/plasma/plasmoids/testpackage-rcc/");
+ QDir dir;
+ dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
+ dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/plasma/"));
+ dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/plasma/plasmoids/"));
+ dir.mkpath(destination);
+ QVERIFY(dir.exists(destination));
+
+ //verify the source files exist
+ QVERIFY(QFile::exists(QStringLiteral("./autotests/testpackage-rcc/metadata.json")));
+ QVERIFY(QFile::exists(QStringLiteral("./autotests/testpackage-rcc/contents.rcc")));
+
+ QFile::copy(QStringLiteral("./autotests/testpackage-rcc/metadata.json"), destination + QStringLiteral("metadata.json"));
+ QFile::copy(QStringLiteral("./autotests/testpackage-rcc/contents.rcc"), destination + QStringLiteral("contents.rcc"));
+
+ //verify they have been copied correctly
+ QVERIFY(QFile::exists(destination + QStringLiteral("metadata.json")));
+ QVERIFY(QFile::exists(destination + QStringLiteral("contents.rcc")));
+
+ m_packagePath = "testpackage-rcc";
+ m_pkg = new KPackage::Package(new KPackage::PlasmoidPackage);
+ m_pkg->setPath(m_packagePath);
+ //Two package instances with the same resource
+ m_pkg2 = new KPackage::Package(new KPackage::PlasmoidPackage);
+ m_pkg2->setPath(m_packagePath);
+}
+
+void RccPackageTest::cleanupTestCase()
+{
+ qDebug() << "cleaning up";
+ // Clean things up.
+ QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)).removeRecursively();
+}
+
+void RccPackageTest::accessFiles()
+{
+ QVERIFY(m_pkg->hasValidStructure());
+ QVERIFY(m_pkg->hasValidStructure());
+
+ //check for existence of all files
+ QVERIFY(!m_pkg->filePath("ui", QStringLiteral("main.qml")).isEmpty());
+ QVERIFY(!m_pkg->filePath("ui", QStringLiteral("otherfile.qml")).isEmpty());
+ QVERIFY(!m_pkg->filePath("images", QStringLiteral("empty.png")).isEmpty());
+
+ QVERIFY(!m_pkg2->filePath("ui", QStringLiteral("main.qml")).isEmpty());
+ QVERIFY(!m_pkg2->filePath("ui", QStringLiteral("otherfile.qml")).isEmpty());
+ QVERIFY(!m_pkg2->filePath("images", QStringLiteral("empty.png")).isEmpty());
+}
+
+void RccPackageTest::validate()
+{
+ QCOMPARE(m_pkg->cryptographicHash(QCryptographicHash::Sha1), QStringLiteral("ed0959c062b7ef7eaab5243e83e2f9afe5b3b15f"));
+ QCOMPARE(m_pkg2->cryptographicHash(QCryptographicHash::Sha1), QStringLiteral("ed0959c062b7ef7eaab5243e83e2f9afe5b3b15f"));
+}
+
+void RccPackageTest::testResourceRefCount()
+{
+ delete m_pkg;
+ m_pkg = nullptr;
+
+ QVERIFY(m_pkg2->isValid());
+ QVERIFY(QFile::exists(QStringLiteral(":/plasma/plasmoids/testpackage-rcc/contents/ui/main.qml")));
+
+ //no reference from this package anymore
+ delete m_pkg2;
+ m_pkg2 = nullptr;
+ QVERIFY(!QFile::exists(QStringLiteral(":/plasma/plasmoids/testpackage-rcc/contents/ui/main.qml")));
+}
+
+QTEST_MAIN(RccPackageTest)
+
diff --git a/qrc.cmake b/qrc.cmake
new file mode 100644
--- /dev/null
+++ b/qrc.cmake
@@ -0,0 +1,17 @@
+set(OUTPUT "\n
+
+")
+
+file(GLOB_RECURSE files LIST_DIRECTORIES FALSE RELATIVE ${DIRECTORY} ${DIRECTORY}/*)
+foreach(v IN LISTS files)
+ set(OUTPUT "${OUTPUT} ${DIRECTORY}/${v}\n")
+endforeach()
+
+if (metadatajson)
+ set(OUTPUT "${OUTPUT} ${metadatajson}\n")
+endif()
+
+set(OUTPUT "${OUTPUT}
+
+")
+file(WRITE "${OUTPUTFILE}" "${OUTPUT}")
diff --git a/src/kpackage/package.h b/src/kpackage/package.h
--- a/src/kpackage/package.h
+++ b/src/kpackage/package.h
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
diff --git a/src/kpackage/package.cpp b/src/kpackage/package.cpp
--- a/src/kpackage/package.cpp
+++ b/src/kpackage/package.cpp
@@ -23,6 +23,7 @@
#include "package.h"
#include
+#include
#include
#include
@@ -213,6 +214,7 @@
//qDebug() << "metadata: " << d->path << filePath("metadata");
if (!d->metadata && !d->path.isEmpty()) {
const QString metadataPath = filePath("metadata");
+
if (!metadataPath.isEmpty()) {
d->createPackageMetadata(metadataPath);
} else {
@@ -401,7 +403,13 @@
QUrl Package::fileUrl(const QByteArray &fileType, const QString &filename) const
{
- return QUrl::fromLocalFile(filePath(fileType, filename));
+ QString path = filePath(fileType, filename);
+ //construct a qrc:/ url or a file:/ url, the only two protocols supported
+ if (path.startsWith(QStringLiteral(":"))) {
+ return QUrl(QStringLiteral("qrc") + path);
+ } else {
+ return QUrl::fromLocalFile(path);
+ }
}
QStringList Package::entryList(const QByteArray &key) const
@@ -504,6 +512,7 @@
}
if (QDir::isRelativePath(p)) {
+ //FIXME: can searching of the qrc be better?
paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, p, QStandardPaths::LocateDirectory);
} else {
const QDir dir(p);
@@ -532,7 +541,16 @@
foreach (const QString &p, paths) {
d->checkedValid = false;
QDir dir(p);
+
Q_ASSERT(QFile::exists(dir.canonicalPath()));
+
+ //if it has a contents.rcc, give priority to it
+ if (dir.exists(QStringLiteral("contents.rcc"))) {
+ d->rccPath = dir.absoluteFilePath(QStringLiteral("contents.rcc"));
+ QResource::registerResource(d->rccPath);
+ dir = QDir(QStringLiteral(":/") + defaultPackageRoot() + path);
+ }
+
d->path = dir.canonicalPath();
// canonicalPath() does not include a trailing slash (unless it is the root dir)
if (!d->path.endsWith(QLatin1Char('/'))) {
@@ -861,6 +879,13 @@
PackagePrivate::~PackagePrivate()
{
+ if (!rccPath.isEmpty()) {
+ //qresource register/unregisterpath is refcounted if we call it two times
+ //on the same path, the resource will actually be unregistered only when
+ //unregister is called 2 times
+ QResource::unregisterResource(rccPath);
+ }
+
if (!tempRoot.isEmpty()) {
QDir dir(tempRoot);
dir.removeRecursively();
diff --git a/src/kpackage/private/package_p.h b/src/kpackage/private/package_p.h
--- a/src/kpackage/private/package_p.h
+++ b/src/kpackage/private/package_p.h
@@ -101,6 +101,7 @@
Package *fallbackPackage;
QStringList mimeTypes;
KPluginMetaData *metadata;
+ QString rccPath;
bool externalPaths : 1;
bool valid : 1;
bool checkedValid : 1;