;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2012-2014, 2020-2023 Ludovic Courtès ;;; Copyright © 2014 David Thompson ;;; Copyright © 2018 Kyle Meyer ;;; Copyright © 2019, 2022 Ricardo Wurmus ;;; Copyright © 2021 Simon Tournier ;;; Copyright © 2021 Xinglu Chen ;;; Copyright © 2022 Philip McGrath ;;; Copyright © 2024 Herman Rimm ;;; ;;; 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 (guix scripts import) #:use-module (guix import utils) #:use-module (guix diagnostics) #:use-module (guix i18n) #:use-module (guix ui) #:use-module (guix scripts) #:use-module (guix read-print) #:use-module (guix utils) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (srfi srfi-37) #:use-module (ice-9 format) #:use-module (ice-9 match) #:export (%standard-import-options guix-import)) ;;; ;;; Command line options. ;;; (define %standard-import-options (list ;; Hidden option for importer-specific file preprocessing. (option '("file-to-insert") #f #t (lambda (opt name arg result) (if (file-exists? arg) (alist-cons 'file-to-insert arg result) (leave (G_ "file '~a' does not exist~%") arg)))))) ;;; ;;; Entry point. ;;; ;; The list of all known importers. These are printed in order by SHOW-HELP, so ;; please keep this list alphabetically sorted! (define importers '("composer" "cpan" "cran" "crate" "egg" "elm" "elpa" "gem" "gnu" "go" "hackage" "hexpm" "json" "luanti" "minetest" ; deprecated "npm-binary" "nuget" "opam" "pypi" "stackage" "texlive")) (define (resolve-importer name) (let ((module (resolve-interface `(guix scripts import ,(string->symbol name)))) (proc (string->symbol (string-append "guix-import-" name)))) (module-ref module proc))) (define (show-help) (display (G_ "Usage: guix import IMPORTER ARGS ... Run IMPORTER with ARGS.\n")) (newline) (display (G_ "IMPORTER must be one of the importers listed below:\n")) (newline) (format #t "~{ ~a~%~}" importers) (display (G_ " -h, --help display this help and exit")) (display (G_ " -i, --insert=FILE insert packages into FILE alphabetically")) (display (G_ " -V, --version display version information and exit")) (newline) (show-bug-report-information)) (define (import-as-definitions importer args proc) "Wrap package expressions from IMPORTER with 'define-public and invoke PROC callback." (if (member importer importers) (match (apply (resolve-importer importer) args) ((and expr (or ('package _ ...) ('let _ ...))) (proc (package->definition expr))) ((and expr (or ('define-public _ ...) ('define _ ...))) (proc expr)) ((expressions ...) (for-each (lambda (expr) (match expr ((and expr (or ('package _ ...) ('let _ ...))) (proc (package->definition expr))) ((and expr (or ('define-public _ ...) ('define _ ...))) (proc expr)))) expressions)) (x (leave (G_ "'~a' import failed~%") importer))) (let ((hint (string-closest importer importers #:threshold 3))) (report-error (G_ "~a: invalid importer~%") importer) (when hint (display-hint (G_ "Did you mean @code{~a}?~%") hint)) (exit 1)))) (define-command (guix-import . args) (category packaging) (synopsis "import a package definition from an external repository") (define (process-args args) (match args ;; Workaround to accpet ‘--insert=FILE’, for the consistency of ;; command-line interface. ((arg . rest) (if (string-prefix? "--insert=" arg) (append (string-split arg #\=) rest) args)) (_ args))) (match (process-args args) (() (format (current-error-port) (G_ "guix import: missing importer name~%"))) ((or ("-h" _ ...) ("--help" _ ...)) (leave-on-EPIPE (show-help)) (exit 0)) ((or ("-V" _ ...) ("--version" _ ...)) (show-version-and-exit "guix import")) ((or ("-i" file importer args ...) ("--insert" file importer args ...)) (let* ((define-prefixes `(,@(if (member importer '("crate")) '(define) '()) define-public)) (define-prefix? (cut member <> define-prefixes)) (find-and-insert (lambda (expr) (match expr (((? define-prefix? define-prefix) term _ ...) ;; Skip existing definition. (unless (find-definition-location file term #:define-prefix define-prefix) (let ((source-properties (find-definition-insertion-location file term #:define-prefix define-prefix))) (if source-properties (insert-expression source-properties expr) (let ((port (open-file file "a"))) (pretty-print-with-comments port expr) (newline port) (newline port) (close-port port)))))))))) (import-as-definitions importer (cons (string-append "--file-to-insert=" file) args) find-and-insert))) ((importer args ...) (let ((print (lambda (expr) (leave-on-EPIPE (pretty-print-with-comments (current-output-port) expr) ;; Two newlines: one after the closing paren, and ;; one to leave a blank line. (newline) (newline))))) (import-as-definitions importer args print)))))