diff options
Diffstat (limited to 'guix')
-rw-r--r-- | guix/build-system/linux-module.scm | 3 | ||||
-rw-r--r-- | guix/build-system/qt.scm | 295 | ||||
-rw-r--r-- | guix/build/emacs-build-system.scm | 39 | ||||
-rw-r--r-- | guix/build/qt-build-system.scm | 109 | ||||
-rw-r--r-- | guix/build/syscalls.scm | 4 | ||||
-rw-r--r-- | guix/gexp.scm | 7 | ||||
-rw-r--r-- | guix/import/opam.scm | 16 | ||||
-rw-r--r-- | guix/lint.scm | 5 | ||||
-rw-r--r-- | guix/scripts/offload.scm | 30 | ||||
-rw-r--r-- | guix/scripts/package.scm | 6 | ||||
-rw-r--r-- | guix/scripts/pull.scm | 73 | ||||
-rwxr-xr-x | guix/scripts/substitute.scm | 16 | ||||
-rw-r--r-- | guix/scripts/system.scm | 15 | ||||
-rw-r--r-- | guix/ssh.scm | 69 | ||||
-rw-r--r-- | guix/ui.scm | 26 |
15 files changed, 609 insertions, 104 deletions
diff --git a/guix/build-system/linux-module.scm b/guix/build-system/linux-module.scm index 6084d22210..dde2423434 100644 --- a/guix/build-system/linux-module.scm +++ b/guix/build-system/linux-module.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org> +;;; Copyright © 2019 Tobias Geerinckx-Rice <me@tobias.gr> ;;; ;;; This file is part of GNU Guix. ;;; @@ -120,6 +121,7 @@ (define* (linux-module-build store name inputs #:key (search-paths '()) + (make-flags '()) (tests? #t) (phases '(@ (guix build linux-module-build-system) %standard-phases)) @@ -146,6 +148,7 @@ search-paths) #:phases ,phases #:system ,system + #:make-flags ,make-flags #:tests? ,tests? #:outputs %outputs #:inputs %build-inputs))) diff --git a/guix/build-system/qt.scm b/guix/build-system/qt.scm new file mode 100644 index 0000000000..b776845377 --- /dev/null +++ b/guix/build-system/qt.scm @@ -0,0 +1,295 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2013 Cyril Roelandt <tipecaml@gmail.com> +;;; Copyright © 2017 Ricardo Wurmus <rekado@elephly.net> +;;; Copyright © 2019 Hartmut Goebel <h.goebel@crazy-compilers.com> +;;; +;;; 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 <http://www.gnu.org/licenses/>. + +(define-module (guix build-system qt) + #:use-module (guix store) + #:use-module (guix utils) + #:use-module (guix derivations) + #:use-module (guix search-paths) + #:use-module (guix build-system) + #:use-module (guix build-system cmake) + #:use-module (guix build-system gnu) + #:use-module (guix packages) + #:use-module (ice-9 match) + #:export (%qt-build-system-modules + qt-build + qt-build-system)) + +;; Commentary: +;; +;; This build system is an extension of the 'cmake-build-system'. It +;; accommodates the needs of Qt and KDE applications by adding a phase run +;; after the 'install' phase: +;; +;; 'qt-wrap' phase: +;; +;; This phase looks for Qt5 plugin paths, QML paths and some XDG paths as well +;; as the corresponding environment variables. If any of these is found in +;; the output or if respective environment variables are set, then all +;; programs in the output's "bin", "sbin", "libexec and "lib/libexec" +;; directories are wrapped in scripts defining the necessary environment +;; variables. +;; +;; Code: + +(define %qt-build-system-modules + ;; Build-side modules imported and used by default. + `((guix build qt-build-system) + ,@%cmake-build-system-modules)) + +(define (default-cmake) + "Return the default CMake package." + + ;; Do not use `@' to avoid introducing circular dependencies. + (let ((module (resolve-interface '(gnu packages cmake)))) + (module-ref module 'cmake-minimal))) + +;; This barely is a copy from (guix build-system cmake), only adjusted to use +;; the variables defined here. +(define* (lower name + #:key source inputs native-inputs outputs system target + (cmake (default-cmake)) + #:allow-other-keys + #:rest arguments) + "Return a bag for NAME." + (define private-keywords + `(#:source #:cmake #:inputs #:native-inputs #:outputs + ,@(if target '() '(#:target)))) + + (bag + (name name) + (system system) + (target target) + (build-inputs `(,@(if source + `(("source" ,source)) + '()) + ,@`(("cmake" ,cmake)) + ,@native-inputs + ,@(if target + ;; Use the standard cross inputs of + ;; 'gnu-build-system'. + (standard-cross-packages target 'host) + '()) + ;; Keep the standard inputs of 'gnu-build-system'. + ,@(standard-packages))) + (host-inputs inputs) + + ;; The cross-libc is really a target package, but for bootstrapping + ;; reasons, we can't put it in 'host-inputs'. Namely, 'cross-gcc' is a + ;; native package, so it would end up using a "native" variant of + ;; 'cross-libc' (built with 'gnu-build'), whereas all the other packages + ;; would use a target variant (built with 'gnu-cross-build'.) + (target-inputs (if target + (standard-cross-packages target 'target) + '())) + (outputs outputs) + (build (if target qt-cross-build qt-build)) + (arguments (strip-keyword-arguments private-keywords arguments)))) + + +(define* (qt-build store name inputs + #:key (guile #f) + (outputs '("out")) (configure-flags ''()) + (search-paths '()) + (make-flags ''()) + (out-of-source? #t) + (build-type "RelWithDebInfo") + (tests? #t) + (test-target "test") + (parallel-build? #t) (parallel-tests? #f) + (validate-runpath? #t) + (patch-shebangs? #t) + (strip-binaries? #t) + (strip-flags ''("--strip-debug")) + (strip-directories ''("lib" "lib64" "libexec" + "bin" "sbin")) + (phases '(@ (guix build qt-build-system) + %standard-phases)) + (qt-wrap-excluded-outputs ''()) + (system (%current-system)) + (imported-modules %qt-build-system-modules) + (modules '((guix build cmake-build-system) + (guix build utils)))) + "Build SOURCE using CMAKE, and with INPUTS. This assumes that SOURCE +provides a 'CMakeLists.txt' file as its build system." + (define builder + `(begin + (use-modules ,@modules) + (cmake-build #:source ,(match (assoc-ref inputs "source") + (((? derivation? source)) + (derivation->output-path source)) + ((source) + source) + (source + source)) + #:system ,system + #:outputs %outputs + #:inputs %build-inputs + #:search-paths ',(map search-path-specification->sexp + search-paths) + #:phases ,phases + #:qt-wrap-excluded-outputs ,qt-wrap-excluded-outputs + #:configure-flags ,configure-flags + #:make-flags ,make-flags + #:out-of-source? ,out-of-source? + #:build-type ,build-type + #:tests? ,tests? + #:test-target ,test-target + #:parallel-build? ,parallel-build? + #:parallel-tests? ,parallel-tests? + #:validate-runpath? ,validate-runpath? + #:patch-shebangs? ,patch-shebangs? + #:strip-binaries? ,strip-binaries? + #:strip-flags ,strip-flags + #:strip-directories ,strip-directories))) + + (define guile-for-build + (match guile + ((? package?) + (package-derivation store guile system #:graft? #f)) + (#f ; the default + (let* ((distro (resolve-interface '(gnu packages commencement))) + (guile (module-ref distro 'guile-final))) + (package-derivation store guile system #:graft? #f))))) + + (build-expression->derivation store name builder + #:system system + #:inputs inputs + #:modules imported-modules + #:outputs outputs + #:guile-for-build guile-for-build)) + + +;;; +;;; Cross-compilation. +;;; + +(define* (qt-cross-build store name + #:key + target native-drvs target-drvs + (guile #f) + (outputs '("out")) + (configure-flags ''()) + (search-paths '()) + (native-search-paths '()) + (make-flags ''()) + (out-of-source? #t) + (build-type "RelWithDebInfo") + (tests? #f) ; nothing can be done + (test-target "test") + (parallel-build? #t) (parallel-tests? #f) + (validate-runpath? #t) + (patch-shebangs? #t) + (strip-binaries? #t) + (strip-flags ''("--strip-debug" + "--enable-deterministic-archives")) + (strip-directories ''("lib" "lib64" "libexec" + "bin" "sbin")) + (phases '(@ (guix build qt-build-system) + %standard-phases)) + (system (%current-system)) + (build (nix-system->gnu-triplet system)) + (imported-modules %qt-build-system-modules) + (modules '((guix build cmake-build-system) + (guix build utils)))) + "Cross-build NAME using CMAKE for TARGET, where TARGET is a GNU triplet and +with INPUTS. This assumes that SOURCE provides a 'CMakeLists.txt' file as its +build system." + (define builder + `(begin + (use-modules ,@modules) + (let () + (define %build-host-inputs + ',(map (match-lambda + ((name (? derivation? drv) sub ...) + `(,name . ,(apply derivation->output-path drv sub))) + ((name path) + `(,name . ,path))) + native-drvs)) + + (define %build-target-inputs + ',(map (match-lambda + ((name (? derivation? drv) sub ...) + `(,name . ,(apply derivation->output-path drv sub))) + ((name (? package? pkg) sub ...) + (let ((drv (package-cross-derivation store pkg + target system))) + `(,name . ,(apply derivation->output-path drv sub)))) + ((name path) + `(,name . ,path))) + target-drvs)) + + (cmake-build #:source ,(match (assoc-ref native-drvs "source") + (((? derivation? source)) + (derivation->output-path source)) + ((source) + source) + (source + source)) + #:system ,system + #:build ,build + #:target ,target + #:outputs %outputs + #:inputs %build-target-inputs + #:native-inputs %build-host-inputs + #:search-paths ',(map search-path-specification->sexp + search-paths) + #:native-search-paths ',(map + search-path-specification->sexp + native-search-paths) + #:phases ,phases + #:configure-flags ,configure-flags + #:make-flags ,make-flags + #:out-of-source? ,out-of-source? + #:build-type ,build-type + #:tests? ,tests? + #:test-target ,test-target + #:parallel-build? ,parallel-build? + #:parallel-tests? ,parallel-tests? + #:validate-runpath? ,validate-runpath? + #:patch-shebangs? ,patch-shebangs? + #:strip-binaries? ,strip-binaries? + #:strip-flags ,strip-flags + #:strip-directories ,strip-directories)))) + + (define guile-for-build + (match guile + ((? package?) + (package-derivation store guile system #:graft? #f)) + (#f ; the default + (let* ((distro (resolve-interface '(gnu packages commencement))) + (guile (module-ref distro 'guile-final))) + (package-derivation store guile system #:graft? #f))))) + + (build-expression->derivation store name builder + #:system system + #:inputs (append native-drvs target-drvs) + #:outputs outputs + #:modules imported-modules + #:guile-for-build guile-for-build)) + +(define qt-build-system + (build-system + (name 'qt) + (description + "The CMake build system augmented with definition of suitable environment +variables for Qt and KDE in program wrappers.") + (lower lower))) diff --git a/guix/build/emacs-build-system.scm b/guix/build/emacs-build-system.scm index f0c41812f1..e2b792d3dc 100644 --- a/guix/build/emacs-build-system.scm +++ b/guix/build/emacs-build-system.scm @@ -40,11 +40,10 @@ ;; ;; Code: -;; Directory suffix where we install ELPA packages. We avoid ".../elpa" as -;; Emacs expects to find the ELPA repository 'archive-contents' file and the -;; archive signature. -(define %legacy-install-suffix "/share/emacs/site-lisp") -(define %install-suffix (string-append %legacy-install-suffix "/guix.d")) +;;; All the packages are installed directly under site-lisp, which means that +;;; having that directory in the EMACSLOADPATH is enough to have them found by +;;; Emacs. +(define %install-dir "/share/emacs/site-lisp") ;; These are the default inclusion/exclusion regexps for the install phase. (define %default-include '("^[^/]*\\.el$" "^[^/]*\\.info$" "^doc/.*\\.info$")) @@ -87,11 +86,10 @@ environment variable\n" source-directory))) "Compile .el files." (let* ((emacs (string-append (assoc-ref inputs "emacs") "/bin/emacs")) (out (assoc-ref outputs "out")) - (elpa-name-ver (store-directory->elpa-name-version out)) - (el-dir (string-append out %install-suffix "/" elpa-name-ver))) + (site-lisp (string-append out %install-dir))) (setenv "SHELL" "sh") (parameterize ((%emacs emacs)) - (emacs-byte-compile-directory el-dir)))) + (emacs-byte-compile-directory site-lisp)))) (define* (patch-el-files #:key outputs #:allow-other-keys) "Substitute the absolute \"/bin/\" directory with the right location in the @@ -108,9 +106,7 @@ store in '.el' files." #:binary #t)) (let* ((out (assoc-ref outputs "out")) - (elpa-name-ver (store-directory->elpa-name-version out)) - (el-dir (string-append out %install-suffix "/" elpa-name-ver)) - + (site-lisp (string-append out %install-dir)) ;; (ice-9 regex) uses libc's regexp routines, which cannot deal with ;; strings containing NULs. Filter out such files. TODO: Remove ;; this workaround when <https://bugs.gnu.org/30116> is fixed. @@ -124,7 +120,7 @@ store in '.el' files." (error "patch-el-files: unable to locate " cmd-name)) (string-append "\"" cmd "\""))))) - (with-directory-excursion el-dir + (with-directory-excursion site-lisp ;; Some old '.el' files (e.g., tex-buf.el in AUCTeX) are still ;; ISO-8859-1-encoded. (unless (false-if-exception (substitute-program-names)) @@ -175,15 +171,14 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND." (not (any (cut match-stripped-file "excluded" <>) exclude))))) (let* ((out (assoc-ref outputs "out")) - (elpa-name-ver (store-directory->elpa-name-version out)) - (target-directory (string-append out %install-suffix "/" elpa-name-ver)) + (site-lisp (string-append out %install-dir)) (files-to-install (find-files source install-file?))) (cond ((not (null? files-to-install)) (for-each (lambda (file) (let* ((stripped-file (string-drop file (string-length source))) - (target-file (string-append target-directory stripped-file))) + (target-file (string-append site-lisp stripped-file))) (format #t "`~a' -> `~a'~%" file target-file) (install-file file (dirname target-file)))) files-to-install) @@ -197,14 +192,12 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND." (define* (move-doc #:key outputs #:allow-other-keys) "Move info files from the ELPA package directory to the info directory." (let* ((out (assoc-ref outputs "out")) - (elpa-name-ver (store-directory->elpa-name-version out)) - (el-dir (string-append out %install-suffix "/" elpa-name-ver)) - (name-ver (strip-store-file-name out)) + (site-lisp (string-append out %install-dir)) (info-dir (string-append out "/share/info/")) - (info-files (find-files el-dir "\\.info$"))) + (info-files (find-files site-lisp "\\.info$"))) (unless (null? info-files) (mkdir-p info-dir) - (with-directory-excursion el-dir + (with-directory-excursion site-lisp (when (file-exists? "dir") (delete-file "dir")) (for-each (lambda (f) (copy-file f (string-append info-dir "/" (basename f))) @@ -216,11 +209,11 @@ parallel. PARALLEL-TESTS? is ignored when using a non-make TEST-COMMAND." "Generate the autoloads file." (let* ((emacs (string-append (assoc-ref inputs "emacs") "/bin/emacs")) (out (assoc-ref outputs "out")) + (site-lisp (string-append out %install-dir)) (elpa-name-ver (store-directory->elpa-name-version out)) - (elpa-name (package-name->name+version elpa-name-ver)) - (el-dir (string-append out %install-suffix "/" elpa-name-ver))) + (elpa-name (package-name->name+version elpa-name-ver))) (parameterize ((%emacs emacs)) - (emacs-generate-autoloads elpa-name el-dir)))) + (emacs-generate-autoloads elpa-name site-lisp)))) (define (emacs-package? name) "Check if NAME correspond to the name of an Emacs package." diff --git a/guix/build/qt-build-system.scm b/guix/build/qt-build-system.scm new file mode 100644 index 0000000000..46fcad7848 --- /dev/null +++ b/guix/build/qt-build-system.scm @@ -0,0 +1,109 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Federico Beffa <beffa@fbengineering.ch> +;;; Copyright © 2014, 2015 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2018 Mark H Weaver <mhw@netris.org> +;;; Copyright © 2019 Hartmut Goebel <h.goebel@crazy-compilers.com> +;;; +;;; 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 <http://www.gnu.org/licenses/>. + +(define-module (guix build qt-build-system) + #:use-module ((guix build cmake-build-system) #:prefix cmake:) + #:use-module (guix build utils) + #:use-module (ice-9 match) + #:use-module (ice-9 regex) + #:use-module (ice-9 ftw) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-26) + #:export (%standard-phases + qt-build)) + +;; Commentary: +;; +;; Builder-side code of the standard Qt build procedure. +;; +;; Code: + +(define (variables-for-wrapping base-directories) + + (define (collect-sub-dirs base-directories subdirectory) + (filter-map + (lambda (dir) + (let ((directory (string-append dir subdirectory))) + (if (directory-exists? directory) directory #f))) + base-directories)) + + (filter + (lambda (var-to-wrap) (not (null? (last var-to-wrap)))) + (map + (lambda (var-spec) + `(,(first var-spec) = ,(collect-sub-dirs base-directories (last var-spec)))) + (list + ;; these shall match the search-path-specification for Qt and KDE + ;; libraries + '("XDG_DATA_DIRS" "/share") + '("XDG_CONFIG_DIRS" "/etc/xdg") + '("QT_PLUGIN_PATH" "/lib/qt5/plugins") + '("QML2_IMPORT_PATH" "/lib/qt5/qml"))))) + +(define* (wrap-all-programs #:key inputs outputs + (qt-wrap-excluded-outputs '()) + #:allow-other-keys) + "Implement phase \"qt-wrap\": look for GSettings schemas and +gtk+-v.0 libraries and create wrappers with suitably set environment variables +if found. + +Wrapping is not applied to outputs whose name is listed in +QT-WRAP-EXCLUDED-OUTPUTS. This is useful when an output is known not +to contain any Qt binaries, and where wrapping would gratuitously +add a dependency of that output on Qt." + (define (find-files-to-wrap directory) + (append-map + (lambda (dir) + (if (directory-exists? dir) (find-files dir ".*") (list))) + (list (string-append directory "/bin") + (string-append directory "/sbin") + (string-append directory "/libexec") + (string-append directory "/lib/libexec")))) + + (define input-directories + ;; FIXME: Filter out unwanted inputs, e.g. cmake + (match inputs + (((_ . dir) ...) + dir))) + + (define handle-output + (match-lambda + ((output . directory) + (unless (member output qt-wrap-excluded-outputs) + (let ((bin-list (find-files-to-wrap directory)) + (vars-to-wrap (variables-for-wrapping + (append (list output) + input-directories)))) + (when (not (null? vars-to-wrap)) + (for-each (cut apply wrap-program <> vars-to-wrap) + bin-list))))))) + + (for-each handle-output outputs) + #t) + +(define %standard-phases + (modify-phases cmake:%standard-phases + (add-after 'install 'qt-wrap wrap-all-programs))) + +(define* (qt-build #:key inputs (phases %standard-phases) + #:allow-other-keys #:rest args) + "Build the given package, applying all of PHASES in order." + (apply cmake:cmake-build #:inputs inputs #:phases phases args)) diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm index a5a9c92a42..ce7999b433 100644 --- a/guix/build/syscalls.scm +++ b/guix/build/syscalls.scm @@ -1127,7 +1127,9 @@ exception if it's already taken." (lambda (key . args) (match key ('flock-error - (handler args)) + (apply handler args) + ;; No open port to the lock, so return #f. + #f) ('system-error ;; When using the statically-linked Guile in the initrd, ;; 'fcntl-flock' returns ENOSYS unconditionally. Ignore diff --git a/guix/gexp.scm b/guix/gexp.scm index b640c079e4..a96592ac76 100644 --- a/guix/gexp.scm +++ b/guix/gexp.scm @@ -320,9 +320,16 @@ It is implemented as a macro to capture the current source directory where it appears." (syntax-case s () ((_ file rest ...) + (string? (syntax->datum #'file)) + ;; FILE is a literal, so resolve it relative to the source directory. #'(%local-file file (delay (absolute-file-name file (current-source-directory))) rest ...)) + ((_ file rest ...) + ;; Resolve FILE relative to the current directory. + #'(%local-file file + (delay (absolute-file-name file (getcwd))) + rest ...)) ((_) #'(syntax-error "missing file name")) (id diff --git a/guix/import/opam.scm b/guix/import/opam.scm index 7f089a5cf3..e258c4197f 100644 --- a/guix/import/opam.scm +++ b/guix/import/opam.scm @@ -49,7 +49,7 @@ (define-peg-pattern COLON none ":") ;; A string character is any character that is not a quote, or a quote preceded by a backslash. (define-peg-pattern STRCHR body - (or " " "!" (and (ignore "\\") "\"") + (or " " "!" "\n" (and (ignore "\\") "\"") (and (ignore "\\") "\\") (range #\# #\頋))) (define-peg-pattern operator all (or "=" "!" "<" ">")) @@ -249,10 +249,7 @@ path to the repository." (url-dict (metadata-ref opam-content "url")) (source-url (metadata-ref url-dict "src")) (requirements (metadata-ref opam-content "depends")) - (dependencies (filter - (lambda (name) - (not (member name '("dune" "jbuilder")))) - (dependency-list->names requirements))) + (dependencies (dependency-list->names requirements)) (native-dependencies (depends->native-inputs requirements)) (inputs (dependency-list->inputs (depends->inputs requirements))) (native-inputs (dependency-list->inputs @@ -264,8 +261,8 @@ path to the repository." native-dependencies)))) ;; If one of these are required at build time, it means we ;; can use the much nicer dune-build-system. - (let ((use-dune? (or (member "dune" native-dependencies) - (member "jbuilder" native-dependencies)))) + (let ((use-dune? (or (member "dune" (append dependencies native-dependencies)) + (member "jbuilder" (append dependencies native-dependencies))))) (call-with-temporary-output-file (lambda (temp port) (and (url-fetch source-url temp) @@ -297,7 +294,10 @@ path to the repository." (synopsis ,(metadata-ref opam-content "synopsis")) (description ,(metadata-ref opam-content "description")) (license #f)) - dependencies))))))) + (filter + (lambda (name) + (not (member name '("dune" "jbuilder")))) + dependencies)))))))) (define (opam-recursive-import package-name) (recursive-import package-name #f diff --git a/guix/lint.scm b/guix/lint.scm index 629604e0e9..cd2ea571ed 100644 --- a/guix/lint.scm +++ b/guix/lint.scm @@ -1122,7 +1122,10 @@ Heritage") ((key . args) (if (eq? key skip-key) '() - (apply throw key args))))))) + (with-networking-fail-safe + (G_ "while connecting to Software Heritage") + '() + (apply throw key args)))))))) ;;; diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm index 18473684eb..e81b6c25f2 100644 --- a/guix/scripts/offload.scm +++ b/guix/scripts/offload.scm @@ -149,19 +149,6 @@ ignoring it~%") (leave (G_ "failed to load machine file '~a': ~s~%") file args)))))) -(define (host-key->type+key host-key) - "Destructure HOST-KEY, an OpenSSH host key string, and return two values: -its key type as a symbol, and the actual base64-encoded string." - (define (type->symbol type) - (and (string-prefix? "ssh-" type) - (string->symbol (string-drop type 4)))) - - (match (string-tokenize host-key) - ((type key x) - (values (type->symbol type) key)) - ((type key) - (values (type->symbol type) key)))) - (define (private-key-from-file* file) "Like 'private-key-from-file', but raise an error that 'with-error-handling' can interpret meaningfully." @@ -203,21 +190,8 @@ private key from '~a': ~a") (build-machine-compression-level machine)))) (match (connect! session) ('ok - ;; Authenticate the server. XXX: Guile-SSH 0.10.1 doesn't know about - ;; ed25519 keys and 'get-key-type' returns #f in that case. - (let-values (((server) (get-server-public-key session)) - ((type key) (host-key->type+key - (build-machine-host-key machine)))) - (unless (and (or (not (get-key-type server)) - (eq? (get-key-type server) type)) - (string=? (public-key->string server) key)) - ;; Key mismatch: something's wrong. XXX: It could be that the server - ;; provided its Ed25519 key when we where expecting its RSA key. - (leave (G_ "server at '~a' returned host key '~a' of type '~a' \ -instead of '~a' of type '~a'~%") - (build-machine-name machine) - (public-key->string server) (get-key-type server) - key type))) + ;; Make sure the server's key is what we expect. + (authenticate-server* session (build-machine-host-key machine)) (let ((auth (userauth-public-key! session private))) (unless (eq? 'success auth) diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm index 97436feee7..92c6e34194 100644 --- a/guix/scripts/package.scm +++ b/guix/scripts/package.scm @@ -866,11 +866,7 @@ processed, #f otherwise." ;; First, acquire a lock on the profile, to ensure only one guix process ;; is modifying it at a time. - (with-file-lock/no-wait (string-append profile ".lock") - (lambda (key . args) - (leave (G_ "profile ~a is locked by another process~%") - profile)) - + (with-profile-lock profile ;; Then, process roll-backs, generation removals, etc. (for-each (match-lambda ((key . arg) diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm index a74776bd7b..19410ad141 100644 --- a/guix/scripts/pull.scm +++ b/guix/scripts/pull.scm @@ -54,6 +54,7 @@ #:use-module (srfi srfi-34) #:use-module (srfi srfi-35) #:use-module (srfi srfi-37) + #:use-module (web uri) #:use-module (ice-9 match) #:use-module (ice-9 vlist) #:use-module (ice-9 format) @@ -184,6 +185,42 @@ Download and deploy the latest version of Guix.\n")) %standard-build-options)) +(define %vcs-web-views + ;; Hard-coded list of host names and corresponding web view URL templates. + ;; TODO: Allow '.guix-channel' files to specify a URL template. + (let ((labhub-url (lambda (repository-url commit) + (string-append + (if (string-suffix? ".git" repository-url) + (string-drop-right repository-url 4) + repository-url) + "/commit/" commit)))) + `(("git.savannah.gnu.org" + ,(lambda (repository-url commit) + (string-append (string-replace-substring repository-url + "/git/" "/cgit/") + "/commit/?id=" commit))) + ("notabug.org" ,labhub-url) + ("framagit.org" ,labhub-url) + ("gitlab.com" ,labhub-url) + ("gitlab.inria.fr" ,labhub-url) + ("github.com" ,labhub-url)))) + +(define* (channel-commit-hyperlink channel + #:optional + (commit (channel-commit channel))) + "Return a hyperlink for COMMIT in CHANNEL, using COMMIT as the hyperlink's +text. The hyperlink links to a web view of COMMIT, when available." + (let* ((url (channel-url channel)) + (uri (string->uri url)) + (host (and uri (uri-host uri)))) + (if host + (match (assoc host %vcs-web-views) + (#f + commit) + ((_ template) + (hyperlink (template url commit) commit))) + commit))) + (define* (display-profile-news profile #:key concise? current-is-newer?) "Display what's up in PROFILE--new packages, and all that. If @@ -247,15 +284,20 @@ purposes." ;; When Texinfo markup is invalid, display it as-is. (const title))))))) -(define (display-news-entry entry language port) - "Display ENTRY, a <channel-news-entry>, in LANGUAGE, a language code, to -PORT." +(define (display-news-entry entry channel language port) + "Display ENTRY, a <channel-news-entry> from CHANNEL, in LANGUAGE, a language +code, to PORT." (define body (channel-news-entry-body entry)) + (define commit + (channel-news-entry-commit entry)) + (display-news-entry-title entry language port) (format port (dim (G_ " commit ~a~%")) - (channel-news-entry-commit entry)) + (if (supports-hyperlinks?) + (channel-commit-hyperlink channel commit) + commit)) (newline port) (let ((body (or (assoc-ref body language) (assoc-ref body (%default-message-language)) @@ -293,7 +335,7 @@ to display." (channel-name channel)) (for-each (if concise? (cut display-news-entry-title <> language port) - (cut display-news-entry <> language port)) + (cut display-news-entry <> channel language port)) entries) (newline port) #t)))))) @@ -528,10 +570,17 @@ way and displaying details about the channel's source code." ('branch branch) ('commit commit) _ ...)) - (format #t (G_ " repository URL: ~a~%") url) - (when branch - (format #t (G_ " branch: ~a~%") branch)) - (format #t (G_ " commit: ~a~%") commit)) + (let ((channel (channel (name 'nameless) + (url url) + (branch branch) + (commit commit)))) + (format #t (G_ " repository URL: ~a~%") url) + (when branch + (format #t (G_ " branch: ~a~%") branch)) + (format #t (G_ " commit: ~a~%") + (if (supports-hyperlinks?) + (channel-commit-hyperlink channel commit) + commit)))) (_ #f))) ;; Show most recently installed packages last. @@ -817,11 +866,7 @@ Use '~/.config/guix/channels.scm' instead.")) (if (assoc-ref opts 'bootstrap?) %bootstrap-guile (canonical-package guile-2.2))))) - (with-file-lock/no-wait (string-append profile ".lock") - (lambda (key . args) - (leave (G_ "profile ~a is locked by another process~%") - profile)) - + (with-profile-lock profile (run-with-store store (build-and-install instances profile #:dry-run? diff --git a/guix/scripts/substitute.scm b/guix/scripts/substitute.scm index 421561a4ea..b6034a75d2 100755 --- a/guix/scripts/substitute.scm +++ b/guix/scripts/substitute.scm @@ -322,22 +322,6 @@ must contain the original contents of a narinfo file." (and=> signature narinfo-signature->canonical-sexp)) str))) -(define* (assert-valid-signature narinfo signature hash - #:optional (acl (current-acl))) - "Bail out if SIGNATURE, a canonical sexp representing the signature of -NARINFO, doesn't match HASH, a bytevector containing the hash of NARINFO." - (let ((uri (uri->string (first (narinfo-uris narinfo))))) - (signature-case (signature hash acl) - (valid-signature #t) - (invalid-signature - (leave (G_ "invalid signature for '~a'~%") uri)) - (hash-mismatch - (leave (G_ "hash mismatch for '~a'~%") uri)) - (unauthorized-key - (leave (G_ "'~a' is signed with an unauthorized key~%") uri)) - (corrupt-signature - (leave (G_ "signature on '~a' is corrupt~%") uri))))) - (define* (read-narinfo port #:optional url #:key size) "Read a narinfo from PORT. If URL is true, it must be a string used to diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index e49c9d36b9..5f0dce2093 100644 --- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -2,7 +2,7 @@ ;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2016 Alex Kost <alezost@gmail.com> ;;; Copyright © 2016, 2017, 2018 Chris Marusich <cmmarusich@gmail.com> -;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com> +;;; Copyright © 2017, 2019 Mathieu Othacehe <m.othacehe@gmail.com> ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net> ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net> ;;; @@ -932,6 +932,8 @@ Some ACTIONS support additional ARGS.\n")) (display (G_ " --skip-checks skip file system and initrd module safety checks")) (display (G_ " + --target=TRIPLET cross-build for TRIPLET--e.g., \"armel-linux-gnu\"")) + (display (G_ " -v, --verbosity=LEVEL use the given verbosity LEVEL")) (newline) (display (G_ " @@ -1004,6 +1006,10 @@ Some ACTIONS support additional ARGS.\n")) (lambda (opt name arg result) (alist-cons 'system arg (alist-delete 'system result eq?)))) + (option '("target") #t #f + (lambda (opt name arg result) + (alist-cons 'target arg + (alist-delete 'target result eq?)))) (option '(#\r "root") #t #f (lambda (opt name arg result) (alist-cons 'gc-root arg result))) @@ -1012,6 +1018,7 @@ Some ACTIONS support additional ARGS.\n")) (define %default-options ;; Alist of default option values. `((system . ,(%current-system)) + (target . #f) (substitutes? . #t) (offload? . #t) (print-build-trace? . #t) @@ -1045,6 +1052,7 @@ resulting from command-line parsing." ((x . _) x))) (expr (assoc-ref opts 'expression)) (system (assoc-ref opts 'system)) + (target (assoc-ref opts 'target)) (os (ensure-operating-system (or file expr) (cond @@ -1061,7 +1069,7 @@ resulting from command-line parsing." (dry? (assoc-ref opts 'dry-run?)) (bootloader? (assoc-ref opts 'install-bootloader?)) - (target (match args + (target-file (match args ((first second) second) (_ #f))) (bootloader-target @@ -1103,9 +1111,10 @@ resulting from command-line parsing." (_ #f)) opts) #:install-bootloader? bootloader? - #:target target + #:target target-file #:bootloader-target bootloader-target #:gc-root (assoc-ref opts 'gc-root))))) + #:target target #:system system)) (warn-about-disk-space))) diff --git a/guix/ssh.scm b/guix/ssh.scm index 5fd3c280e8..291ce20b61 100644 --- a/guix/ssh.scm +++ b/guix/ssh.scm @@ -37,6 +37,8 @@ #:use-module (ice-9 format) #:use-module (ice-9 binary-ports) #:export (open-ssh-session + authenticate-server* + remote-inferior remote-daemon-channel connect-to-remote-daemon @@ -60,15 +62,56 @@ (define %compression "zlib@openssh.com,zlib") +(define (host-key->type+key host-key) + "Destructure HOST-KEY, an OpenSSH host key string, and return two values: +its key type as a symbol, and the actual base64-encoded string." + (define (type->symbol type) + (and (string-prefix? "ssh-" type) + (string->symbol (string-drop type 4)))) + + (match (string-tokenize host-key) + ((type key x) + (values (type->symbol type) key)) + ((type key) + (values (type->symbol type) key)))) + +(define (authenticate-server* session key) + "Make sure the server for SESSION has the given KEY, where KEY is a string +such as \"ssh-ed25519 AAAAC3Nz… root@example.org\". Raise an exception if the +actual key does not match." + (let-values (((server) (get-server-public-key session)) + ((type key) (host-key->type+key key))) + (unless (and (or (not (get-key-type server)) + (eq? (get-key-type server) type)) + (string=? (public-key->string server) key)) + ;; Key mismatch: something's wrong. XXX: It could be that the server + ;; provided its Ed25519 key when we where expecting its RSA key. XXX: + ;; Guile-SSH 0.10.1 doesn't know about ed25519 keys and 'get-key-type' + ;; returns #f in that case. + (raise (condition + (&message + (message (format #f (G_ "server at '~a' returned host key \ +'~a' of type '~a' instead of '~a' of type '~a'~%") + (session-get session 'host) + (public-key->string server) + (get-key-type server) + key type)))))))) + (define* (open-ssh-session host #:key user port identity + host-key (compression %compression) (timeout 3600)) "Open an SSH session for HOST and return it. IDENTITY specifies the file name of a private key to use for authenticating with the host. When USER, PORT, or IDENTITY are #f, use default values or whatever '~/.ssh/config' -specifies; otherwise use them. Install TIMEOUT as the maximum time in seconds -after which a read or write operation on a channel of the returned session is -considered as failing. +specifies; otherwise use them. + +When HOST-KEY is true, it must be a string like \"ssh-ed25519 AAAAC3Nz… +root@example.org\"; the server is authenticated and an error is raised if its +host key is different from HOST-KEY. + +Install TIMEOUT as the maximum time in seconds after which a read or write +operation on a channel of the returned session is considered as failing. Throw an error on failure." (let ((session (make-session #:user user @@ -78,6 +121,11 @@ Throw an error on failure." #:timeout 10 ;seconds ;; #:log-verbosity 'protocol + ;; Prevent libssh from reading + ;; ~/.ssh/known_hosts when the caller provides + ;; a HOST-KEY to match against. + #:knownhosts (and host-key "/dev/null") + ;; We need lightweight compression when ;; exchanging full archives. #:compression compression @@ -88,6 +136,21 @@ Throw an error on failure." (match (connect! session) ('ok + (if host-key + ;; Make sure the server's key is what we expect. + (authenticate-server* session host-key) + + ;; Authenticate against ~/.ssh/known_hosts. + (match (authenticate-server session) + ('ok #f) + (reason + (raise (condition + (&message + (message (format #f (G_ "failed to authenticate \ +server at '~a': ~a") + (session-get session 'host) + reason)))))))) + ;; Use public key authentication, via the SSH agent if it's available. (match (userauth-public-key/auto! session) ('success diff --git a/guix/ui.scm b/guix/ui.scm index e31db33d3b..540671f3dd 100644 --- a/guix/ui.scm +++ b/guix/ui.scm @@ -47,8 +47,8 @@ #:use-module ((guix licenses) #:select (license? license-name license-uri)) #:use-module ((guix build syscalls) - #:select (free-disk-space terminal-columns - terminal-rows)) + #:select (free-disk-space terminal-columns terminal-rows + with-file-lock/no-wait)) #:use-module ((guix build utils) ;; XXX: All we need are the bindings related to ;; '&invoke-error'. However, to work around the bug described @@ -111,6 +111,7 @@ package-specification->name+version+output supports-hyperlinks? + hyperlink file-hyperlink location->hyperlink @@ -118,6 +119,7 @@ package-relevance display-search-results + with-profile-lock string->generations string->duration matching-generations @@ -1662,6 +1664,26 @@ DURATION-RELATION with the current time." (display-diff profile gen1 gen2)) +(define (profile-lock-handler profile errno . _) + "Handle failure to acquire PROFILE's lock." + ;; NFS mounts can return ENOLCK. When that happens, there's not much that + ;; can be done, so warn the user and keep going. + (if (= errno ENOLCK) + (warning (G_ "cannot lock profile ~a: ~a~%") + profile (strerror errno)) + (leave (G_ "profile ~a is locked by another process~%") + profile))) + +(define profile-lock-file + (cut string-append <> ".lock")) + +(define-syntax-rule (with-profile-lock profile exp ...) + "Grab PROFILE's lock and evaluate EXP... Call 'leave' if the lock is +already taken." + (with-file-lock/no-wait (profile-lock-file profile) + (cut profile-lock-handler profile <...>) + exp ...)) + (define (display-profile-content profile number) "Display the packages in PROFILE, generation NUMBER, in a human-readable way." |