2010-04-13 01:02:36 +02:00
|
|
|
|
#!/bin/sh
|
2010-07-04 23:11:19 +02:00
|
|
|
|
# This is actually -*- mode: scheme; coding: utf-8; -*- text.
|
|
|
|
|
main='(module-ref (resolve-module '\''(gnupdate)) '\'gnupdate')'
|
|
|
|
|
exec ${GUILE-guile} -L "$PWD" -l "$0" \
|
|
|
|
|
-c "(apply $main (command-line))" "$@"
|
|
|
|
|
!#
|
|
|
|
|
;;; GNUpdate -- Update GNU packages in Nixpkgs.
|
2011-01-24 23:29:29 +01:00
|
|
|
|
;;; Copyright (C) 2010, 2011 Ludovic Courtès <ludo@gnu.org>
|
2010-07-04 23:11:19 +02:00
|
|
|
|
;;;
|
|
|
|
|
;;; This program 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.
|
|
|
|
|
;;;
|
|
|
|
|
;;; This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
2010-04-13 01:02:36 +02:00
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(cond-expand (guile-2 #t)
|
|
|
|
|
(else (error "GNU Guile 2.0 is required")))
|
|
|
|
|
|
|
|
|
|
(define-module (gnupdate)
|
|
|
|
|
#:use-module (sxml ssax)
|
|
|
|
|
#:use-module (ice-9 popen)
|
|
|
|
|
#:use-module (ice-9 match)
|
|
|
|
|
#:use-module (ice-9 rdelim)
|
2011-04-12 10:05:16 +02:00
|
|
|
|
#:use-module (ice-9 format)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
#:use-module (ice-9 regex)
|
|
|
|
|
#:use-module (ice-9 vlist)
|
|
|
|
|
#:use-module (srfi srfi-1)
|
|
|
|
|
#:use-module (srfi srfi-9)
|
|
|
|
|
#:use-module (srfi srfi-11)
|
|
|
|
|
#:use-module (srfi srfi-26)
|
|
|
|
|
#:use-module (srfi srfi-37)
|
|
|
|
|
#:use-module (system foreign)
|
|
|
|
|
#:use-module (rnrs bytevectors)
|
|
|
|
|
#:export (gnupdate))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; SNix.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define-record-type <location>
|
|
|
|
|
(make-location file line column)
|
|
|
|
|
location?
|
|
|
|
|
(file location-file)
|
|
|
|
|
(line location-line)
|
|
|
|
|
(column location-column))
|
|
|
|
|
|
|
|
|
|
(define (->loc line column path)
|
|
|
|
|
(and line column path
|
|
|
|
|
(make-location path (string->number line) (string->number column))))
|
|
|
|
|
|
|
|
|
|
;; Nix object types visible in the XML output of `nix-instantiate' and
|
|
|
|
|
;; mapping to S-expressions (we map to sexps, not records, so that we
|
|
|
|
|
;; can do pattern matching):
|
|
|
|
|
;;
|
|
|
|
|
;; at (at varpat attrspat)
|
|
|
|
|
;; attr (attribute loc name value)
|
|
|
|
|
;; attrs (attribute-set attributes)
|
|
|
|
|
;; attrspat (attribute-set-pattern patterns)
|
|
|
|
|
;; bool #f|#t
|
|
|
|
|
;; derivation (derivation drv-path out-path attributes)
|
|
|
|
|
;; ellipsis '...
|
|
|
|
|
;; expr (snix loc body ...)
|
|
|
|
|
;; function (function loc at|attrspat|varpat)
|
|
|
|
|
;; int int
|
|
|
|
|
;; list list
|
|
|
|
|
;; null 'null
|
|
|
|
|
;; path string
|
|
|
|
|
;; string string
|
|
|
|
|
;; unevaluated 'unevaluated
|
|
|
|
|
;; varpat (varpat name)
|
|
|
|
|
;;
|
|
|
|
|
;; Initially ATTRIBUTES in `derivation' and `attribute-set' was a promise;
|
|
|
|
|
;; however, handling `repeated' nodes makes it impossible to do anything
|
|
|
|
|
;; lazily because the whole SXML tree has to be traversed to maintain the
|
|
|
|
|
;; list of known derivations.
|
|
|
|
|
|
|
|
|
|
(define (xml-element->snix elem attributes body derivations)
|
|
|
|
|
;; Return an SNix element corresponding to XML element ELEM.
|
|
|
|
|
|
|
|
|
|
(define (loc)
|
|
|
|
|
(->loc (assq-ref attributes 'line)
|
|
|
|
|
(assq-ref attributes 'column)
|
|
|
|
|
(assq-ref attributes 'path)))
|
|
|
|
|
|
|
|
|
|
(case elem
|
|
|
|
|
((at)
|
|
|
|
|
(values `(at ,(car body) ,(cadr body)) derivations))
|
|
|
|
|
((attr)
|
|
|
|
|
(let ((name (assq-ref attributes 'name)))
|
|
|
|
|
(cond ((null? body)
|
|
|
|
|
(values `(attribute-pattern ,name) derivations))
|
|
|
|
|
((and (pair? body) (null? (cdr body)))
|
|
|
|
|
(values `(attribute ,(loc) ,name ,(car body))
|
|
|
|
|
derivations))
|
|
|
|
|
(else
|
|
|
|
|
(error "invalid attribute body" name (loc) body)))))
|
|
|
|
|
((attrs)
|
|
|
|
|
(values `(attribute-set ,(reverse body)) derivations))
|
|
|
|
|
((attrspat)
|
|
|
|
|
(values `(attribute-set-pattern ,body) derivations))
|
|
|
|
|
((bool)
|
|
|
|
|
(values (string-ci=? "true" (assq-ref attributes 'value))
|
|
|
|
|
derivations))
|
|
|
|
|
((derivation)
|
|
|
|
|
(let ((drv-path (assq-ref attributes 'drvPath))
|
|
|
|
|
(out-path (assq-ref attributes 'outPath)))
|
|
|
|
|
(if (equal? body '(repeated))
|
|
|
|
|
(let ((body (vhash-assoc drv-path derivations)))
|
|
|
|
|
(if (pair? body)
|
|
|
|
|
(values `(derivation ,drv-path ,out-path ,(cdr body))
|
|
|
|
|
derivations)
|
2011-04-12 10:05:20 +02:00
|
|
|
|
|
|
|
|
|
;; DRV-PATH hasn't been encountered yet but may be later
|
|
|
|
|
;; (see <http://article.gmane.org/gmane.linux.distributions.nixos/5946>.)
|
|
|
|
|
;; Return an `unresolved' node.
|
|
|
|
|
(values `(unresolved
|
|
|
|
|
,(lambda (derivations)
|
|
|
|
|
(let ((body (vhash-assoc drv-path derivations)))
|
|
|
|
|
(if (pair? body)
|
|
|
|
|
`(derivation ,drv-path ,out-path
|
|
|
|
|
,(cdr body))
|
|
|
|
|
(error "no previous occurrence of derivation"
|
|
|
|
|
drv-path)))))
|
|
|
|
|
derivations)))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(values `(derivation ,drv-path ,out-path ,body)
|
|
|
|
|
(vhash-cons drv-path body derivations)))))
|
|
|
|
|
((ellipsis)
|
|
|
|
|
(values '... derivations))
|
|
|
|
|
((expr)
|
|
|
|
|
(values `(snix ,(loc) ,@body) derivations))
|
|
|
|
|
((function)
|
|
|
|
|
(values `(function ,(loc) ,body) derivations))
|
|
|
|
|
((int)
|
|
|
|
|
(values (string->number (assq-ref attributes 'value))
|
|
|
|
|
derivations))
|
|
|
|
|
((list)
|
|
|
|
|
(values body derivations))
|
|
|
|
|
((null)
|
|
|
|
|
(values 'null derivations))
|
|
|
|
|
((path)
|
|
|
|
|
(values (assq-ref attributes 'value) derivations))
|
|
|
|
|
((repeated)
|
|
|
|
|
(values 'repeated derivations))
|
|
|
|
|
((string)
|
|
|
|
|
(values (assq-ref attributes 'value) derivations))
|
|
|
|
|
((unevaluated)
|
|
|
|
|
(values 'unevaluated derivations))
|
|
|
|
|
((varpat)
|
|
|
|
|
(values `(varpat ,(assq-ref attributes 'name)) derivations))
|
|
|
|
|
(else (error "unhandled Nix XML element" elem))))
|
|
|
|
|
|
2011-04-12 10:05:20 +02:00
|
|
|
|
(define (resolve snix derivations)
|
|
|
|
|
"Return a new SNix tree where `unresolved' nodes from SNIX have been
|
|
|
|
|
replaced by the result of their application to DERIVATIONS, a vhash."
|
|
|
|
|
(let loop ((node snix)
|
|
|
|
|
(seen vlist-null))
|
|
|
|
|
(if (vhash-assq node seen)
|
|
|
|
|
(values node seen)
|
|
|
|
|
(match node
|
|
|
|
|
(('unresolved proc)
|
|
|
|
|
(let ((n (proc derivations)))
|
|
|
|
|
(values n seen)))
|
|
|
|
|
((tag body ...)
|
|
|
|
|
(let ((body+seen (fold (lambda (n body+seen)
|
|
|
|
|
(call-with-values
|
|
|
|
|
(lambda ()
|
|
|
|
|
(loop n (cdr body+seen)))
|
|
|
|
|
(lambda (n* seen)
|
|
|
|
|
(cons (cons n* (car body+seen))
|
|
|
|
|
(vhash-consq n #t seen)))))
|
|
|
|
|
(cons '() (vhash-consq node #t seen))
|
|
|
|
|
body)))
|
|
|
|
|
(values (cons tag (reverse (car body+seen)))
|
|
|
|
|
(vhash-consq node #t (cdr body+seen)))))
|
|
|
|
|
(anything
|
|
|
|
|
(values anything seen))))))
|
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(define xml->snix
|
|
|
|
|
;; Return the SNix represention of TREE, an SXML tree as returned by
|
|
|
|
|
;; parsing the XML output of `nix-instantiate' on Nixpkgs.
|
|
|
|
|
(let ((parse
|
|
|
|
|
(ssax:make-parser NEW-LEVEL-SEED
|
|
|
|
|
(lambda (elem-gi attributes namespaces expected-content
|
|
|
|
|
seed)
|
|
|
|
|
(cons '() (cdr seed)))
|
|
|
|
|
|
|
|
|
|
FINISH-ELEMENT
|
|
|
|
|
(lambda (elem-gi attributes namespaces parent-seed
|
|
|
|
|
seed)
|
|
|
|
|
(let ((snix (car seed))
|
|
|
|
|
(derivations (cdr seed)))
|
|
|
|
|
(let-values (((snix derivations)
|
|
|
|
|
(xml-element->snix elem-gi
|
|
|
|
|
attributes
|
|
|
|
|
snix
|
|
|
|
|
derivations)))
|
|
|
|
|
(cons (cons snix (car parent-seed))
|
|
|
|
|
derivations))))
|
|
|
|
|
|
|
|
|
|
CHAR-DATA-HANDLER
|
|
|
|
|
(lambda (string1 string2 seed)
|
|
|
|
|
;; Discard inter-node strings, which are blanks.
|
|
|
|
|
seed))))
|
|
|
|
|
(lambda (port)
|
2011-04-12 10:05:20 +02:00
|
|
|
|
(match (parse port (cons '() vlist-null))
|
|
|
|
|
(((snix) . derivations)
|
|
|
|
|
(resolve snix derivations))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
(define (call-with-package snix proc)
|
|
|
|
|
(match snix
|
|
|
|
|
(('attribute _ (and attribute-name (? string?))
|
|
|
|
|
('derivation _ _ body))
|
|
|
|
|
;; Ugly pattern matching.
|
|
|
|
|
(let ((meta
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "meta" ('attribute-set metas)) metas)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(package-name
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "name" (and name (? string?)))
|
|
|
|
|
name)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(location
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute loc "name" (? string?))
|
|
|
|
|
loc)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(src
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "src" src)
|
|
|
|
|
src)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body)))
|
|
|
|
|
(proc attribute-name package-name location meta src)))))
|
|
|
|
|
|
|
|
|
|
(define (call-with-src snix proc)
|
|
|
|
|
;; Assume SNIX contains the SNix expression for the value of an `src'
|
|
|
|
|
;; attribute, as returned by `call-with-package', and call PROC with the
|
|
|
|
|
;; relevant SRC information, or #f if SNIX doesn't match.
|
|
|
|
|
(match snix
|
|
|
|
|
(('derivation _ _ body)
|
|
|
|
|
(let ((name
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "name" (and name (? string?)))
|
|
|
|
|
name)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(output-hash
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "outputHash" (and hash (? string?)))
|
|
|
|
|
hash)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(urls
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "urls" (and urls (? pair?)))
|
|
|
|
|
urls)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body)))
|
|
|
|
|
(proc name output-hash urls)))
|
|
|
|
|
(_ (proc #f #f #f))))
|
|
|
|
|
|
|
|
|
|
(define (src->values snix)
|
|
|
|
|
(call-with-src snix values))
|
|
|
|
|
|
|
|
|
|
(define (attribute-value attribute)
|
|
|
|
|
;; Return the value of ATTRIBUTE.
|
|
|
|
|
(match attribute
|
|
|
|
|
(('attribute _ _ value) value)))
|
|
|
|
|
|
|
|
|
|
(define (derivation-source derivation)
|
|
|
|
|
;; Return the "src" attribute of DERIVATION or #f if not found.
|
|
|
|
|
(match derivation
|
|
|
|
|
(('derivation _ _ (attributes ...))
|
|
|
|
|
(find-attribute-by-name "src" attributes))))
|
|
|
|
|
|
|
|
|
|
(define (derivation-output-path derivation)
|
|
|
|
|
;; Return the output path of DERIVATION.
|
|
|
|
|
(match derivation
|
|
|
|
|
(('derivation _ out-path _)
|
|
|
|
|
out-path)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
|
|
|
|
|
(define (source-output-path src)
|
|
|
|
|
;; Return the output path of SRC, the "src" attribute of a derivation.
|
|
|
|
|
(derivation-output-path (attribute-value src)))
|
|
|
|
|
|
|
|
|
|
(define (derivation-source-output-path derivation)
|
|
|
|
|
;; Return the output path of the "src" attribute of DERIVATION or #f if
|
|
|
|
|
;; DERIVATION lacks an "src" attribute.
|
|
|
|
|
(and=> (derivation-source derivation) source-output-path))
|
|
|
|
|
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(define* (open-nixpkgs nixpkgs #:optional attribute)
|
|
|
|
|
;; Return an input pipe to the XML representation of Nixpkgs. When
|
|
|
|
|
;; ATTRIBUTE is true, only that attribute is considered.
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(let ((script (string-append nixpkgs
|
|
|
|
|
"/maintainers/scripts/eval-release.nix")))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(apply open-pipe* OPEN_READ
|
|
|
|
|
"nix-instantiate" "--strict" "--eval-only" "--xml"
|
|
|
|
|
`(,@(if attribute
|
|
|
|
|
`("-A" ,attribute)
|
|
|
|
|
'())
|
|
|
|
|
,script))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
2011-03-04 14:18:56 +01:00
|
|
|
|
(define (pipe-failed? pipe)
|
|
|
|
|
"Close pipe and return its status if it failed."
|
|
|
|
|
(let ((status (close-pipe pipe)))
|
|
|
|
|
(if (or (status:term-sig status)
|
|
|
|
|
(not (= (status:exit-val status) 0)))
|
|
|
|
|
status
|
|
|
|
|
#f)))
|
|
|
|
|
|
2011-10-30 02:00:20 +02:00
|
|
|
|
(define (memoize proc)
|
|
|
|
|
"Return a memoizing version of PROC."
|
|
|
|
|
(let ((cache (make-hash-table)))
|
|
|
|
|
(lambda args
|
|
|
|
|
(let ((results (hash-ref cache args)))
|
|
|
|
|
(if results
|
|
|
|
|
(apply values results)
|
|
|
|
|
(let ((results (call-with-values (lambda ()
|
|
|
|
|
(apply proc args))
|
|
|
|
|
list)))
|
|
|
|
|
(hash-set! cache args results)
|
|
|
|
|
(apply values results)))))))
|
|
|
|
|
|
|
|
|
|
(define nix-prefetch-url
|
|
|
|
|
(memoize
|
|
|
|
|
(lambda (url)
|
|
|
|
|
"Download URL in the Nix store and return the base32-encoded SHA256 hash of
|
|
|
|
|
the file at URL."
|
|
|
|
|
(let* ((pipe (open-pipe* OPEN_READ "nix-prefetch-url" url))
|
|
|
|
|
(hash (read-line pipe)))
|
|
|
|
|
(if (or (pipe-failed? pipe)
|
|
|
|
|
(eof-object? hash))
|
|
|
|
|
(values #f #f)
|
|
|
|
|
(let* ((pipe (open-pipe* OPEN_READ "nix-store" "--print-fixed-path"
|
|
|
|
|
"sha256" hash (basename url)))
|
|
|
|
|
(path (read-line pipe)))
|
|
|
|
|
(if (or (pipe-failed? pipe)
|
|
|
|
|
(eof-object? path))
|
|
|
|
|
(values #f #f)
|
|
|
|
|
(values (string-trim-both hash) (string-trim-both path)))))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
(define (update-nix-expression file
|
|
|
|
|
old-version old-hash
|
|
|
|
|
new-version new-hash)
|
|
|
|
|
;; Modify FILE in-place. Ugly: we call out to sed(1).
|
|
|
|
|
(let ((cmd (format #f "sed -i \"~a\" -e 's/~A/~a/g ; s/~A/~A/g'"
|
|
|
|
|
file
|
|
|
|
|
(regexp-quote old-version) new-version
|
|
|
|
|
old-hash
|
|
|
|
|
(or new-hash "new hash not available, check the log"))))
|
|
|
|
|
(format #t "running `~A'...~%" cmd)
|
|
|
|
|
(system cmd)))
|
|
|
|
|
|
|
|
|
|
(define (find-attribute-by-name name attributes)
|
|
|
|
|
;; Return attribute NAME in ATTRIBUTES, a list of SNix attributes, or #f if
|
|
|
|
|
;; NAME cannot be found.
|
|
|
|
|
(find (lambda (a)
|
|
|
|
|
(match a
|
|
|
|
|
(('attribute _ (? (cut string=? <> name)) _)
|
|
|
|
|
a)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
attributes))
|
|
|
|
|
|
|
|
|
|
(define (find-package-by-attribute-name name packages)
|
|
|
|
|
;; Return the package bound to attribute NAME in PACKAGES, a list of
|
|
|
|
|
;; packages (SNix attributes), or #f if NAME cannot be found.
|
|
|
|
|
(find (lambda (package)
|
|
|
|
|
(match package
|
|
|
|
|
(('attribute _ (? (cut string=? <> name))
|
|
|
|
|
('derivation _ _ _))
|
|
|
|
|
package)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
packages))
|
|
|
|
|
|
|
|
|
|
(define (stdenv-package packages)
|
|
|
|
|
;; Return the `stdenv' package from PACKAGES, a list of SNix attributes.
|
|
|
|
|
(find-package-by-attribute-name "stdenv" packages))
|
|
|
|
|
|
|
|
|
|
(define (package-requisites package)
|
|
|
|
|
;; Return the list of derivations required to build PACKAGE (including that
|
|
|
|
|
;; of PACKAGE) by recurring into its derivation attributes.
|
|
|
|
|
(let loop ((snix package)
|
|
|
|
|
(result '()))
|
|
|
|
|
(match snix
|
|
|
|
|
(('attribute _ _ body)
|
|
|
|
|
(loop body result))
|
|
|
|
|
(('derivation _ out-path body)
|
|
|
|
|
(if (any (lambda (d)
|
|
|
|
|
(match d
|
|
|
|
|
(('derivation _ (? (cut string=? out-path <>)) _) #t)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
result)
|
|
|
|
|
result
|
|
|
|
|
(loop body (cons snix result))))
|
|
|
|
|
((things ...)
|
|
|
|
|
(fold loop result things))
|
|
|
|
|
(_ result))))
|
|
|
|
|
|
|
|
|
|
(define (package-source-output-path package)
|
|
|
|
|
;; Return the output path of the "src" derivation of PACKAGE.
|
|
|
|
|
(derivation-source-output-path (attribute-value package)))
|
|
|
|
|
|
2011-09-05 01:06:07 +02:00
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; GnuPG interface.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define %gpg-command "gpg2")
|
|
|
|
|
(define %openpgp-key-server "keys.gnupg.net")
|
|
|
|
|
|
|
|
|
|
(define (gnupg-verify sig file)
|
2011-10-30 02:00:16 +02:00
|
|
|
|
"Verify signature SIG for FILE. Return a status s-exp if GnuPG failed."
|
2011-09-05 01:06:07 +02:00
|
|
|
|
|
|
|
|
|
(define (status-line->sexp line)
|
|
|
|
|
;; See file `doc/DETAILS' in GnuPG.
|
|
|
|
|
(define sigid-rx
|
|
|
|
|
(make-regexp
|
|
|
|
|
"^\\[GNUPG:\\] SIG_ID ([A-Za-z0-9/]+) ([[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}) ([[:digit:]]+)"))
|
|
|
|
|
(define goodsig-rx
|
|
|
|
|
(make-regexp "^\\[GNUPG:\\] GOODSIG ([[:xdigit:]]+) (.+)$"))
|
|
|
|
|
(define validsig-rx
|
|
|
|
|
(make-regexp
|
|
|
|
|
"^\\[GNUPG:\\] VALIDSIG ([[:xdigit:]]+) ([[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}) ([[:digit:]]+) .*$"))
|
2011-12-01 23:44:04 +01:00
|
|
|
|
(define expkeysig-rx ; good signature, but expired key
|
|
|
|
|
(make-regexp "^\\[GNUPG:\\] EXPKEYSIG ([[:xdigit:]]+) (.*)$"))
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(define errsig-rx
|
|
|
|
|
(make-regexp
|
|
|
|
|
"^\\[GNUPG:\\] ERRSIG ([[:xdigit:]]+) ([^ ]+) ([^ ]+) ([^ ]+) ([[:digit:]]+) ([[:digit:]]+)"))
|
|
|
|
|
|
|
|
|
|
(cond ((regexp-exec sigid-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
`(signature-id ,(match:substring match 1) ; sig id
|
|
|
|
|
,(match:substring match 2) ; date
|
2011-12-01 23:44:04 +01:00
|
|
|
|
,(string->number ; timestamp
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(match:substring match 3)))))
|
|
|
|
|
((regexp-exec goodsig-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
2011-12-01 23:44:04 +01:00
|
|
|
|
`(good-signature ,(match:substring match 1) ; key id
|
2011-09-05 01:06:07 +02:00
|
|
|
|
,(match:substring match 2)))) ; user name
|
|
|
|
|
((regexp-exec validsig-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
`(valid-signature ,(match:substring match 1) ; fingerprint
|
|
|
|
|
,(match:substring match 2) ; sig creation date
|
2011-12-01 23:44:04 +01:00
|
|
|
|
,(string->number ; timestamp
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(match:substring match 3)))))
|
2011-12-01 23:44:04 +01:00
|
|
|
|
((regexp-exec expkeysig-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
`(expired-key-signature ,(match:substring match 1) ; fingerprint
|
|
|
|
|
,(match:substring match 2)))) ; user name
|
2011-09-05 01:06:07 +02:00
|
|
|
|
((regexp-exec errsig-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
`(signature-error ,(match:substring match 1) ; key id or fingerprint
|
|
|
|
|
,(match:substring match 2) ; pubkey algo
|
|
|
|
|
,(match:substring match 3) ; hash algo
|
|
|
|
|
,(match:substring match 4) ; sig class
|
2011-12-01 23:44:04 +01:00
|
|
|
|
,(string->number ; timestamp
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(match:substring match 5))
|
|
|
|
|
,(let ((rc
|
|
|
|
|
(string->number ; return code
|
|
|
|
|
(match:substring match 6))))
|
|
|
|
|
(case rc
|
|
|
|
|
((9) 'missing-key)
|
|
|
|
|
((4) 'unknown-algorithm)
|
|
|
|
|
(else rc))))))
|
|
|
|
|
(else
|
|
|
|
|
`(unparsed-line ,line))))
|
|
|
|
|
|
|
|
|
|
(define (parse-status input)
|
|
|
|
|
(let loop ((line (read-line input))
|
|
|
|
|
(result '()))
|
|
|
|
|
(if (eof-object? line)
|
|
|
|
|
(reverse result)
|
|
|
|
|
(loop (read-line input)
|
|
|
|
|
(cons (status-line->sexp line) result)))))
|
|
|
|
|
|
|
|
|
|
(let* ((pipe (open-pipe* OPEN_READ %gpg-command "--status-fd=1"
|
|
|
|
|
"--verify" sig file))
|
|
|
|
|
(status (parse-status pipe)))
|
2011-10-30 02:00:16 +02:00
|
|
|
|
;; Ignore PIPE's exit status since STATUS above should contain all the
|
|
|
|
|
;; info we need.
|
|
|
|
|
(close-pipe pipe)
|
|
|
|
|
status))
|
2011-09-05 01:06:07 +02:00
|
|
|
|
|
|
|
|
|
(define (gnupg-status-good-signature? status)
|
|
|
|
|
"If STATUS, as returned by `gnupg-verify', denotes a good signature, return
|
|
|
|
|
a key-id/user pair; return #f otherwise."
|
|
|
|
|
(any (lambda (sexp)
|
|
|
|
|
(match sexp
|
2011-12-01 23:44:04 +01:00
|
|
|
|
(((or 'good-signature 'expired-key-signature) key-id user)
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(cons key-id user))
|
|
|
|
|
(_ #f)))
|
|
|
|
|
status))
|
|
|
|
|
|
|
|
|
|
(define (gnupg-status-missing-key? status)
|
|
|
|
|
"If STATUS denotes a missing-key error, then return the key-id of the
|
|
|
|
|
missing key."
|
|
|
|
|
(any (lambda (sexp)
|
|
|
|
|
(match sexp
|
|
|
|
|
(('signature-error key-id _ ...)
|
|
|
|
|
key-id)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
status))
|
|
|
|
|
|
|
|
|
|
(define (gnupg-receive-keys key-id)
|
|
|
|
|
(system* %gpg-command "--keyserver" %openpgp-key-server "--recv-keys" key-id))
|
|
|
|
|
|
|
|
|
|
(define (gnupg-verify* sig file)
|
|
|
|
|
"Like `gnupg-verify', but try downloading the public key if it's missing.
|
|
|
|
|
Return #t if the signature was good, #f otherwise."
|
|
|
|
|
(let ((status (gnupg-verify sig file)))
|
|
|
|
|
(or (gnupg-status-good-signature? status)
|
|
|
|
|
(let ((missing (gnupg-status-missing-key? status)))
|
|
|
|
|
(and missing
|
|
|
|
|
(begin
|
|
|
|
|
;; Download the missing key and try again.
|
|
|
|
|
(gnupg-receive-keys missing)
|
|
|
|
|
(gnupg-status-good-signature? (gnupg-verify sig file))))))))
|
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; FTP client.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define-record-type <ftp-connection>
|
|
|
|
|
(%make-ftp-connection socket addrinfo)
|
|
|
|
|
ftp-connection?
|
|
|
|
|
(socket ftp-connection-socket)
|
|
|
|
|
(addrinfo ftp-connection-addrinfo))
|
|
|
|
|
|
|
|
|
|
(define %ftp-ready-rx
|
|
|
|
|
(make-regexp "^([0-9]{3}) (.+)$"))
|
|
|
|
|
|
|
|
|
|
(define (%ftp-listen port)
|
|
|
|
|
(let loop ((line (read-line port)))
|
|
|
|
|
(cond ((eof-object? line) (values line #f))
|
|
|
|
|
((regexp-exec %ftp-ready-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(values (string->number (match:substring match 1))
|
|
|
|
|
(match:substring match 2))))
|
|
|
|
|
(else
|
|
|
|
|
(loop (read-line port))))))
|
|
|
|
|
|
|
|
|
|
(define (%ftp-command command expected-code port)
|
|
|
|
|
(format port "~A~A~A" command (string #\return) (string #\newline))
|
|
|
|
|
(let-values (((code message) (%ftp-listen port)))
|
|
|
|
|
(if (eqv? code expected-code)
|
|
|
|
|
message
|
|
|
|
|
(throw 'ftp-error port command code message))))
|
|
|
|
|
|
|
|
|
|
(define (%ftp-login user pass port)
|
|
|
|
|
(let ((command (string-append "USER " user (string #\newline))))
|
|
|
|
|
(display command port)
|
|
|
|
|
(let-values (((code message) (%ftp-listen port)))
|
|
|
|
|
(case code
|
|
|
|
|
((230) #t)
|
|
|
|
|
((331) (%ftp-command (string-append "PASS " pass) 230 port))
|
|
|
|
|
(else (throw 'ftp-error port command code message))))))
|
|
|
|
|
|
|
|
|
|
(define (ftp-open host)
|
|
|
|
|
(catch 'getaddrinfo-error
|
|
|
|
|
(lambda ()
|
|
|
|
|
(let* ((ai (car (getaddrinfo host "ftp")))
|
|
|
|
|
(s (socket (addrinfo:fam ai) (addrinfo:socktype ai)
|
|
|
|
|
(addrinfo:protocol ai))))
|
|
|
|
|
(connect s (addrinfo:addr ai))
|
|
|
|
|
(setvbuf s _IOLBF)
|
|
|
|
|
(let-values (((code message) (%ftp-listen s)))
|
|
|
|
|
(if (eqv? code 220)
|
|
|
|
|
(begin
|
|
|
|
|
;(%ftp-command "OPTS UTF8 ON" 200 s)
|
|
|
|
|
(%ftp-login "anonymous" "ludo@example.com" s)
|
|
|
|
|
(%make-ftp-connection s ai))
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port) "FTP to `~a' failed: ~A: ~A~%"
|
|
|
|
|
host code message)
|
|
|
|
|
(close s)
|
|
|
|
|
#f)))))
|
|
|
|
|
(lambda (key errcode)
|
|
|
|
|
(format (current-error-port) "failed to resolve `~a': ~a~%"
|
|
|
|
|
host (gai-strerror errcode))
|
|
|
|
|
#f)))
|
|
|
|
|
|
|
|
|
|
(define (ftp-close conn)
|
|
|
|
|
(close (ftp-connection-socket conn)))
|
|
|
|
|
|
|
|
|
|
(define (ftp-chdir conn dir)
|
|
|
|
|
(%ftp-command (string-append "CWD " dir) 250
|
|
|
|
|
(ftp-connection-socket conn)))
|
|
|
|
|
|
|
|
|
|
(define (ftp-pasv conn)
|
|
|
|
|
(define %pasv-rx
|
|
|
|
|
(make-regexp "([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+)"))
|
|
|
|
|
|
|
|
|
|
(let ((message (%ftp-command "PASV" 227 (ftp-connection-socket conn))))
|
|
|
|
|
(cond ((regexp-exec %pasv-rx message)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(+ (* (string->number (match:substring match 5)) 256)
|
|
|
|
|
(string->number (match:substring match 6)))))
|
|
|
|
|
(else
|
|
|
|
|
(throw 'ftp-error conn "PASV" 227 message)))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(define* (ftp-list conn #:optional directory)
|
|
|
|
|
(define (address-with-port sa port)
|
|
|
|
|
(let ((fam (sockaddr:fam sa))
|
|
|
|
|
(addr (sockaddr:addr sa)))
|
|
|
|
|
(cond ((= fam AF_INET)
|
|
|
|
|
(make-socket-address fam addr port))
|
|
|
|
|
((= fam AF_INET6)
|
|
|
|
|
(make-socket-address fam addr port
|
|
|
|
|
(sockaddr:flowinfo sa)
|
|
|
|
|
(sockaddr:scopeid sa)))
|
|
|
|
|
(else #f))))
|
|
|
|
|
|
|
|
|
|
(if directory
|
|
|
|
|
(ftp-chdir conn directory))
|
|
|
|
|
|
|
|
|
|
(let* ((port (ftp-pasv conn))
|
|
|
|
|
(ai (ftp-connection-addrinfo conn))
|
|
|
|
|
(s (socket (addrinfo:fam ai) (addrinfo:socktype ai)
|
|
|
|
|
(addrinfo:protocol ai))))
|
|
|
|
|
(connect s (address-with-port (addrinfo:addr ai) port))
|
|
|
|
|
(setvbuf s _IOLBF)
|
|
|
|
|
|
|
|
|
|
(dynamic-wind
|
|
|
|
|
(lambda () #t)
|
|
|
|
|
(lambda ()
|
|
|
|
|
(%ftp-command "LIST" 150 (ftp-connection-socket conn))
|
|
|
|
|
|
|
|
|
|
(let loop ((line (read-line s))
|
|
|
|
|
(result '()))
|
|
|
|
|
(cond ((eof-object? line) (reverse result))
|
|
|
|
|
((regexp-exec %ftp-ready-rx line)
|
|
|
|
|
=>
|
|
|
|
|
(lambda (match)
|
|
|
|
|
(let ((code (string->number (match:substring match 1))))
|
|
|
|
|
(if (= 126 code)
|
|
|
|
|
(reverse result)
|
|
|
|
|
(throw 'ftp-error conn "LIST" code)))))
|
|
|
|
|
(else
|
|
|
|
|
(loop (read-line s)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(match (reverse (string-tokenize line))
|
|
|
|
|
((file _ ... permissions)
|
|
|
|
|
(let ((type (case (string-ref permissions 0)
|
|
|
|
|
((#\d) 'directory)
|
|
|
|
|
(else 'file))))
|
|
|
|
|
(cons (list file type) result)))
|
|
|
|
|
((file _ ...)
|
|
|
|
|
(cons (cons file 'file) result))))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(lambda ()
|
|
|
|
|
(close s)
|
|
|
|
|
(let-values (((code message) (%ftp-listen (ftp-connection-socket conn))))
|
|
|
|
|
(or (eqv? code 226)
|
|
|
|
|
(throw 'ftp-error conn "LIST" code message)))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; GNU.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define %ignored-package-attributes
|
|
|
|
|
;; Attribute name of packages to be ignored.
|
|
|
|
|
'("bash" "bashReal" "bashInteractive" ;; the full versioned name is incorrect
|
|
|
|
|
"autoconf213"
|
|
|
|
|
"automake17x"
|
|
|
|
|
"automake19x"
|
|
|
|
|
"automake110x"
|
|
|
|
|
"bison1875"
|
|
|
|
|
"bison23"
|
2011-08-22 19:38:57 +02:00
|
|
|
|
"bison24"
|
|
|
|
|
"bison" ;; = 2.4
|
|
|
|
|
"ccrtp_1_8"
|
2010-07-04 23:11:19 +02:00
|
|
|
|
"emacs22"
|
|
|
|
|
"emacsSnapshot"
|
|
|
|
|
"gcc295"
|
|
|
|
|
"gcc33"
|
|
|
|
|
"gcc34"
|
|
|
|
|
"gcc40"
|
|
|
|
|
"gcc41"
|
|
|
|
|
"gcc42"
|
|
|
|
|
"gcc43"
|
|
|
|
|
"gcc44"
|
|
|
|
|
"gcc45"
|
2011-03-10 17:27:43 +01:00
|
|
|
|
"gcc45_real"
|
|
|
|
|
"gcc45_realCross"
|
2011-04-12 10:05:41 +02:00
|
|
|
|
"gfortran45"
|
|
|
|
|
"gcj45"
|
|
|
|
|
"gcc46"
|
|
|
|
|
"gcc46_real"
|
|
|
|
|
"gcc46_realCross"
|
|
|
|
|
"gfortran46"
|
|
|
|
|
"gcj46"
|
2010-07-04 23:11:19 +02:00
|
|
|
|
"glibc25"
|
|
|
|
|
"glibc27"
|
|
|
|
|
"glibc29"
|
2011-02-23 18:36:24 +01:00
|
|
|
|
"guile_1_8"
|
2011-09-05 01:06:11 +02:00
|
|
|
|
"icecat3"
|
2011-02-23 18:36:24 +01:00
|
|
|
|
"icecat3Xul" ;; redundant with `icecat'
|
|
|
|
|
"icecatWrapper"
|
2011-09-05 01:06:11 +02:00
|
|
|
|
"icecat3Wrapper"
|
2011-02-23 18:36:24 +01:00
|
|
|
|
"icecatXulrunner3"
|
2011-08-22 19:38:57 +02:00
|
|
|
|
"libzrtpcpp_1_6"
|
|
|
|
|
"parted_2_3"
|
2010-07-04 23:11:19 +02:00
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
(define (gnu? package)
|
|
|
|
|
;; Return true if PACKAGE (a snix expression) is a GNU package (according
|
|
|
|
|
;; to a simple heuristic.) Otherwise return #f.
|
|
|
|
|
(match package
|
|
|
|
|
(('attribute _ _ ('derivation _ _ body))
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "meta" ('attribute-set metas))
|
|
|
|
|
(any (lambda (attr)
|
|
|
|
|
(match attr
|
|
|
|
|
(('attribute _ "description" value)
|
|
|
|
|
(string-prefix? "GNU" value))
|
2011-09-17 12:09:26 +02:00
|
|
|
|
(('attribute _ "homepage" (? string? value))
|
2011-11-01 23:38:09 +01:00
|
|
|
|
(or (string-contains value "gnu.org")
|
|
|
|
|
(string-contains value "gnupg.org")))
|
2011-09-17 12:09:26 +02:00
|
|
|
|
(('attribute _ "homepage" ((? string? value) ...))
|
|
|
|
|
(any (cut string-contains <> "www.gnu.org") value))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(_ #f)))
|
|
|
|
|
metas))
|
|
|
|
|
(_ #f)))
|
|
|
|
|
body))
|
|
|
|
|
(_ #f)))
|
|
|
|
|
|
|
|
|
|
(define (gnu-packages packages)
|
|
|
|
|
(fold (lambda (package gnu)
|
|
|
|
|
(match package
|
|
|
|
|
(('attribute _ "emacs23Packages" emacs-packages)
|
|
|
|
|
;; XXX: Should prepend `emacs23Packages.' to attribute names.
|
|
|
|
|
(append (gnu-packages emacs-packages) gnu))
|
|
|
|
|
(('attribute _ attribute-name ('derivation _ _ body))
|
|
|
|
|
(if (member attribute-name %ignored-package-attributes)
|
|
|
|
|
gnu
|
|
|
|
|
(if (gnu? package)
|
|
|
|
|
(cons package gnu)
|
|
|
|
|
gnu)))
|
|
|
|
|
(_ gnu)))
|
|
|
|
|
'()
|
|
|
|
|
packages))
|
|
|
|
|
|
|
|
|
|
(define (ftp-server/directory project)
|
|
|
|
|
(define quirks
|
|
|
|
|
'(("commoncpp2" "ftp.gnu.org" "/gnu/commoncpp" #f)
|
2011-03-10 17:27:43 +01:00
|
|
|
|
("ucommon" "ftp.gnu.org" "/gnu/commoncpp" #f)
|
2011-01-24 23:29:29 +01:00
|
|
|
|
("libzrtpcpp" "ftp.gnu.org" "/gnu/ccrtp" #f)
|
|
|
|
|
("libosip2" "ftp.gnu.org" "/gnu/osip" #f)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
("libgcrypt" "ftp.gnupg.org" "/gcrypt" #t)
|
|
|
|
|
("libgpg-error" "ftp.gnupg.org" "/gcrypt" #t)
|
2011-11-01 23:38:09 +01:00
|
|
|
|
("libassuan" "ftp.gnupg.org" "/gcrypt" #t)
|
2010-09-20 22:46:37 +02:00
|
|
|
|
("freefont-ttf" "ftp.gnu.org" "/gnu/freefont" #f)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
("gnupg" "ftp.gnupg.org" "/gcrypt" #t)
|
2010-09-20 22:46:37 +02:00
|
|
|
|
("gnu-ghostscript" "ftp.gnu.org" "/gnu/ghostscript" #f)
|
2011-02-23 18:36:07 +01:00
|
|
|
|
("mit-scheme" "ftp.gnu.org" "/gnu/mit-scheme/stable.pkg" #f)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
("icecat" "ftp.gnu.org" "/gnu/gnuzilla" #f)
|
2011-02-23 18:36:11 +01:00
|
|
|
|
("source-highlight" "ftp.gnu.org" "/gnu/src-highlite" #f)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
("TeXmacs" "ftp.texmacs.org" "/TeXmacs/targz" #f)))
|
|
|
|
|
|
|
|
|
|
(let ((quirk (assoc project quirks)))
|
|
|
|
|
(match quirk
|
|
|
|
|
((_ server directory subdir?)
|
|
|
|
|
(values server (if (not subdir?)
|
|
|
|
|
directory
|
|
|
|
|
(string-append directory "/" project))))
|
|
|
|
|
(_
|
|
|
|
|
(values "ftp.gnu.org" (string-append "/gnu/" project))))))
|
|
|
|
|
|
|
|
|
|
(define (nixpkgs->gnu-name project)
|
|
|
|
|
(define quirks
|
|
|
|
|
'(("gcc-wrapper" . "gcc")
|
|
|
|
|
("ghostscript" . "gnu-ghostscript") ;; ../ghostscript/gnu-ghoscript-X.Y.tar.gz
|
|
|
|
|
("gnum4" . "m4")
|
|
|
|
|
("gnugrep" . "grep")
|
2010-08-20 17:26:16 +02:00
|
|
|
|
("gnumake" . "make")
|
2010-07-04 23:11:19 +02:00
|
|
|
|
("gnused" . "sed")
|
|
|
|
|
("gnutar" . "tar")
|
|
|
|
|
("mitscheme" . "mit-scheme")
|
|
|
|
|
("texmacs" . "TeXmacs")))
|
|
|
|
|
|
|
|
|
|
(or (assoc-ref quirks project) project))
|
|
|
|
|
|
|
|
|
|
(define (releases project)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
"Return the list of releases of PROJECT as a list of release name/directory
|
|
|
|
|
pairs. Example: (\"mit-scheme-9.0.1\" . \"/gnu/mit-scheme/stable.pkg/9.0.1\"). "
|
2010-09-20 22:46:37 +02:00
|
|
|
|
;; TODO: Parse something like fencepost.gnu.org:/gd/gnuorg/packages-ftp.
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(define release-rx
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(make-regexp (string-append "^" project
|
|
|
|
|
"-([0-9]|[^-])*(-src)?\\.tar\\.")))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(define alpha-rx
|
|
|
|
|
(make-regexp "^.*-.*[0-9](-|~)?(alpha|beta|rc|cvs|svn|git)-?[0-9\\.]*\\.tar\\."))
|
|
|
|
|
|
|
|
|
|
(define (sans-extension tarball)
|
|
|
|
|
(let ((end (string-contains tarball ".tar")))
|
|
|
|
|
(substring tarball 0 end)))
|
|
|
|
|
|
|
|
|
|
(catch 'ftp-error
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(lambda ()
|
|
|
|
|
(let-values (((server directory) (ftp-server/directory project)))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(define conn (ftp-open server))
|
|
|
|
|
|
|
|
|
|
(let loop ((directories (list directory))
|
|
|
|
|
(result '()))
|
|
|
|
|
(if (null? directories)
|
|
|
|
|
(begin
|
|
|
|
|
(ftp-close conn)
|
|
|
|
|
result)
|
|
|
|
|
(let* ((directory (car directories))
|
|
|
|
|
(files (ftp-list conn directory))
|
|
|
|
|
(subdirs (filter-map (lambda (file)
|
|
|
|
|
(match file
|
|
|
|
|
((name 'directory . _) name)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
files)))
|
|
|
|
|
(loop (append (map (cut string-append directory "/" <>)
|
|
|
|
|
subdirs)
|
|
|
|
|
(cdr directories))
|
|
|
|
|
(append
|
|
|
|
|
;; Filter out signatures, deltas, and files which are potentially
|
|
|
|
|
;; not releases of PROJECT (e.g., in /gnu/guile, filter out
|
|
|
|
|
;; guile-oops and guile-www; in mit-scheme, filter out
|
|
|
|
|
;; binaries).
|
|
|
|
|
(filter-map (lambda (file)
|
|
|
|
|
(match file
|
|
|
|
|
((file 'file . _)
|
|
|
|
|
(and (not (string-suffix? ".sig" file))
|
|
|
|
|
(regexp-exec release-rx file)
|
|
|
|
|
(not (regexp-exec alpha-rx file))
|
|
|
|
|
(let ((s (sans-extension file)))
|
|
|
|
|
(and (regexp-exec
|
|
|
|
|
%package-name-rx s)
|
|
|
|
|
(cons s directory)))))
|
|
|
|
|
(_ #f)))
|
|
|
|
|
files)
|
|
|
|
|
result)))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(lambda (key subr message . args)
|
|
|
|
|
(format (current-error-port)
|
2011-02-23 18:36:21 +01:00
|
|
|
|
"failed to get release list for `~A': ~S ~S~%"
|
2010-07-04 23:11:19 +02:00
|
|
|
|
project message args)
|
|
|
|
|
'())))
|
|
|
|
|
|
|
|
|
|
(define version-string>?
|
|
|
|
|
(let ((strverscmp
|
|
|
|
|
(let ((sym (or (dynamic-func "strverscmp" (dynamic-link))
|
|
|
|
|
(error "could not find `strverscmp' (from GNU libc)"))))
|
2010-09-10 13:50:06 +02:00
|
|
|
|
(pointer->procedure int sym (list '* '*)))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(lambda (a b)
|
2010-08-19 18:52:18 +02:00
|
|
|
|
(> (strverscmp (string->pointer a) (string->pointer b)) 0))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
(define (latest-release project)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
"Return (\"FOO-X.Y\" . \"/bar/foo\") or #f."
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(let ((releases (releases project)))
|
|
|
|
|
(and (not (null? releases))
|
|
|
|
|
(fold (lambda (release latest)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(if (version-string>? (car release) (car latest))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
release
|
|
|
|
|
latest))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
'("" . "")
|
2010-07-04 23:11:19 +02:00
|
|
|
|
releases))))
|
|
|
|
|
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(define %package-name-rx
|
|
|
|
|
;; Regexp for a package name, e.g., "foo-X.Y". Since TeXmacs uses
|
|
|
|
|
;; "TeXmacs-X.Y-src", the `-src' suffix is allowed.
|
|
|
|
|
(make-regexp "^(.*)-(([0-9]|\\.)+)(-src)?"))
|
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(define (package/version name+version)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
"Return the package name and version number extracted from NAME+VERSION."
|
|
|
|
|
(let ((match (regexp-exec %package-name-rx name+version)))
|
|
|
|
|
(if (not match)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(values name+version #f)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(values (match:substring match 1) (match:substring match 2)))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
(define (file-extension file)
|
|
|
|
|
(let ((dot (string-rindex file #\.)))
|
|
|
|
|
(and dot (substring file (+ 1 dot) (string-length file)))))
|
|
|
|
|
|
|
|
|
|
(define (packages-to-update gnu-packages)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(define (unpack latest)
|
|
|
|
|
(call-with-values (lambda ()
|
|
|
|
|
(package/version (car latest)))
|
|
|
|
|
(lambda (name version)
|
|
|
|
|
(list name version (cdr latest)))))
|
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(fold (lambda (pkg result)
|
|
|
|
|
(call-with-package pkg
|
|
|
|
|
(lambda (attribute name+version location meta src)
|
|
|
|
|
(let-values (((name old-version)
|
|
|
|
|
(package/version name+version)))
|
|
|
|
|
(let ((latest (latest-release (nixpkgs->gnu-name name))))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(if (not latest)
|
|
|
|
|
(begin
|
|
|
|
|
(format #t "~A [unknown latest version]~%"
|
|
|
|
|
name+version)
|
|
|
|
|
result)
|
|
|
|
|
(match (unpack latest)
|
|
|
|
|
((_ (? (cut string=? old-version <>)) _)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(format #t "~A [up to date]~%" name+version)
|
|
|
|
|
result)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
((project new-version directory)
|
|
|
|
|
(let-values (((old-name old-hash old-urls)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(src->values src)))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(format #t "~A -> ~A [~A]~%"
|
|
|
|
|
name+version (car latest)
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(and (pair? old-urls) (car old-urls)))
|
|
|
|
|
(let* ((url (and (pair? old-urls)
|
|
|
|
|
(car old-urls)))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(new-hash (fetch-gnu project directory
|
|
|
|
|
new-version
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(if url
|
|
|
|
|
(file-extension url)
|
|
|
|
|
"gz"))))
|
|
|
|
|
(cons (list name attribute
|
|
|
|
|
old-version old-hash
|
|
|
|
|
new-version new-hash
|
|
|
|
|
location)
|
2011-02-23 18:36:15 +01:00
|
|
|
|
result)))))))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
'()
|
|
|
|
|
gnu-packages))
|
|
|
|
|
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(define (fetch-gnu project directory version archive-type)
|
2011-10-30 02:00:20 +02:00
|
|
|
|
"Download PROJECT's tarball over FTP."
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(let* ((server (ftp-server/directory project))
|
|
|
|
|
(base (string-append project "-" version ".tar." archive-type))
|
|
|
|
|
(url (string-append "ftp://" server "/" directory "/" base))
|
|
|
|
|
(sig (string-append base ".sig"))
|
|
|
|
|
(sig-url (string-append url ".sig")))
|
|
|
|
|
(let-values (((hash path) (nix-prefetch-url url)))
|
|
|
|
|
(pk 'prefetch-url url hash path)
|
|
|
|
|
(and hash path
|
|
|
|
|
(begin
|
|
|
|
|
(false-if-exception (delete-file sig))
|
|
|
|
|
(system* "wget" sig-url)
|
|
|
|
|
(if (file-exists? sig)
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(let ((ret (gnupg-verify* sig path)))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
(false-if-exception (delete-file sig))
|
2011-09-05 01:06:07 +02:00
|
|
|
|
(if ret
|
2011-02-23 18:36:15 +01:00
|
|
|
|
hash
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"signature verification failed for `~a'~%"
|
|
|
|
|
base)
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"(could be because the public key is not in your keyring)~%")
|
|
|
|
|
#f)))
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"no signature for `~a'~%" base)
|
|
|
|
|
hash)))))))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Main program.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define %options
|
|
|
|
|
;; Specifications of the command-line options.
|
|
|
|
|
(list (option '(#\h "help") #f #f
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(format #t "Usage: gnupdate [OPTIONS...]~%")
|
|
|
|
|
(format #t "GNUpdate -- update Nix expressions of GNU packages in Nixpkgs~%")
|
|
|
|
|
(format #t "~%")
|
|
|
|
|
(format #t " -x, --xml=FILE Read XML output of `nix-instantiate'~%")
|
|
|
|
|
(format #t " from FILE.~%")
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(format #t " -A, --attribute=ATTR~%")
|
|
|
|
|
(format #t " Update only the package pointed to by attribute~%")
|
|
|
|
|
(format #t " ATTR.~%")
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(format #t " -s, --select=SET Update only packages from SET, which may~%")
|
2010-07-04 23:11:31 +02:00
|
|
|
|
(format #t " be either `all', `stdenv', or `non-stdenv'.~%")
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(format #t " -d, --dry-run Don't actually update Nix expressions~%")
|
|
|
|
|
(format #t " -h, --help Give this help list.~%~%")
|
|
|
|
|
(format #t "Report bugs to <ludo@gnu.org>~%")
|
|
|
|
|
(exit 0)))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(option '(#\A "attribute") #t #f
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(alist-cons 'attribute arg result)))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(option '(#\s "select") #t #f
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(cond ((string-ci=? arg "stdenv")
|
|
|
|
|
(alist-cons 'filter 'stdenv result))
|
|
|
|
|
((string-ci=? arg "non-stdenv")
|
|
|
|
|
(alist-cons 'filter 'non-stdenv result))
|
|
|
|
|
((string-ci=? arg "all")
|
|
|
|
|
(alist-cons 'filter #f result))
|
|
|
|
|
(else
|
|
|
|
|
(format (current-error-port)
|
|
|
|
|
"~A: unrecognized selection type~%"
|
|
|
|
|
arg)
|
|
|
|
|
(exit 1)))))
|
|
|
|
|
|
|
|
|
|
(option '(#\d "dry-run") #f #f
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(alist-cons 'dry-run #t result)))
|
|
|
|
|
|
|
|
|
|
(option '(#\x "xml") #t #f
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(alist-cons 'xml-file arg result)))))
|
|
|
|
|
|
|
|
|
|
(define (gnupdate . args)
|
|
|
|
|
;; Assume Nixpkgs is under $NIXPKGS or ~/src/nixpkgs.
|
2011-03-04 14:18:56 +01:00
|
|
|
|
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(define (nixpkgs->snix xml-file attribute)
|
2011-03-04 14:18:56 +01:00
|
|
|
|
(format (current-error-port) "evaluating Nixpkgs...~%")
|
|
|
|
|
(let* ((home (getenv "HOME"))
|
|
|
|
|
(xml (if xml-file
|
|
|
|
|
(open-input-file xml-file)
|
|
|
|
|
(open-nixpkgs (or (getenv "NIXPKGS")
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(string-append home "/src/nixpkgs"))
|
|
|
|
|
attribute)))
|
2011-03-04 14:18:56 +01:00
|
|
|
|
(snix (xml->snix xml)))
|
|
|
|
|
(if (not xml-file)
|
|
|
|
|
(let ((status (pipe-failed? xml)))
|
|
|
|
|
(if status
|
|
|
|
|
(begin
|
|
|
|
|
(format (current-error-port) "`nix-instantiate' failed: ~A~%"
|
|
|
|
|
status)
|
|
|
|
|
(exit 1)))))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
|
|
|
|
|
;; If we asked for a specific attribute, rewrap the thing in an
|
|
|
|
|
;; attribute set to match the expectations of `packages-to-update' & co.
|
|
|
|
|
(if attribute
|
|
|
|
|
(match snix
|
|
|
|
|
(('snix loc ('derivation args ...))
|
|
|
|
|
`(snix ,loc
|
|
|
|
|
(attribute-set
|
|
|
|
|
((attribute #f ,attribute
|
|
|
|
|
(derivation ,@args)))))))
|
|
|
|
|
snix)))
|
|
|
|
|
|
|
|
|
|
(define (selected-gnu-packages packages stdenv selection)
|
|
|
|
|
;; Return the subset of PACKAGES that are/aren't in STDENV, according to
|
|
|
|
|
;; SELECTION. To do that reliably, we check whether their "src"
|
|
|
|
|
;; derivation is a requisite of STDENV.
|
|
|
|
|
(define gnu
|
|
|
|
|
(gnu-packages packages))
|
|
|
|
|
|
|
|
|
|
(case selection
|
|
|
|
|
((stdenv)
|
2011-12-13 17:11:02 +01:00
|
|
|
|
(filter (lambda (p)
|
|
|
|
|
(member (package-source-output-path p)
|
|
|
|
|
(force stdenv)))
|
|
|
|
|
gnu))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
((non-stdenv)
|
|
|
|
|
(filter (lambda (p)
|
|
|
|
|
(not (member (package-source-output-path p)
|
|
|
|
|
(force stdenv))))
|
|
|
|
|
gnu))
|
|
|
|
|
(else gnu)))
|
2011-03-04 14:18:56 +01:00
|
|
|
|
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(let* ((opts (args-fold (cdr args) %options
|
|
|
|
|
(lambda (opt name arg result)
|
|
|
|
|
(error "unrecognized option `~A'" name))
|
|
|
|
|
(lambda (operand result)
|
|
|
|
|
(error "extraneous argument `~A'" operand))
|
|
|
|
|
'()))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(snix (nixpkgs->snix (assq-ref opts 'xml-file)
|
|
|
|
|
(assq-ref opts 'attribute)))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
(packages (match snix
|
|
|
|
|
(('snix _ ('attribute-set attributes))
|
|
|
|
|
attributes)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
(stdenv (delay
|
|
|
|
|
;; The source tarballs that make up stdenv.
|
|
|
|
|
(filter-map derivation-source-output-path
|
|
|
|
|
(package-requisites (stdenv-package packages)))))
|
2011-10-30 02:00:11 +02:00
|
|
|
|
(attribute (assq-ref opts 'attribute))
|
|
|
|
|
(selection (assq-ref opts 'filter))
|
|
|
|
|
(to-update (if attribute
|
|
|
|
|
packages ; already a subset
|
|
|
|
|
(selected-gnu-packages packages stdenv selection)))
|
|
|
|
|
(updates (packages-to-update to-update)))
|
2010-07-04 23:11:19 +02:00
|
|
|
|
|
|
|
|
|
(format #t "~%~A packages to update...~%" (length updates))
|
|
|
|
|
(for-each (lambda (update)
|
|
|
|
|
(match update
|
|
|
|
|
((name attribute
|
|
|
|
|
old-version old-hash
|
|
|
|
|
new-version new-hash
|
|
|
|
|
location)
|
|
|
|
|
(if (assoc-ref opts 'dry-run)
|
|
|
|
|
(format #t "`~a' would be updated from ~a to ~a (~a -> ~a)~%"
|
|
|
|
|
name old-version new-version
|
|
|
|
|
old-hash new-hash)
|
|
|
|
|
(update-nix-expression (location-file location)
|
|
|
|
|
old-version old-hash
|
|
|
|
|
new-version new-hash)))
|
|
|
|
|
(_ #f)))
|
|
|
|
|
updates)
|
|
|
|
|
#t))
|
2011-02-23 18:36:15 +01:00
|
|
|
|
|
|
|
|
|
;;; Local Variables:
|
|
|
|
|
;;; eval: (put 'call-with-package 'scheme-indent-function 1)
|
|
|
|
|
;;; End:
|