From d3e658ca1e3ce2715e25450b794d139d3417c74c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Wed, 30 Nov 2022 15:25:21 +0100 Subject: [PATCH 01/25] extensions-index: Add initial implementation from civodul Related to https://lists.gnu.org/archive/html/guix-devel/2022-01/msg00354.html --- guix/extensions/file-database.scm | 199 ++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 guix/extensions/file-database.scm diff --git a/guix/extensions/file-database.scm b/guix/extensions/file-database.scm new file mode 100644 index 0000000000..83aafbc554 --- /dev/null +++ b/guix/extensions/file-database.scm @@ -0,0 +1,199 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 Ludovic Courtès +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix 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 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix 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 GNU Guix. If not, see . + +(define-module (file-database) + #:use-module (sqlite3) + #:use-module (ice-9 match) + #:use-module (guix store) + #:use-module (guix monads) + #:autoload (guix grafts) (%graft?) + #:use-module (guix derivations) + #:use-module (guix packages) + #:autoload (guix build utils) (find-files) + #:autoload (gnu packages) (fold-packages) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-9) + #:export (file-database)) + +(define schema + " +create table if not exists Packages ( + id integer primary key autoincrement not null, + name text not null, + version text not null +); + +create table if not exists Directories ( + id integer primary key autoincrement not null, + name text not null, + package integer not null, + foreign key (package) references Packages(id) on delete cascade +); + +create table if not exists Files ( + name text not null, + basename text not null, + directory integer not null, + foreign key (directory) references Directories(id) on delete cascade +); + +create index if not exists IndexFiles on Files(basename);") + +(define (call-with-database file proc) + (let ((db (sqlite-open file))) + (dynamic-wind + (lambda () #t) + (lambda () + (sqlite-exec db schema) + (proc db)) + (lambda () + (sqlite-close db))))) + +(define (insert-files db package version directories) + "Insert the files contained in DIRECTORIES as belonging to PACKAGE at +VERSION." + (define last-row-id-stmt + (sqlite-prepare db "SELECT last_insert_rowid();" + #:cache? #t)) + + (define package-stmt + (sqlite-prepare db "\ +INSERT OR REPLACE INTO Packages(name, version) +VALUES (:name, :version);" + #:cache? #t)) + + (define directory-stmt + (sqlite-prepare db "\ +INSERT INTO Directories(name, package) VALUES (:name, :package);" + #:cache? #t)) + + (define file-stmt + (sqlite-prepare db "\ +INSERT INTO Files(name, basename, directory) +VALUES (:name, :basename, :directory);" + #:cache? #t)) + + (sqlite-exec db "begin immediate;") + (sqlite-bind-arguments package-stmt + #:name package + #:version version) + (sqlite-fold (const #t) #t package-stmt) + (match (sqlite-fold cons '() last-row-id-stmt) + ((#(package-id)) + (pk 'package package-id package) + (for-each (lambda (directory) + (define (strip file) + (string-drop file (+ (string-length directory) 1))) + + (sqlite-reset directory-stmt) + (sqlite-bind-arguments directory-stmt + #:name directory + #:package package-id) + (sqlite-fold (const #t) #t directory-stmt) + + (match (sqlite-fold cons '() last-row-id-stmt) + ((#(directory-id)) + (for-each (lambda (file) + ;; If DIRECTORY is a symlink, (find-files + ;; DIRECTORY) returns the DIRECTORY singleton. + (unless (string=? file directory) + (sqlite-reset file-stmt) + (sqlite-bind-arguments file-stmt + #:name (strip file) + #:basename + (basename file) + #:directory + directory-id) + (sqlite-fold (const #t) #t file-stmt))) + (find-files directory))))) + directories) + (sqlite-exec db "commit;")))) + +(define (insert-package db package) + "Insert all the files of PACKAGE into DB." + (mlet %store-monad ((drv (package->derivation package #:graft? #f))) + (match (derivation->output-paths drv) + (((labels . directories) ...) + (when (every file-exists? directories) + (insert-files db (package-name package) (package-version package) + directories)) + (return #t))))) + +(define (insert-packages db) + "Insert all the current packages into DB." + (with-store store + (parameterize ((%graft? #f)) + (fold-packages (lambda (package _) + (run-with-store store + (insert-package db package))) + #t + #:select? (lambda (package) + (and (not (hidden-package? package)) + (not (package-superseded package)) + (supported-package? package))))))) + +(define-record-type + (package-match name version file) + package-match? + (name package-match-name) + (version package-match-version) + (file package-match-file)) + +(define (matching-packages db file) + "Return a list of corresponding to packages containing +FILE." + (define lookup-stmt + (sqlite-prepare db "\ +SELECT Packages.name, Packages.version, Directories.name, Files.name +FROM Packages +INNER JOIN Files, Directories +ON files.basename = :file AND directories.id = files.directory AND packages.id = directories.package;")) + + (sqlite-bind-arguments lookup-stmt #:file file) + (sqlite-fold (lambda (result lst) + (match result + (#(package version directory file) + (cons (package-match package version + (string-append directory "/" file)) + lst)))) + '() lookup-stmt)) + + +(define (file-database . args) + (match args + ((_ "populate") + (call-with-database "/tmp/db" + (lambda (db) + (insert-packages db)))) + ((_ "search" file) + (let ((matches (call-with-database "/tmp/db" + (lambda (db) + (matching-packages db file))))) + (for-each (lambda (result) + (format #t "~20a ~a~%" + (string-append (package-match-name result) + "@" (package-match-version result)) + (package-match-file result))) + matches) + (exit (pair? matches)))) + (_ + (format (current-error-port) + "usage: file-database [populate|search] args ...~%") + (exit 1)))) + +(apply file-database (command-line)) -- 2.38.1 From d9139cc86c26f76bc66f7d82868ebf6a03605f76 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 1 Dec 2022 13:36:28 +0100 Subject: [PATCH 02/25] extensions-index: Transform command into `guix locate` extension --- .../{file-database.scm => locate.scm} | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) rename guix/extensions/{file-database.scm => locate.scm} (82%) diff --git a/guix/extensions/file-database.scm b/guix/extensions/locate.scm similarity index 82% rename from guix/extensions/file-database.scm rename to guix/extensions/locate.scm index 83aafbc554..1e42f5bad8 100644 --- a/guix/extensions/file-database.scm +++ b/guix/extensions/locate.scm @@ -16,7 +16,8 @@ ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see . -(define-module (file-database) +(define-module (guix extensions locate) + #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) #:use-module (guix store) @@ -28,7 +29,7 @@ (define-module (file-database) #:autoload (gnu packages) (fold-packages) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) - #:export (file-database)) + #:export (guix-locate)) (define schema " @@ -155,8 +156,7 @@ (define-record-type (file package-match-file)) (define (matching-packages db file) - "Return a list of corresponding to packages containing -FILE." + "Return list of corresponding to packages containing FILE." (define lookup-stmt (sqlite-prepare db "\ SELECT Packages.name, Packages.version, Directories.name, Files.name @@ -174,26 +174,40 @@ (define lookup-stmt '() lookup-stmt)) -(define (file-database . args) + +(define (index-packages-with-db db-pathname) + "Index packages using db at location DB-PATHNAME." + (call-with-database db-pathname + (lambda (db) + (insert-packages db)))) + +(define (matching-packages-with-db db-pathname file) + "Compute list of packages referencing FILE using db at DB-PATHNAME." + (call-with-database db-pathname + (lambda (db) + (matching-packages db file)))) + +(define (print-matching-results matches) + "Print the MATCHES matching results." + (for-each (lambda (result) + (format #t "~20a ~a~%" + (string-append (package-match-name result) + "@" (package-match-version result)) + (package-match-file result))) + matches)) + +(define-command (guix-locate . args) + (category extension) + (synopsis "Index packages then search what package declares a given file") (match args - ((_ "populate") - (call-with-database "/tmp/db" - (lambda (db) - (insert-packages db)))) - ((_ "search" file) - (let ((matches (call-with-database "/tmp/db" - (lambda (db) - (matching-packages db file))))) - (for-each (lambda (result) - (format #t "~20a ~a~%" - (string-append (package-match-name result) - "@" (package-match-version result)) - (package-match-file result))) - matches) + (("index") + (index-packages-with-db "/tmp/db")) + (("search" file) + (let ((matches (matching-packages-with-db "/tmp/db" file))) + (print-matching-results matches) (exit (pair? matches)))) (_ (format (current-error-port) - "usage: file-database [populate|search] args ...~%") + "usage: guix locate [index|search] args ...~% ~a" + args) (exit 1)))) - -(apply file-database (command-line)) -- 2.38.1 From eb474f3412ba19320dceda7d08c7f960d00cb898 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 1 Dec 2022 13:45:59 +0100 Subject: [PATCH 03/25] extensions-index: Avoid duplicating the hard-coded db path --- guix/extensions/locate.scm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index 1e42f5bad8..830dfc49fb 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -196,14 +196,18 @@ (define (print-matching-results matches) (package-match-file result))) matches)) +;; TODO: Determine the current guile/guix mechanism to provide configuration +;; for this +(define default-location-db-path "/tmp/db") + (define-command (guix-locate . args) (category extension) (synopsis "Index packages then search what package declares a given file") (match args (("index") - (index-packages-with-db "/tmp/db")) + (index-packages-with-db default-location-db-path)) (("search" file) - (let ((matches (matching-packages-with-db "/tmp/db" file))) + (let ((matches (matching-packages-with-db default-location-db-path file))) (print-matching-results matches) (exit (pair? matches)))) (_ -- 2.38.1 From 309ecd5d5b7cdff012b66cbe9643c34725b22a2d Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 1 Dec 2022 13:47:19 +0100 Subject: [PATCH 04/25] extensions-index: Deduplicate lookup matching results --- guix/extensions/locate.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index 830dfc49fb..ab0a0403ec 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -156,10 +156,10 @@ (define-record-type (file package-match-file)) (define (matching-packages db file) - "Return list of corresponding to packages containing FILE." + "Return unique corresponding to packages containing FILE." (define lookup-stmt (sqlite-prepare db "\ -SELECT Packages.name, Packages.version, Directories.name, Files.name +SELECT DISTINCT Packages.name, Packages.version, Directories.name, Files.name FROM Packages INNER JOIN Files, Directories ON files.basename = :file AND directories.id = files.directory AND packages.id = directories.package;")) -- 2.38.1 From 541615ab6638b1fb418531f961cfb6756b41499b Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 14:09:52 +0100 Subject: [PATCH 05/25] extensions-index: Make insertion queries idempotent Prior to this, multiple runs of the index subcommand would append the same packages, directories or files in the db. --- guix/extensions/locate.scm | 71 ++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index ab0a0403ec..ce8306531f 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -36,14 +36,16 @@ (define schema create table if not exists Packages ( id integer primary key autoincrement not null, name text not null, - version text not null + version text not null, + unique (name, version) -- add uniqueness constraint ); create table if not exists Directories ( id integer primary key autoincrement not null, name text not null, package integer not null, - foreign key (package) references Packages(id) on delete cascade + foreign key (package) references Packages(id) on delete cascade, + unique (name, package) -- add uniqueness constraint ); create table if not exists Files ( @@ -51,6 +53,7 @@ (define schema basename text not null, directory integer not null, foreign key (directory) references Directories(id) on delete cascade + unique (name, basename, directory) -- add uniqueness constraint ); create index if not exists IndexFiles on Files(basename);") @@ -66,64 +69,78 @@ (define (call-with-database file proc) (sqlite-close db))))) (define (insert-files db package version directories) - "Insert the files contained in DIRECTORIES as belonging to PACKAGE at -VERSION." - (define last-row-id-stmt - (sqlite-prepare db "SELECT last_insert_rowid();" + "Insert files from DIRECTORIES as belonging to PACKAGE at VERSION." + (define stmt-select-package + (sqlite-prepare db "\ +SELECT id FROM Packages WHERE name = :name AND version = :version;" #:cache? #t)) - (define package-stmt + (define stmt-insert-package (sqlite-prepare db "\ -INSERT OR REPLACE INTO Packages(name, version) +INSERT OR IGNORE INTO Packages(name, version) -- to avoid spurious writes VALUES (:name, :version);" #:cache? #t)) - (define directory-stmt + (define stmt-select-directory (sqlite-prepare db "\ -INSERT INTO Directories(name, package) VALUES (:name, :package);" +SELECT id FROM Directories WHERE name = :name AND package = :package;" #:cache? #t)) - (define file-stmt + (define stmt-insert-directory (sqlite-prepare db "\ -INSERT INTO Files(name, basename, directory) +INSERT OR IGNORE INTO Directories(name, package) -- to avoid spurious writes +VALUES (:name, :package);" + #:cache? #t)) + + (define stmt-insert-file + (sqlite-prepare db "\ +INSERT OR IGNORE INTO Files(name, basename, directory) VALUES (:name, :basename, :directory);" #:cache? #t)) (sqlite-exec db "begin immediate;") - (sqlite-bind-arguments package-stmt + (sqlite-bind-arguments stmt-insert-package #:name package #:version version) - (sqlite-fold (const #t) #t package-stmt) - (match (sqlite-fold cons '() last-row-id-stmt) + (sqlite-fold (const #t) #t stmt-insert-package) + + (sqlite-bind-arguments stmt-select-package + #:name package + #:version version) + (match (sqlite-fold cons '() stmt-select-package) ((#(package-id)) (pk 'package package-id package) (for-each (lambda (directory) (define (strip file) (string-drop file (+ (string-length directory) 1))) - (sqlite-reset directory-stmt) - (sqlite-bind-arguments directory-stmt + (sqlite-reset stmt-insert-directory) + (sqlite-bind-arguments stmt-insert-directory #:name directory #:package package-id) - (sqlite-fold (const #t) #t directory-stmt) + (sqlite-fold (const #t) #t stmt-insert-directory) - (match (sqlite-fold cons '() last-row-id-stmt) + (sqlite-reset stmt-select-directory) + (sqlite-bind-arguments stmt-select-directory + #:name directory + #:package package-id) + (match (sqlite-fold cons '() stmt-select-directory) ((#(directory-id)) (for-each (lambda (file) ;; If DIRECTORY is a symlink, (find-files ;; DIRECTORY) returns the DIRECTORY singleton. (unless (string=? file directory) - (sqlite-reset file-stmt) - (sqlite-bind-arguments file-stmt + (sqlite-reset stmt-insert-file) + (sqlite-bind-arguments stmt-insert-file #:name (strip file) #:basename (basename file) #:directory directory-id) - (sqlite-fold (const #t) #t file-stmt))) + (sqlite-fold (const #t) #t stmt-insert-file))) (find-files directory))))) - directories) - (sqlite-exec db "commit;")))) + directories))) + (sqlite-exec db "commit;")) (define (insert-package db package) "Insert all the files of PACKAGE into DB." @@ -159,10 +176,12 @@ (define (matching-packages db file) "Return unique corresponding to packages containing FILE." (define lookup-stmt (sqlite-prepare db "\ -SELECT DISTINCT Packages.name, Packages.version, Directories.name, Files.name +SELECT Packages.name, Packages.version, Directories.name, Files.name FROM Packages INNER JOIN Files, Directories -ON files.basename = :file AND directories.id = files.directory AND packages.id = directories.package;")) +ON files.basename = :file + AND directories.id = files.directory + AND packages.id = directories.package;")) (sqlite-bind-arguments lookup-stmt #:file file) (sqlite-fold (lambda (result lst) -- 2.38.1 From 09d5f6b30ac24a8e8261994a1011ddd13082a4bb Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 14:10:59 +0100 Subject: [PATCH 06/25] extensions-index: Add debug statement This is conditional in the top-level debug module variable, false by default. --- guix/extensions/locate.scm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index ce8306531f..3b43ea887e 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -31,6 +31,8 @@ (define-module (guix extensions locate) #:use-module (srfi srfi-9) #:export (guix-locate)) +(define debug #f) + (define schema " create table if not exists Packages ( @@ -109,6 +111,9 @@ (define stmt-insert-file #:version version) (match (sqlite-fold cons '() stmt-select-package) ((#(package-id)) + (when debug + (format #t "(pkg, version, pkg-id): (~a, ~a, ~a)" + package version package-id)) (pk 'package package-id package) (for-each (lambda (directory) (define (strip file) @@ -126,6 +131,9 @@ (define (strip file) #:package package-id) (match (sqlite-fold cons '() stmt-select-directory) ((#(directory-id)) + (when debug + (format #t "(name, package, dir-id): (~a, ~a, ~a)\n" + directory package-id directory-id)) (for-each (lambda (file) ;; If DIRECTORY is a symlink, (find-files ;; DIRECTORY) returns the DIRECTORY singleton. -- 2.38.1 From b50267e3d24162cd8c3908bbaa841d13363621e9 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 14:11:50 +0100 Subject: [PATCH 07/25] extensions-index: Play around the packaging filtering functions This keeps the default behavior but allows to change it (by the developer) to determine what's the best policy. --- guix/extensions/locate.scm | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index 3b43ea887e..9679d643a6 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -160,18 +160,27 @@ (define (insert-package db package) directories)) (return #t))))) -(define (insert-packages db) - "Insert all the current packages into DB." +(define (filter-public-current-supported package) + "Filter supported, not hidden (public) and not superseded (current) package." + (and (not (hidden-package? package)) + (not (package-superseded package)) + (supported-package? package))) + +(define (filter-supported-package package) + "Filter supported package (package might be hidden or superseded)." + (and (supported-package? package))) + +(define (no-filter package) "No filtering on package" #t) + +(define* (insert-packages db #:optional (filter-policy filter-public-current-supported)) + "Insert all current packages matching `filter-package-policy` into DB." (with-store store (parameterize ((%graft? #f)) (fold-packages (lambda (package _) (run-with-store store (insert-package db package))) #t - #:select? (lambda (package) - (and (not (hidden-package? package)) - (not (package-superseded package)) - (supported-package? package))))))) + #:select? filter-policy)))) (define-record-type (package-match name version file) @@ -206,7 +215,7 @@ (define (index-packages-with-db db-pathname) "Index packages using db at location DB-PATHNAME." (call-with-database db-pathname (lambda (db) - (insert-packages db)))) + (insert-packages db no-filter)))) (define (matching-packages-with-db db-pathname file) "Compute list of packages referencing FILE using db at DB-PATHNAME." -- 2.38.1 From 3b5c765fc967cef1d6919b66acc2d7872ea1e48c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 15:19:24 +0100 Subject: [PATCH 08/25] extensions-index: Install db in ~/.config/guix/locate-db.sqlite --- guix/extensions/locate.scm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index 9679d643a6..7d19e64a07 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -232,9 +232,12 @@ (define (print-matching-results matches) (package-match-file result))) matches)) -;; TODO: Determine the current guile/guix mechanism to provide configuration -;; for this -(define default-location-db-path "/tmp/db") +(define default-location-db-path + (let ((local-config-path + (and=> (getenv "HOME") + (lambda (home) + (string-append home "/.config/guix/"))))) + (string-append local-config-path "locate-db.sqlite"))) (define-command (guix-locate . args) (category extension) -- 2.38.1 From f101d12acf05c82cf9678d1cffec76cceba9e845 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 17:58:18 +0100 Subject: [PATCH 09/25] extensions-index: Improve cli parsing This unifies with some existing guix commands (import). --- guix/extensions/locate.scm | 80 +++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/guix/extensions/locate.scm b/guix/extensions/locate.scm index 7d19e64a07..630560b231 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/locate.scm @@ -17,9 +17,12 @@ ;;; along with GNU Guix. If not, see . (define-module (guix extensions locate) + #:use-module (guix config) ;; %guix-package-name, ... + #:use-module (guix ui) ;; display G_ #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) + #:use-module (guix describe) #:use-module (guix store) #:use-module (guix monads) #:autoload (guix grafts) (%graft?) @@ -232,25 +235,84 @@ (define (print-matching-results matches) (package-match-file result))) matches)) -(define default-location-db-path +(define default-db-path (let ((local-config-path (and=> (getenv "HOME") (lambda (home) (string-append home "/.config/guix/"))))) (string-append local-config-path "locate-db.sqlite"))) +(define (show-bug-report-information) + ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this + ;; package. Please add another line saying "Report translation bugs to + ;; ...\n" with the address for translation bugs (typically your translation + ;; team's web or email address). + (format #t (G_ " +Report bugs to: ~a.") %guix-bug-report-address) + (format #t (G_ " +~a home page: <~a>") %guix-package-name %guix-home-page-url) + (format #t (G_ " +General help using Guix and GNU software: <~a>") + ;; TRANSLATORS: Change the "/en" bit of this URL appropriately if + ;; the web site is translated in your language. + (G_ "https://guix.gnu.org/en/help/")) + (newline)) + +(define (show-help) + (display (G_ "Usage: guix locate [OPTIONS...] [ARGS...] +Index packages and search what package declares a given file.\n +By default, the local cache db is located in ~/.config/guix/locate-db.sqlite. +See --db-path for customization.")) + (display (G_ " + index Index current packages from the local store (in cache db)")) + (display (G_ " + search FILE Search for packages that declares FILE (from cache db)")) + (newline) + (display (G_ " + --db-path=DIR Change default location of the cache db")) + (newline) + (display (G_ " + -h, --help Display this help and exit")) + (display (G_ " + -V, --version Display version information and exit")) + (newline) + (show-bug-report-information)) + (define-command (guix-locate . args) (category extension) - (synopsis "Index packages then search what package declares a given file") + (synopsis "Index packages to allow searching package for a given filename") + + (define (parse-db-args args) + "Parsing of string key=value where we are only interested in 'value'" + (match (string-split args #\=) + ((unused db-path) + db-path) + (_ #f))) + + (define (display-help-and-exit) + (show-help) + (exit 0)) + (match args + ((or ("-h") ("--help") ()) + (display-help-and-exit)) + ((or ("-V") ("--version")) + (show-version-and-exit "guix locate")) + ((db-path-args "index") + (let ((db-path (parse-db-args db-path-args))) + (if db-path + (index-packages-with-db db-path) + (display-help-and-exit)))) (("index") - (index-packages-with-db default-location-db-path)) + (index-packages-with-db default-db-path)) (("search" file) - (let ((matches (matching-packages-with-db default-location-db-path file))) + (let ((matches (matching-packages-with-db default-db-path file))) (print-matching-results matches) (exit (pair? matches)))) - (_ - (format (current-error-port) - "usage: guix locate [index|search] args ...~% ~a" - args) - (exit 1)))) + ((db-path-args "search" file) + (let ((db-path (parse-db-args db-path-args))) + (if db-path + (let ((matches (matching-packages-with-db db-path file))) + (print-matching-results matches) + (exit (pair? matches))) + (display-help-and-exit)))))) -- 2.38.1 From 9cb0826a71bdada345de100d98e9b44f3503a75a Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Fri, 2 Dec 2022 19:13:46 +0100 Subject: [PATCH 10/25] extensions-index: Improve cli options and help message This also renames the cli from locate to index. --- guix/extensions/{locate.scm => index.scm} | 40 +++++++++++++---------- 1 file changed, 22 insertions(+), 18 deletions(-) rename guix/extensions/{locate.scm => index.scm} (93%) diff --git a/guix/extensions/locate.scm b/guix/extensions/index.scm similarity index 93% rename from guix/extensions/locate.scm rename to guix/extensions/index.scm index 630560b231..ab7661dbac 100644 --- a/guix/extensions/locate.scm +++ b/guix/extensions/index.scm @@ -16,7 +16,7 @@ ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see . -(define-module (guix extensions locate) +(define-module (guix extensions index) #:use-module (guix config) ;; %guix-package-name, ... #:use-module (guix ui) ;; display G_ #:use-module (guix scripts) @@ -32,7 +32,7 @@ (define-module (guix extensions locate) #:autoload (gnu packages) (fold-packages) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) - #:export (guix-locate)) + #:export (guix-index)) (define debug #f) @@ -259,26 +259,30 @@ (define (show-bug-report-information) (newline)) (define (show-help) - (display (G_ "Usage: guix locate [OPTIONS...] [ARGS...] -Index packages and search what package declares a given file.\n -By default, the local cache db is located in ~/.config/guix/locate-db.sqlite. -See --db-path for customization.")) - (display (G_ " - index Index current packages from the local store (in cache db)")) - (display (G_ " - search FILE Search for packages that declares FILE (from cache db)")) + (display (G_ "Usage: guix index [OPTIONS...] [search FILE...] +Without FILE, index (package, file) relationships in the local store. +With 'search FILE', search for packages installing FILEx;x.\n +Note: The internal cache is located at ~/.config/guix/locate-db.sqlite. +See --db-path for customization.\n")) (newline) - (display (G_ " - --db-path=DIR Change default location of the cache db")) + (display (G_ "The valid values for OPTIONS are:")) (newline) (display (G_ " -h, --help Display this help and exit")) (display (G_ " -V, --version Display version information and exit")) + (display (G_ " + --db-path=DIR Change default location of the cache db")) + (newline) + (newline) + (display (G_ "The valid values for ARGS are:")) + (newline) + (display (G_ " + search FILE Search for packages installing the FILE (from cache db)")) (newline) (show-bug-report-information)) -(define-command (guix-locate . args) +(define-command (guix-index . args) (category extension) (synopsis "Index packages to allow searching package for a given filename") @@ -294,17 +298,15 @@ (define (display-help-and-exit) (exit 0)) (match args - ((or ("-h") ("--help") ()) + ((or ("-h") ("--help")) (display-help-and-exit)) ((or ("-V") ("--version")) (show-version-and-exit "guix locate")) - ((db-path-args "index") + ((db-path-args) (let ((db-path (parse-db-args db-path-args))) (if db-path (index-packages-with-db db-path) (display-help-and-exit)))) - (("index") - (index-packages-with-db default-db-path)) (("search" file) (let ((matches (matching-packages-with-db default-db-path file))) (print-matching-results matches) @@ -315,4 +317,6 @@ (define (display-help-and-exit) (let ((matches (matching-packages-with-db db-path file))) (print-matching-results matches) (exit (pair? matches))) - (display-help-and-exit)))))) + (display-help-and-exit)))) + (_ ;; index by default + (index-packages-with-db default-db-path)))) -- 2.38.1 From f18d1f536bf6b13ec0dd8ee1e865ce21448e3836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Sun, 4 Dec 2022 14:42:45 +0100 Subject: [PATCH 11/25] extensions-index: Iterate over system manifests to index This should avoid the extra work of discussing with daemon, computing derivations, etc... --- guix/extensions/index.scm | 84 +++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index ab7661dbac..a7a23c6194 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -25,13 +25,19 @@ (define-module (guix extensions index) #:use-module (guix describe) #:use-module (guix store) #:use-module (guix monads) + #:autoload (guix combinators) (fold2) #:autoload (guix grafts) (%graft?) + #:autoload (guix store roots) (gc-roots) #:use-module (guix derivations) #:use-module (guix packages) + #:use-module (guix profiles) + #:use-module (guix sets) + #:use-module ((guix utils) #:select (cache-directory)) #:autoload (guix build utils) (find-files) #:autoload (gnu packages) (fold-packages) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) + #:use-module (srfi srfi-71) #:export (guix-index)) (define debug #f) @@ -185,6 +191,67 @@ (define* (insert-packages db #:optional (filter-policy filter-public-current-sup #t #:select? filter-policy)))) + +;;; +;;; Indexing from local profiles. +;;; + +(define (all-profiles) + "Return the list of profiles on the system." + (delete-duplicates + (filter-map (lambda (root) + (if (file-exists? (string-append root "/manifest")) + root + (let ((root (string-append root "/profile"))) + (and (file-exists? (string-append root "/manifest")) + root)))) + (gc-roots)))) + +(define (profiles->manifest-entries profiles) + "Return manifest entries for all of PROFILES, without duplicates." + (let loop ((visited (set)) + (profiles profiles) + (entries '())) + (match profiles + (() + entries) + ((profile . rest) + (let* ((manifest (profile-manifest profile)) + (entries visited + (fold2 (lambda (entry lst visited) + (let ((item (manifest-entry-item entry))) + (if (set-contains? visited item) + (values lst visited) + (values (cons entry lst) + (set-insert item + visited))))) + entries + visited + (manifest-transitive-entries manifest)))) + (loop visited rest entries)))))) + +(define (insert-manifest-entry db entry) + "Insert ENTRY, a manifest entry, into DB." + (insert-files db (manifest-entry-name entry) + (manifest-entry-version entry) + (list (manifest-entry-item entry)))) ;FIXME: outputs? + +(define (index-manifests db-file) + "Insert into DB-FILE entries for packages that appear in manifests +available on the system." + (call-with-database db-file + (lambda (db) + (for-each (lambda (entry) + (insert-manifest-entry db entry)) + (let ((lst (profiles->manifest-entries (all-profiles)))) + (pk 'entries (length lst)) + lst))))) + + +;;; +;;; Search. +;;; + (define-record-type (package-match name version file) package-match? @@ -214,6 +281,10 @@ (define lookup-stmt +;;; +;;; CLI +;;; + (define (index-packages-with-db db-pathname) "Index packages using db at location DB-PATHNAME." (call-with-database db-pathname @@ -236,11 +307,8 @@ (define (print-matching-results matches) matches)) (define default-db-path - (let ((local-config-path - (and=> (getenv "HOME") - (lambda (home) - (string-append home "/.config/guix/"))))) - (string-append local-config-path "locate-db.sqlite"))) + (string-append (cache-directory #:ensure? #f) + "/index/db.sqlite")) (define (show-bug-report-information) ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this @@ -261,7 +329,7 @@ (define (show-bug-report-information) (define (show-help) (display (G_ "Usage: guix index [OPTIONS...] [search FILE...] Without FILE, index (package, file) relationships in the local store. -With 'search FILE', search for packages installing FILEx;x.\n +With 'search FILE', search for packages installing FILE.\n Note: The internal cache is located at ~/.config/guix/locate-db.sqlite. See --db-path for customization.\n")) (newline) @@ -318,5 +386,5 @@ (define (display-help-and-exit) (print-matching-results matches) (exit (pair? matches))) (display-help-and-exit)))) - (_ ;; index by default - (index-packages-with-db default-db-path)))) + (_ ;; By default, index + (index-manifests default-db-path)))) -- 2.38.1 From c9b02fc838237ebd7bc38ba7a71587fcdcaf6212 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 14:45:20 +0100 Subject: [PATCH 12/25] extensions-index: Improve help message --- guix/extensions/index.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index a7a23c6194..4a69df326e 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -328,9 +328,9 @@ (define (show-bug-report-information) (define (show-help) (display (G_ "Usage: guix index [OPTIONS...] [search FILE...] -Without FILE, index (package, file) relationships in the local store. +Without argument, indexes (package, file) relationships in the local store. With 'search FILE', search for packages installing FILE.\n -Note: The internal cache is located at ~/.config/guix/locate-db.sqlite. +Note: The internal cache is located at ~/.cache/guix/index/db.sqlite. See --db-path for customization.\n")) (newline) (display (G_ "The valid values for OPTIONS are:")) -- 2.38.1 From d63ef7a97f3fb47b5693b2c1d24bdf276ca6a6a8 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 14:46:04 +0100 Subject: [PATCH 13/25] extensions-index: Improve imports --- guix/extensions/index.scm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 4a69df326e..abaf7df071 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -17,8 +17,10 @@ ;;; along with GNU Guix. If not, see . (define-module (guix extensions index) - #:use-module (guix config) ;; %guix-package-name, ... - #:use-module (guix ui) ;; display G_ + #:use-module ((guix config) #:select (%guix-package-name + %guix-home-page-url + %guix-bug-report-address)) + #:use-module ((guix ui) #:select (G_)) #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) -- 2.38.1 From 14a9dafb2b927ba8435a26fdea04b00644e3ca3c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 15:52:15 +0100 Subject: [PATCH 14/25] extensions-index: Drop code duplication Import directly the right function from guix ui module. --- guix/extensions/index.scm | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index abaf7df071..c40edc7944 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -17,10 +17,9 @@ ;;; along with GNU Guix. If not, see . (define-module (guix extensions index) - #:use-module ((guix config) #:select (%guix-package-name - %guix-home-page-url - %guix-bug-report-address)) - #:use-module ((guix ui) #:select (G_)) + #:use-module ((guix i18n) #:select (G_)) + #:use-module ((guix ui) #:select (show-version-and-exit + show-bug-report-information)) #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) @@ -312,22 +311,6 @@ (define default-db-path (string-append (cache-directory #:ensure? #f) "/index/db.sqlite")) -(define (show-bug-report-information) - ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this - ;; package. Please add another line saying "Report translation bugs to - ;; ...\n" with the address for translation bugs (typically your translation - ;; team's web or email address). - (format #t (G_ " -Report bugs to: ~a.") %guix-bug-report-address) - (format #t (G_ " -~a home page: <~a>") %guix-package-name %guix-home-page-url) - (format #t (G_ " -General help using Guix and GNU software: <~a>") - ;; TRANSLATORS: Change the "/en" bit of this URL appropriately if - ;; the web site is translated in your language. - (G_ "https://guix.gnu.org/en/help/")) - (newline)) - (define (show-help) (display (G_ "Usage: guix index [OPTIONS...] [search FILE...] Without argument, indexes (package, file) relationships in the local store. -- 2.38.1 From ea1d8216bfe5f487de24d883891b6e07c8536cdd Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 16:01:33 +0100 Subject: [PATCH 15/25] extensions-index: Drop dead code we read from local profiles now --- guix/extensions/index.scm | 42 ++------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index c40edc7944..a7c518e903 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -160,38 +160,6 @@ (define (strip file) directories))) (sqlite-exec db "commit;")) -(define (insert-package db package) - "Insert all the files of PACKAGE into DB." - (mlet %store-monad ((drv (package->derivation package #:graft? #f))) - (match (derivation->output-paths drv) - (((labels . directories) ...) - (when (every file-exists? directories) - (insert-files db (package-name package) (package-version package) - directories)) - (return #t))))) - -(define (filter-public-current-supported package) - "Filter supported, not hidden (public) and not superseded (current) package." - (and (not (hidden-package? package)) - (not (package-superseded package)) - (supported-package? package))) - -(define (filter-supported-package package) - "Filter supported package (package might be hidden or superseded)." - (and (supported-package? package))) - -(define (no-filter package) "No filtering on package" #t) - -(define* (insert-packages db #:optional (filter-policy filter-public-current-supported)) - "Insert all current packages matching `filter-package-policy` into DB." - (with-store store - (parameterize ((%graft? #f)) - (fold-packages (lambda (package _) - (run-with-store store - (insert-package db package))) - #t - #:select? filter-policy)))) - ;;; ;;; Indexing from local profiles. @@ -209,7 +177,7 @@ (define (all-profiles) (gc-roots)))) (define (profiles->manifest-entries profiles) - "Return manifest entries for all of PROFILES, without duplicates." + "Return deduplicated manifest entries across all PROFILES." (let loop ((visited (set)) (profiles profiles) (entries '())) @@ -286,12 +254,6 @@ (define lookup-stmt ;;; CLI ;;; -(define (index-packages-with-db db-pathname) - "Index packages using db at location DB-PATHNAME." - (call-with-database db-pathname - (lambda (db) - (insert-packages db no-filter)))) - (define (matching-packages-with-db db-pathname file) "Compute list of packages referencing FILE using db at DB-PATHNAME." (call-with-database db-pathname @@ -358,7 +320,7 @@ (define (display-help-and-exit) ((db-path-args) (let ((db-path (parse-db-args db-path-args))) (if db-path - (index-packages-with-db db-path) + (index-manifests db-path) (display-help-and-exit)))) (("search" file) (let ((matches (matching-packages-with-db default-db-path file))) -- 2.38.1 From 8454f9f417c2781fded2c26a1b920174991ac1dc Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 16:12:10 +0100 Subject: [PATCH 16/25] extensions-index: Rework docstrings --- guix/extensions/index.scm | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index a7c518e903..1c23d9a4f1 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -166,7 +166,7 @@ (define (strip file) ;;; (define (all-profiles) - "Return the list of profiles on the system." + "Return the list of system profiles." (delete-duplicates (filter-map (lambda (root) (if (file-exists? (string-append root "/manifest")) @@ -200,14 +200,13 @@ (define (profiles->manifest-entries profiles) (loop visited rest entries)))))) (define (insert-manifest-entry db entry) - "Insert ENTRY, a manifest entry, into DB." + "Insert a manifest ENTRY into DB." (insert-files db (manifest-entry-name entry) (manifest-entry-version entry) (list (manifest-entry-item entry)))) ;FIXME: outputs? (define (index-manifests db-file) - "Insert into DB-FILE entries for packages that appear in manifests -available on the system." + "Insert packages entries into DB-FILE from the system manifests." (call-with-database db-file (lambda (db) (for-each (lambda (entry) -- 2.38.1 From 98f9899d479cd62e93b86fab3448b2024db02621 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 16:12:24 +0100 Subject: [PATCH 17/25] extensions-index: Fix warning according to repl suggestion --- guix/extensions/index.scm | 1 + 1 file changed, 1 insertion(+) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 1c23d9a4f1..42c2051f13 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -23,6 +23,7 @@ (define-module (guix extensions index) #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) + #:use-module (ice-9 format) #:use-module (guix describe) #:use-module (guix store) #:use-module (guix monads) -- 2.38.1 From bb80ad696e1a47651f2dc4a7c74ea577372c61b5 Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 4 Dec 2022 16:20:01 +0100 Subject: [PATCH 18/25] extensions-index: Ensure directory holding the db is created if needed. The creation is ignore if already present. --- guix/extensions/index.scm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 42c2051f13..627dddc119 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -208,6 +208,10 @@ (define (insert-manifest-entry db entry) (define (index-manifests db-file) "Insert packages entries into DB-FILE from the system manifests." + (let ((db-dirpath (dirname db-file))) + (unless (file-exists? db-dirpath) + (mkdir db-dirpath))) + (call-with-database db-file (lambda (db) (for-each (lambda (entry) -- 2.38.1 From 34a86f977947371d1eae3be9953190464aa01a8c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 11 Dec 2022 20:11:56 +0100 Subject: [PATCH 19/25] extensions-index: Add a schema db version Nothing is done with that version just yet besides displaying it in the --version call. --- guix/extensions/index.scm | 179 ++++++++++++++++++++++++-------------- 1 file changed, 112 insertions(+), 67 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 627dddc119..b89eb9e6c8 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -44,8 +44,15 @@ (define-module (guix extensions index) (define debug #f) +(define application-version 1) + (define schema " +create table if not exists SchemaVersion ( + version integer primary key not null, + unique (version) +); + create table if not exists Packages ( id integer primary key autoincrement not null, name text not null, @@ -81,85 +88,107 @@ (define (call-with-database file proc) (lambda () (sqlite-close db))))) -(define (insert-files db package version directories) - "Insert files from DIRECTORIES as belonging to PACKAGE at VERSION." - (define stmt-select-package +(define (insert-version db version) + "Insert application VERSION into the DB." + (define stmt-insert-version (sqlite-prepare db "\ -SELECT id FROM Packages WHERE name = :name AND version = :version;" +INSERT OR IGNORE INTO SchemaVersion(version) +VALUES (:version);" #:cache? #t)) + (sqlite-exec db "begin immediate;") + (sqlite-bind-arguments stmt-insert-version #:version version) + (sqlite-fold (const #t) #t stmt-insert-version) + (sqlite-exec db "commit;")) - (define stmt-insert-package - (sqlite-prepare db "\ +(define (read-version db) + "Read the current application version from the DB." + + (define stmt-select-version (sqlite-prepare db "\ +SELECT version FROM SchemaVersion;" + #:cache? #t)) + (match (sqlite-fold cons '() stmt-select-version) + ((#(version)) + version))) + +(define (insert-files db package version directories) + "Insert files from DIRECTORIES as belonging to PACKAGE at VERSION." + (define stmt-select-package + (sqlite-prepare db "\ +SELECT id FROM Packages WHERE name = :name AND version = :version;" + #:cache? #t)) + + (define stmt-insert-package + (sqlite-prepare db "\ INSERT OR IGNORE INTO Packages(name, version) -- to avoid spurious writes VALUES (:name, :version);" - #:cache? #t)) + #:cache? #t)) - (define stmt-select-directory - (sqlite-prepare db "\ + (define stmt-select-directory + (sqlite-prepare db "\ SELECT id FROM Directories WHERE name = :name AND package = :package;" - #:cache? #t)) + #:cache? #t)) - (define stmt-insert-directory - (sqlite-prepare db "\ + (define stmt-insert-directory + (sqlite-prepare db "\ INSERT OR IGNORE INTO Directories(name, package) -- to avoid spurious writes VALUES (:name, :package);" - #:cache? #t)) + #:cache? #t)) - (define stmt-insert-file - (sqlite-prepare db "\ + (define stmt-insert-file + (sqlite-prepare db "\ INSERT OR IGNORE INTO Files(name, basename, directory) VALUES (:name, :basename, :directory);" - #:cache? #t)) - - (sqlite-exec db "begin immediate;") - (sqlite-bind-arguments stmt-insert-package - #:name package - #:version version) - (sqlite-fold (const #t) #t stmt-insert-package) - - (sqlite-bind-arguments stmt-select-package - #:name package - #:version version) - (match (sqlite-fold cons '() stmt-select-package) - ((#(package-id)) - (when debug - (format #t "(pkg, version, pkg-id): (~a, ~a, ~a)" - package version package-id)) - (pk 'package package-id package) - (for-each (lambda (directory) - (define (strip file) - (string-drop file (+ (string-length directory) 1))) - - (sqlite-reset stmt-insert-directory) - (sqlite-bind-arguments stmt-insert-directory - #:name directory - #:package package-id) - (sqlite-fold (const #t) #t stmt-insert-directory) - - (sqlite-reset stmt-select-directory) - (sqlite-bind-arguments stmt-select-directory - #:name directory - #:package package-id) - (match (sqlite-fold cons '() stmt-select-directory) - ((#(directory-id)) - (when debug - (format #t "(name, package, dir-id): (~a, ~a, ~a)\n" - directory package-id directory-id)) - (for-each (lambda (file) - ;; If DIRECTORY is a symlink, (find-files - ;; DIRECTORY) returns the DIRECTORY singleton. - (unless (string=? file directory) - (sqlite-reset stmt-insert-file) - (sqlite-bind-arguments stmt-insert-file - #:name (strip file) - #:basename - (basename file) - #:directory - directory-id) - (sqlite-fold (const #t) #t stmt-insert-file))) - (find-files directory))))) - directories))) - (sqlite-exec db "commit;")) + #:cache? #t)) + + (sqlite-exec db "begin immediate;") + (sqlite-bind-arguments stmt-insert-package + #:name package + #:version version) + (sqlite-fold (const #t) #t stmt-insert-package) + + (sqlite-bind-arguments stmt-select-package + #:name package + #:version version) + (match (sqlite-fold cons '() stmt-select-package) + ((#(package-id)) + (when debug + (format #t "(pkg, version, pkg-id): (~a, ~a, ~a)" + package version package-id)) + (pk 'package package-id package) + (for-each (lambda (directory) + (define (strip file) + (string-drop file (+ (string-length directory) 1))) + + (sqlite-reset stmt-insert-directory) + (sqlite-bind-arguments stmt-insert-directory + #:name directory + #:package package-id) + (sqlite-fold (const #t) #t stmt-insert-directory) + + (sqlite-reset stmt-select-directory) + (sqlite-bind-arguments stmt-select-directory + #:name directory + #:package package-id) + (match (sqlite-fold cons '() stmt-select-directory) + ((#(directory-id)) + (when debug + (format #t "(name, package, dir-id): (~a, ~a, ~a)\n" + directory package-id directory-id)) + (for-each (lambda (file) + ;; If DIRECTORY is a symlink, (find-files + ;; DIRECTORY) returns the DIRECTORY singleton. + (unless (string=? file directory) + (sqlite-reset stmt-insert-file) + (sqlite-bind-arguments stmt-insert-file + #:name (strip file) + #:basename + (basename file) + #:directory + directory-id) + (sqlite-fold (const #t) #t stmt-insert-file))) + (find-files directory))))) + directories))) + (sqlite-exec db "commit;")) ;;; @@ -212,6 +241,8 @@ (define (index-manifests db-file) (unless (file-exists? db-dirpath) (mkdir db-dirpath))) + (insert-version-with-db db-file) + (call-with-database db-file (lambda (db) (for-each (lambda (entry) @@ -258,6 +289,16 @@ (define lookup-stmt ;;; CLI ;;; +(define (insert-version-with-db db-pathname) + "Insert application version into the database." + (call-with-database db-pathname + (lambda (db) + (insert-version db application-version)))) + +(define (read-db-version-with-db db-pathname) + "Insert version into the database." + (call-with-database db-pathname read-version)) + (define (matching-packages-with-db db-pathname file) "Compute list of packages referencing FILE using db at DB-PATHNAME." (call-with-database db-pathname @@ -306,7 +347,7 @@ (define-command (guix-index . args) (synopsis "Index packages to allow searching package for a given filename") (define (parse-db-args args) - "Parsing of string key=value where we are only interested in 'value'" + "Parsing of string key=value where we are only interested in 'value'" (match (string-split args #\=) ((unused db-path) db-path) @@ -320,6 +361,10 @@ (define (display-help-and-exit) ((or ("-h") ("--help")) (display-help-and-exit)) ((or ("-V") ("--version")) + (with-exception-handler + (lambda (exn) 'meh) ;; noop exception + (simple-format #t "Extension db version: ~a\n" (read-db-version-with-db default-db-path)) + #:unwind? #t) (show-version-and-exit "guix locate")) ((db-path-args) (let ((db-path (parse-db-args db-path-args))) -- 2.38.1 From 2ecdab01c93fc4872803c5a2d16743214512cb5d Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Sun, 11 Dec 2022 20:13:44 +0100 Subject: [PATCH 20/25] extensions-index: Fix typo in help message --- guix/extensions/index.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index b89eb9e6c8..3a5015afe1 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -365,7 +365,7 @@ (define (display-help-and-exit) (lambda (exn) 'meh) ;; noop exception (simple-format #t "Extension db version: ~a\n" (read-db-version-with-db default-db-path)) #:unwind? #t) - (show-version-and-exit "guix locate")) + (show-version-and-exit "guix index")) ((db-path-args) (let ((db-path (parse-db-args db-path-args))) (if db-path -- 2.38.1 From a30dff0161f60288ce3b260a8429c2fd3c8b8e7c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 15 Dec 2022 13:04:08 +0100 Subject: [PATCH 21/25] extensions-index: Allow user to choose the indexation method To do so, this: - reverted the old code removal to reuse the indexing packages out of the local store functions - rewrites the cli argument parsing logic. This allows more flexibility in indexation method (for a bit more code though) --- guix/extensions/index.scm | 250 ++++++++++++++++++++++++++++++-------- guix/scripts/home.scm | 2 +- 2 files changed, 199 insertions(+), 53 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 3a5015afe1..878daf4fb6 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -19,11 +19,15 @@ (define-module (guix extensions index) #:use-module ((guix i18n) #:select (G_)) #:use-module ((guix ui) #:select (show-version-and-exit - show-bug-report-information)) + show-bug-report-information + with-error-handling + string->number*)) + #:use-module ((guix status) #:select (with-status-verbosity)) #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) #:use-module (ice-9 format) + #:use-module (ice-9 getopt-long) #:use-module (guix describe) #:use-module (guix store) #:use-module (guix monads) @@ -39,10 +43,11 @@ (define-module (guix extensions index) #:autoload (gnu packages) (fold-packages) #:use-module (srfi srfi-1) #:use-module (srfi srfi-9) + #:use-module (srfi srfi-37) ;; option #:use-module (srfi srfi-71) #:export (guix-index)) -(define debug #f) +(define debug #t) (define application-version 1) @@ -190,6 +195,34 @@ (define (strip file) directories))) (sqlite-exec db "commit;")) + +;;; +;;; Indexing from local packages. +;;; + +(define (insert-package db package) + "Insert all the files of PACKAGE into DB." + (mlet %store-monad ((drv (package->derivation package #:graft? #f))) + (match (derivation->output-paths drv) + (((labels . directories) ...) + (when (every file-exists? directories) + (insert-files db (package-name package) (package-version package) + directories)) + (return #t))))) + +(define* (index-packages-from-store db) + "Insert all current packages from the local store into the DB." + (with-store store + (parameterize ((%graft? #f)) + (fold-packages (lambda (package _) + (run-with-store store + (insert-package db package))) + #t + #:select? (lambda (package) + (and (not (hidden-package? package)) + (not (package-superseded package)) + (supported-package? package))))))) + ;;; ;;; Indexing from local profiles. @@ -235,14 +268,8 @@ (define (insert-manifest-entry db entry) (manifest-entry-version entry) (list (manifest-entry-item entry)))) ;FIXME: outputs? -(define (index-manifests db-file) - "Insert packages entries into DB-FILE from the system manifests." - (let ((db-dirpath (dirname db-file))) - (unless (file-exists? db-dirpath) - (mkdir db-dirpath))) - - (insert-version-with-db db-file) - +(define (index-packages-from-manifests-with-db db-file) + "Index packages entries into DB-FILE from the system manifests." (call-with-database db-file (lambda (db) (for-each (lambda (entry) @@ -289,6 +316,12 @@ (define lookup-stmt ;;; CLI ;;; +(define (index-packages-from-store-with-db db-pathname) + "Index packages using db at location DB-PATHNAME." + (call-with-database db-pathname + (lambda (db) + (index-packages-from-store db)))) + (define (insert-version-with-db db-pathname) "Insert application version into the database." (call-with-database db-pathname @@ -320,67 +353,180 @@ (define default-db-path (define (show-help) (display (G_ "Usage: guix index [OPTIONS...] [search FILE...] -Without argument, indexes (package, file) relationships in the local store. +Without argument, indexes (package, file) relationships from the machine. +This allows indexation with 2 methods: + +- out of the local manifests. This is the fastest implementation but this +indexes less packages. That'd be typically the use case for user local +indexation. + +- out of the local store. This is slower due to implementation details (it +discusses with the store daemon for one). That'd be typically the use case for +building the largest db in one of the build farm node. + With 'search FILE', search for packages installing FILE.\n -Note: The internal cache is located at ~/.cache/guix/index/db.sqlite. +Note: Internal cache is located at ~/.cache/guix/index/db.sqlite by default. See --db-path for customization.\n")) (newline) (display (G_ "The valid values for OPTIONS are:")) (newline) (display (G_ " - -h, --help Display this help and exit")) + -h, --help Display this help and exit")) (display (G_ " - -V, --version Display version information and exit")) + -V, --version Display version information and exit")) (display (G_ " - --db-path=DIR Change default location of the cache db")) + --db-path=DIR Change default location of the cache db")) (newline) + (display (G_ " + --with-method=METH Change default indexation method. By default it uses the + local \"manifests\" (faster). It can also uses the local + \"store\" (slower, typically on the farm build ci).")) (newline) (display (G_ "The valid values for ARGS are:")) (newline) (display (G_ " search FILE Search for packages installing the FILE (from cache db)")) (newline) + (display (G_ " + Without any argument, it index packages. This fills in the + db cache using whatever indexation method is defined.")) (show-bug-report-information)) +(define (verbosity-level opts) + "Return the verbosity level based on OPTS, the alist of parsed options." + (or (assoc-ref opts 'verbosity) + (if (eq? (assoc-ref opts 'action) 'build) + 3 1))) + +(define %options + (list + (option '(#\h "help") #f #f + (lambda args (show-help) (exit 0))) + (option '(#\V "version") #f #f + (lambda args (show-version-and-exit "guix index"))) + (option '(#\v "verbosity") #f #t + (lambda (opt name arg result) + (let ((level (string->number* arg))) + (alist-cons 'verbosity level + (alist-delete 'verbosity result))))) + ;; index data out of the method (store or package) + (option '(#\d "db-path") #f #t + (lambda (opt name arg result) + (when debug + (format #t "%options: --with-method: opt ~a\n" opt) + (format #t "%options: --with-method: name ~a\n" name) + (format #t "%options: --with-method: arg ~a\n" arg) + (format #t "%options: --with-method: result ~a\n" result)) + (alist-cons 'db-path arg + (alist-delete 'db-path result)))) + + ;; index data out of the method (store or package) + (option '(#\m "with-method") #f #t + (lambda (opt name arg result) + (when debug + (format #t "%options: --with-method: opt ~a\n" opt) + (format #t "%options: --with-method: name ~a\n" name) + (format #t "%options: --with-method: arg ~a\n" arg) + (format #t "%options: --with-method: result ~a\n" result)) + (match arg + ((or "manifests" "store") + (alist-cons 'with-method arg + (alist-delete 'with-method result))) + (_ + (G_ "guix index: Wrong indexation method, either manifests + (fast) or store (slow)~%"))))))) + +(define %default-options + `((db-path . ,default-db-path) + (verbosity . #f) + (with-method . "manifests"))) + (define-command (guix-index . args) (category extension) - (synopsis "Index packages to allow searching package for a given filename") - - (define (parse-db-args args) - "Parsing of string key=value where we are only interested in 'value'" - (match (string-split args #\=) - ((unused db-path) - db-path) + (synopsis "Index packages to search package for a given filename") + + (define (parse-sub-command arg result) + ;; Parse sub-command ARG and augment RESULT accordingly. + (when debug + (format #t "parse-sub-command: arg: ~a\n" arg) + (format #t "parse-sub-command: result: ~a\n" result) + (format #t "parse-sub-command: (assoc-ref result 'action): ~a\n" (assoc-ref result 'action)) + (format #t "parse-sub-command: (assoc-ref result 'argument): ~a\n" (assoc-ref result 'argument))) + (if (assoc-ref result 'action) + (alist-cons 'argument arg result) + (let ((action (string->symbol arg))) + (case action + ((search) + (alist-cons 'action action result)) + (else (leave (G_ "~a: unknown action~%") action)))))) + + (define (match-pair car) + ;; Return a procedure that matches a pair with CAR. + (match-lambda + ((head . tail) + (and (eq? car head) tail)) (_ #f))) - (define (display-help-and-exit) - (show-help) - (exit 0)) - - (match args - ((or ("-h") ("--help")) - (display-help-and-exit)) - ((or ("-V") ("--version")) - (with-exception-handler - (lambda (exn) 'meh) ;; noop exception - (simple-format #t "Extension db version: ~a\n" (read-db-version-with-db default-db-path)) - #:unwind? #t) - (show-version-and-exit "guix index")) - ((db-path-args) - (let ((db-path (parse-db-args db-path-args))) - (if db-path - (index-manifests db-path) - (display-help-and-exit)))) - (("search" file) - (let ((matches (matching-packages-with-db default-db-path file))) - (print-matching-results matches) - (exit (pair? matches)))) - ((db-path-args "search" file) - (let ((db-path (parse-db-args db-path-args))) - (if db-path - (let ((matches (matching-packages-with-db db-path file))) + (define (option-arguments opts) + ;; Extract the plain arguments from OPTS. + (let* ((args (reverse (filter-map (match-pair 'argument) opts))) + (count (length args)) + (action (or (assoc-ref opts 'action) 'index))) + + (when debug + (format #t "option-arguments: args: ~a\n" args) + (format #t "option-arguments: count: ~a\n" count) + (format #t "option-arguments: action: ~a\n" action)) + + (define (fail) + (leave (G_ "wrong number of arguments for action '~a'~%") + action)) + + (unless action + (format (current-error-port) + (G_ "guix index: missing command name~%")) + (format (current-error-port) + (G_ "Try 'guix index --help' for more information.~%")) + (exit 1)) + (alist-cons 'argument (string-concatenate args) + (alist-delete 'argument + (alist-cons 'action action + (alist-delete 'action opts)))))) + + (with-error-handling + (let* ((opts (parse-command-line args %options + (list %default-options) + #:argument-handler + parse-sub-command)) + (args (option-arguments opts)) + (action (assoc-ref args 'action)) + (db-path (assoc-ref args 'db-path)) + (with-method (assoc-ref args 'with-method))) + (with-status-verbosity (verbosity-level opts) + (when debug + (format #t "main: opts: ~a\n" opts) + (format #t "main: args: ~a\n" args) + (format #t "main: action: ~a\n" action) + (format #t "main: db-path: ~a\n" db-path) + (format #t "main: with-method: ~a\n" with-method)) + + (match action + ('search + (unless (file-exists? db-path) + (format (current-error-port) + (G_ "guix index: The local cache db does not exist yet. +You need to index packages first.\nTry 'guix index --help' for more information.~%")) + (exit 1)) + (let* ((file (assoc-ref args 'argument)) + (matches (matching-packages-with-db db-path file))) (print-matching-results matches) - (exit (pair? matches))) - (display-help-and-exit)))) - (_ ;; By default, index - (index-manifests default-db-path)))) + (exit (pair? matches)))) + ('index + (let ((db-dirpath (dirname db-path))) + (unless (file-exists? db-dirpath) + (mkdir db-dirpath))) + ;; FIXME: Deal with check on version and destruction/migration if need be + (insert-version-with-db db-path) + (if (string= with-method "manifests") + (index-packages-from-manifests-with-db db-path) + (index-packages-from-store-with-db db-path)))))))) diff --git a/guix/scripts/home.scm b/guix/scripts/home.scm index 1c481ccf91..bdc903f393 100644 --- a/guix/scripts/home.scm +++ b/guix/scripts/home.scm @@ -69,7 +69,7 @@ (define-module (guix scripts home) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (srfi srfi-35) - #:use-module (srfi srfi-37) + #:use-module ((srfi srfi-37) #:select (option)) #:use-module (srfi srfi-71) #:use-module (ice-9 match) #:export (guix-home)) -- 2.38.1 From 295e4f85b6a967cd714712fe67bcaaef6bb5c29d Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 15 Dec 2022 16:34:06 +0100 Subject: [PATCH 22/25] extensions-index: Deal with db schema migrations The schema db version is dealt with. It's transparent for users. As we modify along the schema for evolution, we should also provide the intermediary migration sql script so the existing db can be migrated along without losing data. --- guix/extensions/index.scm | 153 ++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 63 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 878daf4fb6..0fd361a485 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -22,7 +22,6 @@ (define-module (guix extensions index) show-bug-report-information with-error-handling string->number*)) - #:use-module ((guix status) #:select (with-status-verbosity)) #:use-module (guix scripts) #:use-module (sqlite3) #:use-module (ice-9 match) @@ -49,12 +48,18 @@ (define-module (guix extensions index) (define debug #t) -(define application-version 1) +(define application-version 2) -(define schema +;; The following schema is the full schema at the `application-version`. It +;; should be modified according to the development required. If the schema +;; needs modification across time, those should be changed directly in the +;; full-schema and the incremental changes should be referenced below +;; as migration step (for the existing dbs) below. +(define schema-full " create table if not exists SchemaVersion ( version integer primary key not null, + date date, unique (version) ); @@ -83,22 +88,32 @@ (define schema create index if not exists IndexFiles on Files(basename);") +;; List of tuple ((version . sqlite schema migration script)). There should +;; be as much version increments with each step needed to migrate the db. +(define schema-to-migrate '((1 . " +create table if not exists SchemaVersion ( + version integer primary key not null, + unique (version) +); +") + (2 . " +alter table SchemaVersion +add column date date; +"))) + (define (call-with-database file proc) (let ((db (sqlite-open file))) (dynamic-wind (lambda () #t) - (lambda () - (sqlite-exec db schema) - (proc db)) - (lambda () - (sqlite-close db))))) + (lambda () (proc db)) + (lambda () (sqlite-close db))))) (define (insert-version db version) "Insert application VERSION into the DB." (define stmt-insert-version (sqlite-prepare db "\ -INSERT OR IGNORE INTO SchemaVersion(version) -VALUES (:version);" +INSERT OR IGNORE INTO SchemaVersion(version, date) +VALUES (:version, CURRENT_TIMESTAMP);" #:cache? #t)) (sqlite-exec db "begin immediate;") (sqlite-bind-arguments stmt-insert-version #:version version) @@ -109,8 +124,8 @@ (define (read-version db) "Read the current application version from the DB." (define stmt-select-version (sqlite-prepare db "\ -SELECT version FROM SchemaVersion;" - #:cache? #t)) +SELECT version FROM SchemaVersion ORDER BY version DESC LIMIT 1;" + #:cache? #f)) (match (sqlite-fold cons '() stmt-select-version) ((#(version)) version))) @@ -268,9 +283,9 @@ (define (insert-manifest-entry db entry) (manifest-entry-version entry) (list (manifest-entry-item entry)))) ;FIXME: outputs? -(define (index-packages-from-manifests-with-db db-file) - "Index packages entries into DB-FILE from the system manifests." - (call-with-database db-file +(define (index-packages-from-manifests-with-db db-pathname) + "Index packages entries into DB-PATHNAME from the system manifests." + (call-with-database db-pathname (lambda (db) (for-each (lambda (entry) (insert-manifest-entry db entry)) @@ -322,21 +337,40 @@ (define (index-packages-from-store-with-db db-pathname) (lambda (db) (index-packages-from-store db)))) -(define (insert-version-with-db db-pathname) - "Insert application version into the database." +(define (matching-packages-with-db db-pathname file) + "Compute list of packages referencing FILE using db at DB-PATHNAME." (call-with-database db-pathname (lambda (db) - (insert-version db application-version)))) + (matching-packages db file)))) -(define (read-db-version-with-db db-pathname) - "Insert version into the database." - (call-with-database db-pathname read-version)) +(define (read-version-from-db db-pathname) + (call-with-database db-pathname + (lambda (db) (read-version db)))) -(define (matching-packages-with-db db-pathname file) - "Compute list of packages referencing FILE using db at DB-PATHNAME." +(define (migrate-schema-to-version db-pathname) (call-with-database db-pathname (lambda (db) - (matching-packages db file)))) + (catch #t + (lambda () + ;; Migrate from the current version to the full migrated schema + ;; This can raise sqlite-error if the db is not properly configured yet + (let* ((current-db-version (read-version db)) + (next-db-version (+ 1 current-db-version))) + (when (< current-db-version application-version) + ;; when the current db version is older than the current application + (let ((schema-migration-at-version (assoc-ref schema-to-migrate next-db-version))) + (when schema-migration-at-version + ;; migrate the schema to the next version (if it exists) + (sqlite-exec db schema-migration-at-version) + ;; insert current version + (insert-version db next-db-version) + ;; iterate over the next migration if any + (migrate-schema-to-version db)))))) + (lambda (key . arg) + ;; exception handler in case failure to read an inexisting db + ;; Fallback to boostrap the schema + (sqlite-exec db schema-full) + (insert-version db application-version)))))) (define (print-matching-results matches) "Print the MATCHES matching results." @@ -392,23 +426,17 @@ (define (show-help) db cache using whatever indexation method is defined.")) (show-bug-report-information)) -(define (verbosity-level opts) - "Return the verbosity level based on OPTS, the alist of parsed options." - (or (assoc-ref opts 'verbosity) - (if (eq? (assoc-ref opts 'action) 'build) - 3 1))) - (define %options (list (option '(#\h "help") #f #f (lambda args (show-help) (exit 0))) (option '(#\V "version") #f #f - (lambda args (show-version-and-exit "guix index"))) - (option '(#\v "verbosity") #f #t (lambda (opt name arg result) - (let ((level (string->number* arg))) - (alist-cons 'verbosity level - (alist-delete 'verbosity result))))) + (catch 'sqlite-error + (lambda () + (simple-format #t "Extension db version: ~a\n" (read-version-from-db (assoc-ref result 'db-path)))) + (lambda (key . arg) 'no-db-yet-so-nothing-to-display)) + (show-version-and-exit "guix index"))) ;; index data out of the method (store or package) (option '(#\d "db-path") #f #t (lambda (opt name arg result) @@ -438,7 +466,6 @@ (define %options (define %default-options `((db-path . ,default-db-path) - (verbosity . #f) (with-method . "manifests"))) (define-command (guix-index . args) @@ -502,31 +529,31 @@ (define (fail) (action (assoc-ref args 'action)) (db-path (assoc-ref args 'db-path)) (with-method (assoc-ref args 'with-method))) - (with-status-verbosity (verbosity-level opts) - (when debug - (format #t "main: opts: ~a\n" opts) - (format #t "main: args: ~a\n" args) - (format #t "main: action: ~a\n" action) - (format #t "main: db-path: ~a\n" db-path) - (format #t "main: with-method: ~a\n" with-method)) - - (match action - ('search - (unless (file-exists? db-path) - (format (current-error-port) - (G_ "guix index: The local cache db does not exist yet. + (when debug + (format #t "main: opts: ~a\n" opts) + (format #t "main: args: ~a\n" args) + (format #t "main: action: ~a\n" action) + (format #t "main: db-path: ~a\n" db-path) + (format #t "main: with-method: ~a\n" with-method)) + + (match action + ('search + (unless (file-exists? db-path) + (format (current-error-port) + (G_ "guix index: The local cache db does not exist yet. You need to index packages first.\nTry 'guix index --help' for more information.~%")) - (exit 1)) - (let* ((file (assoc-ref args 'argument)) - (matches (matching-packages-with-db db-path file))) - (print-matching-results matches) - (exit (pair? matches)))) - ('index - (let ((db-dirpath (dirname db-path))) - (unless (file-exists? db-dirpath) - (mkdir db-dirpath))) - ;; FIXME: Deal with check on version and destruction/migration if need be - (insert-version-with-db db-path) - (if (string= with-method "manifests") - (index-packages-from-manifests-with-db db-path) - (index-packages-from-store-with-db db-path)))))))) + (exit 1)) + (let* ((file (assoc-ref args 'argument)) + (matches (matching-packages-with-db db-path file))) + (print-matching-results matches) + (exit (pair? matches)))) + ('index + (let ((db-dirpath (dirname db-path))) + (unless (file-exists? db-dirpath) + (mkdir db-dirpath))) + ;; Migrate/initialize db to schema at version application-version + (migrate-schema-to-version db-path) + ;; Finally index packages + (if (string= with-method "manifests") + (index-packages-from-manifests-with-db db-path) + (index-packages-from-store-with-db db-path))))))) -- 2.38.1 From 60b2d6e1e6c9ce286844354298a3c9f2fed0adff Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 15 Dec 2022 17:21:15 +0100 Subject: [PATCH 23/25] extensions-index: Deactivate debug --- guix/extensions/index.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 0fd361a485..56841a4666 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -46,7 +46,7 @@ (define-module (guix extensions index) #:use-module (srfi srfi-71) #:export (guix-index)) -(define debug #t) +(define debug #f) (define application-version 2) -- 2.38.1 From b7485e7302862ef3e96279eca9df6f4c63bfb94c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 15 Dec 2022 17:22:39 +0100 Subject: [PATCH 24/25] extensions-index: Expose db information in guix index -V output --- guix/extensions/index.scm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 56841a4666..256a43d7fd 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -434,7 +434,12 @@ (define %options (lambda (opt name arg result) (catch 'sqlite-error (lambda () - (simple-format #t "Extension db version: ~a\n" (read-version-from-db (assoc-ref result 'db-path)))) + (let ((db-path (assoc-ref result 'db-path))) + (simple-format + #t + "Extension local cache database:\n- path: ~a\n- version: ~a\n\n" + db-path (read-version-from-db db-path)) + )) (lambda (key . arg) 'no-db-yet-so-nothing-to-display)) (show-version-and-exit "guix index"))) ;; index data out of the method (store or package) -- 2.38.1 From 93bb890ac2f887f338a9e2fa06e6d605bfc6722c Mon Sep 17 00:00:00 2001 From: "Antoine R. Dumont (@ardumont)" Date: Thu, 15 Dec 2022 17:22:53 +0100 Subject: [PATCH 25/25] extensions-index: Wrap index computations with progress bar output --- guix/extensions/index.scm | 48 +++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/guix/extensions/index.scm b/guix/extensions/index.scm index 256a43d7fd..12237f82ba 100644 --- a/guix/extensions/index.scm +++ b/guix/extensions/index.scm @@ -36,6 +36,8 @@ (define-module (guix extensions index) #:use-module (guix derivations) #:use-module (guix packages) #:use-module (guix profiles) + #:use-module ((guix progress) #:select (progress-reporter/bar + call-with-progress-reporter)) #:use-module (guix sets) #:use-module ((guix utils) #:select (cache-directory)) #:autoload (guix build utils) (find-files) @@ -173,8 +175,8 @@ (define stmt-insert-file ((#(package-id)) (when debug (format #t "(pkg, version, pkg-id): (~a, ~a, ~a)" - package version package-id)) - (pk 'package package-id package) + package version package-id) + (pk 'package package-id package)) (for-each (lambda (directory) (define (strip file) (string-drop file (+ (string-length directory) 1))) @@ -229,14 +231,24 @@ (define* (index-packages-from-store db) "Insert all current packages from the local store into the DB." (with-store store (parameterize ((%graft? #f)) - (fold-packages (lambda (package _) - (run-with-store store - (insert-package db package))) - #t - #:select? (lambda (package) - (and (not (hidden-package? package)) - (not (package-superseded package)) - (supported-package? package))))))) + (let* ((packages (fold-packages + (lambda (package result) + (cons package result)) + '() + #:select? (lambda (package) + (and (not (hidden-package? package)) + (not (package-superseded package)) + (supported-package? package))))) + (nb-entries (length packages)) + (prefix (format #f "Registering ~a packages" nb-entries)) + (progress (progress-reporter/bar nb-entries prefix))) + (call-with-progress-reporter progress + (lambda (report) + (for-each (lambda (package) + (run-with-store store + (insert-package db package)) + (report)) + packages))))))) ;;; @@ -287,11 +299,17 @@ (define (index-packages-from-manifests-with-db db-pathname) "Index packages entries into DB-PATHNAME from the system manifests." (call-with-database db-pathname (lambda (db) - (for-each (lambda (entry) - (insert-manifest-entry db entry)) - (let ((lst (profiles->manifest-entries (all-profiles)))) - (pk 'entries (length lst)) - lst))))) + (let* ((profiles (all-profiles)) + (entries (profiles->manifest-entries profiles)) + (nb-entries (length entries)) + (prefix (format #f "Registering ~a packages" nb-entries)) + (progress (progress-reporter/bar nb-entries prefix))) + (call-with-progress-reporter progress + (lambda (report) + (for-each (lambda (entry) + (insert-manifest-entry db entry) + (report)) + entries))))))) ;;; -- 2.38.1