1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of GNU Guix.
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (guix utils)
20 #:use-module (guix config)
21 #:use-module (srfi srfi-1)
22 #:use-module (srfi srfi-9)
23 #:use-module (srfi srfi-26)
24 #:use-module (srfi srfi-39)
25 #:use-module (srfi srfi-60)
26 #:use-module (rnrs bytevectors)
27 #:use-module ((rnrs io ports) #:select (put-bytevector))
28 #:use-module (ice-9 vlist)
29 #:use-module (ice-9 format)
30 #:autoload (ice-9 popen) (open-pipe*)
31 #:autoload (ice-9 rdelim) (read-line)
32 #:use-module (ice-9 regex)
33 #:use-module (ice-9 match)
34 #:use-module (ice-9 format)
35 #:autoload (system foreign) (pointer->procedure)
36 #:export (bytevector->base16-string
37 base16-string->bytevector
47 default-keyword-arguments
48 substitute-keyword-arguments
56 source-properties->location
58 gnu-triplet->nix-system
60 package-name->name+version))
64 ;;; Compile-time computations.
67 (define-syntax compile-time-value
69 "Evaluate the given expression at compile time. The expression must
70 evaluate to a simple datum."
72 (let-syntax ((v (lambda (s)
75 (_ #`'#,(datum->syntax s val)))))))
83 (define (bytevector->base16-string bv)
84 "Return the hexadecimal representation of BV's contents."
86 (bytevector-length bv))
88 (let-syntax ((base16-chars (lambda (s)
91 (let ((v (list->vector
92 (unfold (cut > <> 255)
94 (format #f "~2,'0x" n))
98 (define chars base16-chars)
102 (string-concatenate-reverse r)
104 (cons (vector-ref chars (bytevector-u8-ref bv i)) r))))))
106 (define base16-string->bytevector
107 (let ((chars->value (fold (lambda (i r)
108 (vhash-consv (string-ref (number->string i 16)
114 "Return the bytevector whose hexadecimal representation is string S."
116 (make-bytevector (quotient (string-length s) 2) 0))
118 (string-fold (lambda (chr i)
119 (let ((j (quotient i 2))
120 (v (and=> (vhash-assv chr chars->value) cdr)))
122 (if (zero? (logand i 1))
123 (bytevector-u8-set! bv j
124 (arithmetic-shift v 4))
125 (let ((w (bytevector-u8-ref bv j)))
126 (bytevector-u8-set! bv j (logior v w))))
127 (error "invalid hexadecimal character" chr)))
139 (let ((hash (pointer->procedure void
140 (dynamic-func "gcry_md_hash_buffer"
141 (dynamic-link %libgcrypt))
142 `(,int * * ,size_t)))
143 (sha256 8)) ; GCRY_MD_SHA256, as of 1.5.0
145 "Return the SHA256 of BV as a bytevector."
146 (let ((digest (make-bytevector (/ 256 8))))
147 (hash sha256 (bytevector->pointer digest)
148 (bytevector->pointer bv) (bytevector-length bv))
156 (define %nixpkgs-directory
158 ;; Capture the build-time value of $NIXPKGS.
160 (and=> (getenv "NIXPKGS")
162 ;; Bail out when passed an empty string, otherwise
163 ;; `nix-instantiate' will sit there and attempt to read
164 ;; from its standard input.
165 (if (string=? val "")
169 (define* (nixpkgs-derivation attribute #:optional (system (%current-system)))
170 "Return the derivation path of ATTRIBUTE in Nixpkgs."
171 (let* ((p (open-pipe* OPEN_READ (or (getenv "NIX_INSTANTIATE")
173 "-A" attribute (%nixpkgs-directory)
174 "--argstr" "system" system))
177 (and (zero? (status:exit-val s))
178 (not (eof-object? l))
181 (define-syntax-rule (nixpkgs-derivation* attribute)
182 "Evaluate the given Nixpkgs derivation at compile-time."
183 (compile-time-value (nixpkgs-derivation attribute)))
190 (define-syntax define-record-type*
192 "Define the given record type such that an additional \"syntactic
193 constructor\" is defined, which allows instances to be constructed with named
194 field initializers, à la SRFI-35, as well as default values."
195 (define (make-syntactic-constructor type name ctor fields defaults)
196 "Make the syntactic constructor NAME for TYPE, that calls CTOR, and
197 expects all of FIELDS to be initialized. DEFAULTS is the list of
198 FIELD/DEFAULT-VALUE tuples."
199 (with-syntax ((type type)
204 #`(define-syntax name
206 (define (record-inheritance orig-record field+value)
207 ;; Produce code that returns a record identical to
208 ;; ORIG-RECORD, except that values for the FIELD+VALUE alist
210 (define (field-inherited-value f)
211 (and=> (find (lambda (x)
212 (eq? f (car (syntax->datum x))))
216 #`(make-struct type 0
217 #,@(map (lambda (field index)
218 (or (field-inherited-value field)
219 #`(struct-ref #,orig-record
222 (iota (length 'expected)))))
225 (syntax-case s (inherit #,@fields)
226 ((_ (inherit orig-record) (field value) (... ...))
227 #`(letrec* ((field value) (... ...))
228 #,(record-inheritance #'orig-record
229 #'((field value) (... ...)))))
230 ((_ (field value) (... ...))
231 (let ((fields (map syntax->datum #'(field (... ...))))
232 (dflt (map (match-lambda
234 (list (syntax->datum f) v)))
237 (define (field-value f)
238 (or (and=> (find (lambda (x)
239 (eq? f (car (syntax->datum x))))
240 #'((field value) (... ...)))
242 (car (assoc-ref dflt (syntax->datum f)))))
246 ((_ fmt args (... ...))
247 (syntax-violation 'name
251 (let ((fields (append fields (map car dflt))))
252 (cond ((lset= eq? fields 'expected)
253 #`(letrec* ((field value) (... ...))
254 (ctor #,@(map field-value 'expected))))
255 ((pair? (lset-difference eq? fields 'expected))
256 (error* "extraneous field initializers ~a"
257 (lset-difference eq? fields 'expected)))
259 (error* "missing field initializers ~a"
260 (lset-difference eq? 'expected
263 (define (field-default-value s)
264 (syntax-case s (default)
265 ((field (default val) _ ...)
266 (list #'field #'val))
267 ((field _ options ...)
268 (field-default-value #'(field options ...)))
272 ((_ type syntactic-ctor ctor pred
273 (field get options ...) ...)
275 (define-record-type type
279 #,(make-syntactic-constructor #'type #'syntactic-ctor #'ctor
281 (filter-map field-default-value
282 #'((field options ...)
285 (define (memoize proc)
286 "Return a memoizing version of PROC."
287 (let ((cache (make-hash-table)))
289 (let ((results (hash-ref cache args)))
291 (apply values results)
292 (let ((results (call-with-values (lambda ()
295 (hash-set! cache args results)
296 (apply values results)))))))
298 (define (default-keyword-arguments args defaults)
299 "Return ARGS augmented with any keyword/value from DEFAULTS for
300 keywords not already present in ARGS."
301 (let loop ((defaults defaults)
306 (if (assoc-ref kw args)
308 (cons* kw value args))))
312 (define-syntax substitute-keyword-arguments
314 "Return a new list of arguments where the value for keyword arg KW is
315 replaced by EXP. EXP is evaluated in a context where VAR is boud to the
316 previous value of the keyword argument."
317 ((_ original-args ((kw var) exp) ...)
318 (let loop ((args original-args)
321 ((kw var rest (... ...))
322 (loop rest (cons* exp kw before)))
325 (loop rest (cons x before)))
327 (reverse before)))))))
329 (define (gnu-triplet->nix-system triplet)
330 "Return the Nix system type corresponding to TRIPLET, a GNU triplet as
331 returned by `config.guess'."
332 (let ((triplet (cond ((string-match "^i[345]86-(.*)$" triplet)
335 (string-append "i686-" (match:substring m 1))))
337 (cond ((string-match "^([^-]+)-([^-]+-)?linux-gnu.*" triplet)
340 ;; Nix omits `-gnu' for GNU/Linux.
341 (string-append (match:substring m 1) "-linux")))
342 ((string-match "^([^-]+)-([^-]+-)?([[:alpha:]]+)([0-9]+\\.?)*$" triplet)
345 ;; Nix strip the version number from names such as `gnu0.3',
346 ;; `darwin10.2.0', etc., and always strips the vendor part.
347 (string-append (match:substring m 1) "-"
348 (match:substring m 3))))
351 (define %current-system
352 ;; System type as expected by Nix, usually ARCHITECTURE-KERNEL.
353 ;; By default, this is equal to (gnu-triplet->nix-system %host-type).
354 (make-parameter %system))
356 (define (package-name->name+version name)
357 "Given NAME, a package name like \"foo-0.9.1b\", return two values:
358 \"foo\" and \"0.9.1b\". When the version part is unavailable, NAME and
359 #f are returned. The first hyphen followed by a digit is considered to
360 introduce the version part."
361 ;; See also `DrvName' in Nix.
364 (cut char-set-contains? char-set:digit <>))
366 (let loop ((chars (string->list name))
371 ((#\- (? number? n) rest ...)
372 (values (list->string (reverse prefix))
373 (list->string (cons n rest))))
375 (loop tail (cons head prefix))))))
382 ;; A source location.
383 (define-record-type <location>
384 (make-location file line column)
386 (file location-file) ; file name
387 (line location-line) ; 1-indexed line
388 (column location-column)) ; 0-indexed column
392 (lambda (file line column)
393 "Return the <location> object for the given FILE, LINE, and COLUMN."
394 (and line column file
395 (make-location file line column)))))
397 (define (source-properties->location loc)
398 "Return a location object based on the info in LOC, an alist as returned
399 by Guile's `source-properties', `frame-source', `current-source-location',
401 (let ((file (assq-ref loc 'filename))
402 (line (assq-ref loc 'line))
403 (col (assq-ref loc 'column)))
404 ;; In accordance with the GCS, start line and column numbers at 1.
405 (location file (and line (+ line 1)) (and col (+ col 1)))))