diff --git a/desktop/misc/packagekit/actions.py b/desktop/misc/packagekit/actions.py new file mode 100644 index 0000000000..392233583a --- /dev/null +++ b/desktop/misc/packagekit/actions.py @@ -0,0 +1,30 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Licensed under the GNU General Public License, version 3. +# See the file http://www.gnu.org/licenses/gpl.txt + +from pisi.actionsapi import get +from pisi.actionsapi import autotools +from pisi.actionsapi import pisitools +from pisi.actionsapi import shelltools + +def setup(): + #shelltools.system("sed -i 's@self.filesdb@# self.filesdb@g' backends/pisi/pisiBackend.py") + + autotools.configure("--enable-pisi \ + --with-default-backend=pisi \ + --disable-dummy \ + --disable-bash-completion \ + --disable-man-pages \ + --disable-static \ + --disable-systemd \ + --disable-offline-update") + +def build(): + autotools.make() + +def install(): + autotools.rawInstall("DESTDIR=%s" % get.installDIR()) + + pisitools.dodoc("AUTHORS", "MAINTAINERS", "COPYING", "README", "NEWS") diff --git a/desktop/misc/packagekit/files/0001-Added-PiSi-support-back.patch b/desktop/misc/packagekit/files/0001-Added-PiSi-support-back.patch new file mode 100644 index 0000000000..367345fa2a --- /dev/null +++ b/desktop/misc/packagekit/files/0001-Added-PiSi-support-back.patch @@ -0,0 +1,1068 @@ +From: Berk Cakar +Date: Thu, 24 Jun 2021 00:01:23 +0300 +Subject: [PATCH] Added PiSi support back + +--- + backends/pisi/meson.build | 21 ++ + backends/pisi/pisiBackend.py | 632 ++++++++++++++++++++++++++++++++ + backends/pisi/pk-backend-pisi.c | 369 +++++++++++++++++++ + meson_options.txt | 2 +- + 4 files changed, 1023 insertions(+), 1 deletion(-) + create mode 100644 backends/pisi/meson.build + create mode 100644 backends/pisi/pisiBackend.py + create mode 100644 backends/pisi/pk-backend-pisi.c + +diff --git a/backends/pisi/meson.build b/backends/pisi/meson.build +new file mode 100644 +index 000000000..71236ad11 +--- /dev/null ++++ b/backends/pisi/meson.build +@@ -0,0 +1,21 @@ ++shared_module( ++ 'pk_backend_pisi', ++ 'pk-backend-pisi.c', ++ include_directories: packagekit_src_include, ++ dependencies: [ ++ packagekit_glib2_dep, ++ gmodule_dep, ++ ], ++ c_args: [ ++ '-DPK_COMPILATION', ++ '-DG_LOG_DOMAIN="PackageKit-Pisi"', ++ ], ++ install: true, ++ install_dir: pk_plugin_dir, ++) ++ ++install_data( ++ 'pisiBackend.py', ++ install_dir: join_paths(get_option('datadir'), 'PackageKit', 'helpers', 'pisi'), ++ install_mode: 'rwxr--r--', ++) +diff --git a/backends/pisi/pisiBackend.py b/backends/pisi/pisiBackend.py +new file mode 100644 +index 000000000..f9c19eeaf +--- /dev/null ++++ b/backends/pisi/pisiBackend.py +@@ -0,0 +1,632 @@ ++#!/usr/bin/python ++# -*- coding: utf-8 -*- ++# ++# Licensed under the GNU General Public License Version 2 ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program 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 General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++# ++# Copyright (C) 2007 S.Çağlar Onur ++# Copyright (C) 2013 Ikey Doherty ++# Copyright (C) 2021 Berk Çakar ++ ++# Notes to PiSi based distribution maintainers ++# /etc/PackageKit/pisi.conf must contain a mapping of PiSi component to ++# PackageKit groups for correct operation, i.e. ++# system.utils = system ++# desktop.gnome = desktop-gnome ++# If you have a BTS you must also provide Bug-Regex and Bug-URI fields, i.e: ++# Bug-Regex = Bug-SolusOS: T(\d+) ++# Bug-URI = http://inf.solusos.com/T%s ++# We use simple python string formatting to replace the %s with the first ++# matched group in the regular expression. So in the example above, we expect ++# to see "Bug-SolusOS: T9" for example, on its own line in a package update ++# comment. ++ ++import pisi ++import pisi.ui ++from packagekit.backend import * ++from packagekit.package import PackagekitPackage ++from packagekit import enums ++import os.path ++import piksemel ++import re ++ ++class SimplePisiHandler(pisi.ui.UI): ++ ++ def __init(self): ++ pisi.ui.UI.__init__(self, False, False) ++ ++ def display_progress(self, **ka): ++ self.the_callback(**ka) ++ ++ ++class PackageKitPisiBackend(PackageKitBaseBackend, PackagekitPackage): ++ ++ SETTINGS_FILE = "/etc/PackageKit/pisi.conf" ++ ++ def __init__(self, args): ++ self.bug_regex = None ++ self.bug_uri = None ++ self._load_settings() ++ PackageKitBaseBackend.__init__(self, args) ++ ++ self.componentdb = pisi.db.componentdb.ComponentDB() ++ # self.filesdb = pisi.db.filesdb.FilesDB() ++ self.installdb = pisi.db.installdb.InstallDB() ++ self.packagedb = pisi.db.packagedb.PackageDB() ++ self.repodb = pisi.db.repodb.RepoDB() ++ ++ # Do not ask any question to users ++ self.options = pisi.config.Options() ++ self.options.yes_all = True ++ ++ self.saved_ui = pisi.context.ui ++ ++ def _load_settings(self): ++ """ Load the PK Group-> PiSi component mapping """ ++ if os.path.exists(self.SETTINGS_FILE): ++ with open(self.SETTINGS_FILE, "r") as mapping: ++ self.groups = {} ++ for line in mapping.readlines(): ++ line = line.replace("\r", "").replace("\n", "").strip() ++ if line.strip() == "" or "#" in line: ++ continue ++ ++ splits = line.split("=") ++ key = splits[0].strip() ++ value = splits[1].strip() ++ ++ # Check if this contains our bug keys ++ if key == "Bug-Regex": ++ self.bug_regex = re.compile(value) ++ continue ++ if key == "Bug-URI": ++ self.bug_uri = value ++ continue ++ self.groups[key] = value ++ else: ++ self.groups = {} ++ ++ def __get_package_version(self, package): ++ """ Returns version string of given package """ ++ # Internal FIXME: PiSi may provide this ++ if package.build is not None: ++ version = "%s-%s-%s" % (package.version, package.release, ++ package.build) ++ else: ++ version = "%s-%s" % (package.version, package.release) ++ return version ++ ++ def __get_package(self, package, filters=None): ++ """ Returns package object suitable for other methods """ ++ if self.installdb.has_package(package): ++ status = INFO_INSTALLED ++ pkg = self.installdb.get_package(package) ++ elif self.packagedb.has_package(package): ++ status = INFO_AVAILABLE ++ pkg = self.packagedb.get_package(package) ++ else: ++ self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found") ++ ++ if filters: ++ if "none" not in filters: ++ if FILTER_INSTALLED in filters and status != INFO_INSTALLED: ++ return ++ if FILTER_NOT_INSTALLED in filters and status == INFO_INSTALLED: ++ return ++ if FILTER_GUI in filters and "app:gui" not in pkg.isA: ++ return ++ if FILTER_NOT_GUI in filters and "app:gui" in pkg.isA: ++ return ++ ++ version = self.__get_package_version(pkg) ++ ++ id = self.get_package_id(pkg.name, version, pkg.architecture, "") ++ ++ return self.package(id, status, pkg.summary) ++ ++ def depends_on(self, filters, package_ids, recursive): ++ """ Prints a list of depends for a given package """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ package = self.get_package_from_id(package_ids[0])[0] ++ ++ for pkg in self.packagedb.get_package(package).runtimeDependencies(): ++ # FIXME: PiSi API has really inconsistent for return types ++ # and arguments! ++ self.__get_package(pkg.package) ++ ++ def get_details(self, package_ids): ++ """ Prints a detailed description for a given package """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ package = self.get_package_from_id(package_ids[0])[0] ++ ++ if self.packagedb.has_package(package): ++ pkg = self.packagedb.get_package(package) ++ repo = self.packagedb.get_package_repo(pkg.name, None) ++ pkg_id = self.get_package_id(pkg.name, ++ self.__get_package_version(pkg), ++ pkg.architecture, repo[1]) ++ ++ if pkg.partOf in self.groups: ++ group = self.groups[pkg.partOf] ++ else: ++ group = GROUP_UNKNOWN ++ ++ homepage = pkg.source.homepage if pkg.source.homepage is not None\ ++ else '' ++ ++ self.details(pkg_id, '', ",".join(pkg.license), group, pkg.description, ++ homepage, pkg.packageSize) ++ else: ++ self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found") ++ ++ def get_files(self, package_ids): ++ """ Prints a file list for a given package """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ package = self.get_package_from_id(package_ids[0])[0] ++ ++ if self.installdb.has_package(package): ++ pkg = self.packagedb.get_package(package) ++ repo = self.packagedb.get_package_repo(pkg.name, None) ++ pkg_id = self.get_package_id(pkg.name, ++ self.__get_package_version(pkg), ++ pkg.architecture, repo[1]) ++ ++ pkg = self.installdb.get_files(package) ++ ++ files = map(lambda y: "/%s" % y.path, pkg.list) ++ ++ file_list = ";".join(files) ++ self.files(pkg_id, file_list) ++ ++ def get_repo_list(self, filters): ++ """ Prints available repositories """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ for repo in pisi.api.list_repos(): ++ # Internal FIXME: What an ugly way to get repo uri ++ # FIXME: Use repository enabled/disabled state ++ uri = self.repodb.get_repo(repo).indexuri.get_uri() ++ self.repo_detail(repo, uri, True) ++ ++ def required_by(self, filters, package_ids, recursive): ++ """ Prints a list of requires for a given package """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ package = self.get_package_from_id(package_ids[0])[0] ++ ++ # FIXME: Handle packages which is not installed from repository ++ for pkg in self.packagedb.get_rev_deps(package): ++ self.__get_package(pkg[0]) ++ ++ def get_updates(self, filter): ++ """ Prints available updates and types """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ self._updates = dict() ++ for package in pisi.api.list_upgradable(): ++ pkg = self.packagedb.get_package(package) ++ version = self.__get_package_version(pkg) ++ id = self.get_package_id(pkg.name, version, pkg.architecture, "") ++ installed_package = self.installdb.get_package(package) ++ ++ repo = self.packagedb.get_package_repo(pkg.name, None)[1] ++ pindex = "/var/lib/pisi/index/%s/pisi-index.xml" % repo ++ ++ self._updates[pkg.name] = \ ++ self._extract_update_details(pindex, pkg.name) ++ bug_uri = self._updates[pkg.name][3] ++ ++ # FIXME: PiSi must provide this information as a single API call :( ++ updates = [i for i in self.packagedb.get_package(package).history ++ if pisi.version.Version(i.release) > ++ installed_package.release] ++ if pisi.util.any(lambda i: i.type == "security", updates): ++ self.package(id, INFO_SECURITY, pkg.summary) ++ elif bug_uri != "": ++ self.package(id, INFO_BUGFIX, pkg.summary) ++ else: ++ self.package(id, INFO_NORMAL, pkg.summary) ++ ++ def _extract_update_details(self, pindex, package_name): ++ document = piksemel.parse(pindex) ++ packages = document.tags("Package") ++ for pkg in packages: ++ if pkg.getTagData("Name") == package_name: ++ history = pkg.getTag("History") ++ update = history.tags("Update") ++ update_message = "Updated" ++ update_release = 0 ++ update_date = "" ++ needsReboot = False ++ bugURI = "" ++ for update in update: ++ if int(update.getAttribute("release")) > update_release: ++ update_release = int(update.getAttribute("release")) ++ # updater = update.getTagData("Name") ++ update_message = update.getTagData("Comment") ++ update_message = update_message.replace("\n", ";") ++ update_date = update.getTagData("Date") ++ needsReboot = False ++ try: ++ requires = update.getTag("Requires") ++ action = requires.getTagData("Action") ++ if action == "systemRestart": ++ needsReboot = True ++ except Exception: ++ pass ++ # Determine if this is a bug fix ++ for line in update_message.split(";"): ++ m = self.bug_regex.match(line) ++ if m is not None: ++ bugURI = self.bug_uri % m.group(1) ++ break ++ return (update_message, update_date, needsReboot, bugURI) ++ pkg = pkg.nextTag("Package") ++ return("Log not found", "", False, "") ++ ++ def get_update_detail(self, package_ids): ++ for package_id in package_ids: ++ package = self.get_package_from_id(package_id)[0] ++ the_package = self.installdb.get_package(package) ++ updates = [package_id] ++ obsoletes = "" ++ # TODO: Add regex matching for #FIXES:ID or something similar ++ cve_url = "" ++ package_url = the_package.source.homepage ++ vendor_url = package_url if package_url is not None else "" ++ issued = "" ++ ++ changelog = "" ++ # TODO: Set to security_issued if security update ++ issued = updated = "" ++ update_message, security_issued, needsReboot, bugURI = \ ++ self._updates[package] ++ ++ # TODO: Add tagging to repo's, or a mapping file ++ state = UPDATE_STATE_STABLE ++ reboot = "system" if needsReboot else "none" ++ ++ self.update_detail(package_id, updates, obsoletes, vendor_url, ++ bugURI, cve_url, reboot, update_message, ++ changelog, state, issued, updated) ++ ++ def download_packages(self, directory, package_ids): ++ """ Download the given packages to a directory """ ++ self.allow_cancel(False) ++ self.percentage(None) ++ self.status(STATUS_DOWNLOAD) ++ ++ packages = list() ++ ++ def progress_cb(**kw): ++ self.percentage(int(kw['percent'])) ++ ++ ui = SimplePisiHandler() ++ for package_id in package_ids: ++ package = self.get_package_from_id(package_id)[0] ++ packages.append(package) ++ try: ++ pkg = self.packagedb.get_package(package) ++ except: ++ self.error(ERROR_PACKAGE_NOT_FOUND, "Package was not found") ++ try: ++ pisi.api.set_userinterface(ui) ++ ui.the_callback = progress_cb ++ if directory is None: ++ directory = os.path.curdir ++ pisi.api.fetch(packages, directory) ++ # Scan for package ++ for package in packages: ++ package_obj = self.packagedb.get_package(package) ++ uri = package_obj.packageURI.split("/")[-1] ++ location = os.path.join(directory, uri) ++ self.files(package_id, location) ++ pisi.api.set_userinterface(self.saved_ui) ++ except Exception, e: ++ self.error(ERROR_PACKAGE_DOWNLOAD_FAILED, ++ "Could not download package: %s" % e) ++ self.percentage(None) ++ ++ def install_files(self, only_trusted, files): ++ """ Installs given package into system""" ++ ++ # FIXME: use only_trusted ++ ++ # FIXME: install progress ++ self.allow_cancel(False) ++ self.percentage(None) ++ ++ def progress_cb(**kw): ++ self.percentage(int(kw['percent'])) ++ ++ ui = SimplePisiHandler() ++ ++ self.status(STATUS_INSTALL) ++ pisi.api.set_userinterface(ui) ++ ui.the_callback = progress_cb ++ ++ try: ++ self.status(STATUS_INSTALL) ++ pisi.api.install(files) ++ except pisi.Error, e: ++ # FIXME: Error: internal-error : Package re-install declined ++ # Force needed? ++ self.error(ERROR_PACKAGE_ALREADY_INSTALLED, e) ++ pisi.api.set_userinterface(self.saved_ui) ++ ++ def _report_all_for_package(self, package, remove=False): ++ """ Report all deps for the given package """ ++ if not remove: ++ deps = self.packagedb.get_package(package).runtimeDependencies() ++ # TODO: Add support to report conflicting packages requiring removal ++ #conflicts = self.packagedb.get_package (package).conflicts ++ for dep in deps: ++ if not self.installdb.has_package(dep.name()): ++ dep_pkg = self.packagedb.get_package(dep.name()) ++ repo = self.packagedb.get_package_repo(dep_pkg.name, None) ++ version = self.__get_package_version(dep_pkg) ++ pkg_id = self.get_package_id(dep_pkg.name, version, ++ dep_pkg.architecture, repo[1]) ++ self.package(pkg_id, INFO_INSTALLING, dep_pkg.summary) ++ else: ++ rev_deps = self.installdb.get_rev_deps(package) ++ for rev_dep, depinfo in rev_deps: ++ if self.installdb.has_package(rev_dep): ++ dep_pkg = self.packagedb.get_package(rev_dep) ++ repo = self.packagedb.get_package_repo(dep_pkg.name, None) ++ version = self.__get_package_version(dep_pkg) ++ pkg_id = self.get_package_id(dep_pkg.name, version, ++ dep_pkg.architecture, repo[1]) ++ self.package(pkg_id, INFO_REMOVING, dep_pkg.summary) ++ ++ def install_packages(self, transaction_flags, package_ids): ++ """ Installs given package into system""" ++ # FIXME: fetch/install progress ++ self.allow_cancel(False) ++ self.percentage(None) ++ ++ packages = list() ++ ++ # FIXME: use only_trusted ++ for package_id in package_ids: ++ package = self.get_package_from_id(package_id)[0] ++ if self.installdb.has_package(package): ++ self.error(ERROR_PACKAGE_NOT_INSTALLED, ++ "Package is already installed") ++ packages.append(package) ++ ++ def progress_cb(**kw): ++ self.percentage(int(kw['percent'])) ++ ++ ui = SimplePisiHandler() ++ ++ self.status(STATUS_INSTALL) ++ pisi.api.set_userinterface(ui) ++ ui.the_callback = progress_cb ++ ++ if TRANSACTION_FLAG_SIMULATE in transaction_flags: ++ # Simulated, not real. ++ for package in packages: ++ self._report_all_for_package(package) ++ return ++ try: ++ pisi.api.install(packages) ++ except pisi.Error, e: ++ self.error(ERROR_UNKNOWN, e) ++ pisi.api.set_userinterface(self.saved_ui) ++ ++ def refresh_cache(self, force): ++ """ Updates repository indexes """ ++ # TODO: use force ? ++ self.allow_cancel(False) ++ self.percentage(0) ++ self.status(STATUS_REFRESH_CACHE) ++ ++ slice = (100 / len(pisi.api.list_repos())) / 2 ++ ++ percentage = 0 ++ for repo in pisi.api.list_repos(): ++ pisi.api.update_repo(repo) ++ percentage += slice ++ self.percentage(percentage) ++ ++ self.percentage(100) ++ ++ def remove_packages(self, transaction_flags, package_ids, ++ allowdeps, autoremove): ++ """ Removes given package from system""" ++ self.allow_cancel(False) ++ self.percentage(None) ++ # TODO: use autoremove ++ packages = list() ++ ++ for package_id in package_ids: ++ package = self.get_package_from_id(package_id)[0] ++ if not self.installdb.has_package(package): ++ self.error(ERROR_PACKAGE_NOT_INSTALLED, ++ "Package is not installed") ++ packages.append(package) ++ ++ def progress_cb(**kw): ++ self.percentage(int(kw['percent'])) ++ ++ ui = SimplePisiHandler() ++ ++ package = self.get_package_from_id(package_ids[0])[0] ++ self.status(STATUS_REMOVE) ++ ++ if TRANSACTION_FLAG_SIMULATE in transaction_flags: ++ # Simulated, not real. ++ for package in packages: ++ self._report_all_for_package(package, remove=True) ++ return ++ try: ++ pisi.api.remove(packages) ++ except pisi.Error, e: ++ self.error(ERROR_CANNOT_REMOVE_SYSTEM_PACKAGE, e) ++ pisi.api.set_userinterface(self.saved_ui) ++ ++ def repo_set_data(self, repo_id, parameter, value): ++ """ Sets a parameter for the repository specified """ ++ self.allow_cancel(False) ++ self.percentage(None) ++ ++ if parameter == "add-repo": ++ try: ++ pisi.api.add_repo(repo_id, value, parameter) ++ except pisi.Error, e: ++ self.error(ERROR_UNKNOWN, e) ++ ++ try: ++ pisi.api.update_repo(repo_id) ++ except pisi.fetcher.FetchError: ++ pisi.api.remove_repo(repo_id) ++ err = "Could not reach the repository, removing from system" ++ self.error(ERROR_REPO_NOT_FOUND, err) ++ elif parameter == "remove-repo": ++ try: ++ pisi.api.remove_repo(repo_id) ++ except pisi.Error: ++ self.error(ERROR_REPO_NOT_FOUND, "Repository does not exist") ++ else: ++ self.error(ERROR_NOT_SUPPORTED, "Parameter not supported") ++ ++ def resolve(self, filters, package): ++ """ Turns a single package name into a package_id ++ suitable for the other methods """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ ++ self.__get_package(package[0], filters) ++ ++ def search_details(self, filters, values): ++ """ Prints a detailed list of packages contains search term """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ self.status(STATUS_INFO) ++ ++ # Internal FIXME: Use search_details instead of _package when API ++ # gains that ability :) ++ for pkg in pisi.api.search_package(values): ++ self.__get_package(pkg, filters) ++ ++ def search_file(self, filters, values): ++ """ Prints the installed package which contains the specified file """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ self.status(STATUS_INFO) ++ ++ for value in values: ++ # Internal FIXME: Why it is needed? ++ value = value.lstrip("/") ++ ++ for pkg, files in pisi.api.search_file(value): ++ self.__get_package(pkg) ++ ++ def search_group(self, filters, values): ++ """ Prints a list of packages belongs to searched group """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ self.status(STATUS_INFO) ++ ++ for value in values: ++ packages = list() ++ for item in self.groups: ++ if self.groups[item] == value: ++ try: ++ pkgs = self.componentdb.get_packages(item, walk=False) ++ packages.extend(pkgs) ++ except: ++ self.error(ERROR_GROUP_NOT_FOUND, ++ "Component %s was not found" % value) ++ for pkg in packages: ++ self.__get_package(pkg, filters) ++ ++ def search_name(self, filters, values): ++ """ Prints a list of packages contains search term in its name """ ++ self.allow_cancel(True) ++ self.percentage(None) ++ self.status(STATUS_INFO) ++ ++ for value in values: ++ for pkg in pisi.api.search_package([value]): ++ self.__get_package(pkg, filters) ++ ++ def update_packages(self, transaction_flags, package_ids): ++ """ Updates given package to its latest version """ ++ ++ # FIXME: use only_trusted ++ ++ # FIXME: fetch/install progress ++ self.allow_cancel(False) ++ self.percentage(None) ++ ++ packages = list() ++ for package_id in package_ids: ++ package = self.get_package_from_id(package_id)[0] ++ if not self.installdb.has_package(package): ++ self.error(ERROR_PACKAGE_NOT_INSTALLED, ++ "Cannot update a package that is not installed") ++ packages.append(package) ++ ++ def progress_cb(**kw): ++ self.percentage(int(kw['percent'])) ++ ++ ui = SimplePisiHandler() ++ pisi.api.set_userinterface(ui) ++ ui.the_callback = progress_cb ++ ++ if TRANSACTION_FLAG_SIMULATE in transaction_flags: ++ for package in packages: ++ self._report_all_for_package(package) ++ return ++ try: ++ pisi.api.upgrade(packages) ++ except pisi.Error, e: ++ self.error(ERROR_UNKNOWN, e) ++ pisi.api.set_userinterface(self.saved_ui) ++ ++ def update_system(self, only_trusted): ++ """ Updates all available packages """ ++ # FIXME: use only_trusted ++ # FIXME: fetch/install progress ++ self.allow_cancel(False) ++ self.percentage(None) ++ ++ if not len(pisi.api.list_upgradable()) > 0: ++ self.error(ERROR_NO_PACKAGES_TO_UPDATE, "System is already up2date") ++ ++ try: ++ pisi.api.upgrade(pisi.api.list_upgradable()) ++ except pisi.Error, e: ++ self.error(ERROR_UNKNOWN, e) ++ ++ ++def main(): ++ backend = PackageKitPisiBackend('') ++ backend.dispatcher(sys.argv[1:]) ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/backends/pisi/pk-backend-pisi.c b/backends/pisi/pk-backend-pisi.c +new file mode 100644 +index 000000000..beb2f5836 +--- /dev/null ++++ b/backends/pisi/pk-backend-pisi.c +@@ -0,0 +1,369 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2007 Richard Hughes ++ * Copyright (C) 2007 S.Çağlar Onur ++ * ++ * Licensed under the GNU General Public License Version 2 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program 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 General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++ ++static PkBackendSpawn *spawn; ++ ++void ++pk_backend_start_job (PkBackend *backend, PkBackendJob *job) ++{ ++ if (pk_backend_spawn_is_busy (spawn)) { ++ pk_backend_job_error_code (job, ++ PK_ERROR_ENUM_LOCK_REQUIRED, ++ "spawned backend requires lock"); ++ return; ++ } ++} ++ ++void ++pk_backend_initialize (GKeyFile *conf, PkBackend *backend) ++{ ++ g_debug ("backend: initialize"); ++ ++ /* BACKEND MAINTAINER: feel free to remove this when you've ++ * added support for ONLY_DOWNLOAD and merged the simulate ++ * methods as specified in backends/PORTING.txt */ ++ /*g_error ("Backend needs to be ported to 0.8.x -- " ++ "see backends/PORTING.txt for details"); */ ++ ++ spawn = pk_backend_spawn_new (conf); ++ pk_backend_spawn_set_name (spawn, "pisi"); ++} ++ ++void ++pk_backend_destroy (PkBackend *backend) ++{ ++ g_debug ("backend: destroy"); ++ g_object_unref (spawn); ++} ++ ++PkBitfield ++pk_backend_get_groups (PkBackend *backend) ++{ ++ return pk_bitfield_from_enums ( ++ PK_GROUP_ENUM_ACCESSORIES, ++ PK_GROUP_ENUM_EDUCATION, ++ PK_GROUP_ENUM_GAMES, ++ PK_GROUP_ENUM_INTERNET, ++ PK_GROUP_ENUM_OTHER, ++ PK_GROUP_ENUM_PROGRAMMING, ++ PK_GROUP_ENUM_MULTIMEDIA, ++ PK_GROUP_ENUM_SYSTEM, ++ PK_GROUP_ENUM_DESKTOP_GNOME, ++ PK_GROUP_ENUM_DESKTOP_KDE, ++ PK_GROUP_ENUM_DESKTOP_OTHER, ++ PK_GROUP_ENUM_PUBLISHING, ++ PK_GROUP_ENUM_SERVERS, ++ PK_GROUP_ENUM_FONTS, ++ PK_GROUP_ENUM_ADMIN_TOOLS, ++ PK_GROUP_ENUM_LOCALIZATION, ++ PK_GROUP_ENUM_VIRTUALIZATION, ++ PK_GROUP_ENUM_SECURITY, ++ PK_GROUP_ENUM_POWER_MANAGEMENT, ++ PK_GROUP_ENUM_UNKNOWN, ++ -1); ++} ++ ++PkBitfield ++pk_backend_get_filters (PkBackend *backend) ++{ ++ return pk_bitfield_from_enums( ++ PK_FILTER_ENUM_GUI, ++ PK_FILTER_ENUM_INSTALLED, ++ -1); ++} ++ ++void ++pk_backend_cancel (PkBackend *backend, PkBackendJob *job) ++{ ++ /* this feels bad... */ ++ pk_backend_spawn_kill (spawn); ++} ++ ++void ++pk_backend_download_packages (PkBackend *backend, PkBackendJob *job, gchar **package_ids, const gchar *directory) ++{ ++ gchar *package_ids_temp; ++ ++ /* send the complete list as stdin */ ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "download-packages", directory, package_ids_temp, NULL); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_depends_on (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids, gboolean recursive) ++{ ++ gchar *filters_text; ++ gchar *package_ids_temp; ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ filters_text = pk_filter_bitfield_to_string (filters); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "depends-on", filters_text, package_ids_temp, pk_backend_bool_to_string (recursive), NULL); ++ g_free (filters_text); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_get_details (PkBackend *backend, PkBackendJob *job, gchar **package_ids) ++{ ++ gchar *package_ids_temp; ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-details", package_ids_temp, NULL); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_get_files (PkBackend *backend, PkBackendJob *job, gchar **package_ids) ++{ ++ gchar *package_ids_temp; ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-files", package_ids_temp, NULL); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_required_by (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids, gboolean recursive) ++{ ++ gchar *filters_text; ++ gchar *package_ids_temp; ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ filters_text = pk_filter_bitfield_to_string (filters); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "required-by", filters_text, package_ids_temp, pk_backend_bool_to_string (recursive), NULL); ++ g_free (filters_text); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_get_updates (PkBackend *backend, PkBackendJob *job, PkBitfield filters) ++{ ++ gchar *filters_text; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-updates", filters_text, NULL); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_get_update_detail (PkBackend *backend, PkBackendJob *job, gchar **package_ids) ++{ ++ gchar *package_ids_temp; ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-update-detail", package_ids_temp, NULL); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_install_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) ++{ ++ gchar *package_ids_temp; ++ gchar *transaction_flags_temp; ++ ++ /* check network state */ ++ if (!pk_backend_is_online (backend)) { ++ pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot install when offline"); ++ pk_backend_job_finished (job); ++ return; ++ } ++ ++ /* send the complete list as stdin */ ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags); ++ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "install-packages", transaction_flags_temp, package_ids_temp, NULL); ++ g_free (package_ids_temp); ++ g_free (transaction_flags_temp); ++} ++ ++void ++pk_backend_install_files (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **full_paths) ++{ ++ gchar *package_ids_temp; ++ gchar *transaction_flags_temp; ++ ++ /* send the complete list as stdin */ ++ package_ids_temp = g_strjoinv (PK_BACKEND_SPAWN_FILENAME_DELIM, full_paths); ++ transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags); ++ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "install-files", transaction_flags_temp, package_ids_temp, NULL); ++ g_free (package_ids_temp); ++ g_free (transaction_flags_temp); ++} ++ ++void ++pk_backend_refresh_cache (PkBackend *backend, PkBackendJob *job, gboolean force) ++{ ++ /* check network state */ ++ if (!pk_backend_is_online (backend)) { ++ pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot refresh cache whilst offline"); ++ pk_backend_job_finished (job); ++ return; ++ } ++ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "refresh-cache", pk_backend_bool_to_string (force), NULL); ++} ++ ++void ++pk_backend_remove_packages (PkBackend *backend, PkBackendJob *job, ++ PkBitfield transaction_flags, ++ gchar **package_ids, ++ gboolean allow_deps, ++ gboolean autoremove) ++{ ++ gchar *package_ids_temp; ++ gchar *transaction_flags_temp; ++ ++ /* send the complete list as stdin */ ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags); ++ pk_backend_spawn_helper (spawn, ++ job, "pisiBackend.py", ++ "remove-packages", ++ transaction_flags_temp, ++ package_ids_temp, ++ pk_backend_bool_to_string (allow_deps), ++ pk_backend_bool_to_string (autoremove), ++ NULL); ++ ++ g_free (transaction_flags_temp); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_search_details (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) ++{ ++ gchar *filters_text; ++ gchar *search; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ search = g_strjoinv ("&", values); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-details", filters_text, search, NULL); ++ g_free (search); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_search_files (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) ++{ ++ gchar *filters_text; ++ gchar *search; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ search = g_strjoinv ("&", values); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-file", filters_text, search, NULL); ++ g_free (search); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_search_groups (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) ++{ ++ gchar *filters_text; ++ gchar *search; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ search = g_strjoinv ("&", values); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-group", filters_text, search, NULL); ++ g_free (search); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) ++{ ++ gchar *filters_text; ++ gchar *search; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ search = g_strjoinv ("&", values); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "search-name", filters_text, search, NULL); ++ g_free (search); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_update_packages (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags, gchar **package_ids) ++{ ++ gchar *package_ids_temp; ++ gchar *transaction_flags_temp; ++ ++ /* check network state */ ++ if (!pk_backend_is_online (backend)) { ++ pk_backend_job_error_code (job, PK_ERROR_ENUM_NO_NETWORK, "Cannot install when offline"); ++ pk_backend_job_finished (job); ++ return; ++ } ++ ++ /* send the complete list as stdin */ ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags); ++ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "update-packages", transaction_flags_temp, package_ids_temp, NULL); ++ g_free (package_ids_temp); ++ g_free (transaction_flags_temp); ++} ++ ++void ++pk_backend_update_system (PkBackend *backend, PkBackendJob *job, PkBitfield transaction_flags) ++{ ++ gchar *transaction_flags_temp; ++ transaction_flags_temp = pk_transaction_flag_bitfield_to_string (transaction_flags); ++ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "update-system", transaction_flags_temp, NULL); ++ ++ g_free (transaction_flags_temp); ++} ++ ++void ++pk_backend_resolve (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **package_ids) ++{ ++ gchar *filters_text; ++ gchar *package_ids_temp; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ package_ids_temp = pk_package_ids_to_string (package_ids); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "resolve", filters_text, package_ids_temp, NULL); ++ g_free (filters_text); ++ g_free (package_ids_temp); ++} ++ ++void ++pk_backend_get_repo_list (PkBackend *backend, PkBackendJob *job, PkBitfield filters) ++{ ++ gchar *filters_text; ++ filters_text = pk_filter_bitfield_to_string (filters); ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "get-repo-list", filters_text, NULL); ++ g_free (filters_text); ++} ++ ++void ++pk_backend_repo_set_data (PkBackend *backend, PkBackendJob *job, const gchar *rid, const gchar *parameter, const gchar *value) ++{ ++ pk_backend_spawn_helper (spawn, job, "pisiBackend.py", "repo-set-data", rid, parameter, value, NULL); ++} ++ ++const gchar * ++pk_backend_get_description (PkBackend *backend) ++{ ++ return "PiSi - Packages Installed Successfully as Intended"; ++} ++ ++const gchar * ++pk_backend_get_author (PkBackend *backend) ++{ ++ return "S.Çağlar Onur \nIkey Doherty \nBerk Çakar "; ++} +diff --git a/meson_options.txt b/meson_options.txt +index 2f3a1dac9..7623dc9c2 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -1,4 +1,4 @@ +-option('packaging_backend', type : 'array', choices : ['alpm', 'aptcc', 'dnf', 'dummy', 'entropy', 'poldek', 'portage', 'slack', 'zypp', 'nix'], value : ['dummy'], description : 'The name of the backend to use') ++option('packaging_backend', type : 'array', choices : ['alpm', 'aptcc', 'dnf', 'dummy', 'entropy', 'pisi', 'poldek', 'portage', 'slack', 'zypp', 'nix'], value : ['dummy'], description : 'The name of the backend to use') + option('dnf_vendor', type : 'combo', choices : ['fedora', 'mageia', 'openmandriva', 'rosa', 'opensuse'], value : 'fedora', description : 'Vendor configuration when using the dnf backend') + option('systemd', type : 'boolean', value : true, description : 'Use systemd and logind') + option('systemdsystemunitdir', type : 'string', value : '', description : 'Directory for systemd service files') +-- +2.24.3 (Apple Git-128) + diff --git a/desktop/misc/packagekit/files/pisi.conf b/desktop/misc/packagekit/files/pisi.conf new file mode 100644 index 0000000000..dd6855cc8e --- /dev/null +++ b/desktop/misc/packagekit/files/pisi.conf @@ -0,0 +1,36 @@ +system.base = system +system.boot = system +system.utils = system +desktop.gnome = desktop-gnome +desktop.gnome.base = desktop-gnome +desktop.gtk = desktop-gnome + +security.crypto = security + +editor = unknown +kernel.image = admin-tools +kernel.devel = admin-tools + +#desktop.font = fonts +desktop.library = desktop-other +desktop.xfce = desktop-xfce +desktop.core = desktop-other +programming = programming +programming.python = programming +system.devel = programming + +network = internet +network.clients = network +#network.web = internet + +xorg.apps = desktop-other +xorg.base = desktop-other +xorg.display = desktop-other +xorg.driver = desktop-other +xorg.fonts = fonts +xorg.proto = programming +xorg.server = desktop-other + +#FIXME: +Bug-Regex = Bug-PisiLinux: T(\d+) +Bug-URI = https://www.pisilinux.org/T%s \ No newline at end of file diff --git a/desktop/misc/packagekit/pspec.xml b/desktop/misc/packagekit/pspec.xml new file mode 100644 index 0000000000..4990f5f71a --- /dev/null +++ b/desktop/misc/packagekit/pspec.xml @@ -0,0 +1,86 @@ + + + + + packagekit + https://www.freedesktop.org/software/PackageKit/ + + Pisi Linux Admins + admins@pisilinux.org + + GPLv2 + A system designed to make installation and updates of packages easier. + PackageKit is a system designed to make installing and updating software on your computer easier. The primary design goal is to unify all the software graphical tools used in different distributions, and use some of the latest technology like PolicyKit. + http://www.freedesktop.org/software/PackageKit/releases/PackageKit-1.1.5.tar.xz + + sqlite-devel + gtk3-devel + gtk2-devel + intltool + polkit-devel + python3-devel + elogind-devel + gobject-introspection-devel + + + + + packagekit + + atk + gtk2 + gtk3 + cairo + glib2 + pango + sqlite + freetype + harfbuzz + fontconfig + gdk-pixbuf + + + /var + /etc + /usr/bin + /usr/lib + /usr/libexec + /usr/share/locale + /usr/share/doc + /usr/share/gtk-doc + /usr/share/bash-completion + /usr/share/dbus-1 + /usr/share/PackageKit + /usr/share/polkit-1 + + + pisi.conf + + + + + packagekit-devel + Development files for packagekit + + packagekit + glib2-devel + sqlite-devel + + + /usr/include + /usr/lib/pkgconfig + /usr/share/gir-1.0 + + + + + + + 2021-06-05 + 1.1.5 + First release for Pisi Linux + Berk Çakar + berk2238@hotmail.com + + + diff --git a/programming/library/appstream-glib/actions.py b/programming/library/appstream-glib/actions.py index d2ee6e89e0..2eadf097e0 100755 --- a/programming/library/appstream-glib/actions.py +++ b/programming/library/appstream-glib/actions.py @@ -4,23 +4,19 @@ # Licensed under the GNU General Public License, version 3. # See the file http://www.gnu.org/copyleft/gpl.txt -from pisi.actionsapi import autotools from pisi.actionsapi import pisitools -from pisi.actionsapi import shelltools -from pisi.actionsapi import get +from pisi.actionsapi import mesontools def setup(): - shelltools.makedirs("build") - shelltools.cd("build") - shelltools.system("meson --prefix=/usr -Dstemmer=false -Drpm=false ..") + mesontools.configure("--prefix=/usr \ + -Dstemmer=false \ + -Drpm=false \ + -Dpisi=true") def build(): - shelltools.cd("build") - shelltools.system("ninja") + mesontools.build() def install(): - shelltools.cd("build") - shelltools.system("DESTDIR=%s ninja install" % get.installDIR()) - - shelltools.cd("..") + mesontools.install() + pisitools.dodoc("README*", "NEWS", "COPYING", "AUTHORS") diff --git a/programming/library/appstream-glib/files/0001-builder-Implement-support-for-.pisi-format.patch b/programming/library/appstream-glib/files/0001-builder-Implement-support-for-.pisi-format.patch new file mode 100644 index 0000000000..8d2b1907e4 --- /dev/null +++ b/programming/library/appstream-glib/files/0001-builder-Implement-support-for-.pisi-format.patch @@ -0,0 +1,714 @@ +From b31fb56588c264d297ca04f36a7b3162d3db7736 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Sun, 26 Jul 2015 16:01:47 +0100 +Subject: [PATCH 1/2] builder: Implement support for Solus .pisi format + +This format is employed by the Solus Operating System, and is implemented +in such a manner that it does not need to rely on executing the external +pisi program, which is Pythonic, thus slower. + +This utilises code that is part of an ongoing rewrite of pisi, as such +in the future it would be refactored to use a 'libpisi'. As such no pretense +is held to support the "PiSi" format, which the Pythonic pisi is forked from, +as that support will not hold in the future in libpisi. + +As of 25 Jun 2021, this patch adapted for appstream-glib 0.7.18 and .pisi format by: +Berk Çakar + +Signed-off-by: Ikey Doherty +--- + meson.build | 6 ++++++ + meson_options.txt | 1 + + libappstream-builder/meson.build | 8 ++++++++ + libappstream-builder/asb-context.c | 7 +++++++ + libappstream-builder/asb-package-pisi.c | 532 +++++++++++++++++++++++++++++++ + libappstream-builder/asb-package-pisi.h | 60 ++++ + 5 files changed, 613 insertions(+) + create mode 100644 libappstream-builder/asb-package-pisi.c + create mode 100644 libappstream-builder/asb-package-pisi.h + +--- a/meson.build ++++ b/meson.build +@@ -97,6 +97,12 @@ if get_option('rpm') + conf.set('HAVE_RPM', 1) + endif + ++if get_option('pisi') ++ # dummy dependency for now... ++ pisi = dependency('libxml-2.0') ++ conf.set('HAVE_PISI', 1) ++endif ++ + # support loading yaml files + if get_option('dep11') + yaml = dependency('yaml-0.1') +diff --git a/meson_options.txt b/meson_options.txt +index 518ed89..9d6d543 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -2,6 +2,7 @@ option('dep11', type : 'boolean', value : true, description : 'enable DEP-11') + option('builder', type : 'boolean', value : true, description : 'enable AppStream builder') + option('rpm', type : 'boolean', value : true, description : 'enable RPM support') + option('alpm', type : 'boolean', value : false, description : 'enable ALPM support') ++option('pisi', type : 'boolean', value : false, description : 'enable PiSi support') + option('fonts', type : 'boolean', value : true, description : 'enable font support') + option('stemmer', type : 'boolean', value : true, description : 'enable stemmer support') + option('man', type : 'boolean', value : true, description : 'generate man pages') +diff --git a/libappstream-builder/meson.build b/libappstream-builder/meson.build +index 79e8a74..bf85c5d 100644 +--- a/libappstream-builder/meson.build ++++ b/libappstream-builder/meson.build +@@ -25,6 +25,10 @@ if get_option('alpm') + deps = deps + [alpm] + endif + ++if get_option('pisi') ++ deps = deps + [pisi] ++endif ++ + sources = [ + 'asb-app.c', + 'asb-context.c', +@@ -45,6 +49,10 @@ if get_option('alpm') + sources = sources + ['asb-package-alpm.c'] + endif + ++if get_option('pisi') ++ sources = sources + ['asb-package-pisi.c'] ++endif ++ + top_build_incdir = include_directories('..') + + asbuilder = static_library( +diff --git a/libappstream-builder/asb-context.c b/libappstream-builder/asb-context.c +index e2b83df..4fefadd 100644 +--- a/libappstream-builder/asb-context.c ++++ b/libappstream-builder/asb-context.c +@@ -32,6 +32,9 @@ + #ifdef HAVE_ALPM + #include "asb-package-alpm.h" + #endif ++#ifdef HAVE_PISI ++#include "asb-package-pisi.h" ++#endif + #include "asb-package-cab.h" + #include "asb-package-deb.h" + +@@ -447,6 +452,10 @@ asb_context_add_filename (AsbContext *ctx, const gchar *filename, GError **error + if (g_str_has_suffix (filename, ".pkg.tar") || + g_str_has_suffix (filename, ".pkg.tar.xz")) + pkg = asb_package_alpm_new (); +-#endif ++#endif ++#ifdef HAVE_PISI ++ if (g_str_has_suffix (filename, ".pisi")) ++ pkg = asb_package_pisi_new (); ++#endif + if (g_str_has_suffix (filename, ".cab")) + pkg = asb_package_cab_new (); +diff --git a/libappstream-builder/asb-package-pisi.c b/libappstream-builder/asb-package-pisi.c +new file mode 100644 +index 0000000..cf6572c +--- /dev/null ++++ b/libappstream-builder/asb-package-pisi.c +@@ -0,0 +1,532 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2015 Ikey Doherty ++ * ++ * Licensed under the GNU Lesser General Public License Version 2.1 ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/** ++ * SECTION:asb-package-pisi ++ * @short_description: Object representing a .PISI package file. ++ * @stability: Unstable ++ * ++ * This object represents one .pisi package file. ++ */ ++ ++#include "config.h" ++ ++#include "asb-package-pisi.h" ++#include "asb-plugin.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++G_DEFINE_TYPE (AsbPackagePisi, asb_package_pisi, ASB_TYPE_PACKAGE) ++ ++/** ++ * Storage for pisi metadata ++ */ ++typedef struct pisi_meta_t { ++ gchar *name; /**in_source = !self->in_source; ++ } else if (xmlStrEqual(name, BAD_CAST "Package")) { ++ self->in_package = !self->in_package; ++ } else if (xmlStrEqual(name, BAD_CAST "Update")) { ++ self->in_update = !self->in_update; ++ if (self->in_update) { ++ xmlChar *attr = xmlTextReaderGetAttribute(r, BAD_CAST "release"); ++ if (!attr) { ++ fprintf(stderr, "Malformed spec: No release ID\n"); ++ return FALSE; ++ } ++ rel = atoi((const char*)attr); ++ if (rel > meta->release) { ++ meta->release = rel; ++ self->need_update = TRUE; ++ } ++ xmlFree(attr); ++ } ++ } ++ ++ if (self->in_source) { ++ if (xmlStrEqual(name, BAD_CAST "Name")) { ++ self->in_name = !self->in_name; ++ } else if (xmlStrEqual(name, BAD_CAST "Packager")) { ++ self->in_packager = !self->in_packager; ++ } else if (xmlStrEqual(name, BAD_CAST "Homepage")) { ++ self->in_url = !self->in_url; ++ } ++ if (self->in_name && !self->in_packager && !meta->source) { ++ val = xmlTextReaderConstValue(r); ++ if (!val) { ++ return TRUE; ++ } ++ meta->source = g_strdup((gchar*)val); ++ } else if (self->in_url && !meta->url) { ++ val = xmlTextReaderConstValue(r); ++ if (!val) { ++ return TRUE; ++ } ++ meta->url = g_strdup((gchar*)val); ++ } ++ } else if (self->in_package) { ++ if (xmlStrEqual(name, BAD_CAST "Name")) { ++ self->in_name = !self->in_name; ++ } else if (xmlStrEqual(name, BAD_CAST "License")) { ++ self->in_license = !self->in_license; ++ } else if (xmlStrEqual(name, BAD_CAST "RuntimeDependencies")) { ++ self->in_rundeps = !self->in_rundeps; ++ } ++ if (self->in_name && !self->in_update) { ++ val = xmlTextReaderConstValue(r); ++ if (!val) { ++ return TRUE; ++ } ++ meta->name = g_strdup((gchar*)val); ++ } else if (self->in_license) { ++ val = xmlTextReaderConstValue(r); ++ if (!val) { ++ return TRUE; ++ } ++ meta->licenses = g_slist_prepend(meta->licenses, g_strdup((gchar*)val)); ++ } else if (self->in_rundeps) { ++ if (xmlStrEqual(name, BAD_CAST "Dependency")) { ++ self->in_dep = !self->in_dep; ++ } ++ val = xmlTextReaderConstValue(r); ++ if (self->in_dep && val) { ++ meta->deps = g_slist_prepend(meta->deps, g_strdup((gchar*)val)); ++ } ++ } else if (self->in_update && self->need_update) { ++ /* invariably we'll hit here only once from sorted (default) high-to-low history in spec */ ++ if (xmlStrEqual(name, BAD_CAST "Version")) { ++ self->in_version = !self->in_version; ++ } ++ if (self->in_version) { ++ val = xmlTextReaderConstValue(r); ++ if (!val) { ++ return TRUE; ++ } ++ if (meta->version) { ++ g_free(meta->version); ++ } ++ meta->version = g_strdup((gchar*)val); ++ self->need_update = FALSE; ++ } ++ } ++ } ++ return TRUE; ++} ++ ++pisi_meta_t *examine_metadata(const char *filename) ++{ ++ xmlTextReaderPtr r = xmlReaderForFile(filename, NULL, 0); ++ int ret; ++ meta_state_t self = {0}; ++ pisi_meta_t *meta = NULL; ++ ++ meta = calloc(1, sizeof(pisi_meta_t)); ++ if (!meta) { ++ fprintf(stderr, "OOM\n"); ++ return NULL; ++ } ++ ++ while ((ret = xmlTextReaderRead(r)) > 0) { ++ if (!process_meta_node(&self, meta, r)) { ++ fprintf(stderr, "process_meta_node exited abnormally\n"); ++ break; ++ } ++ } ++ ++ xmlFreeTextReader(r); ++ return meta; ++} ++ ++gboolean process_file_node(file_state_t *self, GPtrArray *ret, xmlTextReaderPtr r) ++{ ++ const xmlChar *name = NULL; ++ ++ name = xmlTextReaderConstName(r); ++ if (!name) { ++ return FALSE; ++ } ++ ++ if (xmlStrEqual(name, BAD_CAST "File")) { ++ self->in_file = !self->in_file; ++ } else if (self->in_file && xmlStrEqual(name, BAD_CAST "Path")) { ++ self->in_path = !self->in_path; ++ } else if (self->in_path) { ++ const xmlChar *val = xmlTextReaderConstValue(r); ++ gchar *tmp = NULL; ++ if (!val) { ++ return TRUE; ++ } ++ if (val[0] != '/') { ++ tmp = g_strdup_printf("/%s", (gchar*)val); ++ } else { ++ tmp = g_strdup((gchar*)val); ++ } ++ g_ptr_array_add(ret, tmp); ++ } ++ return TRUE; ++} ++ ++GPtrArray *examine_files(const char *filename) ++{ ++ xmlTextReaderPtr r = xmlReaderForFile(filename, NULL, 0); ++ int ret; ++ file_state_t self = {0}; ++ GPtrArray *arr = NULL; ++ ++ arr = g_ptr_array_new_with_free_func(g_free); ++ if (!arr) { ++ fprintf(stderr, "OOM\n"); ++ return NULL; ++ } ++ ++ while ((ret = xmlTextReaderRead(r)) > 0) { ++ if (!process_file_node(&self, arr, r)) { ++ fprintf(stderr, "process_file_node exited abnormally\n"); ++ break; ++ } ++ } ++ ++ g_ptr_array_add(arr, NULL); ++ ++ xmlFreeTextReader(r); ++ return arr; ++} ++ ++void pisi_meta_free(pisi_meta_t *pisi) ++{ ++ if (!pisi) { ++ return; ++ } ++ if (pisi->name) { ++ g_free(pisi->name); ++ } ++ if (pisi->source) { ++ g_free(pisi->source); ++ } ++ if (pisi->version) { ++ g_free(pisi->version); ++ } ++ if (pisi->url) { ++ g_free(pisi->url); ++ } ++ if (pisi->deps) { ++ g_slist_free_full(pisi->deps, g_free); ++ } ++ if (pisi->licenses) { ++ g_slist_free_full(pisi->licenses, g_free); ++ } ++ free(pisi); ++} ++ ++void close_pisi(pisi_t *pisi) ++{ ++ if (pisi->meta) { ++ pisi_meta_free(pisi->meta); ++ } ++ if (pisi->files) { ++ g_ptr_array_unref(pisi->files); ++ } ++ free(pisi); ++} ++ ++pisi_t *open_pisi(const char *filename) ++{ ++ pisi_t *ret = NULL; ++ pisi_meta_t *meta = NULL; ++ GPtrArray *files = NULL; ++ struct archive *a = NULL; ++ struct archive_entry *entry = NULL; ++ int r; ++ char fname[PATH_MAX]; ++ char template[] = "/tmp/solus-pisi-XXXXXX"; ++ int fd; ++ ++ a = archive_read_new(); ++ archive_read_support_filter_all(a); ++ archive_read_support_format_all(a); ++ ++ /* open 'er up */ ++ r = archive_read_open_filename(a, filename, 10480); ++ if (r != ARCHIVE_OK) { ++ fprintf(stderr, "Unable to open archive\n"); ++ goto clean; ++ } ++ ++ while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { ++ const char *name = archive_entry_pathname(entry); ++ gboolean filesxml = FALSE; ++ ++ if ((filesxml = g_str_equal(name, "files.xml")) || g_str_equal(name, "metadata.xml")) { ++ strncpy(fname, template, sizeof(template)); ++ fd = mkstemp(fname); ++ if (fd <= 0) { ++ fprintf(stderr, "Failed to open temporary file: %s\n", strerror(errno)); ++ goto clean; ++ } ++ ++ r = archive_read_data_into_fd(a, fd); ++ if (r != ARCHIVE_OK) { ++ fprintf(stderr, "Failed to extra file: %s\n", name); ++ close(fd); ++ (void)unlink(fname); ++ goto clean; ++ } ++ ++ if (filesxml) { ++ files = examine_files(fname); ++ } else { ++ meta = examine_metadata(fname); ++ } ++ close(fd); ++ (void)unlink(fname); ++ ++ } else { ++ archive_read_data_skip(a); ++ } ++ } ++ ++ if (!meta) { ++ fprintf(stderr, "Failed to inspect metadata\n"); ++ goto bail; ++ } ++ if (!files) { ++ fprintf(stderr, "Failed to inspect files\n"); ++ goto bail; ++ } ++ ++ ret = calloc(1, sizeof(pisi_t)); ++ if (!ret) { ++ fprintf(stderr, "OOM\n"); ++ goto bail; ++ } ++ ++ ret->meta = meta; ++ ret->files = files; ++ ++clean: ++ archive_read_free(a); ++ ++ return ret; ++ ++bail: ++ if (meta) { ++ pisi_meta_free(meta); ++ } ++ if (files) { ++ g_ptr_array_unref(files); ++ } ++ return NULL; ++} ++ ++/** ++ * asb_package_pisi_init: ++ **/ ++static void ++asb_package_pisi_init (AsbPackagePisi *pkg) ++{ ++} ++ ++ ++/** ++ * asb_package_pisi_open: ++ **/ ++static gboolean ++asb_package_pisi_open (AsbPackage *pkg, const gchar *filename, GError **error) ++{ ++ pisi_t *pisi = NULL; ++ gchar *rel = NULL; ++ GSList *elem = NULL; ++ ++ pisi = open_pisi(filename); ++ if (!pisi) ++ return FALSE; ++ ++ asb_package_set_name (pkg, pisi->meta->name); ++ asb_package_set_source (pkg, pisi->meta->source); ++ ++ rel = g_strdup_printf ("%d", pisi->meta->release); ++ asb_package_set_release (pkg, rel); ++ asb_package_set_version (pkg, pisi->meta->version); ++ asb_package_set_epoch (pkg, 1); ++ g_free(rel); ++ ++ for (elem = pisi->meta->deps; elem; elem = elem->next) { ++ asb_package_add_dep (pkg, elem->data); ++ } ++ asb_package_set_filelist (pkg, (gchar**)pisi->files->pdata); ++ ++ asb_package_set_license (pkg, pisi->meta->licenses->data); ++ ++ close_pisi(pisi); ++ ++ return TRUE; ++} ++ ++/** ++ * asb_package_pisi_explode: ++ **/ ++static gboolean ++asb_package_pisi_explode (AsbPackage *pkg, ++ const gchar *dir, ++ GPtrArray *glob, ++ GError **error) ++{ ++ const char *name = "install.tar.xz"; ++ g_autofree gchar *tpath = NULL; ++ ++ if (!asb_utils_explode (asb_package_get_filename (pkg), ++ dir, NULL, error)) { ++ return FALSE; ++ } ++ ++ tpath = g_build_filename(dir, name, NULL); ++ if (!g_file_test (tpath, G_FILE_TEST_EXISTS)) { ++ return FALSE; ++ } ++ ++ if (!asb_utils_explode (tpath, dir, glob, error)) { ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * asb_package_pisi_class_init: ++ **/ ++static void ++asb_package_pisi_class_init (AsbPackagePisiClass *klass) ++{ ++ AsbPackageClass *package_class = ASB_PACKAGE_CLASS (klass); ++ package_class->open = asb_package_pisi_open; ++ package_class->explode = asb_package_pisi_explode; ++} ++ ++/** ++ * asb_package_pisi_new: ++ * ++ * Creates a new PISI package. ++ * ++ * Returns: a package ++ * ++ * Since: 0.1.0 ++ **/ ++AsbPackage * ++asb_package_pisi_new (void) ++{ ++ AsbPackage *pkg; ++ pkg = g_object_new (ASB_TYPE_PACKAGE_PISI, NULL); ++ return ASB_PACKAGE (pkg); ++} +diff --git a/libappstream-builder/asb-package-pisi.h b/libappstream-builder/asb-package-pisi.h +new file mode 100644 +index 0000000..08dd01d +--- /dev/null ++++ b/libappstream-builder/asb-package-pisi.h +@@ -0,0 +1,60 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2015 Ikey Doherty ++ * ++ * Licensed under the GNU Lesser General Public License Version 2.1 ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 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 ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef ASB_PACKAGE_PISI_H ++#define ASB_PACKAGE_PISI_H ++ ++#include ++ ++#include ++#include ++ ++#include "asb-package.h" ++ ++#define ASB_TYPE_PACKAGE_PISI (asb_package_pisi_get_type()) ++#define ASB_PACKAGE_PISI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), ASB_TYPE_PACKAGE_PISI, AsbPackagePisi)) ++#define ASB_PACKAGE_PISI_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), ASB_TYPE_PACKAGE_PISI, AsbPackagePisiClass)) ++#define ASB_IS_PACKAGE_PISI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ASB_TYPE_PACKAGE_PISI)) ++#define ASB_IS_PACKAGE_PISI_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), ASB_TYPE_PACKAGE_PISI)) ++#define ASB_PACKAGE_PISI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), ASB_TYPE_PACKAGE_PISI, AsbPackagePisiClass)) ++ ++G_BEGIN_DECLS ++ ++typedef struct _AsbPackagePisi AsbPackagePisi; ++typedef struct _AsbPackagePisiClass AsbPackagePisiClass; ++ ++struct _AsbPackagePisi ++{ ++ AsbPackage parent; ++}; ++ ++struct _AsbPackagePisiClass ++{ ++ AsbPackageClass parent_class; ++}; ++ ++GType asb_package_pisi_get_type (void); ++ ++AsbPackage *asb_package_pisi_new (void); ++ ++G_END_DECLS ++ ++#endif /* ASB_PACKAGE_PISI_H */ +-- +2.11.0 diff --git a/programming/library/appstream-glib/files/0002-pisi-Enable-comparison-by-release-field-highest-win.patch b/programming/library/appstream-glib/files/0002-pisi-Enable-comparison-by-release-field-highest-win.patch new file mode 100644 index 0000000000..a126000086 --- /dev/null +++ b/programming/library/appstream-glib/files/0002-pisi-Enable-comparison-by-release-field-highest-win.patch @@ -0,0 +1,128 @@ +From 7c3980b2dc488e789b9df8d2fe376bf3c8cd2df7 Mon Sep 17 00:00:00 2001 +From: Ikey Doherty +Date: Sun, 26 Jul 2015 17:01:01 +0100 +Subject: [PATCH 2/2] pisi: Enable comparison by release field (highest wins) + +As of 25 Jun 2021, this patch adapted for appstream-glib 0.7.18 and .pisi format by: +Berk Çakar + +Signed-off-by: Ikey Doherty +--- + libappstream-builder/asb-package-pisi.c | 54 ++++++++++++++++++++++++++++++-- + 1 file changed, 51 insertions(+), 3 deletions(-) + +diff --git a/libappstream-builder/asb-package-pisi.c b/libappstream-builder/asb-package-pisi.c +index cf6572c..4a5bfa6 100644 +--- a/libappstream-builder/asb-package-pisi.c ++++ b/libappstream-builder/asb-package-pisi.c +@@ -38,8 +38,6 @@ + #include + #include + +-G_DEFINE_TYPE (AsbPackagePisi, asb_package_pisi, ASB_TYPE_PACKAGE) +- + /** + * Storage for pisi metadata + */ +@@ -87,6 +85,17 @@ typedef struct pisi_t { + } pisi_t; + + ++ ++typedef struct _AsbPackagePisiPrivate AsbPackagePisiPrivate; ++struct _AsbPackagePisiPrivate ++{ ++ pisi_t *package; ++}; ++ ++G_DEFINE_TYPE_WITH_PRIVATE (AsbPackagePisi, asb_package_pisi, ASB_TYPE_PACKAGE) ++ ++#define GET_PRIVATE(o) (asb_package_pisi_get_instance_private (o)) ++ + /** + * Process metadata.xml node + */ +@@ -438,6 +447,19 @@ asb_package_pisi_init (AsbPackagePisi *pkg) + { + } + ++/** ++ * asb_package_pisi_finalize: ++ **/ ++static void ++asb_package_pisi_finalize (GObject *object) ++{ ++ AsbPackagePisi *pkg = ASB_PACKAGE_PISI (object); ++ AsbPackagePisiPrivate *priv = GET_PRIVATE (pkg); ++ ++ close_pisi (priv->package); ++ ++ G_OBJECT_CLASS (asb_package_pisi_parent_class)->finalize (object); ++} + + /** + * asb_package_pisi_open: +@@ -448,11 +470,14 @@ asb_package_pisi_open (AsbPackage *pkg, const gchar *filename, GError **error) + pisi_t *pisi = NULL; + gchar *rel = NULL; + GSList *elem = NULL; ++ AsbPackagePisiPrivate *priv = NULL; + + pisi = open_pisi(filename); + if (!pisi) + return FALSE; + ++ priv = GET_PRIVATE(ASB_PACKAGE_PISI(pkg)); ++ + asb_package_set_name (pkg, pisi->meta->name); + asb_package_set_source (pkg, pisi->meta->source); + +@@ -469,7 +494,7 @@ asb_package_pisi_open (AsbPackage *pkg, const gchar *filename, GError **error) + + asb_package_set_license (pkg, pisi->meta->licenses->data); + +- close_pisi(pisi); ++ priv->package = pisi; + + return TRUE; + } +@@ -504,14 +529,37 @@ asb_package_pisi_explode (AsbPackage *pkg, + } + + /** ++ * asb_package_pisi_compare: ++ **/ ++static gint ++asb_package_pisi_compare (AsbPackage *pkg1, AsbPackage *pkg2) ++{ ++ AsbPackagePisi *pkg_pisi1 = ASB_PACKAGE_PISI (pkg1); ++ AsbPackagePisiPrivate *priv1 = GET_PRIVATE (pkg_pisi1); ++ ++ AsbPackagePisi *pkg_pisi2 = ASB_PACKAGE_PISI (pkg2); ++ AsbPackagePisiPrivate *priv2 = GET_PRIVATE (pkg_pisi2); ++ ++ gint rel1 = priv1->package->meta->release; ++ gint rel2 = priv2->package->meta->release; ++ ++ return (rel1 > rel2) - (rel1 < rel2); ++} ++ ++/** + * asb_package_pisi_class_init: + **/ + static void + asb_package_pisi_class_init (AsbPackagePisiClass *klass) + { + AsbPackageClass *package_class = ASB_PACKAGE_CLASS (klass); ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = asb_package_pisi_finalize; ++ + package_class->open = asb_package_pisi_open; + package_class->explode = asb_package_pisi_explode; ++ package_class->compare = asb_package_pisi_compare; + } + + /** +-- +2.11.0 diff --git a/programming/library/appstream-glib/pspec.xml b/programming/library/appstream-glib/pspec.xml index b54cdb3ce0..4a5c407494 100755 --- a/programming/library/appstream-glib/pspec.xml +++ b/programming/library/appstream-glib/pspec.xml @@ -30,13 +30,17 @@ libyaml-devel libutil-linux-devel gobject-introspection-devel + libxml2-devel + + 0001-builder-Implement-support-for-.pisi-format.patch + 0002-pisi-Enable-comparison-by-release-field-highest-win.patch + appstream-glib - gcap gtk3 cairo pango @@ -49,6 +53,7 @@ libarchive fontconfig libutil-linux + libxml2 /usr/bin @@ -58,34 +63,35 @@ /usr/share/bash-completion/ /usr/share/aclocal /usr/share/gettext - /usr/share/gir-1.0 /usr/share/installed-tests/appstream-glib /usr/share/doc - + appstream-glib-devel appstream-glib - gcap-devel - libxslt-devel - glib2-devel - libarchive-devel - libsoup-devel - json-glib-devel - gdk-pixbuf-devel - libpng-devel - gtk3-devel libutil-linux-devel + libarchive-devel + glib2-devel + gdk-pixbuf-devel /usr/include /usr/lib/pkgconfig + /usr/share/gir-1.0 + + 2021-06-23 + 0.7.18 + Added PiSi Support. + Berk Çakar + berk2238@hotmail.com + 2020-10-10 0.7.18