system: Add 'locale-libcs' field.

* gnu/system/locale.scm (localedef-command)[maybe-version-directory]:
  New procedure.
  Use it.
  (locale-directory): Rename to...
  (single-locale-directory): ... this.  Check the version of LIBC to
  determine whether to create a "X.Y" sub-directory or to make it a
  symlink to ".".  Add the version number in the derivation name.
  (locale-directory): New procedure.
  (%default-locale-libcs): New variable.
* gnu/system.scm (<operating-system>)[locale-libcs]: New field.
  (operating-system-locale-directory): Pass it to 'locale-directory'.
* doc/guix.texi (operating-system Reference): Document 'locale-libcs'.
  (Locales)[Locale Data Compatibility Considerations]: New section.
This commit is contained in:
Ludovic Courtès 2015-10-30 23:05:52 +01:00
parent 137d957ed3
commit 34760ae703
3 changed files with 118 additions and 6 deletions

View File

@ -5562,6 +5562,11 @@ Library Reference Manual}). @xref{Locales}, for more information.
The list of locale definitions to be compiled and that may be used at The list of locale definitions to be compiled and that may be used at
run time. @xref{Locales}. run time. @xref{Locales}.
@item @code{locale-libcs} (default: @code{(list @var{glibc})})
The list of GNU@tie{}libc packages whose locale data and tools are used
to build the locale definitions. @xref{Locales}, for compatibility
considerations that justify this option.
@item @code{name-service-switch} (default: @var{%default-nss}) @item @code{name-service-switch} (default: @var{%default-nss})
Configuration of libc's name service switch (NSS)---a Configuration of libc's name service switch (NSS)---a
@code{<name-service-switch>} object. @xref{Name Service Switch}, for @code{<name-service-switch>} object. @xref{Name Service Switch}, for
@ -6020,6 +6025,57 @@ instance it has @code{uk_UA.utf8} but @emph{not}, say,
@code{uk_UA.UTF-8}. @code{uk_UA.UTF-8}.
@end defvr @end defvr
@subsubsection Locale Data Compatibility Considerations
@cindex incompatibility, of locale data
@code{operating-system} declarations provide a @code{locale-libcs} field
to specify the GNU@tie{}libc packages that are used to compile locale
declarations (@pxref{operating-system Reference}). ``Why would I
care?'', you may ask. Well, it turns out that the binary format of
locale data is occasionally incompatible from one libc version to
another.
@c See <https://sourceware.org/ml/libc-alpha/2015-09/msg00575.html>
@c and <https://lists.gnu.org/archive/html/guix-devel/2015-08/msg00737.html>.
For instance, a program linked against libc version 2.21 is unable to
read locale data produced with libc 2.22; worse, that program
@emph{aborts} instead of simply ignoring the incompatible locale
data@footnote{Versions 2.23 and later of GNU@tie{}libc will simply skip
the incompatible locale data, which is already an improvement.}.
Similarly, a program linked against libc 2.22 can read most, but not
all, the locale data from libc 2.21 (specifically, @code{LC_COLLATE}
data is incompatible); thus calls to @code{setlocale} may fail, but
programs will not abort.
The ``problem'' in GuixSD is that users have a lot of freedom: They can
choose whether and when to upgrade software in their profiles, and might
be using a libc version different from the one the system administrator
used to build the system-wide locale data.
Fortunately, unprivileged users can also install their own locale data
and define @var{GUIX_LOCPATH} accordingly (@pxref{locales-and-locpath,
@code{GUIX_LOCPATH} and locale packages}).
Still, it is best if the system-wide locale data at
@file{/run/current-system/locale} is built for all the libc versions
actually in use on the system, so that all the programs can access
it---this is especially crucial on a multi-user system. To do that, the
administrator can specify several libc packages in the
@code{locale-libcs} field of @code{operating-system}:
@example
(use-package-modules base)
(operating-system
;; @dots{}
(locale-libcs (list glibc-2.21 (canonical-package glibc))))
@end example
This example would lead to a system containing locale definitions for
both libc 2.21 and the current version of libc in
@file{/run/current-system/locale}.
@node Services @node Services
@subsection Services @subsection Services

View File

@ -76,6 +76,7 @@
operating-system-timezone operating-system-timezone
operating-system-locale operating-system-locale
operating-system-locale-definitions operating-system-locale-definitions
operating-system-locale-libcs
operating-system-mapped-devices operating-system-mapped-devices
operating-system-file-systems operating-system-file-systems
operating-system-activation-script operating-system-activation-script
@ -144,6 +145,8 @@
(default "en_US.utf8")) (default "en_US.utf8"))
(locale-definitions operating-system-locale-definitions ; list of <locale-definition> (locale-definitions operating-system-locale-definitions ; list of <locale-definition>
(default %default-locale-definitions)) (default %default-locale-definitions))
(locale-libcs operating-system-locale-libcs ; list of <packages>
(default %default-locale-libcs))
(name-service-switch operating-system-name-service-switch ; <name-service-switch> (name-service-switch operating-system-name-service-switch ; <name-service-switch>
(default %default-nss)) (default %default-nss))
@ -643,7 +646,8 @@ listed in OS. The C library expects to find it under
(raise (condition (raise (condition
(&message (message "system locale lacks a definition"))))) (&message (message "system locale lacks a definition")))))
(locale-directory (operating-system-locale-definitions os))) (locale-directory (operating-system-locale-definitions os)
#:libcs (operating-system-locale-libcs os)))
(define (kernel->grub-label kernel) (define (kernel->grub-label kernel)
"Return a label for the GRUB menu entry that boots KERNEL." "Return a label for the GRUB menu entry that boots KERNEL."

View File

@ -18,11 +18,15 @@
(define-module (gnu system locale) (define-module (gnu system locale)
#:use-module (guix gexp) #:use-module (guix gexp)
#:use-module (guix store)
#:use-module (guix monads)
#:use-module (guix records) #:use-module (guix records)
#:use-module (guix packages) #:use-module (guix packages)
#:use-module (guix utils)
#:use-module (gnu packages base) #:use-module (gnu packages base)
#:use-module (gnu packages compression) #:use-module (gnu packages compression)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (ice-9 match)
#:export (locale-definition #:export (locale-definition
locale-definition? locale-definition?
locale-definition-name locale-definition-name
@ -31,6 +35,7 @@
locale-directory locale-directory
%default-locale-libcs
%default-locale-definitions)) %default-locale-definitions))
;;; Commentary: ;;; Commentary:
@ -50,6 +55,15 @@
(define* (localedef-command locale (define* (localedef-command locale
#:key (libc (canonical-package glibc))) #:key (libc (canonical-package glibc)))
"Return a gexp that runs 'localedef' from LIBC to build LOCALE." "Return a gexp that runs 'localedef' from LIBC to build LOCALE."
(define (maybe-version-directory)
;; XXX: For libc prior to 2.22, GuixSD did not store locale data in a
;; version-specific sub-directory. Check whether this is the case.
;; TODO: Remove this hack once libc 2.21 is buried.
(let ((version (package-version libc)))
(if (version>=? version "2.22")
(list version "/")
'())))
#~(begin #~(begin
(format #t "building locale '~a'...~%" (format #t "building locale '~a'...~%"
#$(locale-definition-name locale)) #$(locale-definition-name locale))
@ -58,20 +72,29 @@
"-i" #$(locale-definition-source locale) "-i" #$(locale-definition-source locale)
"-f" #$(locale-definition-charset locale) "-f" #$(locale-definition-charset locale)
(string-append #$output "/" (string-append #$output "/"
#$(package-version libc) "/" #$@(maybe-version-directory)
#$(locale-definition-name locale)))))) #$(locale-definition-name locale))))))
(define* (locale-directory locales (define* (single-locale-directory locales
#:key (libc (canonical-package glibc))) #:key (libc (canonical-package glibc)))
"Return a directory containing all of LOCALES for LIBC compiled. "Return a directory containing all of LOCALES for LIBC compiled.
Because locale data formats are incompatible when switching from one libc to 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 another, locale data is put in a sub-directory named after the 'version' field
of LIBC." of LIBC."
(define version
(package-version libc))
(define build (define build
#~(begin #~(begin
(mkdir #$output) (mkdir #$output)
(mkdir (string-append #$output "/" #$(package-version libc)))
;; XXX: For libcs < 2.22, locale data is stored in the top-level
;; directory.
;; TODO: Remove this hack once libc 2.21 is buried.
#$(if (version>=? version "2.22")
#~(mkdir (string-append #$output "/" #$version))
#~(symlink "." (string-append #$output "/" #$version)))
;; 'localedef' executes 'gzip' to access compressed locale sources. ;; 'localedef' executes 'gzip' to access compressed locale sources.
(setenv "PATH" (string-append #$gzip "/bin")) (setenv "PATH" (string-append #$gzip "/bin"))
@ -80,9 +103,38 @@ of LIBC."
(and #$@(map (cut localedef-command <> #:libc libc) (and #$@(map (cut localedef-command <> #:libc libc)
locales))))) locales)))))
(gexp->derivation "locale" build (gexp->derivation (string-append "locale-" version) build
#:local-build? #t)) #:local-build? #t))
(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)
(mlet %store-monad ((dirs (mapm %store-monad
(lambda (libc)
(single-locale-directory locales
#:libc libc))
libcs)))
(gexp->derivation "locale-multiple-versions"
#~(begin
(use-modules (guix build union))
(union-build #$output (list #$@dirs)))
#:modules '((guix build union))
#:local-build? #t
#:substitutable? #f)))))
(define %default-locale-libcs
;; The libcs for which we build locales by default.
(list (canonical-package glibc)))
(define %default-locale-definitions (define %default-locale-definitions
;; Arbitrary set of locales that are built by default. They are here mostly ;; Arbitrary set of locales that are built by default. They are here mostly
;; to facilitate first-time use to some people, while others may have to add ;; to facilitate first-time use to some people, while others may have to add