Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Lachu:SystemServices
PackageKit
n-0074-Update-Nix-backend-to-use-Nix-Flakes-414...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File n-0074-Update-Nix-backend-to-use-Nix-Flakes-414.patch of Package PackageKit
From e8140563ec7f78dd723068396dcad40e47580379 Mon Sep 17 00:00:00 2001 From: Matthew Bauer <mjbauer95@gmail.com> Date: Mon, 7 Feb 2022 21:06:01 -0600 Subject: [PATCH 074/102] Update Nix backend to use Nix Flakes (#414) * Update Nix backend to use Nix Flakes This is a large overhaul of the Nix backend. It now is much faster since it can make use of the eval caching that is available in Nix 2.6. diff --git a/backends/nix/buildenv.nix.gen.hh b/backends/nix/buildenv.nix.gen.hh new file mode 100644 index 000000000..bf4037a2c --- /dev/null +++ b/backends/nix/buildenv.nix.gen.hh @@ -0,0 +1,27 @@ +R"foo( +{ derivations, manifest }: + +derivation { + name = "user-environment"; + system = "builtin"; + builder = "builtin:buildenv"; + + inherit manifest; + + # !!! grmbl, need structured data for passing this in a clean way. + derivations = + map (d: + [ (d.meta.active or "true") + (d.meta.priority or 5) + (builtins.length d.outputs) + ] ++ map (output: builtins.getAttr output d) d.outputs) + derivations; + + # Building user environments remotely just causes huge amounts of + # network traffic, so don't do that. + preferLocalBuild = true; + + # Also don't bother substituting. + allowSubstitutes = false; +} +)foo" diff --git a/backends/nix/meson.build b/backends/nix/meson.build index 2213ce991..64bdba94c 100644 --- a/backends/nix/meson.build +++ b/backends/nix/meson.build @@ -1,23 +1,24 @@ add_languages('cpp') -nix_expr_dep = dependency('nix-expr', version: '>=1.12') -nix_main_dep = dependency('nix-main', version: '>=1.12') -nix_store_dep = dependency('nix-store', version: '>=1.12') +nix_expr_dep = dependency('nix-expr', version: '>=2.6') +nix_main_dep = dependency('nix-main', version: '>=2.6') +nix_store_dep = dependency('nix-store', version: '>=2.6') +nix_cmd_dep = dependency('nix-cmd', version: '>=2.6') shared_module( 'pk_backend_nix', 'pk-backend-nix.cc', - 'nix-helpers.cc', 'nix-lib-plus.cc', include_directories: packagekit_src_include, dependencies: [ packagekit_glib2_dep, nix_expr_dep, nix_main_dep, + nix_cmd_dep, nix_store_dep, gmodule_dep, ], - c_args: [ + cpp_args: [ '-DPK_COMPILATION=1', '-DG_LOG_DOMAIN="PackageKit-Nix"', ], diff --git a/backends/nix/nix-helpers.cc b/backends/nix/nix-helpers.cc deleted file mode 100644 index ceddb6bc7..000000000 --- a/backends/nix/nix-helpers.cc +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tab-modes: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016 Matthew Bauer <mjbauer95@gmail.com> - * - * 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 i3n 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 "nix-helpers.hh" - -// find drv based on attrpath and system -DrvInfo -nix_find_drv (EvalState & state, DrvInfos drvs, gchar* package_id) -{ - gchar** package_id_parts = pk_package_id_split (package_id); - - // string name (package_id_parts[0]); - // string version (package_id_parts[1]); - string system (package_id_parts[2]); - string attrPath (package_id_parts[3]); - - for (auto drv : drvs) - if (drv.attrPath == attrPath && drv.querySystem() == system) - return drv; - - DrvInfo drv (state); - return drv; -} - -// generate package id from derivation -gchar* -nix_drv_package_id (DrvInfo & drv) -{ - DrvName name (drv.queryName()); - - return pk_package_id_build ( - name.name.c_str (), - name.version.c_str (), - drv.querySystem().c_str (), - drv.attrPath.c_str () - ); -} - -// get all drvs from list of ids -DrvInfos -nix_get_drvs_from_ids (EvalState & state, DrvInfos drvs, gchar** package_ids) -{ - DrvInfos _drvs; - - for (; *package_ids != NULL; package_ids++) - _drvs.push_back ( - nix_find_drv ( - state, - drvs, - *package_ids - ) - ); - - return _drvs; -} - -// return false if drvinfo doesn't conflicts with a filter -bool -nix_filter_drv (EvalState & state, DrvInfo & drv, const Settings & settings, PkBitfield filters) -{ - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_VISIBLE) || pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_VISIBLE)) - if (!drv.hasFailed ()) - { - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_VISIBLE)) - return FALSE; - } - else - { - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_VISIBLE)) - return FALSE; - } - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_ARCH) || pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_ARCH)) - if (drv.querySystem() == settings.thisSystem) - { - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_ARCH)) - return FALSE; - } - else - { - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_ARCH)) - return FALSE; - } - - return TRUE; -} - -// get current state -EvalState* -nix_get_state () -{ - auto store = openStore (); - Strings searchPath; - return new EvalState (searchPath, store); -} - -// get all derivations -// TODO: figure out how to speed this up -DrvInfos -nix_get_all_derivations (EvalState & state, const Path & homedir) -{ - Value v; - loadSourceExpr (state, homedir + "/.nix-defexpr", v); - - Bindings & bindings(*state.allocBindings(0)); - - DrvInfos drvs; - getDerivations (state, v, "", bindings, drvs, true); - - return drvs; -} - -// get current nix profile frmo job's uid -Path -nix_get_profile (PkBackendJob* job) -{ - guint uid = pk_backend_job_get_uid (job); - - struct passwd* uid_ent = NULL; - if ((uid_ent = getpwuid (uid)) == NULL) - g_error ("Failed to get HOME"); - - string homedir (uid_ent->pw_dir); - - return homedir + "/.nix-profile"; -} - -// run func in a thread -void -pk_nix_run (PkBackendJob *job, PkStatusEnum status, PkBackendJobThreadFunc func, gpointer data) -{ - g_return_if_fail (func != NULL); - - pk_backend_job_set_percentage (job, 0); - pk_backend_job_set_allow_cancel (job, TRUE); - pk_backend_job_set_status (job, status); - pk_backend_job_set_started (job, TRUE); - - pk_backend_job_thread_create (job, func, data, NULL); -} - -// emit an error if error not NULL -void -pk_nix_error_emit (PkBackendJob* job, GError* error) -{ - PkErrorEnum code = PK_ERROR_ENUM_UNKNOWN; - g_return_if_fail (error != NULL); - pk_backend_job_error_code (job, code, "%s", error->message); -} - -// finish running job -gboolean -pk_nix_finish (PkBackendJob* job, GError* error) -{ - if (error != NULL) { - pk_nix_error_emit (job, error); - return FALSE; - } - - pk_backend_job_set_percentage (job, 100); - pk_backend_job_finished (job); - - return TRUE; -} diff --git a/backends/nix/nix-helpers.hh b/backends/nix/nix-helpers.hh deleted file mode 100644 index e3fd2c468..000000000 --- a/backends/nix/nix-helpers.hh +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tab-modes: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2016 Matthew Bauer <mjbauer95@gmail.com> - * - * 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 i3n 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. - */ - -#ifndef NIX_HELPERS_HH -#define NIX_HELPERS_HH - -#include <pwd.h> -#include <glib.h> - -#include <pk-backend.h> -#include <pk-backend-job.h> - -#include "nix-lib-plus.hh" - -void -pk_nix_run (PkBackendJob *job, PkStatusEnum status, PkBackendJobThreadFunc func, gpointer data); - -void -pk_nix_error_emit (PkBackendJob* job, GError* error); - -gboolean -pk_nix_finish (PkBackendJob* job, GError* error); - -DrvInfos -nix_get_drvs_from_ids (EvalState & state, DrvInfos drvs, gchar** package_ids); - -EvalState* -nix_get_state (); - -DrvInfos -nix_get_all_derivations (EvalState & state, const Path & path); - -gchar* -nix_drv_package_id (DrvInfo & drv); - -DrvInfo -nix_find_drv (EvalState & state, DrvInfos drvs, gchar* package_id); - -bool -nix_filter_drv (EvalState & state, DrvInfo & drv, const Settings & settings, PkBitfield filters); - -Path -nix_get_profile (PkBackendJob* job); - -#endif diff --git a/backends/nix/nix-lib-plus.cc b/backends/nix/nix-lib-plus.cc index 754386760..a83c39d13 100644 --- a/backends/nix/nix-lib-plus.cc +++ b/backends/nix/nix-lib-plus.cc @@ -11,11 +11,28 @@ For more information visit http://nixos.org/nix/ */ +#include <nix/config.h> + +#include <nix/util.hh> +#include <nix/derivations.hh> +#include <nix/store-api.hh> +#include <nix/path-with-outputs.hh> +#include <nix/local-fs-store.hh> +#include <nix/globals.hh> +#include <nix/shared.hh> +#include <nix/eval.hh> +#include <nix/eval-inline.hh> +#include <nix/profiles.hh> + #include "nix-lib-plus.hh" +namespace nix { + DrvInfos queryInstalled(EvalState & state, const Path & userEnv) { DrvInfos elems; + if (pathExists(userEnv + "/manifest.json")) + throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv); Path manifestFile = userEnv + "/manifest.nix"; if (pathExists(manifestFile)) { Value v; @@ -32,104 +49,113 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, { /* Build the components in the user environment, if they don't exist already. */ - PathSet drvsToBuild; + std::vector<StorePathWithOutputs> drvsToBuild; for (auto & i : elems) if (i.queryDrvPath() != "") - drvsToBuild.insert(i.queryDrvPath()); + drvsToBuild.push_back({state.store->parseStorePath(i.queryDrvPath()), {}}); debug(format("building user environment dependencies")); - state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal); + state.store->buildPaths( + toDerivedPaths(drvsToBuild), + state.repair ? bmRepair : bmNormal); /* Construct the whole top level derivation. */ - PathSet references; + StorePathSet references; Value manifest; state.mkList(manifest, elems.size()); - unsigned int n = 0; + size_t n = 0; for (auto & i : elems) { /* Create a pseudo-derivation containing the name, system, output paths, and optionally the derivation path, as well as the meta attributes. */ Path drvPath = keepDerivations ? i.queryDrvPath() : ""; + DrvInfo::Outputs outputs = i.queryOutputs(true); + StringSet metaNames = i.queryMetaNames(); - Value & v(*state.allocValue()); - manifest.listElems()[n++] = &v; - state.mkAttrs(v, 16); + auto attrs = state.buildBindings(7 + outputs.size()); - mkString(*state.allocAttr(v, state.sType), "derivation"); - mkString(*state.allocAttr(v, state.sName), i.queryName()); + attrs.alloc(state.sType).mkString("derivation"); + attrs.alloc(state.sName).mkString(i.queryName()); auto system = i.querySystem(); if (!system.empty()) - mkString(*state.allocAttr(v, state.sSystem), system); - mkString(*state.allocAttr(v, state.sOutPath), i.queryOutPath()); + attrs.alloc(state.sSystem).mkString(system); + attrs.alloc(state.sOutPath).mkString(i.queryOutPath()); if (drvPath != "") - mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath()); + attrs.alloc(state.sDrvPath).mkString(i.queryDrvPath()); // Copy each output meant for installation. - DrvInfo::Outputs outputs = i.queryOutputs(true); - Value & vOutputs = *state.allocAttr(v, state.sOutputs); + auto & vOutputs = attrs.alloc(state.sOutputs); state.mkList(vOutputs, outputs.size()); - unsigned int m = 0; - for (auto & j : outputs) { - mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first); - Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first)); - state.mkAttrs(vOutputs, 2); - mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second); + for (const auto & [m, j] : enumerate(outputs)) { + (vOutputs.listElems()[m] = state.allocValue())->mkString(j.first); + auto outputAttrs = state.buildBindings(2); + outputAttrs.alloc(state.sOutPath).mkString(j.second); + attrs.alloc(j.first).mkAttrs(outputAttrs); /* This is only necessary when installing store paths, e.g., `nix-env -i /nix/store/abcd...-foo'. */ - state.store->addTempRoot(j.second); - state.store->ensurePath(j.second); + state.store->addTempRoot(state.store->parseStorePath(j.second)); + state.store->ensurePath(state.store->parseStorePath(j.second)); - references.insert(j.second); + references.insert(state.store->parseStorePath(j.second)); } // Copy the meta attributes. - Value & vMeta = *state.allocAttr(v, state.sMeta); - state.mkAttrs(vMeta, 16); - StringSet metaNames = i.queryMetaNames(); + auto meta = state.buildBindings(metaNames.size()); for (auto & j : metaNames) { Value * v = i.queryMeta(j); if (!v) continue; - vMeta.attrs->push_back(Attr(state.symbols.create(j), v)); + meta.insert(state.symbols.create(j), v); } - vMeta.attrs->sort(); - v.attrs->sort(); - if (drvPath != "") references.insert(drvPath); + attrs.alloc(state.sMeta).mkAttrs(meta); + + (manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs); + + if (drvPath != "") references.insert(state.store->parseStorePath(drvPath)); } /* Also write a copy of the list of user environment elements to the store; we need it for future modifications of the environment. */ - Path manifestFile = state.store->addTextToStore("env-manifest.nix", - (format("%1%") % manifest).str(), references); + auto manifestFile = state.store->addTextToStore("env-manifest.nix", + fmt("%s", manifest), references); /* Get the environment builder expression. */ Value envBuilder; - state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder); + state.eval(state.parseExprFromString( + #include "buildenv.nix.gen.hh" + , "/"), envBuilder); /* Construct a Nix expression that calls the user environment builder with the manifest as argument. */ - Value args, topLevel; - state.mkAttrs(args, 3); - mkString(*state.allocAttr(args, state.symbols.create("manifest")), - manifestFile, {manifestFile}); - args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest)); - args.attrs->sort(); - mkApp(topLevel, envBuilder, args); + auto attrs = state.buildBindings(3); + attrs.alloc("manifest").mkString( + state.store->printStorePath(manifestFile), + {state.store->printStorePath(manifestFile)}); + attrs.insert(state.symbols.create("derivations"), &manifest); + Value args; + args.mkAttrs(attrs); + + Value topLevel; + topLevel.mkApp(&envBuilder, &args); /* Evaluate it. */ debug("evaluating user environment builder"); state.forceValue(topLevel); PathSet context; Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); - Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context); + auto topLevelDrv = state.store->parseStorePath(state.coerceToPath(*aDrvPath.pos, *aDrvPath.value, context)); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); - Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context); + Path topLevelOut = state.coerceToPath(*aOutPath.pos, *aOutPath.value, context); /* Realise the resulting store expression. */ debug("building user environment"); - state.store->buildPaths({topLevelDrv}, state.repair ? bmRepair : bmNormal); + std::vector<StorePathWithOutputs> topLevelDrvs; + topLevelDrvs.push_back({topLevelDrv, {}}); + state.store->buildPaths( + toDerivedPaths(topLevelDrvs), + state.repair ? bmRepair : bmNormal); /* Switch the current user environment to the output path. */ auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>(); @@ -140,131 +166,17 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, Path lockTokenCur = optimisticLockProfile(profile); if (lockToken != lockTokenCur) { - printError(format("profile '%1%' changed while we were busy; restarting") % profile); + printInfo("profile '%1%' changed while we were busy; restarting", profile); return false; } debug(format("switching to new user environment")); - Path generation = createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut); + Path generation = createGeneration(ref<LocalFSStore>(store2), profile, + store2->parseStorePath(topLevelOut)); switchLink(profile, generation); } return true; } -bool isNixExpr(const Path & path, struct stat & st) -{ - return S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && pathExists(path + "/default.nix")); -} - - -void getAllExprs(EvalState & state, - const Path & path, StringSet & attrs, Value & v) -{ - StringSet namesSorted; - for (auto & i : readDirectory(path)) namesSorted.insert(i.name); - - for (auto & i : namesSorted) { - /* Ignore the manifest.nix used by profiles. This is - necessary to prevent it from showing up in channels (which - are implemented using profiles). */ - if (i == "manifest.nix") continue; - - Path path2 = path + "/" + i; - - struct stat st; - if (stat(path2.c_str(), &st) == -1) - continue; // ignore dangling symlinks in ~/.nix-defexpr - - if (isNixExpr(path2, st) && (!S_ISREG(st.st_mode) || hasSuffix(path2, ".nix"))) { - /* Strip off the `.nix' filename suffix (if applicable), - otherwise the attribute cannot be selected with the - `-A' option. Useful if you want to stick a Nix - expression directly in ~/.nix-defexpr. */ - string attrName = i; - if (hasSuffix(attrName, ".nix")) - attrName = string(attrName, 0, attrName.size() - 4); - if (attrs.find(attrName) != attrs.end()) { - printError(format("warning: name collision in input Nix expressions, skipping '%1%'") % path2); - continue; - } - attrs.insert(attrName); - /* Load the expression on demand. */ - Value & vFun = state.getBuiltin("import"); - Value & vArg(*state.allocValue()); - mkString(vArg, path2); - if (v.attrs->size() == v.attrs->capacity()) - throw Error(format("too many Nix expressions in directory '%1%'") % path); - mkApp(*state.allocAttr(v, state.symbols.create(attrName)), vFun, vArg); - } - else if (S_ISDIR(st.st_mode)) - /* `path2' is a directory (with no default.nix in it); - recurse into it. */ - getAllExprs(state, path2, attrs, v); - } -} - -void loadSourceExpr(EvalState & state, const Path & path, Value & v) -{ - struct stat st; - if (stat(path.c_str(), &st) == -1) - throw SysError(format("getting information about '%1%'") % path); - - if (isNixExpr(path, st)) { - state.evalFile(path, v); - return; - } - - /* The path is a directory. Put the Nix expressions in the - directory in a set, with the file name of each expression as - the attribute name. Recurse into subdirectories (but keep the - set flat, not nested, to make it easier for a user to have a - ~/.nix-defexpr directory that includes some system-wide - directory). */ - if (S_ISDIR(st.st_mode)) { - state.mkAttrs(v, 1024); - state.mkList(*state.allocAttr(v, state.symbols.create("_combineChannels")), 0); - StringSet attrs; - getAllExprs(state, path, attrs, v); - v.attrs->sort(); - } -} - - -static void loadDerivations(EvalState & state, Path nixExprPath, - string systemFilter, Bindings & autoArgs, - const string & pathPrefix, DrvInfos & elems) -{ - Value vRoot; - loadSourceExpr(state, nixExprPath, vRoot); - - Value & v(*findAlongAttrPath(state, pathPrefix, autoArgs, vRoot)); - - getDerivations(state, v, pathPrefix, autoArgs, elems, true); - - /* Filter out all derivations not applicable to the current - system. */ - for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) { - j = i; j++; - if (systemFilter != "*" && i->querySystem() != systemFilter) - elems.erase(i); - } -} - -int -getPriority (EvalState & state, DrvInfo & drv) -{ - return drv.queryMetaInt ("priority", 0); -} - -int -comparePriorities (EvalState & state, DrvInfo & drv1, DrvInfo & drv2) -{ - return getPriority (state, drv2) - getPriority (state, drv1); -} - -bool -keep (DrvInfo & drv) -{ - return drv.queryMetaBool ("keep", false); } diff --git a/backends/nix/nix-lib-plus.hh b/backends/nix/nix-lib-plus.hh index 743747300..cdd0d6faa 100644 --- a/backends/nix/nix-lib-plus.hh +++ b/backends/nix/nix-lib-plus.hh @@ -11,39 +11,14 @@ For more information visit http://nixos.org/nix/ */ -#ifndef NIX_LIB_PLUS_HH -#define NIX_LIB_PLUS_HH +#pragma once -#include <nix/config.h> - -#include <nix/nixexpr.hh> -#include <nix/shared.hh> -#include <nix/eval.hh> -#include <nix/eval-inline.hh> -#include <nix/derivations.hh> -#include <nix/store-api.hh> #include <nix/get-drvs.hh> -#include <nix/names.hh> -#include <nix/profiles.hh> -#include <nix/globals.hh> -#include <nix/attr-path.hh> - -using namespace nix; -DrvInfos queryInstalled(EvalState & state, const Path & userEnv); +namespace nix { bool createUserEnv(EvalState & state, DrvInfos & elems, const Path & profile, bool keepDerivations, const string & lockToken); -bool isNixExpr(const Path & path, const struct stat & st); - -void loadSourceExpr(EvalState & state, const Path & path, Value & v); - -void getAllExprs(EvalState & state, const Path & path, StringSet & strings, Value & v); - -int getPriority (EvalState & state, DrvInfo & drv); - -int comparePriorities (EvalState & state, DrvInfo & drv1, DrvInfo & drv2); - -bool keep (DrvInfo & drv); +DrvInfos queryInstalled(EvalState & state, const Path & userEnv); -#endif +} diff --git a/backends/nix/pk-backend-nix.cc b/backends/nix/pk-backend-nix.cc index 09c61b980..265277cae 100644 --- a/backends/nix/pk-backend-nix.cc +++ b/backends/nix/pk-backend-nix.cc @@ -19,52 +19,55 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <string.h> -#include <stdlib.h> -#include <gio/gio.h> +#include <pk-backend.h> +#include <pk-backend-job.h> + +#include <nix/config.h> + +#include <nix/globals.hh> +#include <nix/eval.hh> +#include <nix/store-api.hh> +#include <nix/names.hh> +#include <nix/eval-cache.hh> +#include <nix/attr-path.hh> +#include <nix/profiles.hh> +#include <nix/flake/flake.hh> +#include <nix/experimental-features.hh> +#include <nix/installables.hh> + +#include <pwd.h> -#include "nix-helpers.hh" #include "nix-lib-plus.hh" typedef struct { - Path roothome; + nix::ref<nix::EvalState> state; + std::string defaultFlake; } PkBackendNixPrivate; - static PkBackendNixPrivate* priv; -static EvalState* state; -static DrvInfos drvs; void pk_backend_initialize (GKeyFile* conf, PkBackend* backend) { - g_debug ("backend initalize start"); - priv = g_new0 (PkBackendNixPrivate, 1); - struct passwd* uid_ent = NULL; - if ((uid_ent = getpwuid (getuid ())) == NULL) - g_error ("Failed to get HOME"); - priv->roothome = uid_ent->pw_dir; + nix::loadConfFile (); + nix::initGC (); - verbosity = (Verbosity) -1; + nix::verbosity = nix::lvlWarn; + nix::settings.verboseBuild = false; + nix::settings.experimentalFeatures = {nix::ExperimentalFeature::Flakes}; + nix::evalSettings.pureEval = true; - try - { - initNix(); - initGC(); + const nix::Strings searchPath; + priv->state = nix::ref<nix::EvalState> (std::make_shared<nix::EvalState>(searchPath, nix::openStore ())); - state = nix_get_state(); - } - catch (std::exception & e) - { - } + // this might be useful as a configuration setting in the future + priv->defaultFlake = "nixpkgs"; } void pk_backend_destroy (PkBackend* backend) { - drvs.empty (); - g_free (state); g_free (priv); } @@ -86,7 +89,6 @@ pk_backend_get_author (PkBackend* backend) return "Matthew Bauer <mjbauer95@gmail.com>"; } -// TODO PkBitfield pk_backend_get_groups (PkBackend* backend) { @@ -97,41 +99,10 @@ PkBitfield pk_backend_get_filters (PkBackend* backend) { return pk_bitfield_from_enums ( - PK_FILTER_ENUM_VISIBLE, - PK_FILTER_ENUM_NOT_VISIBLE, - PK_FILTER_ENUM_ARCH, - PK_FILTER_ENUM_NOT_ARCH, PK_FILTER_ENUM_SUPPORTED, PK_FILTER_ENUM_NOT_SUPPORTED, PK_FILTER_ENUM_INSTALLED, PK_FILTER_ENUM_NOT_INSTALLED, - - /* TODO: - - - breaks with unfree packages - PK_FILTER_ENUM_DOWNLOADED, - PK_FILTER_ENUM_NOT_DOWNLOADED, - PK_FILTER_ENUM_FREE, - PK_FILTER_ENUM_NOT_FREE, - - - others - PK_FILTER_ENUM_NONE, - PK_FILTER_ENUM_DEVELOPMENT, - PK_FILTER_ENUM_NOT_DEVELOPMENT, - PK_FILTER_ENUM_GUI, - PK_FILTER_ENUM_NOT_GUI, - PK_FILTER_ENUM_BASENAME, - PK_FILTER_ENUM_NOT_BASENAME, - PK_FILTER_ENUM_NEWEST, - PK_FILTER_ENUM_NOT_NEWEST, - PK_FILTER_ENUM_SOURCE, - PK_FILTER_ENUM_NOT_SOURCE, - PK_FILTER_ENUM_COLLECTIONS, - PK_FILTER_ENUM_NOT_COLLECTIONS, - PK_FILTER_ENUM_APPLICATION, - PK_FILTER_ENUM_NOT_APPLICATION, - */ - -1 ); } @@ -141,48 +112,16 @@ pk_backend_get_roles (PkBackend* backend) { return pk_bitfield_from_enums ( PK_ROLE_ENUM_CANCEL, - PK_ROLE_ENUM_DOWNLOAD_PACKAGES, PK_ROLE_ENUM_GET_DETAILS, PK_ROLE_ENUM_GET_PACKAGES, - PK_ROLE_ENUM_GET_UPDATES, PK_ROLE_ENUM_INSTALL_PACKAGES, PK_ROLE_ENUM_REFRESH_CACHE, PK_ROLE_ENUM_REMOVE_PACKAGES, PK_ROLE_ENUM_RESOLVE, PK_ROLE_ENUM_SEARCH_DETAILS, PK_ROLE_ENUM_SEARCH_NAME, - - /* TODO - - need to use binary cache db - PK_ROLE_ENUM_SEARCH_FILE, - PK_ROLE_ENUM_GET_FILES_LOCAL, - PK_ROLE_ENUM_GET_FILES, - - - need to get data from repos through nix-channel - PK_ROLE_ENUM_REPO_ENABLE, - PK_ROLE_ENUM_GET_REPO_LIST, - PK_ROLE_ENUM_REPO_REMOVE, - PK_ROLE_ENUM_REPO_SET_DATA, - - - need to integrate with nixos - PK_ROLE_ENUM_REPAIR_SYSTEM, - PK_ROLE_ENUM_UPGRADE_SYSTEM, - PK_ROLE_ENUM_GET_DISTRO_UPGRADES, - - - etc. - PK_ROLE_ENUM_DEPENDS_ON, - PK_ROLE_ENUM_REQUIRED_BY, - PK_ROLE_ENUM_INSTALL_FILES, - PK_ROLE_ENUM_INSTALL_SIGNATURE, - PK_ROLE_ENUM_SEARCH_GROUP, - PK_ROLE_ENUM_WHAT_PROVIDES, - PK_ROLE_ENUM_ACCEPT_EULA, - PK_ROLE_ENUM_GET_CATEGORIES, - PK_ROLE_ENUM_GET_OLD_TRANSACTIONS, - PK_ROLE_ENUM_WHAT_PROVIDES, - PK_ROLE_ENUM_GET_UPDATE_DETAIL, - PK_ROLE_ENUM_GET_DETAILS_LOCAL, - */ + PK_ROLE_ENUM_UPDATE_PACKAGES, + PK_ROLE_ENUM_GET_UPDATES, -1 ); } @@ -194,695 +133,591 @@ pk_backend_get_mime_types (PkBackend* backend) return g_strdupv ((gchar **) mime_types); } -static void -pk_backend_get_details_thread (PkBackendJob* job, GVariant* params, gpointer p) +static std::shared_ptr<nix::eval_cache::AttrCursor> +nix_get_cursor (nix::EvalState & state, std::string flake, std::string attrPath) { - g_autoptr (GError) error = NULL; + nix::flake::LockFlags lockFlags; + auto lockedFlake = std::make_shared<nix::flake::LockedFlake> (nix::flake::lockFlake (state, nix::parseFlakeRef(flake), lockFlags)); - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); + auto evalCache = nix::openEvalCache (state, lockedFlake); - DrvInfos _drvs = nix_get_drvs_from_ids (*state, drvs, (gchar**) p); - - for (auto drv : _drvs) - { - if (pk_backend_job_is_cancelled (job)) - break; + return evalCache->getRoot()->findAlongAttrPath (nix::parseAttrPath (state, attrPath)); +} - string license = "unknown"; - auto licenseMeta = drv.queryMeta ("license"); - if (licenseMeta != NULL && licenseMeta->type == tAttrs) - { - auto symbol = state->symbols.create ("fullName"); - Bindings::iterator fullName = licenseMeta->attrs->find (symbol); - if (fullName != licenseMeta->attrs->end () && fullName->value->type == tString) - license = fullName->value->string.s; +static void +pk_backend_get_details_thread (PkBackendJob* job, GVariant* params, gpointer p) +{ + gchar** package_ids; + g_variant_get (params, "(^a&s)", &package_ids); + + gchar** parts; + for (size_t i = 0; package_ids[i] != NULL; i++) { + // we put the attr path in "PK_PACKAGE_ID_NAME" because that’s how we identify it + parts = pk_package_id_split (package_ids[i]); + std::string attrPath = std::string (parts[PK_PACKAGE_ID_NAME]); + std::string flake = std::string (parts[PK_PACKAGE_ID_DATA]); + g_strfreev (parts); + + auto cursor = nix_get_cursor (*priv->state, flake, "legacyPackages." + nix::settings.thisSystem.get () + "." + attrPath); + + if (pk_backend_job_is_cancelled (job)) + return; + + if (cursor && cursor->isDerivation ()) { + auto aMeta = cursor->maybeGetAttr ("meta"); + + auto aDescription = aMeta ? aMeta->maybeGetAttr ("description") : NULL; + std::string description = aDescription ? aDescription->getString () : ""; + std::replace (description.begin (), description.end (), '\n', ' '); + + std::string license = "unknown"; + auto licenseMeta = aMeta ? aMeta->maybeGetAttr ("license") : NULL; + if (licenseMeta) { + auto fullName = licenseMeta->maybeGetAttr ("fullName"); + if (fullName) + license = fullName->getString (); } - string longDescription = drv.queryMetaString ("longDescription"); - if (longDescription == "") - longDescription = drv.queryMetaString ("description"); - - pk_backend_job_details ( - job, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str (), - license.c_str(), - PK_GROUP_ENUM_UNKNOWN, // TODO: hack in group support - longDescription.c_str (), - drv.queryMetaString ("homepage").c_str (), - 0 - ); + auto aLongDescription = aMeta ? aMeta->maybeGetAttr ("longDescription") : NULL; + std::string longDescription = aLongDescription ? aLongDescription->getString () : ""; + + auto aHomepage = aMeta ? aMeta->maybeGetAttr ("homepage") : NULL; + std::string homepage = aHomepage ? aHomepage->getString () : ""; + + pk_backend_job_details (job, + package_ids[i], + description.c_str (), + license.c_str (), + PK_GROUP_ENUM_UNKNOWN, + longDescription.c_str (), + homepage.c_str (), + 0); + } else { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "%s is not a package", + attrPath.c_str ()); + return; } } - catch (std::exception & e) - { - } - pk_nix_finish (job, error); + pk_backend_job_set_percentage (job, 100); } void pk_backend_get_details (PkBackend* backend, PkBackendJob* job, gchar** packages) { - pk_nix_run (job, PK_STATUS_ENUM_INFO, pk_backend_get_details_thread, packages); + pk_backend_job_set_status(job, PK_STATUS_ENUM_QUERY); + pk_backend_job_thread_create(job, pk_backend_get_details_thread, NULL, NULL); } -static void -pk_backend_get_packages_thread (PkBackendJob* job, GVariant* params, gpointer p) +static nix::Path +nix_get_user_profile (PkBackendJob* job) { - g_autoptr (GError) error = NULL; + guint uid = pk_backend_job_get_uid (job); - PkBitfield filters; - g_variant_get (params, "(t)", &filters); - - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); - - auto profile = nix_get_profile (job); - DrvInfos installedDrvs = queryInstalled (*state, profile); - - int n = 0; - double percentFactor = 100 / drvs.size (); - - for (auto drv : drvs) - { - if (pk_backend_job_is_cancelled (job)) - break; - - pk_backend_job_set_percentage (job, (n++) * percentFactor); - - if (!nix_filter_drv (*state, drv, settings, filters)) - continue; - - auto info = PK_INFO_ENUM_AVAILABLE; - - for (auto _drv : installedDrvs) - if (_drv.queryName() == drv.queryName()) - { - info = PK_INFO_ENUM_INSTALLED; - break; - } - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && info != PK_INFO_ENUM_INSTALLED) - continue; - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && info == PK_INFO_ENUM_INSTALLED) - continue; - - pk_backend_job_package ( - job, - info, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } - } - catch (std::exception & e) - { - } - - pk_nix_finish (job, error); -} + struct passwd* uid_ent = NULL; + if ((uid_ent = getpwuid (uid)) == NULL) + g_error ("Failed to get HOME"); -void -pk_backend_get_packages (PkBackend* backend, PkBackendJob* job, PkBitfield filters) -{ - pk_nix_run (job, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST, pk_backend_get_packages_thread, NULL); + return std::string(uid_ent->pw_dir) + "/.nix-profile"; } static void -pk_backend_resolve_thread (PkBackendJob* job, GVariant* params, gpointer p) +nix_search_thread (PkBackendJob* job, GVariant* params, gpointer p) { - g_autoptr (GError) error = NULL; - const gchar **search; PkBitfield filters; - g_variant_get (params, "(t^a&s)", &filters, &search); - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); + PkRoleEnum role = pk_backend_job_get_role (job); + + switch(role) { + case PK_ROLE_ENUM_GET_PACKAGES: + g_variant_get (params, "(t)", &filters); + break; + case PK_ROLE_ENUM_SEARCH_NAME: + case PK_ROLE_ENUM_SEARCH_DETAILS: + case PK_ROLE_ENUM_RESOLVE: + g_variant_get (params, "(t^a&s)", &filters, &search); + break; + default: + g_assert_not_reached (); + break; + } - auto profile = nix_get_profile (job); - DrvInfos installedDrvs = queryInstalled (*state, profile); + std::string attrPath = "legacyPackages." + nix::settings.thisSystem.get () + "."; + auto cursor = nix_get_cursor (*priv->state, priv->defaultFlake, attrPath); - for (; *search != NULL; ++search) - { - if (pk_backend_job_is_cancelled (job)) - break; + if (pk_backend_job_is_cancelled (job)) + return; - DrvName searchName (*search); + std::vector<std::regex> regexes; + if (search) + for (; *search != NULL; search++) + regexes.push_back (std::regex (*search, std::regex::extended | std::regex::icase)); - for (auto drv : drvs) - { - DrvName drvName (drv.queryName()); - if (searchName.matches (drvName)) - { - if (!nix_filter_drv (*state, drv, settings, filters)) - continue; + nix::DrvInfos installedDrvs; - auto info = PK_INFO_ENUM_AVAILABLE; - - for (auto _drv : installedDrvs) - if (_drv.queryName() == drv.queryName()) - { - info = PK_INFO_ENUM_INSTALLED; - break; - } + if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) + || pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED)) { + std::optional<nix::PathSet> oldAllowedPaths = priv->state->allowedPaths; + priv->state->allowedPaths = std::nullopt; - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && info != PK_INFO_ENUM_INSTALLED) - continue; - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && info == PK_INFO_ENUM_INSTALLED) - continue; + std::string userProfile = nix_get_user_profile (job); + if (nix::pathExists (userProfile + "/manifest.nix")) { + nix::Value v; + priv->state->evalFile (userProfile + "/manifest.nix", v); + nix::Bindings & bindings (*priv->state->allocBindings(0)); + nix::getDerivations (*priv->state, v, "", bindings, installedDrvs, false); + } - pk_backend_job_package ( - job, - info, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } - } + std::string defaultProfile = nix::settings.nixStateDir + "/profiles/default"; + if (nix::pathExists (defaultProfile + "/manifest.nix")) { + nix::Value v; + priv->state->evalFile (defaultProfile + "/manifest.nix", v); + nix::Bindings & bindings (*priv->state->allocBindings(0)); + nix::getDerivations (*priv->state, v, "", bindings, installedDrvs, false); } - } - catch (std::exception & e) - { + + priv->state->allowedPaths = oldAllowedPaths; } - pk_nix_finish (job, error); -} + int totalDrvs = 0; + int foundDrvs = 0; -void -pk_backend_resolve (PkBackend* self, PkBackendJob* job, PkBitfield filters, gchar** search) -{ - pk_nix_run (job, PK_STATUS_ENUM_QUERY, pk_backend_resolve_thread, NULL); -} + std::function<void(nix::eval_cache::AttrCursor & cursor, const std::vector<nix::Symbol> & attrPath)> visit; + visit = [&](nix::eval_cache::AttrCursor & cursor, const std::vector<nix::Symbol> & attrPath) { + try { + if (pk_backend_job_is_cancelled (job)) + return; -static void -pk_backend_search_names_thread (PkBackendJob* job, GVariant* params, gpointer p) -{ - g_autoptr (GError) error = NULL; + auto recurse = [&] () { + auto attrs = cursor.getAttrs (); - gchar **search; - PkBitfield filters; - g_variant_get (params, "(t^a&s)", &filters, &search); + totalDrvs += attrs.size(); + if (totalDrvs > 0) + pk_backend_job_set_percentage (job, 100 * foundDrvs / totalDrvs); - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); + for (const auto & attr : attrs) { + auto cursor2 = cursor.getAttr (attr); + auto attrPath2 (attrPath); + attrPath2.push_back (attr); + visit (*cursor2, attrPath2); + } + }; - auto profile = nix_get_profile (job); - DrvInfos installedDrvs = queryInstalled (*state, profile); + if (cursor.isDerivation ()) { + foundDrvs++; - for (; *search != NULL; ++search) - { - if (pk_backend_job_is_cancelled (job)) - break; + size_t found = 0; - for (auto drv : drvs) - if (drv.queryName().find(*search) != -1) - { - if (!nix_filter_drv (*state, drv, settings, filters)) - continue; + nix::DrvName name (cursor.getAttr ("name")->getString()); - auto info = PK_INFO_ENUM_AVAILABLE; + auto aMeta = cursor.maybeGetAttr ("meta"); + auto aDescription = aMeta ? aMeta->maybeGetAttr ("description") : NULL; - for (auto _drv : installedDrvs) - if (_drv.queryName() == drv.queryName()) - { - info = PK_INFO_ENUM_INSTALLED; + auto description = aDescription ? aDescription->getString() : ""; + std::replace (description.begin (), description.end (), '\n', ' '); + + auto attrPath2 = concatStringsSep (".", attrPath); + + for (auto & regex : regexes) { + switch (role) { + case PK_ROLE_ENUM_SEARCH_NAME: + case PK_ROLE_ENUM_RESOLVE: { + std::smatch nameMatch; + std::regex_search (name.name, nameMatch, regex); + std::smatch attrMatch; + std::regex_search (attrPath2, attrMatch, regex); + if (!nameMatch.empty () || !attrMatch.empty()) + found++; + break; + } + case PK_ROLE_ENUM_SEARCH_DETAILS: { + std::smatch descriptionMatch; + std::regex_search (description, descriptionMatch, regex); + if (!descriptionMatch.empty ()) + found++; + break; + } + default: + found++; + break; + } + } + + if (found == regexes.size () || regexes.empty ()) { + bool isInstalled = false; + for (auto drv : installedDrvs) { + if (nix::DrvName (drv.queryName ()).matches(name)) { + isInstalled = true; break; } + } - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && info != PK_INFO_ENUM_INSTALLED) - continue; + if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && isInstalled) + return; + if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && !isInstalled) + return; + + auto available = aMeta ? aMeta->maybeGetAttr ("available") : NULL; + bool isSupported = available ? available->getBool () : true; + + if (pk_bitfield_contain (filters, PK_FILTER_ENUM_SUPPORTED) && !isSupported) + return; + if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_SUPPORTED) && isSupported) + return; + + std::string system = cursor.getAttr ("system")->getString(); + + PkInfoEnum info = PK_INFO_ENUM_UNKNOWN; + if (isSupported) + info = PK_INFO_ENUM_AVAILABLE; + if (isInstalled) + info = PK_INFO_ENUM_INSTALLED; + + if (totalDrvs > 0) + pk_backend_job_set_percentage (job, 100 * foundDrvs / totalDrvs); + + pk_backend_job_package (job, + info, + pk_package_id_build (attrPath2.c_str (), + name.version.c_str (), + system.c_str (), + priv->defaultFlake.c_str ()), + description.c_str()); + } + } - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && info == PK_INFO_ENUM_INSTALLED) - continue; + else if (attrPath.size() == 0) + recurse(); - pk_backend_job_package ( - job, - info, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } + else if (attrPath.size() >= 1) { + auto attr = cursor.maybeGetAttr(priv->state->sRecurseForDerivations); + if (attr && attr->getBool()) + recurse(); + } + } catch (nix::EvalError & e) { } - } - catch (std::exception & e) - { - } - - pk_nix_finish (job, error); + }; + visit(*cursor, {}); + pk_backend_job_set_percentage (job, 100); } void -pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) +pk_backend_get_packages (PkBackend* backend, PkBackendJob* job, PkBitfield filters) { - pk_nix_run (job, PK_STATUS_ENUM_QUERY, pk_backend_search_names_thread, NULL); + pk_backend_job_set_status (job, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST); + pk_backend_job_thread_create(job, nix_search_thread, NULL, NULL); } -static void -pk_backend_search_details_thread (PkBackendJob* job, GVariant* params, gpointer p) -{ - g_autoptr (GError) error = NULL; - - gchar **value; - PkBitfield filters; - g_variant_get (params, "(t^a&s)", &filters, &value); - - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); - - auto profile = nix_get_profile (job); - DrvInfos installedDrvs = queryInstalled (*state, profile); - - for (; *value != NULL; ++value) - { - if (pk_backend_job_is_cancelled (job)) - break; - - for (auto drv : drvs) - if (drv.queryMetaString ("description").find (*value) != -1) - { - if (!nix_filter_drv (*state, drv, settings, filters)) - continue; - - auto info = PK_INFO_ENUM_AVAILABLE; - - for (auto _drv : installedDrvs) - if (_drv.queryName() == drv.queryName()) - { - info = PK_INFO_ENUM_INSTALLED; - break; - } - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_INSTALLED) && info != PK_INFO_ENUM_INSTALLED) - continue; - - if (pk_bitfield_contain (filters, PK_FILTER_ENUM_NOT_INSTALLED) && info == PK_INFO_ENUM_INSTALLED) - continue; - - pk_backend_job_package ( - job, - info, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } - } - } - catch (std::exception & e) - { - } - - pk_nix_finish (job, error); +void +pk_backend_search_names (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { + pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); + pk_backend_job_thread_create (job, nix_search_thread, NULL, NULL); } void pk_backend_search_details (PkBackend *backend, PkBackendJob *job, PkBitfield filters, gchar **values) { - pk_nix_run (job, PK_STATUS_ENUM_QUERY, pk_backend_search_details_thread, NULL); + pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); + pk_backend_job_thread_create (job, nix_search_thread, NULL, NULL); } -static void -pk_backend_refresh_cache_thread (PkBackendJob* job, GVariant* params, gpointer p) +void +pk_backend_resolve(PkBackend* self, PkBackendJob* job, PkBitfield filters, gchar** search) { - g_autoptr (GError) error = NULL; + pk_backend_job_set_status (job, PK_STATUS_ENUM_QUERY); + pk_backend_job_thread_create (job, nix_search_thread, NULL, NULL); +} - try - { - state = nix_get_state (); - drvs = nix_get_all_derivations (*state, priv->roothome); - } - catch (std::exception & e) - { - } +static void +nix_refresh_thread (PkBackendJob* job, GVariant* params, gpointer p) +{ + nix::settings.tarballTtl = 0; + nix_search_thread (job, params, p); + nix::settings.tarballTtl = 60 * 60; - pk_nix_finish (job, error); + pk_backend_job_set_percentage (job, 100); } void pk_backend_refresh_cache (PkBackend* backend, PkBackendJob* job, gboolean force) { - pk_nix_run (job, PK_STATUS_ENUM_REFRESH_CACHE, pk_backend_refresh_cache_thread, NULL); + pk_backend_job_set_status (job, PK_STATUS_ENUM_REFRESH_CACHE); + pk_backend_job_thread_create (job, nix_refresh_thread, NULL, NULL); } static void -pk_backend_install_packages_thread (PkBackendJob* job, GVariant* params, gpointer p) +nix_install_thread (PkBackendJob* job, GVariant* params, gpointer p) { - g_autoptr (GError) error = NULL; - PkBitfield flags; gchar** package_ids; - g_variant_get (params, "(t^a&s)", &flags, &package_ids); - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); - - DrvInfos newElems = nix_get_drvs_from_ids (*state, drvs, package_ids); - - for (auto drv : newElems) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_INSTALLING, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); + nix::DrvInfos newElems; + gchar** parts; + + for (size_t i = 0; package_ids[i] != NULL; i++) { + if (pk_backend_job_is_cancelled (job)) + return; + + // we put the attr path in "PK_PACKAGE_ID_NAME" because that’s how we identify it + parts = pk_package_id_split (package_ids[i]); + std::string attrPath = std::string (parts[PK_PACKAGE_ID_NAME]); + std::string flake = std::string (parts[PK_PACKAGE_ID_DATA]); + g_strfreev (parts); + + auto cursor = nix_get_cursor (*priv->state, flake, "legacyPackages." + nix::settings.thisSystem.get () + "." + attrPath); + + if (cursor && cursor->isDerivation ()) { + std::optional<nix::DrvInfo> drv; + drv = nix::getDerivation (*priv->state, cursor->forceValue(), false); + if (drv) { + auto aMeta = cursor->maybeGetAttr ("meta"); + auto aDescription = aMeta ? aMeta->maybeGetAttr ("description") : NULL; + auto description = aDescription ? aDescription->getString() : ""; + + try { + drv->queryDrvPath (); + } catch (nix::Error & e) { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "failed to evaluate %s", + attrPath.c_str ()); + return; + } + + pk_backend_job_package (job, + PK_INFO_ENUM_INSTALLING, + package_ids[i], + description.c_str()); + newElems.push_back (*drv); + } else { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "failed to evaluate %s", + attrPath.c_str ()); + return; + } + } else { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "%s is not a package", + attrPath.c_str ()); + return; } + } - Path profile = nix_get_profile (job); + std::string profile = nix_get_user_profile (job); - while (true) - { - if (pk_backend_job_is_cancelled (job)) - break; + std::optional<nix::PathSet> oldAllowedPaths = priv->state->allowedPaths; + priv->state->allowedPaths = std::nullopt; - string lockToken = optimisticLockProfile (profile); - DrvInfos allElems (newElems); + while (true) { + if (pk_backend_job_is_cancelled (job)) { + priv->state->allowedPaths = oldAllowedPaths; + return; + } - DrvInfos installedElems = queryInstalled (*state, profile); + std::string lockToken = nix::optimisticLockProfile (profile); + + nix::DrvInfos allElems (newElems); + + /* Add in the already installed derivations, unless they have + the same name as a to-be-installed element. */ + nix::DrvInfos installedElems; + try { + installedElems = nix::queryInstalled (*priv->state, profile); + } catch (nix::Error & e) { + priv->state->allowedPaths = oldAllowedPaths; + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "failed to create new environment: %s", e.what ()); + return; + } - for (auto & i : installedElems) - { - DrvName drvName(i.queryName()); - allElems.push_back (i); + for (auto & i : installedElems) { + bool found = false; + + for (auto & j : newElems) { + if (nix::DrvName(i.queryName ()).name == nix::DrvName (j.queryName ()).name) { + found = true; + break; + } } - if (createUserEnv (*state, allElems, profile, false, lockToken)) - break; + if (!found) + allElems.push_back (i); } - for (auto drv : newElems) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_INSTALLED, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); + try { + if (nix::createUserEnv (*priv->state, allElems, profile, false, lockToken)) + break; + } catch (nix::Error & e) { + priv->state->allowedPaths = oldAllowedPaths; + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "failed to create new environment: %s", e.what ()); + return; } } - catch (std::exception & e) - { - } - pk_nix_finish (job, error); + priv->state->allowedPaths = oldAllowedPaths; + + for (size_t i = 0; package_ids[i] != NULL; i++) + pk_backend_job_package (job, PK_INFO_ENUM_INSTALLED, package_ids[i], NULL); + + pk_backend_job_set_percentage (job, 100); } void pk_backend_install_packages (PkBackend* backend, PkBackendJob* job, PkBitfield transaction_flags, gchar** package_ids) { - pk_nix_run (job, PK_STATUS_ENUM_INSTALL, pk_backend_install_packages_thread, NULL); + pk_backend_job_set_status (job, PK_STATUS_ENUM_INSTALL); + pk_backend_job_thread_create (job, nix_install_thread, NULL, NULL); } static void -pk_backend_remove_packages_thread (PkBackendJob* job, GVariant* params, gpointer p) +nix_remove_thread (PkBackendJob* job, GVariant* params, gpointer p) { - g_autoptr (GError) error = NULL; - PkBitfield transaction_flags; gchar** package_ids; gboolean allow_deps, autoremove; g_variant_get (params, "(t^a&sbb)", &transaction_flags, &package_ids, &allow_deps, &autoremove); - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations(*state, priv->roothome); - - DrvInfos _drvs = nix_get_drvs_from_ids (*state, drvs, package_ids); - - for (auto drv : _drvs) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_REMOVING, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); + nix::Path profile = nix_get_user_profile (job); + + nix::DrvInfos elemsToDelete; + gchar** parts; + + for (size_t i = 0; package_ids[i] != NULL; i++) { + // we put the attr path in "PK_PACKAGE_ID_NAME" because that’s how we identify it + parts = pk_package_id_split (package_ids[i]); + std::string attrPath = std::string (parts[PK_PACKAGE_ID_NAME]); + std::string flake = std::string (parts[PK_PACKAGE_ID_DATA]); + g_strfreev (parts); + + auto cursor = nix_get_cursor (*priv->state, flake, "legacyPackages." + nix::settings.thisSystem.get () + "." + attrPath); + if (cursor && cursor->isDerivation ()) { + auto drv = nix::getDerivation (*priv->state, cursor->forceValue(), false); + if (drv) + elemsToDelete.push_back (*drv); + else { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "failed to evaluate %s", + attrPath.c_str ()); + return; + } + } else { + pk_backend_job_error_code (job, + PK_ERROR_ENUM_UNKNOWN, + "%s is not a package", + attrPath.c_str ()); + return; } - Path profile = nix_get_profile (job); + auto aMeta = cursor->maybeGetAttr ("meta"); + auto aDescription = aMeta ? aMeta->maybeGetAttr ("description") : NULL; + auto description = aDescription ? aDescription->getString() : ""; - while (true) - { - if (pk_backend_job_is_cancelled (job)) - break; + pk_backend_job_package (job, PK_INFO_ENUM_REMOVING, package_ids[i], description.c_str()); + } - string lockToken = optimisticLockProfile (profile); + std::optional<nix::PathSet> oldAllowedPaths = priv->state->allowedPaths; + priv->state->allowedPaths = std::nullopt; - DrvInfos installedElems = queryInstalled (*state, profile); - DrvInfos newElems; + while (true) { + if (pk_backend_job_is_cancelled (job)) { + priv->state->allowedPaths = oldAllowedPaths; + return; + } - for (auto & drv : installedElems) - { - bool found = false; + std::string lockToken = nix::optimisticLockProfile (profile); + + nix::DrvInfos installedElems = nix::queryInstalled (*priv->state, profile); + nix::DrvInfos newElems; + + for (auto & i : installedElems) { + bool found = false; - for (auto & _drv : _drvs) - if (drv.attrPath == _drv.attrPath) - { - found = true; - break; - } - if (!found) - newElems.push_back (drv); + for (auto & j : elemsToDelete) { + if (nix::DrvName (i.queryName ()).name == nix::DrvName (j.queryName ()).name) { + found = true; + break; + } } - if (createUserEnv (*state, newElems, profile, false, lockToken)) - break; + if (!found) + newElems.push_back (i); } - for (auto drv : _drvs) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_AVAILABLE, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } - } - catch (std::exception & e) - { + if (nix::createUserEnv (*priv->state, newElems, profile, false, lockToken)) + break; } - pk_nix_finish (job, error); + priv->state->allowedPaths = oldAllowedPaths; + + for (size_t i = 0; package_ids[i] != NULL; i++) + pk_backend_job_package (job, PK_INFO_ENUM_AVAILABLE, package_ids[i], NULL); + + pk_backend_job_set_percentage (job, 100); } void pk_backend_remove_packages (PkBackend* backend, PkBackendJob* job, PkBitfield transaction_flags, gchar** package_ids, gboolean allow_deps, gboolean autoremove) { - pk_nix_run (job, PK_STATUS_ENUM_REMOVE, pk_backend_remove_packages_thread, NULL); -} - -static void -pk_backend_update_packages_thread (PkBackendJob* job, GVariant* params, gpointer p) -{ - g_autoptr (GError) error = NULL; - - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); - - auto profile = nix_get_profile (job); - - while (true) - { - if (pk_backend_job_is_cancelled (job)) - break; - - string lockToken = optimisticLockProfile (profile); - - DrvInfos installedElems = queryInstalled (*state, profile); - - /* Go through all installed derivations. */ - DrvInfos newElems; - for (auto & i : installedElems) - { - DrvName drvName (i.queryName()); - - try - { - if (keep (i)) - { - newElems.push_back (i); - continue; - } - - /* Find the derivation in the input Nix expression - with the same name that satisfies the version - constraints specified by upgradeType. If there are - multiple matches, take the one with the highest - priority. If there are still multiple matches, - take the one with the highest version. - Do not upgrade if it would decrease the priority. */ - DrvInfos::iterator bestElem = drvs.end (); - string bestVersion; - - for (auto j = drvs.begin (); j != drvs.end (); ++j) - { - if (comparePriorities (*state, i, *j) > 0) - continue; - - DrvName newName (j->queryName()); - if (newName.name == drvName.name) - { - int d = compareVersions (drvName.version, newName.version); - if (d < 0) - { - int d2 = -1; - if (bestElem != drvs.end ()) - { - d2 = comparePriorities (*state, *bestElem, *j); - if (d2 == 0) - d2 = compareVersions (bestVersion, newName.version); - } - if (d2 < 0) - { - bestElem = j; - bestVersion = newName.version; - } - } - } - } - - if (bestElem != drvs.end () && i.queryOutPath () != bestElem->queryOutPath ()) - { - const char * action; - auto _drv = *bestElem; - if (compareVersions (drvName.version, bestVersion) <= 0) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_UPDATING, - nix_drv_package_id (_drv), - _drv.queryMetaString ("description").c_str () - ); - - action = "upgrading"; - } - else - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_DOWNGRADING, - nix_drv_package_id (_drv), - _drv.queryMetaString ("description").c_str () - ); - - action = "downgrading"; - } - newElems.push_back (*bestElem); - } - else - newElems.push_back (i); - } - catch (Error & e) - { - throw; - } - } - - if (createUserEnv (*state, newElems, profile, false, lockToken)) - { - for (auto drv : newElems) - { - pk_backend_job_package ( - job, - PK_INFO_ENUM_INSTALLED, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - } - - break; - } - } - } - catch (std::exception & e) - { - } - - pk_nix_finish (job, error); + pk_backend_job_set_status (job, PK_STATUS_ENUM_REMOVE); + pk_backend_job_thread_create (job, nix_remove_thread, NULL, NULL); } void pk_backend_update_packages (PkBackend* backend, PkBackendJob* job, PkBitfield transaction_flags, gchar** package_ids) { - pk_nix_run (job, PK_STATUS_ENUM_UPDATE, pk_backend_update_packages_thread, NULL); + pk_backend_job_set_status (job, PK_STATUS_ENUM_UPDATE); + pk_backend_job_thread_create (job, nix_install_thread, NULL, NULL); } static void -pk_backend_download_packages_thread (PkBackendJob* job, GVariant* params, gpointer p) +nix_get_updates_thread (PkBackendJob* job, GVariant* params, gpointer p) { - g_autoptr (GError) error = NULL; - - gchar* directory; - gchar** package_ids; - g_variant_get (params, "(^a&ss)", &package_ids, &directory); - - try - { - // possibly slow call - if (drvs.empty ()) - drvs = nix_get_all_derivations (*state, priv->roothome); - - DrvInfos _drvs = nix_get_drvs_from_ids (*state, drvs, package_ids); - - PathSet paths; - for (auto drv : _drvs) - { - if (pk_backend_job_is_cancelled (job)) - break; - - pk_backend_job_package ( - job, - PK_INFO_ENUM_DOWNLOADING, - nix_drv_package_id (drv), - drv.queryMetaString ("description").c_str () - ); - - // should provide updates to status - // just build one path at a time - PathSet paths; - paths.insert (drv.queryOutPath ()); - state->store->buildPaths (paths); + auto profile = nix_get_user_profile (job); + + std::optional<nix::PathSet> oldAllowedPaths = priv->state->allowedPaths; + priv->state->allowedPaths = std::nullopt; + + nix::DrvInfos installedElems = nix::queryInstalled (*priv->state, profile); + + priv->state->allowedPaths = oldAllowedPaths; + + int progress = 0; + for (auto & i : installedElems) { + pk_backend_job_set_percentage (job, 100 * progress++ / installedElems.size()); + + auto cursor = nix_get_cursor (*priv->state, priv->defaultFlake, "legacyPackages." + nix::settings.thisSystem.get () + "." + i.attrPath); + if (cursor && cursor->isDerivation ()) { + auto drv = nix::getDerivation (*priv->state, cursor->forceValue(), false); + if (drv && drv->queryDrvPath () != i.queryDrvPath ()) { + nix::DrvName name (drv->queryName ()); + pk_backend_job_package (job, + PK_INFO_ENUM_NORMAL, + pk_package_id_build (drv->attrPath.c_str (), + name.version.c_str (), + drv->querySystem ().c_str (), + priv->defaultFlake.c_str ()), + drv->queryMetaString ("description").c_str ()); + } } } - catch (std::exception & e) - { - } - pk_nix_finish (job, error); + pk_backend_job_set_percentage (job, 100); } void -pk_backend_download_packages (PkBackend* backend, PkBackendJob* job, gchar** package_ids, const gchar* directory) +pk_backend_get_updates(PkBackend *backend, PkBackendJob *job, PkBitfield filters) { - pk_nix_run (job, PK_STATUS_ENUM_DOWNLOAD, pk_backend_download_packages_thread, NULL); + pk_backend_job_set_status(job, PK_STATUS_ENUM_QUERY); + pk_backend_job_thread_create(job, nix_get_updates_thread, NULL, NULL); } -- 2.35.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor