gnu: Add signing-party.
[jackhill/guix/guix.git] / guix / utils.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
bbb7a00e 2;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
e3deeebb 3;;;
233e7676 4;;; This file is part of GNU Guix.
e3deeebb 5;;;
233e7676 6;;; GNU Guix is free software; you can redistribute it and/or modify it
e3deeebb
LC
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.
10;;;
233e7676 11;;; GNU Guix is distributed in the hope that it will be useful, but
e3deeebb
LC
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.
15;;;
16;;; You should have received a copy of the GNU General Public License
233e7676 17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
e3deeebb
LC
18
19(define-module (guix utils)
00e219d1 20 #:use-module (guix config)
38b3122a 21 #:use-module (srfi srfi-1)
72d86963 22 #:use-module (srfi srfi-9)
38b3122a 23 #:use-module (srfi srfi-26)
de4c3f26 24 #:use-module (srfi srfi-39)
e3deeebb
LC
25 #:use-module (srfi srfi-60)
26 #:use-module (rnrs bytevectors)
dba6b34b 27 #:use-module ((rnrs io ports) #:select (put-bytevector))
c8369cac 28 #:use-module (ice-9 vlist)
38b3122a 29 #:use-module (ice-9 format)
de4c3f26
LC
30 #:autoload (ice-9 popen) (open-pipe*)
31 #:autoload (ice-9 rdelim) (read-line)
98090557 32 #:use-module (ice-9 regex)
72d86963 33 #:use-module (ice-9 match)
8ef3401f 34 #:use-module (ice-9 format)
39b9372c 35 #:autoload (system foreign) (pointer->procedure)
ddc29a78 36 #:export (bytevector->base16-string
6d800a80 37 base16-string->bytevector
de4c3f26
LC
38 sha256
39
40 %nixpkgs-directory
41 nixpkgs-derivation
ce5d658c 42 nixpkgs-derivation*
de4c3f26 43
72d86963 44 define-record-type*
900f7267 45 compile-time-value
98090557 46 memoize
0af2c24e
LC
47 default-keyword-arguments
48 substitute-keyword-arguments
ff352cfb 49
64fc89b6 50 <location>
ff352cfb
LC
51 location
52 location?
53 location-file
54 location-line
55 location-column
56 source-properties->location
57
98090557 58 gnu-triplet->nix-system
9b48fb88
LC
59 %current-system
60 package-name->name+version))
e3deeebb 61
38b3122a 62\f
dba6b34b
LC
63;;;
64;;; Compile-time computations.
65;;;
66
67(define-syntax compile-time-value
68 (syntax-rules ()
69 "Evaluate the given expression at compile time. The expression must
70evaluate to a simple datum."
71 ((_ exp)
72 (let-syntax ((v (lambda (s)
73 (let ((val exp))
74 (syntax-case s ()
75 (_ #`'#,(datum->syntax s val)))))))
76 v))))
77
78\f
38b3122a
LC
79;;;
80;;; Base 16.
81;;;
82
83(define (bytevector->base16-string bv)
84 "Return the hexadecimal representation of BV's contents."
85 (define len
86 (bytevector-length bv))
87
88 (let-syntax ((base16-chars (lambda (s)
89 (syntax-case s ()
90 (_
91 (let ((v (list->vector
92 (unfold (cut > <> 255)
93 (lambda (n)
94 (format #f "~2,'0x" n))
95 1+
96 0))))
97 v))))))
98 (define chars base16-chars)
99 (let loop ((i 0)
100 (r '()))
101 (if (= i len)
102 (string-concatenate-reverse r)
103 (loop (+ 1 i)
104 (cons (vector-ref chars (bytevector-u8-ref bv i)) r))))))
105
6d800a80
LC
106(define base16-string->bytevector
107 (let ((chars->value (fold (lambda (i r)
108 (vhash-consv (string-ref (number->string i 16)
109 0)
110 i r))
111 vlist-null
112 (iota 16))))
113 (lambda (s)
114 "Return the bytevector whose hexadecimal representation is string S."
115 (define bv
116 (make-bytevector (quotient (string-length s) 2) 0))
117
118 (string-fold (lambda (chr i)
119 (let ((j (quotient i 2))
120 (v (and=> (vhash-assv chr chars->value) cdr)))
121 (if v
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)))
128 (+ i 1))
129 0
130 s)
131 bv)))
132
d0a92b75
LC
133\f
134;;;
135;;; Hash.
136;;;
137
39b9372c 138(define sha256
d388c2c4
LC
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
39b9372c
LC
144 (lambda (bv)
145 "Return the SHA256 of BV as a bytevector."
d388c2c4
LC
146 (let ((digest (make-bytevector (/ 256 8))))
147 (hash sha256 (bytevector->pointer digest)
148 (bytevector->pointer bv) (bytevector-length bv))
149 digest))))
de4c3f26
LC
150
151\f
152;;;
153;;; Nixpkgs.
154;;;
155
156(define %nixpkgs-directory
900f7267
LC
157 (make-parameter
158 ;; Capture the build-time value of $NIXPKGS.
a09ec3a5
LC
159 (or %nixpkgs
160 (and=> (getenv "NIXPKGS")
161 (lambda (val)
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 "")
166 #f
167 val))))))
de4c3f26 168
fbc93bed 169(define* (nixpkgs-derivation attribute #:optional (system (%current-system)))
de4c3f26 170 "Return the derivation path of ATTRIBUTE in Nixpkgs."
b86b0056 171 (let* ((p (open-pipe* OPEN_READ (or (getenv "NIX_INSTANTIATE")
00e219d1 172 %nix-instantiate)
fbc93bed
LC
173 "-A" attribute (%nixpkgs-directory)
174 "--argstr" "system" system))
de4c3f26
LC
175 (l (read-line p))
176 (s (close-pipe p)))
177 (and (zero? (status:exit-val s))
178 (not (eof-object? l))
179 l)))
180
ce5d658c
LC
181(define-syntax-rule (nixpkgs-derivation* attribute)
182 "Evaluate the given Nixpkgs derivation at compile-time."
183 (compile-time-value (nixpkgs-derivation attribute)))
184
de4c3f26
LC
185\f
186;;;
187;;; Miscellaneous.
188;;;
189
72d86963
LC
190(define-syntax define-record-type*
191 (lambda (s)
192 "Define the given record type such that an additional \"syntactic
193constructor\" is defined, which allows instances to be constructed with named
194field initializers, à la SRFI-35, as well as default values."
bbb7a00e 195 (define (make-syntactic-constructor type name ctor fields thunked defaults)
dcd60f43
LC
196 "Make the syntactic constructor NAME for TYPE, that calls CTOR, and
197expects all of FIELDS to be initialized. DEFAULTS is the list of
bbb7a00e
LC
198FIELD/DEFAULT-VALUE tuples, and THUNKED is the list of identifiers of
199thunked fields."
dcd60f43
LC
200 (with-syntax ((type type)
201 (name name)
72d86963
LC
202 (ctor ctor)
203 (expected fields)
204 (defaults defaults))
dcd60f43 205 #`(define-syntax name
72d86963 206 (lambda (s)
dcd60f43
LC
207 (define (record-inheritance orig-record field+value)
208 ;; Produce code that returns a record identical to
209 ;; ORIG-RECORD, except that values for the FIELD+VALUE alist
210 ;; prevail.
211 (define (field-inherited-value f)
212 (and=> (find (lambda (x)
213 (eq? f (car (syntax->datum x))))
214 field+value)
215 car))
216
217 #`(make-struct type 0
218 #,@(map (lambda (field index)
219 (or (field-inherited-value field)
220 #`(struct-ref #,orig-record
221 #,index)))
222 'expected
223 (iota (length 'expected)))))
224
bbb7a00e
LC
225 (define (thunked-field? f)
226 (memq (syntax->datum f) '#,thunked))
227
228 (define (field-bindings field+value)
229 ;; Return field to value bindings, for use in `letrec*' below.
230 (map (lambda (field+value)
231 (syntax-case field+value ()
232 ((field value)
233 #`(field
234 #,(if (thunked-field? #'field)
235 #'(lambda () value)
236 #'value)))))
237 field+value))
dcd60f43
LC
238
239 (syntax-case s (inherit #,@fields)
240 ((_ (inherit orig-record) (field value) (... ...))
bbb7a00e 241 #`(letrec* #,(field-bindings #'((field value) (... ...)))
dcd60f43
LC
242 #,(record-inheritance #'orig-record
243 #'((field value) (... ...)))))
72d86963 244 ((_ (field value) (... ...))
8fd5bd2b
LC
245 (let ((fields (map syntax->datum #'(field (... ...))))
246 (dflt (map (match-lambda
247 ((f v)
248 (list (syntax->datum f) v)))
249 #'defaults)))
250
dcd60f43
LC
251 (define (field-value f)
252 (or (and=> (find (lambda (x)
253 (eq? f (car (syntax->datum x))))
254 #'((field value) (... ...)))
255 car)
bbb7a00e
LC
256 (let ((value
257 (car (assoc-ref dflt
258 (syntax->datum f)))))
259 (if (thunked-field? f)
260 #`(lambda () #,value)
261 value))))
72d86963 262
8ef3401f
LC
263 (let-syntax ((error*
264 (syntax-rules ()
265 ((_ fmt args (... ...))
266 (syntax-violation 'name
267 (format #f fmt args
268 (... ...))
269 s)))))
270 (let ((fields (append fields (map car dflt))))
271 (cond ((lset= eq? fields 'expected)
bbb7a00e
LC
272 #`(letrec* #,(field-bindings
273 #'((field value) (... ...)))
8fd5bd2b 274 (ctor #,@(map field-value 'expected))))
8ef3401f
LC
275 ((pair? (lset-difference eq? fields 'expected))
276 (error* "extraneous field initializers ~a"
277 (lset-difference eq? fields 'expected)))
278 (else
279 (error* "missing field initializers ~a"
280 (lset-difference eq? 'expected
281 fields)))))))))))))
72d86963
LC
282
283 (define (field-default-value s)
284 (syntax-case s (default)
285 ((field (default val) _ ...)
286 (list #'field #'val))
287 ((field _ options ...)
288 (field-default-value #'(field options ...)))
289 (_ #f)))
290
bbb7a00e
LC
291 (define (thunked-field? s)
292 ;; Return the field name if the field defined by S is thunked.
293 (syntax-case s (thunked)
294 ((field (thunked) _ ...)
295 #'field)
296 ((field _ options ...)
297 (thunked-field? #'(field options ...)))
298 (_ #f)))
299
300 (define (thunked-field-accessor-name field)
301 ;; Return the name (an unhygienic syntax object) of the "real"
302 ;; getter for field, which is assumed to be a thunked field.
303 (syntax-case field ()
304 ((field get options ...)
305 (let* ((getter (syntax->datum #'get))
306 (real-getter (symbol-append '% getter '-real)))
307 (datum->syntax #'get real-getter)))))
308
309 (define (field-spec->srfi-9 field)
310 ;; Convert a field spec of our style to a SRFI-9 field spec of the
311 ;; form (field get).
312 (syntax-case field ()
313 ((name get options ...)
314 #`(name
315 #,(if (thunked-field? field)
316 (thunked-field-accessor-name field)
317 #'get)))))
318
319 (define (thunked-field-accessor-definition field)
320 ;; Return the real accessor for FIELD, which is assumed to be a
321 ;; thunked field.
322 (syntax-case field ()
323 ((name get _ ...)
324 (with-syntax ((real-get (thunked-field-accessor-name field)))
325 #'(define-inlinable (get x)
326 ;; The real value of that field is a thunk, so call it.
327 ((real-get x)))))))
328
72d86963
LC
329 (syntax-case s ()
330 ((_ type syntactic-ctor ctor pred
331 (field get options ...) ...)
bbb7a00e
LC
332 (let* ((field-spec #'((field get options ...) ...)))
333 (with-syntax (((field-spec* ...)
334 (map field-spec->srfi-9 field-spec))
335 ((thunked-field-accessor ...)
336 (filter-map (lambda (field)
337 (and (thunked-field? field)
338 (thunked-field-accessor-definition
339 field)))
340 field-spec)))
341 #`(begin
342 (define-record-type type
343 (ctor field ...)
344 pred
345 field-spec* ...)
346 (begin thunked-field-accessor ...)
347 #,(make-syntactic-constructor #'type #'syntactic-ctor #'ctor
348 #'(field ...)
349 (filter-map thunked-field? field-spec)
350 (filter-map field-default-value
351 #'((field options ...)
352 ...))))))))))
72d86963 353
de4c3f26
LC
354(define (memoize proc)
355 "Return a memoizing version of PROC."
356 (let ((cache (make-hash-table)))
357 (lambda args
358 (let ((results (hash-ref cache args)))
359 (if results
360 (apply values results)
361 (let ((results (call-with-values (lambda ()
362 (apply proc args))
363 list)))
364 (hash-set! cache args results)
365 (apply values results)))))))
98090557 366
0af2c24e
LC
367(define (default-keyword-arguments args defaults)
368 "Return ARGS augmented with any keyword/value from DEFAULTS for
369keywords not already present in ARGS."
370 (let loop ((defaults defaults)
371 (args args))
372 (match defaults
373 ((kw value rest ...)
374 (loop rest
375 (if (assoc-ref kw args)
376 args
377 (cons* kw value args))))
378 (()
379 args))))
380
381(define-syntax substitute-keyword-arguments
382 (syntax-rules ()
383 "Return a new list of arguments where the value for keyword arg KW is
384replaced by EXP. EXP is evaluated in a context where VAR is boud to the
385previous value of the keyword argument."
386 ((_ original-args ((kw var) exp) ...)
387 (let loop ((args original-args)
388 (before '()))
389 (match args
390 ((kw var rest (... ...))
391 (loop rest (cons* exp kw before)))
392 ...
393 ((x rest (... ...))
394 (loop rest (cons x before)))
395 (()
396 (reverse before)))))))
397
98090557
LC
398(define (gnu-triplet->nix-system triplet)
399 "Return the Nix system type corresponding to TRIPLET, a GNU triplet as
400returned by `config.guess'."
401 (let ((triplet (cond ((string-match "^i[345]86-(.*)$" triplet)
402 =>
403 (lambda (m)
404 (string-append "i686-" (match:substring m 1))))
405 (else triplet))))
406 (cond ((string-match "^([^-]+)-([^-]+-)?linux-gnu.*" triplet)
407 =>
408 (lambda (m)
409 ;; Nix omits `-gnu' for GNU/Linux.
410 (string-append (match:substring m 1) "-linux")))
411 ((string-match "^([^-]+)-([^-]+-)?([[:alpha:]]+)([0-9]+\\.?)*$" triplet)
412 =>
413 (lambda (m)
414 ;; Nix strip the version number from names such as `gnu0.3',
415 ;; `darwin10.2.0', etc., and always strips the vendor part.
416 (string-append (match:substring m 1) "-"
417 (match:substring m 3))))
418 (else triplet))))
419
420(define %current-system
421 ;; System type as expected by Nix, usually ARCHITECTURE-KERNEL.
d8eea3d2
LC
422 ;; By default, this is equal to (gnu-triplet->nix-system %host-type).
423 (make-parameter %system))
ff352cfb 424
9b48fb88
LC
425(define (package-name->name+version name)
426 "Given NAME, a package name like \"foo-0.9.1b\", return two values:
427\"foo\" and \"0.9.1b\". When the version part is unavailable, NAME and
428#f are returned. The first hyphen followed by a digit is considered to
429introduce the version part."
430 ;; See also `DrvName' in Nix.
431
432 (define number?
433 (cut char-set-contains? char-set:digit <>))
434
435 (let loop ((chars (string->list name))
436 (prefix '()))
437 (match chars
438 (()
439 (values name #f))
440 ((#\- (? number? n) rest ...)
441 (values (list->string (reverse prefix))
442 (list->string (cons n rest))))
443 ((head tail ...)
444 (loop tail (cons head prefix))))))
445
ff352cfb
LC
446\f
447;;;
448;;; Source location.
449;;;
450
451;; A source location.
452(define-record-type <location>
453 (make-location file line column)
454 location?
455 (file location-file) ; file name
456 (line location-line) ; 1-indexed line
457 (column location-column)) ; 0-indexed column
458
459(define location
460 (memoize
461 (lambda (file line column)
462 "Return the <location> object for the given FILE, LINE, and COLUMN."
463 (and line column file
464 (make-location file line column)))))
465
466(define (source-properties->location loc)
467 "Return a location object based on the info in LOC, an alist as returned
468by Guile's `source-properties', `frame-source', `current-source-location',
469etc."
470 (let ((file (assq-ref loc 'filename))
471 (line (assq-ref loc 'line))
472 (col (assq-ref loc 'column)))
5e6c9012
LC
473 ;; In accordance with the GCS, start line and column numbers at 1.
474 (location file (and line (+ line 1)) (and col (+ col 1)))))