aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014-2017, 2019-2021, 2023-2024 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2018 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2023 Janneke Nieuwenhuizen <jannek@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 (gnu system locale)
  #:use-module (guix gexp)
  #:use-module (guix store)
  #:use-module (guix modules)
  #:use-module (guix records)
  #:use-module (guix packages)
  #:use-module (guix utils)
  #:use-module (gnu packages base)
  #:use-module (gnu packages compression)
  #:use-module (srfi srfi-26)
  #:use-module (ice-9 match)
  #:export (locale-definition
            locale-definition?
            locale-definition-name
            locale-definition-source
            locale-definition-charset

            locale-name->definition
            locale-directory

            %default-locale-libcs
            %default-locale-definitions

            glibc-supported-locales))

;;; Commentary:
;;;
;;; Locale definitions, and compilation thereof.
;;;
;;; Code:

(define-record-type* <locale-definition> locale-definition
  make-locale-definition
  locale-definition?
  (name    locale-definition-name)                ;string--e.g., "fr_FR.utf8"
  (source  locale-definition-source)              ;string--e.g., "fr_FR"
  (charset locale-definition-charset              ;string--e.g., "UTF-8"
           (default "UTF-8")))

(define %not-dot
  (char-set-complement (char-set #\.)))

(define (denormalize-codeset codeset)
  "Attempt to guess the \"real\" name of CODESET, a normalized codeset as
defined in (info \"(libc) Using gettextized software\")."
  (cond ((string=? codeset "utf8")
         "UTF-8")
        ((string-prefix? "iso8859" codeset)
         (string-append "ISO-8859-" (string-drop codeset 7)))
        ((string=? codeset "eucjp")
         "EUC-JP")
        (else                                ;cross fingers, hope for the best
         codeset)))

(define (locale-name->definition name)
  "Return a <locale-definition> corresponding to NAME, guessing the charset,
or #f on failure."
  (match (string-tokenize name %not-dot)
    ((source charset)
     ;; XXX: NAME is supposed to use the "normalized codeset", such as "utf8",
     ;; whereas the actual name used is different.  Add a special case to make
     ;; the right guess for UTF-8.
     (locale-definition (name name)
                        (source source)
                        (charset (denormalize-codeset charset))))
    (_
     #f)))

(define* (single-locale-directory locales
                                  #:key (libc glibc))
  "Return a directory containing all of LOCALES for LIBC compiled.

Because locale data formats are incompatible when switching from one libc to
another, locale data is put in a sub-directory named after the 'version' field
of LIBC."
  (define version
    (version-major+minor (package-version libc)))

  (define build
    (with-imported-modules (source-module-closure
                            '((gnu build locale)))
     #~(begin
         (use-modules (gnu build locale))

         (mkdir #$output)
         (mkdir (string-append #$output "/" #$version))

         ;; 'localedef' executes 'gzip' to access compressed locale sources.
         (setenv "PATH"
                 (string-append #+gzip "/bin:" #+libc "/bin"))

         (setvbuf (current-output-port) 'line)
         (setvbuf (current-error-port) 'line)
         (for-each (lambda (locale codeset name)
                     (build-locale locale
                                   #:codeset codeset
                                   #:name name
                                   #:directory
                                   (string-append #$output "/" #$version)))
                   '#$(map locale-definition-source locales)
                   '#$(map locale-definition-charset locales)
                   '#$(map locale-definition-name locales)))))

  (computed-file (string-append "locale-" version) build))

(define* (locale-directory locales
                           #:key (libcs %default-locale-libcs))
  "Return a locale directory containing all of LOCALES for each libc package
listed in LIBCS.

It is useful to list more than one libc when willing to support
already-installed packages built against a different libc since the locale
data format changes between libc versions."
  (match libcs
    ((libc)
     (single-locale-directory locales #:libc libc))
    ((libcs ..1)
     (let ((dirs (map (lambda (libc)
                        (single-locale-directory locales #:libc libc))
                      libcs)))
       (computed-file "locale-multiple-versions"
                      (with-imported-modules '((guix build union))
                        #~(begin
                            (use-modules (guix build union))
                            (union-build #$output (list #$@dirs))))
                      #:options '(#:local-build? #t
                                  #:substitutable? #f))))))

(define %default-locale-libcs
  ;; The libcs for which we build locales by default.
  (list glibc glibc-2.35))

(define %default-locale-definitions
  ;; Arbitrary set of locales that are built by default.  They come as a
  ;; "bonus" in addition to that specified in the 'locale' field of the
  ;; operating system, for the user's convenience, so they shouldn't take too
  ;; much space.
  (letrec-syntax ((utf8-locale (syntax-rules ()
                                 ((_ name*)
                                  (locale-definition
                                   ;; Note: We choose "utf8", which is the
                                   ;; "normalized codeset".
                                   (name (string-append name* ".utf8"))
                                   (source name*)
                                   (charset "UTF-8")))))
                  (utf8-locales (syntax-rules ()
                                  ((_ name ...)
                                   (list (utf8-locale name) ...)))))
    ;; The six UN official languages plus Portuguese, with at most two
    ;; variants per language.
    (utf8-locales "ar_DZ"
                  "en_GB"
                  "en_US"
                  "es_AR"
                  "es_ES"
                  "fr_FR"
                  "pt_BR"
                  "pt_PT"
                  "ru_RU"
                  "zh_CN")))


;;;
;;; Locales supported by glibc.
;;;

(define* (glibc-supported-locales #:optional (glibc glibc))
  "Return a file-like object that contains a list of locale name/encoding
pairs such as (\"oc_FR.UTF-8\" . \"UTF-8\").  Each pair corresponds to a
locale supported by GLIBC."
  (define build
    (with-imported-modules (source-module-closure
                            '((guix build gnu-build-system)
                              (gnu build locale)))
      #~(begin
          (use-modules (guix build gnu-build-system)
                       (gnu build locale)
                       (ice-9 pretty-print))

          (define unpack
            (assq-ref %standard-phases 'unpack))


          (setenv "PATH"
                  (string-append #+(file-append tar "/bin") ":"
                                 #+(file-append zstd "/bin") ":"
                                 #+(file-append gzip "/bin")))
          (unpack #:source #+(package-source glibc))

          (let ((locales (call-with-input-file "localedata/SUPPORTED"
                           read-supported-locales)))
            (call-with-output-file #$output
              (lambda (port)
                (pretty-print locales port)))))))

  (computed-file "glibc-supported-locales.scm" build))

;;; locale.scm ends here
>mapped-devices: Add 'location' and 'check' fields....* gnu/system/mapped-devices.scm (<mapped-device>)[location]: New field. (<mapped-device-type>)[check]: New field. Ludovic Courtès 2017-09-11system: Introduce a disjoint UUID type....Conceptually a UUID is just a bytevector. However, there's software out there such as GRUB that relies on the string representation of different UUID types (e.g., the string representation of DCE UUIDs differs from that of ISO-9660 UUIDs, even if they are actually bytevectors of the same length). This new <uuid> record type allows us to preserve information about the type of UUID so we can eventually convert it to a string using the right representation. * gnu/system/uuid.scm (<uuid>): New record type. (bytevector->uuid): New procedure. (uuid): Return calls to 'make-uuid'. (uuid->string): Rewrite using 'match-lambda*' to accept a single 'uuid?' argument. * gnu/bootloader/grub.scm (grub-root-search): Check for 'uuid?' instead of 'bytevector?'. * gnu/system.scm (bootable-kernel-arguments): Check whether ROOT-DEVICE is 'uuid?'. (read-boot-parameters): Use 'bytevector->uuid' when the store device is a bytevector. (read-boot-parameters-file): Check for 'uuid?' instead of 'bytevector?'. (device->sexp): New procedure. (operating-system-boot-parameters-file): Use it for 'root-device' and 'store'. (operating-system-bootcfg): Remove conditional in definition of 'root-device'. * gnu/system/file-systems.scm (file-system->spec): Check for 'uuid?' on DEVICE and take its bytevector. * gnu/system/mapped-devices.scm (open-luks-device): Likewise. * gnu/system/vm.scm (iso9660-image): Call 'uuid-bytevector' for the #:volume-uuid argument. Ludovic Courtès 2017-06-07mapped-devices: Cope with delayed appearance of LUKS source....Fixes <https://bugs.gnu.org/27242>. * gnu/system/mapped-devices.scm (open-luks-device): If 'find-partition-by-luks-uuid' fails, try again once per second, up to ten times. Mark H Weaver 2017-01-24mapped-devices: 'source' can be a list of strings....Reported by myglc2 <myglc2@gmail.com>. * gnu/system/mapped-devices.scm (<mapped-device>)[source]: Update comment to note that this can be a list of strings. Ludovic Courtès 2016-11-23mapped-devices: Use 'cryptsetup-static' in 'luks-device-mapping'....* gnu/system/mapped-devices.scm (open-luks-device): Use CRYPTSETUP-STATIC instead of CRYPTSETUP. Use 'file-append'. (close-luks-device): Likewise. Ludovic Courtès 2016-10-27mapped-devices: Use 'mdadm-static' in 'raid-device-mapping'....* gnu/system/mapped-devices.scm (open-raid-device, close-raid-device): Use MDADM-STATIC instead of MDADM. Use 'file-append'. Ludovic Courtès 2016-10-04mapped-devices: Properly open RAID devices....This fixes a type error introduced in 7f8ad82bf23b032ad6bd85bb1daa87cc83de509c, given that SOURCES is a list. * gnu/system/mapped-devices.scm (open-raid-device): Add 'apply' invocation. Ludovic Courtès 2016-09-05system: Use 'source-module-closure' where needed....* gnu/system/vm.scm (%vm-module-closure): Remove. (expression->derivation-in-linux-vm): Use 'source-module-closure' instead of %VM-MODULE-CLOSURE. (qemu-image): Likewise. * gnu/system/linux-initrd.scm (expression->initrd): Likewise. (flat-linux-module-directory, base-initrd): Likewise. * gnu/system/mapped-devices.scm (open-luks-device): Likewise. Ludovic Courtès 2016-08-03mapped-devices: raid-device-mapping: Avoid non-top-level 'use-modules'....Fixes <http://bugs.gnu.org/24135>. Reported by myglc2 <myglc2@gmail.com>. * gnu/system/mapped-devices.scm (open-raid-device): Avoid non-top-level 'use-modules' form. Ludovic Courtès 2016-08-02mapped-devices: Bail out when RAID sources don't show up....* gnu/system/mapped-devices.scm (open-raid-device): Bail out after 20 loop iterations. Ludovic Courtès 2016-08-02mapped-devices: Make RAID device opening message clearer....* gnu/system/mapped-devices.scm (open-raid-device): Rename 'source' to 'sources'. Make 'waiting' message more informative. (close-raid-device): Rename 'source' to 'sources'. Ludovic Courtès 2016-08-02mapped-devices: Do not always use (gnu build file-systems)....Fixes <http://bugs.gnu.org/24129>. Reported by myglc2 <myglc2@gmail.com>. * gnu/system/mapped-devices.scm (device-mapping-service-type): Remove 'modules' field from 'shepherd-service' form. (open-luks-device): Add 'use-modules' form. Ludovic Courtès 2016-07-25system: Add mapped devices for RAID....* gnu/system/mapped-devices.scm (raid-device-mapping, open-raid-device, close-raid-device): New variables. * doc/guix.texi (Mapped Devices): Add documentation for RAID devices, reorganize documentation for LUKS devices. Co-authored-by: Ludovic Courtès <ludo@gnu.org> Andreas Enge 2016-07-12services: <shepherd-service> no longer has an 'imported-modules' field....* gnu/services/shepherd.scm (<shepherd-service>)[imported-modules]: Remove. (%default-imported-modules): Make private. (shepherd-service-file): Use 'with-imported-modules'. (shepherd-configuration-file): Remove 'modules' and the calls to 'imported-modules' and 'compiled-modules'. Use 'with-imported-modules' instead. * doc/guix.texi (Shepherd Services): Adjust accordingly. * gnu/services/base.scm (file-system-shepherd-service): Use 'with-imported-modules'. Remove 'imported-modules' field. * gnu/system/mapped-devices.scm (device-mapping-service-type): Remove 'imported-modules'. (open-luks-device): Use 'with-imported-modules'. * gnu/tests.scm (marionette-shepherd-service): Remove 'imported-modules' field and use 'with-imported-modules'. Ludovic Courtès 2016-04-18mapped-devices: LUKS partitions can be designated by their UUID....* gnu/system/mapped-devices.scm (device-mapping-service-type): Add 'modules' and 'imported-modules' fields to 'shepherd-service'. (open-luks-device): Use 'find-partition-by-luks-uuid' to lookup the partition when SOURCE is a bytevector. * gnu/system/linux-initrd.scm (base-initrd): Augment 'use-modules' form. * doc/guix.texi (Mapped Devices): Give example with a UUID. Ludovic Courtès 2016-04-18mapped-devices: 'mapped-device-service' takes a <mapped-device>....* gnu/system/mapped-devices.scm (device-mapping-service): Take a <mapped-device> instead of 3 parameters. (device-mapping-service-type): Adjust accordingly. * gnu/system.scm (device-mapping-services): Adjust accordingly. Ludovic Courtès 2016-04-18services: Move 'device-mapping-service' to (gnu system mapped-devices)....* gnu/services/base.scm (device-mapping-service-type) (device-mapping-service): Move to... * gnu/system/mapped-devices.scm (device-mapping-service-type): (device-mapping-service): ... here. New variables. Ludovic Courtès 2016-04-18system: Move 'luks-device-mapping' to (gnu system mapped-devices)....* gnu/system.scm (open-luks-device, close-luks-device) (luks-device-mapping): Move to... * gnu/system/mapped-devices.scm: ... here. New file. Ludovic Courtès 2016-04-18system: Add (gnu system mapped-devices)....* gnu/system/file-systems.scm (<mapped-device>, <mapped-device-type>): Move to... * gnu/system/mapped-devices.scm: ... here. New file. * gnu/system.scm, gnu/services/base.scm, gnu/system/linux-initrd.scm: Use it. * gnu-system.am (GNU_SYSTEM_MODULES): Add it. * gnu.scm (%public-modules): Add it. Ludovic Courtès