maav / guix-mirror (public) (License: GPLv3+) (since 2019-11-02) (hash sha1)
Mirror of GNU Guix (https://git.savannah.gnu.org/git/guix.git) with personal branches integrated into master branch.

/guix/scripts.scm (9792aaebe91763551b3aaaf2c3d54eae04a0b294) (12588 bytes) (mode 100644) (type blob)

;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013, 2014, 2015, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2014 Deck Pickard <deck.r.pickard@gmail.com>
;;; Copyright © 2015, 2016 Alex Kost <alezost@gmail.com>
;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; 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 scripts)
  #:use-module (guix grafts)
  #:use-module (guix utils)
  #:use-module (guix ui)
  #:use-module (guix store)
  #:use-module (guix monads)
  #:use-module (guix packages)
  #:use-module (guix derivations)
  #:use-module ((guix profiles) #:select (%profile-directory))
  #:autoload   (guix describe) (current-profile-date)
  #:use-module (guix build syscalls)
  #:use-module (srfi srfi-1)
  #:use-module (srfi srfi-19)
  #:use-module (srfi srfi-37)
  #:use-module (ice-9 match)
  #:export (synopsis
            category
            define-command
            %command-categories

            args-fold*
            parse-command-line
            maybe-build
            build-package
            build-package-source
            %distro-age-warning
            warn-about-old-distro
            %disk-space-warning
            warn-about-disk-space))

;;; Commentary:
;;;
;;; General code for Guix scripts.
;;;
;;; Code:

;; Syntactic keywords.
(define synopsis 'command-synopsis)
(define category 'command-category)

(define-syntax define-command-categories
  (syntax-rules (G_)
    "Define command categories."
    ((_ name assert-valid (identifiers (G_ synopses)) ...)
     (begin
       (define-public identifiers
         ;; Define and export syntactic keywords.
         (list 'syntactic-keyword-for-command-category))
       ...

       (define-syntax assert-valid
         ;; Validate at expansion time that we're passed a valid category.
         (syntax-rules (identifiers ...)
           ((_ identifiers) #t)
           ...))

       (define name
         ;; Alist mapping category name to synopsis.
         `((identifiers . synopses) ...))))))

;; Command categories.
(define-command-categories %command-categories
  assert-valid-command-category
  (main        (G_ "main commands"))
  (development (G_ "software development commands"))
  (packaging   (G_ "packaging commands"))
  (plumbing    (G_ "plumbing commands"))
  (internal    (G_ "internal commands")))

(define-syntax define-command
  (syntax-rules (category synopsis)
    "Define the given command as a procedure along with its synopsis and,
optionally, its category.  The synopsis becomes the docstring of the
procedure, but both the category and synopsis are meant to be read (parsed) by
'guix help'."
    ;; The (synopsis ...) form is here so that xgettext sees those strings as
    ;; translatable.
    ((_ (name . args)
        (synopsis doc) body ...)
     (define (name . args)
       doc
       body ...))
    ((_ (name . args)
        (category cat) (synopsis doc)
        body ...)
     (begin
       (assert-valid-command-category cat)
       (define (name . args)
         doc
         body ...)))))

(define (args-fold* args options unrecognized-option-proc operand-proc . seeds)
  "A wrapper on top of `args-fold' that does proper user-facing error
reporting."
  (catch 'misc-error
    (lambda ()
      (apply args-fold args options unrecognized-option-proc
             operand-proc seeds))
    (lambda (key proc msg args . rest)
      ;; XXX: MSG is not i18n'd.
      (leave (G_ "invalid argument: ~a~%")
             (apply format #f msg args)))))

(define (environment-build-options)
  "Return additional build options passed as environment variables."
  (arguments-from-environment-variable "GUIX_BUILD_OPTIONS"))

(define %default-argument-handler
  ;; The default handler for non-option command-line arguments.
  (lambda (arg result)
    (alist-cons 'argument arg result)))

(define* (parse-command-line args options seeds
                             #:key
                             (build-options? #t)
                             (argument-handler %default-argument-handler))
  "Parse the command-line arguments ARGS according to OPTIONS (a list of
SRFI-37 options) and return the result, seeded by SEEDS.  When BUILD-OPTIONS?
is true, also pass arguments passed via the 'GUIX_BUILD_OPTIONS' environment
variable.  Command-line options take precedence those passed via
'GUIX_BUILD_OPTIONS'.

ARGUMENT-HANDLER is called for non-option arguments, like the 'operand-proc'
parameter of 'args-fold'."
  (define (parse-options-from args seeds)
    ;; Actual parsing takes place here.
    (apply args-fold* args options
           (lambda (opt name arg . rest)
             (leave (G_ "~A: unrecognized option~%") name))
           argument-handler
           seeds))

  (call-with-values
      (lambda ()
        (if build-options?
            (parse-options-from (environment-build-options) seeds)
            (apply values seeds)))
    (lambda seeds
      ;; ARGS take precedence over what the environment variable specifies.
      (parse-options-from args seeds))))

(define* (maybe-build drvs
                      #:key dry-run? use-substitutes?)
  "Show what will/would be built, and actually build DRVS, unless DRY-RUN? is
true."
  (with-monad %store-monad
    (>>= (show-what-to-build* drvs
                              #:dry-run? dry-run?
                              #:use-substitutes? use-substitutes?)
         (lambda (_)
           (if dry-run?
               (return #f)
               (built-derivations drvs))))))

(define* (build-package package
                        #:key dry-run? (use-substitutes? #t)
                        #:allow-other-keys
                        #:rest build-options)
  "Build PACKAGE using BUILD-OPTIONS acceptable by 'set-build-options'.
Show what and how will/would be built."
  (mlet %store-monad ((grafting? ((lift0 %graft? %store-monad))))
    (apply set-build-options*
           #:use-substitutes? use-substitutes?
           (strip-keyword-arguments '(#:dry-run?) build-options))
    (mlet %store-monad ((derivation (package->derivation
                                     package #:graft? (and (not dry-run?)
                                                           grafting?))))
      (mbegin %store-monad
        (maybe-build (list derivation)
                     #:use-substitutes? use-substitutes?
                     #:dry-run? dry-run?)
        (return (show-derivation-outputs derivation))))))

(define* (build-package-source package
                               #:key dry-run? (use-substitutes? #t)
                               #:allow-other-keys
                               #:rest build-options)
  "Build PACKAGE source using BUILD-OPTIONS."
  (mbegin %store-monad
    (apply set-build-options*
           #:use-substitutes? use-substitutes?
           (strip-keyword-arguments '(#:dry-run?) build-options))
    (mlet %store-monad ((derivation (origin->derivation
                                     (package-source package))))
      (mbegin %store-monad
        (maybe-build (list derivation)
                     #:use-substitutes? use-substitutes?
                     #:dry-run? dry-run?)
        (return (show-derivation-outputs derivation))))))

(define %distro-age-warning
  ;; The age (in seconds) above which we warn that the distro is too old.
  (make-parameter (match (and=> (getenv "GUIX_DISTRO_AGE_WARNING")
                                string->duration)
                    (#f  (* 7 24 3600))
                    (age (time-second age)))))

(define* (warn-about-old-distro #:optional (old (%distro-age-warning))
                                #:key (suggested-command
                                       "guix package -u"))
  "Emit a warning if Guix is older than OLD seconds."
  (define (seconds->days seconds)
    (round (/ seconds (* 3600 24))))

  (define age
    (match (current-profile-date)
      (#f    #f)
      (date  (- (time-second (current-time time-utc))
                date))))

  (when (and age (>= age old))
    (warning (N_ "Your Guix installation is ~a day old.\n"
                 "Your Guix installation is ~a days old.\n"
                 (seconds->days age))
             (seconds->days age)))
  (when (and (or (not age) (>= age old))
             (not (getenv "GUIX_UNINSTALLED")))
    (warning (G_ "Consider running 'guix pull' followed by
'~a' to get up-to-date packages and security updates.\n")
             suggested-command)
    (newline (guix-warning-port))))

(define %disk-space-warning
  ;; Return a pair of absolute threshold (number of bytes) and relative
  ;; threshold (fraction between 0 and 1) for the free disk space below which
  ;; a warning is emitted.
  ;; GUIX_DISK_SPACE_WARNING can contain both thresholds.  A value in [0;100)
  ;; is a relative threshold, otherwise it's absolute.  The following
  ;; example values are valid:
  ;; - 1GiB;10%      ;1 GiB absolute, and 10% relative.
  ;; - 15G           ;15 GiB absolute, and default relative.
  ;; - 99%           ;99% relative, and default absolute.
  ;; - 99            ;Same.
  ;; - 100           ;100 absolute, and default relative.
  (let* ((default-absolute-threshold (size->number "5GiB"))
         (default-relative-threshold 0.05)
         (percentage->float (lambda (percentage)
                              (or (and=> (string->number
                                          (car (string-split percentage #\%)))
                                         (lambda (n) (/ n 100.0)))
                                  default-relative-threshold)))
         (size->number* (lambda (size)
                          (or (false-if-exception (size->number size))
                              default-absolute-threshold)))
         (absolute? (lambda (size)
                      (not (or (string-suffix? "%" size)
                               (false-if-exception (< (size->number size) 100)))))))
    (make-parameter
     (match (getenv "GUIX_DISK_SPACE_WARNING")
       (#f (list default-absolute-threshold
                 default-relative-threshold))
       (env-string (match (string-split env-string #\;)
                     ((threshold)
                      (if (absolute? threshold)
                          (list (size->number* threshold)
                                default-relative-threshold)
                          (list default-absolute-threshold
                                (percentage->float threshold))))
                     ((threshold1 threshold2)
                      (if (absolute? threshold1)
                          (list (size->number* threshold1)
                                (percentage->float threshold2))
                          (list (size->number* threshold2)
                                (percentage->float threshold1))))))))))

(define* (warn-about-disk-space #:optional profile
                                #:key
                                (thresholds (%disk-space-warning)))
  "Display a hint about 'guix gc' if less than THRESHOLD of /gnu/store is
available.
THRESHOLDS is a pair (ABSOLUTE-THRESHOLD . RELATIVE-THRESHOLD)."
  (define GiB (expt 2 30))

  (let* ((stats      (statfs (%store-prefix)))
         (block-size (file-system-block-size stats))
         (available  (* block-size (file-system-blocks-available stats)))
         (total      (* block-size (file-system-block-count stats)))
         (relative-threshold-in-bytes (* total (cadr thresholds)))
         (absolute-threshold-in-bytes (car thresholds)))
    (when (< available (min relative-threshold-in-bytes
                            absolute-threshold-in-bytes))
      (warning (G_ "only ~,1f GiB of free space available on ~a~%")
               (/ available 1. GiB) (%store-prefix))
      (display-hint (format #f (G_ "Consider deleting old profile
generations and collecting garbage, along these lines:

@example
guix gc --delete-generations=1m
@end example\n"))))))

;;; scripts.scm ends here


Mode Type Size Ref File
100644 blob 6139 7f310d2612983845551d0a641b730a269a180a3f .dir-locals.el
100644 blob 2533 e2f745b42a5a1591a93a3e9a7ccfc107a6178eff .gitignore
100644 blob 6179 ee164083c8379d90c6e7ab3b38576266f458f40c .guix-authorizations
100644 blob 182 b852180cf2563ec7b74b93c954de38d77237f23f .guix-channel
100644 blob 4499 146e65184a2f987b0307d0ec7300c93e84110625 .mailmap
100644 blob 472 1e30a74a64f51ec735dcc44ff4dfe5fa4fa13c6f AUTHORS
100644 blob 3273 ef90330cdacb9ecf7dbf38a03cdb490db131a4ad CODE-OF-CONDUCT
100644 blob 35147 94a9ed024d3859793618152ea559a168bbcbb5e2 COPYING
100644 blob 163 d6ea6943261fcae51c095ad39fe59140fc62de22 ChangeLog
100644 blob 749 aaa673fc93b0bb74feca4783ae427b9ea1b604ea HACKING
100644 blob 33109 a75d9c1ffc4237c0478b62a235b9b521fd840517 Makefile.am
100644 blob 359348 bb1de1e93802064ff44392db56d05cd0a11fcc15 NEWS
100644 blob 5260 5e9069f80f58d3946cdd588f30919a177eaccb55 README
100644 blob 3237 2475cb637ceb6eb43f54d080c56e5793041b76e5 ROADMAP
100644 blob 2381 af7afd3576f2e6aa5cbafc3c6354bbab1ae00774 THANKS
100644 blob 4360 f854f7fa98e09c7b512f3efb702c290b615186a0 TODO
100755 blob 906 a47269d87f1d6fd27bbaf634ac7439b38b32cca3 bootstrap
040000 tree - b15b9ede344760715e240528bc322c7b0194bbe7 build-aux
100644 blob 4808 50ead355a81edebf5c9419bd76a1dd69e85f5adf config-daemon.ac
100644 blob 8760 6861112eafaed85e107f8976f12e0ddb795571b7 configure.ac
100644 blob 339545 d234c4ec8668cead20279d903589d29c513b4cb6 d3.v3.js
040000 tree - ad0dbfb1b2956f33070d044c3cb173030dd3c5f1 doc
040000 tree - 8cc2bd0b87d4ef774820c72703a0b3ae914e8601 etc
100644 blob 5289 f139531ef3ecf56a790ae73934e2d91016c1aba4 gnu.scm
040000 tree - 34d1107337432fed7fa3f18c9affd932681e9b6a gnu
100644 blob 4207 ad8279395d8eb1fe5a836d54ec563a4577f4d135 graph.js
100644 blob 1357 8753c21e423f880e7a6d9f7f6f6ff1139f8b7254 guix.scm
040000 tree - 9c3a3e887916002138b68277e856e1f0e6416659 guix
040000 tree - 8df9aaabfb400159e2559fd4331fb861cb0a5adc m4
040000 tree - 84cedf0076fb8362febdffa59ba83b63e8c3f9e8 nix
040000 tree - 8dac6dd305591d733ef087c35eee3b3acb1daee2 po
040000 tree - 8c4db11917d51c4d71a841813cf8951000b76687 scripts
040000 tree - 867e6957c2eed1c47764e42093bb26ae37b221e3 tests
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/maav/guix-mirror

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/maav/guix-mirror

Clone this repository using git:
git clone git://git.rocketgit.com/user/maav/guix-mirror

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main