summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi56
-rw-r--r--gnu/system.scm6
-rw-r--r--gnu/system/locale.scm62
3 files changed, 118 insertions, 6 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index a23d8244ff..a164608b73 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -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
 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})
 Configuration of libc's name service switch (NSS)---a
 @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}.
 @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
 @subsection Services
 
diff --git a/gnu/system.scm b/gnu/system.scm
index 3d570c0d1f..8fed857b39 100644
--- a/gnu/system.scm
+++ b/gnu/system.scm
@@ -76,6 +76,7 @@
             operating-system-timezone
             operating-system-locale
             operating-system-locale-definitions
+            operating-system-locale-libcs
             operating-system-mapped-devices
             operating-system-file-systems
             operating-system-activation-script
@@ -144,6 +145,8 @@
             (default "en_US.utf8"))
   (locale-definitions operating-system-locale-definitions ; list of <locale-definition>
                       (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>
                        (default %default-nss))
 
@@ -643,7 +646,8 @@ listed in OS.  The C library expects to find it under
     (raise (condition
             (&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)
   "Return a label for the GRUB menu entry that boots KERNEL."
diff --git a/gnu/system/locale.scm b/gnu/system/locale.scm
index 010fb45272..e798827a01 100644
--- a/gnu/system/locale.scm
+++ b/gnu/system/locale.scm
@@ -18,11 +18,15 @@
 
 (define-module (gnu system locale)
   #:use-module (guix gexp)
+  #:use-module (guix store)
+  #:use-module (guix monads)
   #: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
@@ -31,6 +35,7 @@
 
             locale-directory
 
+            %default-locale-libcs
             %default-locale-definitions))
 
 ;;; Commentary:
@@ -50,6 +55,15 @@
 (define* (localedef-command locale
                             #:key (libc (canonical-package glibc)))
   "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
       (format #t "building locale '~a'...~%"
               #$(locale-definition-name locale))
@@ -58,20 +72,29 @@
                       "-i" #$(locale-definition-source locale)
                       "-f" #$(locale-definition-charset locale)
                       (string-append #$output "/"
-                                     #$(package-version libc) "/"
+                                     #$@(maybe-version-directory)
                                      #$(locale-definition-name locale))))))
 
-(define* (locale-directory locales
-                           #:key (libc (canonical-package glibc)))
+(define* (single-locale-directory locales
+                                  #:key (libc (canonical-package 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
+    (package-version libc))
+
   (define build
     #~(begin
         (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.
         (setenv "PATH" (string-append #$gzip "/bin"))
@@ -80,9 +103,38 @@ of LIBC."
          (and #$@(map (cut localedef-command <> #:libc libc)
                       locales)))))
 
-  (gexp->derivation "locale" build
+  (gexp->derivation (string-append "locale-" version) build
                     #: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
   ;; 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