diff options
author | Robert Griebl <robert.griebl@pelagicore.com> | 2019-04-03 00:48:22 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@pelagicore.com> | 2019-08-01 11:23:31 +0200 |
commit | b4aee167d3bc6b9f64229317fbc428b3f3b83c0d (patch) | |
tree | 6b526d57203f34f4b85a82e2e958ace227c83960 /src/application-lib/packagedatabase.cpp | |
parent | 3bc3dc4c8e912beb18aec7ab84af40c0129d84c0 (diff) | |
download | qtapplicationmanager-b4aee167d3bc6b9f64229317fbc428b3f3b83c0d.tar.gz |
Add new package abstraction, which allows multiple executables per package
This is part 1 which is missing doc updates and missing the update-builtin-
applications functionality. Both will be added in a follow-up commit.
Change-Id: I2b493cfb7585143962067674690b02cc132ef78b
Reviewed-by: Dominik Holland <dominik.holland@pelagicore.com>
Diffstat (limited to 'src/application-lib/packagedatabase.cpp')
-rw-r--r-- | src/application-lib/packagedatabase.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/application-lib/packagedatabase.cpp b/src/application-lib/packagedatabase.cpp new file mode 100644 index 00000000..6ca32e1c --- /dev/null +++ b/src/application-lib/packagedatabase.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Luxoft Sweden AB +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Luxoft Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include <QDir> +#include <QFile> +#include <QScopedPointer> + +#include "packagedatabase.h" +#include "packageinfo.h" +#include "yamlpackagescanner.h" +#include "applicationinfo.h" +#include "installationreport.h" +#include "exception.h" +#include "logging.h" + +QT_BEGIN_NAMESPACE_AM + +PackageDatabase::PackageDatabase(const QStringList &builtInAppsDirs, + const QString &installedAppsDir) + : m_builtInAppsDirs(builtInAppsDirs) + , m_installedAppsDir(installedAppsDir) +{ } + +PackageDatabase::PackageDatabase(const QString &singlePackagePath) + : m_singlePackagePath(singlePackagePath) +{ + Q_ASSERT(!singlePackagePath.isEmpty()); +} + +void PackageDatabase::enableLoadFromCache() +{ + if (m_parsed) + qCWarning(LogSystem) << "PackageDatabase cannot change the caching mode after the initial load"; + m_loadFromCache = true; +} + +void PackageDatabase::enableSaveToCache() +{ + if (m_parsed) + qCWarning(LogSystem) << "PackageDatabase cannot change the caching mode after the initial load"; + m_saveToCache = true; +} + +bool PackageDatabase::loadFromCache() +{ + return false; + //TODO: read cache file +} + +void PackageDatabase::saveToCache() +{ + //TODO: write cache file +} + +bool PackageDatabase::canBeRevertedToBuiltIn(PackageInfo *pi) +{ + if (!pi || pi->isBuiltIn() || !m_installedPackages.contains(pi)) + return false; + for (auto it = m_builtInPackages.cbegin(); it != m_builtInPackages.cend(); ++it) { + if (it.key()->id() == pi->id()) + return true; + } + return false; +} + + +QMap<PackageInfo *, QString> PackageDatabase::loadManifestsFromDir(YamlPackageScanner *yps, const QString &manifestDir, bool scanningBuiltInApps) +{ + QMap<PackageInfo *, QString> result; + + auto flags = scanningBuiltInApps ? QDir::Dirs | QDir::NoDotAndDotDot + : QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks; + const QDir baseDir(manifestDir); + const QStringList pkgDirNames = baseDir.entryList(flags); + + for (const QString &pkgDirName : pkgDirNames) { + try { + // ignore left-overs from the installer + if (pkgDirName.endsWith('+') || pkgDirName.endsWith('-')) + continue; + + // ignore filesystem problems + QDir pkgDir = baseDir.absoluteFilePath(pkgDirName); + if (!pkgDir.exists()) + continue; + + // ignore directory names with weird/forbidden characters + QString pkgIdError; + if (!PackageInfo::isValidApplicationId(pkgDirName, &pkgIdError)) + throw Exception("directory name is not a valid package-id: %1").arg(pkgIdError); + + if (!pkgDir.exists(qSL("info.yaml"))) + throw Exception("couldn't find an info.yaml manifest"); + if (!scanningBuiltInApps && !pkgDir.exists(qSL(".installation-report.yaml"))) + throw Exception("found a non-built-in package without an installation report"); + + QString manifestPath = pkgDir.absoluteFilePath(yps->metaDataFileName()); + QScopedPointer<PackageInfo> pkg(loadManifest(yps, manifestPath)); + + if (pkg->id() != pkgDir.dirName()) { + throw Exception("an info.yaml must be in a directory that has" + " the same name as the package's id: found '%1'").arg(pkg->id()); + } + if (scanningBuiltInApps) { + pkg->setBuiltIn(true); + } else { // 3rd-party apps + QFile f(pkgDir.absoluteFilePath(qSL(".installation-report.yaml"))); + if (!f.open(QFile::ReadOnly)) + throw Exception(f, "failed to open the installation report"); + + QScopedPointer<InstallationReport> report(new InstallationReport(pkg->id())); + if (!report->deserialize(&f)) + throw Exception(f, "failed to deserialize the installation report"); + + pkg->setInstallationReport(report.take()); + } + result.insert(pkg.take(), manifestPath); + } catch (const Exception &e) { + qCDebug(LogSystem) << "Ignoring package" << pkgDirName << ":" << e.what(); + } + } + return result; +} + +PackageInfo *PackageDatabase::loadManifest(YamlPackageScanner *yps, const QString &manifestPath) +{ + QScopedPointer<PackageInfo> pkg(yps->scan(manifestPath)); + Q_ASSERT(pkg); + + if (pkg->applications().isEmpty()) + throw Exception("package contains no applications"); + + return pkg.take(); +} + +void PackageDatabase::parse() +{ + if (m_parsed) + throw Exception("PackageDatabase::parse() has been called multiple times"); + m_parsed = true; + + if (m_loadFromCache) { + if (loadFromCache()) + return; + } + + YamlPackageScanner yps; + + if (!m_singlePackagePath.isEmpty()) { + try { + m_builtInPackages.insert(loadManifest(&yps, m_singlePackagePath), m_singlePackagePath); + } catch (const Exception &e) { + throw Exception("Failed to load manifest for package: %1").arg(e.errorString()); + } + } else { + for (const QString &dir : m_builtInAppsDirs) + m_builtInPackages.unite(loadManifestsFromDir(&yps, dir, true)); + + if (!m_installedAppsDir.isEmpty()) + m_installedPackages = loadManifestsFromDir(&yps, m_installedAppsDir, false); + } + + if (m_saveToCache) + saveToCache(); +} + +QVector<PackageInfo *> PackageDatabase::installedPackages() const +{ + return m_installedPackages.keys().toVector(); +} + +QVector<PackageInfo *> PackageDatabase::builtInPackages() const +{ + return m_builtInPackages.keys().toVector(); +} + +QT_END_NAMESPACE_AM |