12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316 |
- ;;; GNU Guix --- Functional package management for GNU
- ;;; Copyright © 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
- ;;; Copyright © 2020 Martin Becze <mjbecze@riseup.net>
- ;;;
- ;;; 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 self)
- #:use-module (guix config)
- #:use-module (guix i18n)
- #:use-module (guix modules)
- #:use-module (guix gexp)
- #:use-module (guix store)
- #:use-module (guix monads)
- #:use-module (guix discovery)
- #:use-module (guix packages)
- #:use-module (guix sets)
- #:use-module (guix modules)
- #:use-module ((guix utils) #:select (version-major+minor))
- #:use-module ((guix build utils) #:select (find-files))
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-35)
- #:use-module (ice-9 match)
- #:export (make-config.scm
- whole-package ;for internal use in 'guix pull'
- compiled-guix
- guix-derivation))
- ;;;
- ;;; Dependency handling.
- ;;;
- (define specification->package
- ;; Use our own variant of that procedure because that of (gnu packages)
- ;; would traverse all the .scm files, which is wasteful.
- (let ((ref (lambda (module variable)
- (module-ref (resolve-interface module) variable))))
- (match-lambda
- ("guile" (ref '(gnu packages guile) 'guile-3.0-latest))
- ("guile-avahi" (ref '(gnu packages guile-xyz) 'guile-avahi))
- ("guile-json" (ref '(gnu packages guile) 'guile-json-4))
- ("guile-ssh" (ref '(gnu packages ssh) 'guile-ssh))
- ("guile-git" (ref '(gnu packages guile) 'guile-git))
- ("guile-semver" (ref '(gnu packages guile-xyz) 'guile-semver))
- ("guile-lib" (ref '(gnu packages guile-xyz) 'guile-lib))
- ("guile-sqlite3" (ref '(gnu packages guile) 'guile-sqlite3))
- ("guile-zlib" (ref '(gnu packages guile) 'guile-zlib))
- ("guile-lzlib" (ref '(gnu packages guile) 'guile-lzlib))
- ("guile-zstd" (ref '(gnu packages guile) 'guile-zstd))
- ("guile-gcrypt" (ref '(gnu packages gnupg) 'guile-gcrypt))
- ("gnutls" (ref '(gnu packages tls) 'gnutls))
- ("disarchive" (ref '(gnu packages backup) 'disarchive))
- ("gzip" (ref '(gnu packages compression) 'gzip))
- ("bzip2" (ref '(gnu packages compression) 'bzip2))
- ("xz" (ref '(gnu packages compression) 'xz))
- ("po4a" (ref '(gnu packages gettext) 'po4a))
- ("gettext" (ref '(gnu packages gettext) 'gettext-minimal))
- ("gcc-toolchain" (ref '(gnu packages commencement) 'gcc-toolchain))
- (_ #f)))) ;no such package
- ;;;
- ;;; Derivations.
- ;;;
- ;; Node in a DAG of build tasks. Each node maps to a derivation, but it's
- ;; easier to express things this way.
- (define-record-type <node>
- (node name modules source dependencies compiled)
- node?
- (name node-name) ;string
- (modules node-modules) ;list of module names
- (source node-source) ;list of source files
- (dependencies node-dependencies) ;list of nodes
- (compiled node-compiled)) ;node -> lowerable object
- ;; File mappings are essentially an alist as passed to 'imported-files'.
- (define-record-type <file-mapping>
- (file-mapping name alist)
- file-mapping?
- (name file-mapping-name)
- (alist file-mapping-alist))
- (define-gexp-compiler (file-mapping-compiler (mapping <file-mapping>)
- system target)
- ;; Here we use 'imported-files', which can arrange to directly import all
- ;; the files instead of creating a derivation, when possible.
- (imported-files (map (match-lambda
- ((destination (? local-file? file))
- (cons destination
- (local-file-absolute-file-name file)))
- ((destination source)
- (cons destination source))) ;silliness
- (file-mapping-alist mapping))
- #:name (file-mapping-name mapping)
- #:system system))
- (define (node-source+compiled node)
- "Return a \"bundle\" containing both the source code and object files for
- NODE's modules, under their FHS directories: share/guile/site and lib/guile."
- (define build
- (with-imported-modules '((guix build utils))
- #~(begin
- (use-modules (guix build utils))
- (define source
- (string-append #$output "/share/guile/site/"
- (effective-version)))
- (define object
- (string-append #$output "/lib/guile/" (effective-version)
- "/site-ccache"))
- (mkdir-p (dirname source))
- (symlink #$(node-source node) source)
- (mkdir-p (dirname object))
- (symlink #$(node-compiled node) object))))
- (computed-file (string-append (node-name node) "-modules")
- build
- #:options '(#:local-build? #t
- ;; "Building" it locally is faster.
- #:substitutable? #f)))
- (define (node-fold proc init nodes)
- (let loop ((nodes nodes)
- (visited (setq))
- (result init))
- (match nodes
- (() result)
- ((head tail ...)
- (if (set-contains? visited head)
- (loop tail visited result)
- (loop tail (set-insert head visited)
- (proc head result)))))))
- (define (node-modules/recursive nodes)
- (node-fold (lambda (node modules)
- (append (node-modules node) modules))
- '()
- nodes))
- (define* (closure modules #:optional (except '()))
- (source-module-closure modules
- #:select?
- (match-lambda
- (('guix 'config)
- #f)
- ((and module
- (or ('guix _ ...) ('gnu _ ...)))
- (not (member module except)))
- (rest #f))))
- (define module->import
- ;; Return a file-name/file-like object pair for the specified module and
- ;; suitable for 'imported-files'.
- (match-lambda
- ((module '=> thing)
- (let ((file (module-name->file-name module)))
- (list file thing)))
- (module
- (let ((file (module-name->file-name module)))
- (list file
- (local-file (search-path %load-path file)))))))
- (define* (scheme-node name modules #:optional (dependencies '())
- #:key (extra-modules '()) (extra-files '())
- (extensions '())
- parallel? guile-for-build)
- "Return a node that builds the given Scheme MODULES, and depends on
- DEPENDENCIES (a list of nodes). EXTRA-MODULES is a list of additional modules
- added to the source, and EXTRA-FILES is a list of additional files.
- EXTENSIONS is a set of full-blown Guile packages (e.g., 'guile-json') that
- must be present in the search path."
- (let* ((modules (append extra-modules
- (closure modules
- (node-modules/recursive dependencies))))
- (module-files (map module->import modules))
- (source (file-mapping (string-append name "-source")
- (append module-files extra-files))))
- (node name modules source dependencies
- (compiled-modules name source
- (map car module-files)
- (map node-source dependencies)
- (map node-compiled dependencies)
- #:extensions extensions
- #:parallel? parallel?
- #:guile-for-build guile-for-build))))
- (define (file-imports directory sub-directory pred)
- "List all the files matching PRED under DIRECTORY/SUB-DIRECTORY. Return a
- list of file-name/file-like objects suitable as inputs to 'imported-files'."
- (map (lambda (file)
- (list (string-drop file (+ 1 (string-length directory)))
- (local-file file #:recursive? #t)))
- (find-files (string-append directory "/" sub-directory) pred)))
- (define* (file-append* item file #:key (recursive? #t))
- "Return FILE within ITEM, which may be a file name or a file-like object.
- When ITEM is a plain file name (a string), simply return a 'local-file'
- record with the new file name."
- (match item
- ((? string?)
- ;; This is the optimal case: we return a new "source". Thus, a
- ;; derivation that depends on this sub-directory does not depend on ITEM
- ;; itself.
- (local-file (string-append item "/" file)
- #:recursive? recursive?))
- ((? local-file? base)
- ;; Likewise, but with a <local-file>.
- (if (local-file-recursive? base)
- (local-file (string-append (local-file-absolute-file-name base)
- "/" file)
- (basename file)
- #:recursive? recursive?
- #:select? (local-file-select? base))
- (file-append base file)))
- (_
- ;; In this case, anything that refers to the result also depends on ITEM,
- ;; which isn't great.
- (file-append item "/" file))))
- (define* (locale-data source domain
- #:optional (directory domain))
- "Return the locale data from 'po/DIRECTORY' in SOURCE, corresponding to
- DOMAIN, a gettext domain."
- (define gettext
- (module-ref (resolve-interface '(gnu packages gettext))
- 'gettext-minimal))
- (define build
- (with-imported-modules '((guix build utils))
- #~(begin
- (use-modules (guix build utils)
- (srfi srfi-26)
- (ice-9 match) (ice-9 ftw))
- (define po-directory
- #+(file-append* source (string-append "po/" directory)))
- (define (compile language)
- (let ((gmo (string-append #$output "/" language "/LC_MESSAGES/"
- #$domain ".mo")))
- (mkdir-p (dirname gmo))
- (invoke #+(file-append gettext "/bin/msgfmt")
- "-c" "--statistics" "--verbose"
- "-o" gmo
- (string-append po-directory "/" language ".po"))))
- (define (linguas)
- ;; Return the list of languages. Note: don't read 'LINGUAS'
- ;; because it contains things like 'en@boldquot' that do not have
- ;; a corresponding .po file.
- (map (cut basename <> ".po")
- (scandir po-directory
- (cut string-suffix? ".po" <>))))
- (for-each compile (linguas)))))
- (computed-file (string-append "guix-locale-" domain)
- build))
- (define (translate-texi-manuals source)
- "Return the translated texinfo manuals built from SOURCE."
- (define po4a
- (specification->package "po4a"))
-
- (define gettext
- (specification->package "gettext"))
- (define glibc-utf8-locales
- (module-ref (resolve-interface '(gnu packages base))
- 'glibc-utf8-locales))
- (define documentation
- (file-append* source "doc"))
- (define documentation-po
- (file-append* source "po/doc"))
-
- (define build
- (with-imported-modules '((guix build utils) (guix build po))
- #~(begin
- (use-modules (guix build utils) (guix build po)
- (ice-9 match) (ice-9 regex) (ice-9 textual-ports)
- (ice-9 vlist) (ice-9 threads)
- (srfi srfi-1))
- (define (translate-tmp-texi po source output)
- "Translate Texinfo file SOURCE using messages from PO, and write
- the result to OUTPUT."
- (invoke #+(file-append po4a "/bin/po4a-translate")
- "-M" "UTF-8" "-L" "UTF-8" "-k" "0" "-f" "texinfo"
- "-m" source "-p" po "-l" output))
- (define (canonicalize-whitespace str)
- ;; Change whitespace (newlines, etc.) in STR to #\space.
- (string-map (lambda (chr)
- (if (char-set-contains? char-set:whitespace chr)
- #\space
- chr))
- str))
- (define xref-regexp
- ;; Texinfo cross-reference regexp.
- (make-regexp "@(px|x)?ref\\{([^,}]+)"))
- (define (translate-cross-references texi translations)
- ;; Translate the cross-references that appear in TEXI, a Texinfo
- ;; file, using the msgid/msgstr pairs from TRANSLATIONS.
- (define content
- (call-with-input-file texi get-string-all))
- (define matches
- (list-matches xref-regexp content))
- (define translation-map
- (fold (match-lambda*
- (((msgid . str) result)
- (vhash-cons msgid str result)))
- vlist-null
- translations))
- (define translated
- ;; Iterate over MATCHES and replace cross-references with their
- ;; translation found in TRANSLATION-MAP. (We can't use
- ;; 'substitute*' because matches can span multiple lines.)
- (let loop ((matches matches)
- (offset 0)
- (result '()))
- (match matches
- (()
- (string-concatenate-reverse
- (cons (string-drop content offset) result)))
- ((head . tail)
- (let ((prefix (match:substring head 1))
- (ref (canonicalize-whitespace (match:substring head 2))))
- (define translated
- (string-append "@" (or prefix "")
- "ref{"
- (match (vhash-assoc ref translation-map)
- (#f ref)
- ((_ . str) str))))
- (loop tail
- (match:end head)
- (append (list translated
- (string-take
- (string-drop content offset)
- (- (match:start head) offset)))
- result)))))))
- (format (current-error-port)
- "translated ~a cross-references in '~a'~%"
- (length matches) texi)
- (call-with-output-file texi
- (lambda (port)
- (display translated port))))
- (define* (translate-texi prefix po lang
- #:key (extras '()))
- "Translate the manual for one language LANG using the PO file.
- PREFIX must be the prefix of the manual, 'guix' or 'guix-cookbook'. EXTRAS is
- a list of extra files, such as '(\"contributing\")."
- (let ((translations (call-with-input-file po read-po-file)))
- (for-each (lambda (file)
- (translate-tmp-texi po (string-append file ".texi")
- (string-append file "." lang
- ".texi.tmp")))
- (cons prefix extras))
- (for-each (lambda (file)
- (let* ((texi (string-append file "." lang ".texi"))
- (tmp (string-append texi ".tmp")))
- (copy-file tmp texi)
- (translate-cross-references texi
- translations)))
- (cons prefix extras))))
- (define (available-translations directory domain)
- ;; Return the list of available translations under DIRECTORY for
- ;; DOMAIN, a gettext domain such as "guix-manual". The result is
- ;; a list of language/PO file pairs.
- (filter-map (lambda (po)
- (let ((base (basename po)))
- (and (string-prefix? (string-append domain ".")
- base)
- (match (string-split base #\.)
- ((_ ... lang "po")
- (cons lang po))))))
- (find-files directory
- "\\.[a-z]{2}(_[A-Z]{2})?\\.po$")))
- (define parallel-jobs
- ;; Limit thread creation by 'n-par-for-each', mostly to put an
- ;; upper bound on memory usage.
- (min (parallel-job-count) 4))
- (mkdir #$output)
- (copy-recursively #$documentation "."
- #:log (%make-void-port "w"))
- (for-each
- (lambda (file)
- (copy-file file (basename file)))
- (find-files #$documentation-po ".*.po$"))
- (setenv "GUIX_LOCPATH"
- #+(file-append glibc-utf8-locales "/lib/locale"))
- (setenv "PATH" #+(file-append gettext "/bin"))
- (setenv "LC_ALL" "en_US.UTF-8")
- (setlocale LC_ALL "en_US.UTF-8")
- (n-par-for-each parallel-jobs
- (match-lambda
- ((language . po)
- (translate-texi "guix" po language
- #:extras '("contributing"))))
- (available-translations "." "guix-manual"))
- (n-par-for-each parallel-jobs
- (match-lambda
- ((language . po)
- (translate-texi "guix-cookbook" po language)))
- (available-translations "." "guix-cookbook"))
- (for-each (lambda (file)
- (install-file file #$output))
- (append
- (find-files "." "contributing\\..*\\.texi$")
- (find-files "." "guix\\..*\\.texi$")
- (find-files "." "guix-cookbook\\..*\\.texi$"))))))
- (computed-file "guix-translated-texinfo" build))
- (define (info-manual source)
- "Return the Info manual built from SOURCE."
- (define texinfo
- (module-ref (resolve-interface '(gnu packages texinfo))
- 'texinfo))
- (define graphviz
- (module-ref (resolve-interface '(gnu packages graphviz))
- 'graphviz))
- (define glibc-utf8-locales
- (module-ref (resolve-interface '(gnu packages base))
- 'glibc-utf8-locales))
- (define documentation
- (file-append* source "doc"))
- (define examples
- (file-append* source "gnu/system/examples"))
- (define build
- (with-imported-modules '((guix build utils))
- #~(begin
- (use-modules (guix build utils)
- (ice-9 match))
- (mkdir #$output)
- ;; Create 'version.texi'.
- ;; XXX: Can we use a more meaningful version string yet one that
- ;; doesn't change at each commit?
- (call-with-output-file "version.texi"
- (lambda (port)
- (let ((version "0.0-git"))
- (format port "
- @set UPDATED 1 January 1970
- @set UPDATED-MONTH January 1970
- @set EDITION ~a
- @set VERSION ~a\n" version version))))
- ;; Copy configuration templates that the manual includes.
- (for-each (lambda (template)
- (copy-file template
- (string-append
- "os-config-"
- (basename template ".tmpl")
- ".texi")))
- (find-files #$examples "\\.tmpl$"))
- ;; Build graphs.
- (mkdir-p (string-append #$output "/images"))
- (for-each (lambda (dot-file)
- (invoke #+(file-append graphviz "/bin/dot")
- "-Tpng" "-Gratio=.9" "-Gnodesep=.005"
- "-Granksep=.00005" "-Nfontsize=9"
- "-Nheight=.1" "-Nwidth=.1"
- "-o" (string-append #$output "/images/"
- (basename dot-file ".dot")
- ".png")
- dot-file))
- (find-files (string-append #$documentation "/images")
- "\\.dot$"))
- ;; Copy other PNGs.
- (for-each (lambda (png-file)
- (install-file png-file
- (string-append #$output "/images")))
- (find-files (string-append #$documentation "/images")
- "\\.png$"))
- ;; Finally build the manual. Copy it the Texinfo files to $PWD and
- ;; add a symlink to the 'images' directory so that 'makeinfo' can
- ;; see those images and produce image references in the Info output.
- (copy-recursively #$documentation "."
- #:log (%make-void-port "w"))
- (copy-recursively #+(translate-texi-manuals source) "."
- #:log (%make-void-port "w"))
- (delete-file-recursively "images")
- (symlink (string-append #$output "/images") "images")
- ;; Provide UTF-8 locales needed by the 'xspara.c' code in makeinfo.
- (setenv "GUIX_LOCPATH"
- #+(file-append glibc-utf8-locales "/lib/locale"))
- (for-each (lambda (texi)
- (match (string-split (basename texi) #\.)
- (("guix" language "texi")
- ;; Create 'version-LL.texi'.
- (symlink "version.texi"
- (string-append "version-" language
- ".texi")))
- (_ #f))
- (invoke #+(file-append texinfo "/bin/makeinfo")
- texi "-I" #$documentation
- "-I" "."
- "-o" (string-append #$output "/"
- (basename texi ".texi")
- ".info")))
- (cons "guix.texi"
- (append (find-files "."
- "^guix\\.[a-z]{2}(_[A-Z]{2})?\\.texi$")
- (find-files "."
- "^guix-cookbook.*\\.texi$"))))
- ;; Compress Info files.
- (setenv "PATH"
- #+(file-append (specification->package "gzip") "/bin"))
- (for-each (lambda (file)
- (invoke "gzip" "-9n" file))
- (find-files #$output "\\.info(-[0-9]+)?$")))))
- (computed-file "guix-manual" build))
- (define-syntax-rule (prevent-inlining! identifier ...)
- (begin (set! identifier identifier) ...))
- ;; XXX: These procedures are actually used by 'doc/build.scm'. Protect them
- ;; from inlining on Guile 3.
- (prevent-inlining! file-append* translate-texi-manuals info-manual)
- (define* (guile-module-union things #:key (name "guix-module-union"))
- "Return the union of the subset of THINGS (packages, computed files, etc.)
- that provide Guile modules."
- (define build
- (with-imported-modules '((guix build union))
- #~(begin
- (use-modules (guix build union))
- (define (modules directory)
- (string-append directory "/share/guile/site"))
- (define (objects directory)
- (string-append directory "/lib/guile"))
- (union-build #$output
- (filter (lambda (directory)
- (or (file-exists? (modules directory))
- (file-exists? (objects directory))))
- '#$things)
- #:log-port (%make-void-port "w")))))
- (computed-file name build))
- (define (quiet-guile guile)
- "Return a wrapper that does the same as the 'guile' executable of GUILE,
- except that it does not complain about locales and falls back to 'en_US.utf8'
- instead of 'C'."
- (define gcc
- (specification->package "gcc-toolchain"))
- (define source
- (search-path %load-path
- "gnu/packages/aux-files/guile-launcher.c"))
- (define effective
- (version-major+minor (package-version guile)))
- (define build
- ;; XXX: Reuse <c-compiler> from (guix scripts pack) instead?
- (with-imported-modules '((guix build utils))
- #~(begin
- (use-modules (guix build utils)
- (srfi srfi-26))
- (mkdir-p (string-append #$output "/bin"))
- (setenv "PATH" #$(file-append gcc "/bin"))
- (setenv "C_INCLUDE_PATH"
- (string-join
- (map (cut string-append <> "/include")
- '#$(match (bag-transitive-build-inputs
- (package->bag guile))
- (((labels packages . _) ...)
- (filter package? packages))))
- ":"))
- (setenv "LIBRARY_PATH" #$(file-append gcc "/lib"))
- (invoke "gcc" #$(local-file source) "-Wall" "-g0" "-O2"
- "-I" #$(file-append guile "/include/guile/" effective)
- "-L" #$(file-append guile "/lib")
- #$(string-append "-lguile-" effective)
- "-o" (string-append #$output "/bin/guile")))))
- (computed-file "guile-wrapper" build))
- (define* (guix-command modules
- #:key source (dependencies '())
- guile (guile-version (effective-version)))
- "Return the 'guix' command such that it adds MODULES and DEPENDENCIES in its
- load path."
- (define glibc-utf8-locales
- (module-ref (resolve-interface '(gnu packages base))
- 'glibc-utf8-locales))
- (define module-directory
- ;; To minimize the number of 'stat' calls needed to locate a module,
- ;; create the union of all the module directories.
- (guile-module-union (cons modules dependencies)))
- (program-file "guix-command"
- #~(begin
- ;; Remove the empty extension from the search path.
- (set! %load-extensions '(".scm"))
- (set! %load-path
- (append (list (string-append #$module-directory
- "/share/guile/site/"
- (effective-version))
- (string-append #$guile "/share/guile/"
- (effective-version)))
- %load-path))
- (set! %load-compiled-path
- (append (list (string-append #$module-directory
- "/lib/guile/"
- (effective-version)
- "/site-ccache")
- (string-append #$guile "/lib/guile/"
- (effective-version)
- "/ccache"))
- %load-compiled-path))
- ;; To maximize the chances that locales are set up right
- ;; out-of-the-box, bundle "common" UTF-8 locales.
- (let ((locpath (getenv "GUIX_LOCPATH")))
- (setenv "GUIX_LOCPATH"
- (string-append (if locpath
- (string-append locpath ":")
- "")
- #$(file-append glibc-utf8-locales
- "/lib/locale"))))
- (let ((guix-main (module-ref (resolve-interface '(guix ui))
- 'guix-main)))
- #$(if source
- #~(begin
- (bindtextdomain "guix"
- #$(locale-data source "guix"))
- (bindtextdomain "guix-packages"
- #$(locale-data source
- "guix-packages"
- "packages")))
- #t)
- ;; XXX: It would be more convenient to change it to:
- ;; (exit (apply guix-main (command-line)))
- (apply guix-main (command-line))))
- ;; Use a 'guile' variant that doesn't complain about locales.
- #:guile (quiet-guile guile)))
- (define (miscellaneous-files source)
- "Return data files taken from SOURCE."
- (file-mapping "guix-misc"
- `(("etc/bash_completion.d/guix"
- ,(file-append* source "/etc/completion/bash/guix"))
- ("etc/bash_completion.d/guix-daemon"
- ,(file-append* source "/etc/completion/bash/guix-daemon"))
- ("share/zsh/site-functions/_guix"
- ,(file-append* source "/etc/completion/zsh/_guix"))
- ("share/fish/vendor_completions.d/guix.fish"
- ,(file-append* source "/etc/completion/fish/guix.fish"))
- ("share/guix/berlin.guix.gnu.org.pub"
- ,(file-append* source
- "/etc/substitutes/berlin.guix.gnu.org.pub"))
- ("share/guix/ci.guix.gnu.org.pub" ;alias
- ,(file-append* source "/etc/substitutes/berlin.guix.gnu.org.pub"))
- ("share/guix/ci.guix.info.pub" ;alias
- ,(file-append* source "/etc/substitutes/berlin.guix.gnu.org.pub"))
- ("share/guix/bordeaux.guix.gnu.org.pub"
- ,(file-append* source "/etc/substitutes/bordeaux.guix.gnu.org.pub")))))
- (define* (whole-package name modules dependencies
- #:key
- (guile-version (effective-version))
- info daemon miscellany
- guile
- (command (guix-command modules
- #:dependencies dependencies
- #:guile guile
- #:guile-version guile-version)))
- "Return the whole Guix package NAME that uses MODULES, a derivation of all
- the modules (under share/guile/site and lib/guile), and DEPENDENCIES, a list
- of packages depended on. COMMAND is the 'guix' program to use; INFO is the
- Info manual."
- (define (wrap daemon)
- (program-file "guix-daemon"
- #~(begin
- ;; Refer to the right 'guix' command for 'guix
- ;; substitute' & co.
- (setenv "GUIX" #$command)
- ;; Honor the user's settings rather than those hardcoded
- ;; in the 'guix-daemon' package.
- (unless (getenv "GUIX_STATE_DIRECTORY")
- (setenv "GUIX_STATE_DIRECTORY"
- #$(string-append %localstatedir "/guix")))
- (unless (getenv "GUIX_CONFIGURATION_DIRECTORY")
- (setenv "GUIX_CONFIGURATION_DIRECTORY"
- #$(string-append %sysconfdir "/guix")))
- (unless (getenv "NIX_STORE_DIR")
- (setenv "NIX_STORE_DIR" #$%storedir))
- (apply execl #$(file-append daemon "/bin/guix-daemon")
- "guix-daemon" (cdr (command-line))))))
- (computed-file name
- (with-imported-modules '((guix build utils))
- #~(begin
- (use-modules (guix build utils))
- (define daemon
- #$(and daemon (wrap daemon)))
- (mkdir-p (string-append #$output "/bin"))
- (symlink #$command
- (string-append #$output "/bin/guix"))
- (when daemon
- (symlink daemon
- (string-append #$output "/bin/guix-daemon")))
- (let ((share (string-append #$output "/share"))
- (lib (string-append #$output "/lib"))
- (info #$info))
- (mkdir-p share)
- (symlink #$(file-append modules "/share/guile")
- (string-append share "/guile"))
- (when info
- (symlink #$info (string-append share "/info")))
- (mkdir-p lib)
- (symlink #$(file-append modules "/lib/guile")
- (string-append lib "/guile")))
- (when #$miscellany
- (copy-recursively #$miscellany #$output
- #:log (%make-void-port "w")))))))
- (define (transitive-package-dependencies package)
- "Return the list of packages propagated by PACKAGE, including PACKAGE
- itself."
- (match (package-transitive-propagated-inputs package)
- (((labels packages _ ...) ...)
- (cons package packages))))
- (define* (compiled-guix source #:key
- (version %guix-version)
- (channel-metadata #f)
- (pull-version 1)
- (name (string-append "guix-" version))
- (guile-version (effective-version))
- (guile-for-build (default-guile))
- (gzip (specification->package "gzip"))
- (bzip2 (specification->package "bzip2"))
- (xz (specification->package "xz"))
- (guix (specification->package "guix")))
- "Return a file-like object that contains a compiled Guix."
- (define guile-avahi
- (specification->package "guile-avahi"))
- (define guile-json
- (specification->package "guile-json"))
- (define guile-ssh
- (specification->package "guile-ssh"))
- (define guile-lib
- (specification->package "guile-lib"))
- (define guile-git
- (specification->package "guile-git"))
- (define guile-sqlite3
- (specification->package "guile-sqlite3"))
- (define guile-zlib
- (specification->package "guile-zlib"))
- (define guile-lzlib
- (specification->package "guile-lzlib"))
- (define guile-zstd
- (specification->package "guile-zstd"))
- (define guile-gcrypt
- (specification->package "guile-gcrypt"))
- (define guile-semver
- (specification->package "guile-semver"))
- (define gnutls
- (specification->package "gnutls"))
- (define disarchive
- (specification->package "disarchive"))
- (define dependencies
- (append-map transitive-package-dependencies
- (list guile-gcrypt gnutls guile-git guile-avahi
- guile-json guile-semver guile-ssh guile-sqlite3
- guile-lib guile-zlib guile-lzlib guile-zstd)))
- (define *core-modules*
- (scheme-node "guix-core"
- '((guix)
- (guix monad-repl)
- (guix packages)
- (guix download)
- (guix discovery)
- (guix profiles)
- (guix build-system gnu)
- (guix build-system trivial)
- (guix build profiles)
- (guix build gnu-build-system))
- ;; Provide a dummy (guix config) with the default version
- ;; number, storedir, etc. This is so that "guix-core" is the
- ;; same across all installations and doesn't need to be
- ;; rebuilt when the version changes, which in turn means we
- ;; can have substitutes for it.
- #:extra-modules
- `(((guix config) => ,(make-config.scm)))
- ;; (guix man-db) is needed at build-time by (guix profiles)
- ;; but we don't need to compile it; not compiling it allows
- ;; us to avoid an extra dependency on guile-gdbm-ffi.
- #:extra-files
- `(("guix/man-db.scm" ,(local-file "../guix/man-db.scm"))
- ("guix/build/po.scm" ,(local-file "../guix/build/po.scm"))
- ("guix/store/schema.sql"
- ,(local-file "../guix/store/schema.sql")))
- #:extensions (list guile-gcrypt
- guile-json) ;for (guix swh)
- #:guile-for-build guile-for-build))
- (define *extra-modules*
- (scheme-node "guix-extra"
- (filter-map (match-lambda
- (('guix 'scripts _ ..1) #f)
- (('guix 'man-db) #f)
- (('guix 'tests _ ...) #f)
- (name name))
- (scheme-modules* source "guix"))
- (list *core-modules*)
- #:extra-files
- `(("guix/graph.js" ,(local-file "../guix/graph.js"))
- ("guix/d3.v3.js" ,(local-file "../guix/d3.v3.js")))
- #:extensions dependencies
- #:guile-for-build guile-for-build))
- (define *core-package-modules*
- (scheme-node "guix-packages-base"
- `((gnu packages)
- (gnu packages base))
- (list *core-modules* *extra-modules*)
- #:extensions dependencies
- ;; Add all the non-Scheme files here. We must do it here so
- ;; that 'search-patches' & co. can find them. Ideally we'd
- ;; keep them next to the .scm files that use them but it's
- ;; difficult to do (XXX).
- #:extra-files
- (file-imports source "gnu/packages"
- (lambda (file stat)
- (and (eq? 'regular (stat:type stat))
- (not (string-suffix? ".scm" file))
- (not (string-suffix? ".go" file))
- (not (string-prefix? ".#" file))
- (not (string-suffix? "~" file)))))
- #:guile-for-build guile-for-build))
- (define *package-modules*
- (scheme-node "guix-packages"
- (scheme-modules* source "gnu/packages")
- (list *core-modules* *extra-modules* *core-package-modules*)
- #:extensions dependencies
- #:guile-for-build guile-for-build))
- (define *system-modules*
- (scheme-node "guix-system"
- `((gnu system)
- (gnu services)
- ,@(scheme-modules* source "gnu/bootloader")
- ,@(scheme-modules* source "gnu/system")
- ,@(scheme-modules* source "gnu/services")
- ,@(scheme-modules* source "gnu/machine"))
- (list *core-package-modules* *package-modules*
- *extra-modules* *core-modules*)
- #:extensions dependencies
- #:extra-files
- (append (file-imports source "gnu/system/examples"
- (const #t))
- ;; All the installer code is on the build-side.
- (file-imports source "gnu/installer/"
- (const #t))
- ;; Build-side code that we don't build. Some of
- ;; these depend on guile-rsvg, the Shepherd, etc.
- (file-imports source "gnu/build" (const #t)))
- #:guile-for-build
- guile-for-build))
- (define *cli-modules*
- (scheme-node "guix-cli"
- (append (scheme-modules* source "/guix/scripts")
- `((gnu ci)))
- (list *core-modules* *extra-modules*
- *core-package-modules* *package-modules*
- *system-modules*)
- #:extensions dependencies
- #:guile-for-build guile-for-build))
- (define *system-test-modules*
- ;; Ship these modules mostly so (gnu ci) can discover them.
- (scheme-node "guix-system-tests"
- `((gnu tests)
- ,@(scheme-modules* source "gnu/tests"))
- (list *core-package-modules* *package-modules*
- *extra-modules* *system-modules* *core-modules*
- *cli-modules*) ;for (guix scripts pack), etc.
- #:extensions dependencies
- #:guile-for-build guile-for-build))
- (define *config*
- (scheme-node "guix-config"
- '()
- #:extra-modules
- `(((guix config)
- => ,(make-config.scm #:gzip gzip
- #:bzip2 bzip2
- #:xz xz
- #:package-name
- %guix-package-name
- #:package-version
- version
- #:channel-metadata
- channel-metadata
- #:bug-report-address
- %guix-bug-report-address
- #:home-page-url
- %guix-home-page-url)))
- #:guile-for-build guile-for-build))
- (define (built-modules node-subset)
- (directory-union (string-append name "-modules")
- (append-map node-subset
- ;; Note: *CONFIG* comes first so that it
- ;; overrides the (guix config) module that
- ;; comes with *CORE-MODULES*.
- (list *config*
- *cli-modules*
- *system-test-modules*
- *system-modules*
- *package-modules*
- *core-package-modules*
- *extra-modules*
- *core-modules*))
- ;; Silently choose the first entry upon collision so that
- ;; we choose *CONFIG*.
- #:resolve-collision 'first
- ;; When we do (add-to-store "utils.scm"), "utils.scm" must
- ;; be a regular file, not a symlink. Thus, arrange so that
- ;; regular files appear as regular files in the final
- ;; output.
- #:copy? #t
- #:quiet? #t))
- ;; Version 0 of 'guix pull' meant we'd just return Scheme modules.
- ;; Version 1 is when we return the full package.
- (cond ((= 1 pull-version)
- ;; The whole package, with a standard file hierarchy.
- (let* ((modules (built-modules (compose list node-source+compiled)))
- (command (guix-command modules
- #:source source
- #:dependencies
- (cons disarchive dependencies)
- #:guile guile-for-build
- #:guile-version guile-version)))
- (whole-package name modules dependencies
- #:command command
- #:guile guile-for-build
- ;; Include 'guix-daemon'. XXX: Here we inject an
- ;; older snapshot of guix-daemon, but that's a good
- ;; enough approximation for now.
- #:daemon (module-ref (resolve-interface
- '(gnu packages
- package-management))
- 'guix-daemon)
- #:info (info-manual source)
- #:miscellany (miscellaneous-files source)
- #:guile-version guile-version)))
- ((= 0 pull-version)
- ;; Legacy 'guix pull': return the .scm and .go files as one
- ;; directory.
- (built-modules (lambda (node)
- (list (node-source node)
- (node-compiled node)))))
- (else
- ;; Unsupported 'guix pull' version.
- #f)))
- ;;;
- ;;; Generating (guix config).
- ;;;
- (define %persona-variables
- ;; (guix config) variables that define Guix's persona.
- '(%guix-package-name
- %guix-version
- %guix-bug-report-address
- %guix-home-page-url))
- (define %config-variables
- ;; (guix config) variables corresponding to Guix configuration.
- (letrec-syntax ((variables (syntax-rules ()
- ((_)
- '())
- ((_ variable rest ...)
- (cons `(variable . ,variable)
- (variables rest ...))))))
- (variables %localstatedir %storedir %sysconfdir)))
- (define* (make-config.scm #:key gzip xz bzip2
- (package-name "GNU Guix")
- (package-version "0")
- (channel-metadata #f)
- (bug-report-address "bug-guix@gnu.org")
- (home-page-url "https://guix.gnu.org"))
- ;; Hack so that Geiser is not confused.
- (define defmod 'define-module)
- (scheme-file "config.scm"
- #~(;; The following expressions get spliced.
- (#$defmod (guix config)
- #:export (%guix-package-name
- %guix-version
- %guix-bug-report-address
- %guix-home-page-url
- %channel-metadata
- %system
- %store-directory
- %state-directory
- %store-database-directory
- %config-directory
- %gzip
- %bzip2
- %xz))
- (define %system
- #$(%current-system))
- #$@(map (match-lambda
- ((name . value)
- #~(define-public #$name #$value)))
- %config-variables)
- (define %store-directory
- (or (and=> (getenv "NIX_STORE_DIR") canonicalize-path)
- %storedir))
- (define %state-directory
- ;; This must match `NIX_STATE_DIR' as defined in
- ;; `nix/local.mk'.
- (or (getenv "GUIX_STATE_DIRECTORY")
- (string-append %localstatedir "/guix")))
- (define %store-database-directory
- (or (getenv "GUIX_DATABASE_DIRECTORY")
- (string-append %state-directory "/db")))
- (define %config-directory
- ;; This must match `GUIX_CONFIGURATION_DIRECTORY' as
- ;; defined in `nix/local.mk'.
- (or (getenv "GUIX_CONFIGURATION_DIRECTORY")
- (string-append %sysconfdir "/guix")))
- (define %guix-package-name #$package-name)
- (define %guix-version #$package-version)
- (define %guix-bug-report-address #$bug-report-address)
- (define %guix-home-page-url #$home-page-url)
- (define %channel-metadata
- ;; Metadata for the 'guix' channel in use. This
- ;; information is used by (guix describe).
- '#$channel-metadata)
- (define %gzip
- #+(and gzip (file-append gzip "/bin/gzip")))
- (define %bzip2
- #+(and bzip2 (file-append bzip2 "/bin/bzip2")))
- (define %xz
- #+(and xz (file-append xz "/bin/xz"))))
- ;; Guile 2.0 *requires* the 'define-module' to be at the
- ;; top-level or the 'toplevel-ref' in the resulting .go file are
- ;; made relative to a nonexistent anonymous module.
- #:splice? #t))
- ;;;
- ;;; Building.
- ;;;
- (define* (compiled-modules name module-tree module-files
- #:optional
- (dependencies '())
- (dependencies-compiled '())
- #:key
- (extensions '()) ;full-blown Guile packages
- parallel?
- guile-for-build)
- "Build all the MODULE-FILES from MODULE-TREE. MODULE-FILES must be a list
- like '(\"guix/foo.scm\" \"gnu/bar.scm\") and MODULE-TREE is the directory
- containing MODULE-FILES and possibly other files as well."
- ;; This is a non-monadic, enhanced version of 'compiled-file' from (guix
- ;; gexp).
- (define build
- (with-imported-modules (source-module-closure
- '((guix build compile)
- (guix build utils)))
- #~(begin
- (use-modules (srfi srfi-26)
- (ice-9 match)
- (ice-9 format)
- (ice-9 threads)
- (guix build compile)
- (guix build utils))
- (define (regular? file)
- (not (member file '("." ".."))))
- (define (report-load file total completed)
- (display #\cr)
- (format #t
- "[~3@a/~3@a] loading...\t~5,1f% of ~d files"
- ;; Note: Multiply TOTAL by two to account for the
- ;; compilation phase that follows.
- completed (* total 2)
- (* 100. (/ completed total)) total)
- (force-output))
- (define (report-compilation file total completed)
- (display #\cr)
- (format #t "[~3@a/~3@a] compiling...\t~5,1f% of ~d files"
- ;; Add TOTAL to account for the load phase that came
- ;; before.
- (+ total completed) (* total 2)
- (* 100. (/ completed total)) total)
- (force-output))
- (define (process-directory directory files output)
- ;; Hide compilation warnings.
- (parameterize ((current-warning-port (%make-void-port "w")))
- (compile-files directory #$output files
- #:workers (parallel-job-count)
- #:report-load report-load
- #:report-compilation report-compilation)))
- (setvbuf (current-output-port) 'line)
- (setvbuf (current-error-port) 'line)
- (set! %load-path (cons #+module-tree %load-path))
- (set! %load-path
- (append '#+dependencies
- (map (lambda (extension)
- (string-append extension "/share/guile/site/"
- (effective-version)))
- '#+extensions)
- %load-path))
- (set! %load-compiled-path
- (append '#+dependencies-compiled
- (map (lambda (extension)
- (string-append extension "/lib/guile/"
- (effective-version)
- "/site-ccache"))
- '#+extensions)
- %load-compiled-path))
- ;; Load the compiler modules upfront.
- (compile #f)
- (mkdir #$output)
- (chdir #+module-tree)
- (process-directory "." '#+module-files #$output)
- (newline))))
- (computed-file name build
- #:guile guile-for-build
- #:options
- `(#:local-build? #f ;allow substitutes
- ;; Don't annoy people about _IONBF deprecation.
- ;; Initialize 'terminal-width' in (system repl debug)
- ;; to a large-enough value to make backtrace more
- ;; verbose.
- #:env-vars (("GUILE_WARN_DEPRECATED" . "no")
- ("COLUMNS" . "200")))))
- ;;;
- ;;; Building.
- ;;;
- (define* (guix-derivation source version
- #:optional (guile-version (effective-version))
- #:key (pull-version 0)
- channel-metadata)
- "Return, as a monadic value, the derivation to build the Guix from SOURCE
- for GUILE-VERSION. Use VERSION as the version string. Use CHANNEL-METADATA
- as the channel metadata sexp to include in (guix config).
- PULL-VERSION specifies the version of the 'guix pull' protocol. Return #f if
- this PULL-VERSION value is not supported."
- (define (shorten version)
- (if (and (string-every char-set:hex-digit version)
- (> (string-length version) 9))
- (string-take version 9) ;Git commit
- version))
- (define guile
- ;; When PULL-VERSION >= 1, produce a self-contained Guix and use the
- ;; current Guile unconditionally.
- (specification->package "guile"))
- (when (and (< pull-version 1)
- (not (string=? (package-version guile) guile-version)))
- ;; Guix < 0.15.0 has PULL-VERSION = 0, where the host Guile is reused and
- ;; can be any version. When that happens and Guile is not current (e.g.,
- ;; it's Guile 2.0), just bail out.
- (raise (condition
- (&message
- (message "Guix is too old and cannot be upgraded")))))
- (mbegin %store-monad
- (set-guile-for-build guile)
- (let ((guix (compiled-guix source
- #:version version
- #:channel-metadata channel-metadata
- #:name (string-append "guix-"
- (shorten version))
- #:pull-version pull-version
- #:guile-version (if (>= pull-version 1)
- "3.0" guile-version)
- #:guile-for-build guile)))
- (if guix
- (lower-object guix)
- (return #f)))))
|