2023-01-04 18:18:21 +01:00
|
|
|
|
#!/usr/bin/env -S guix repl --
|
|
|
|
|
!#
|
|
|
|
|
;;; GNU Guix --- Functional package management for GNU
|
2023-05-12 11:45:04 +02:00
|
|
|
|
;;; Copyright © 2020, 2021, 2023 Simon Tournier <zimon.toutoune@gmail.com>
|
2023-01-04 18:18:21 +01:00
|
|
|
|
;;; Copyright © 2023 Ludovic Courtès <ludo@gnu.org>
|
|
|
|
|
;;;
|
|
|
|
|
;;; 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/>.
|
|
|
|
|
|
|
|
|
|
;;; Build package metadata: 'packages.json', for package metadata, and
|
|
|
|
|
;;; 'sources.json', for source code metadata.
|
|
|
|
|
|
|
|
|
|
(use-modules (gnu packages)
|
|
|
|
|
(guix packages)
|
|
|
|
|
(guix channels)
|
|
|
|
|
(guix diagnostics)
|
|
|
|
|
(guix i18n)
|
|
|
|
|
(guix utils)
|
|
|
|
|
(guix gexp)
|
|
|
|
|
((guix build download) #:select (maybe-expand-mirrors))
|
2023-04-25 14:43:31 +02:00
|
|
|
|
((guix base32) #:select (bytevector->nix-base32-string))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
((guix base64) #:select (base64-encode))
|
|
|
|
|
((guix describe) #:select (current-profile))
|
|
|
|
|
((guix config) #:select (%guix-version))
|
2023-10-09 14:26:10 +02:00
|
|
|
|
((guix modules) #:select (file-name->module-name))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(guix download)
|
|
|
|
|
(guix git-download)
|
|
|
|
|
(guix svn-download)
|
|
|
|
|
(guix hg-download)
|
|
|
|
|
(json)
|
2023-10-09 10:57:31 +02:00
|
|
|
|
(zlib)
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(web uri)
|
2023-10-09 14:26:10 +02:00
|
|
|
|
((ice-9 control) #:select (let/ec))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(ice-9 match)
|
|
|
|
|
(ice-9 vlist)
|
|
|
|
|
(srfi srfi-1)
|
|
|
|
|
(srfi srfi-26))
|
|
|
|
|
|
|
|
|
|
(define (all-packages) ;XXX: copied form 'etc/source-manifest.scm'
|
|
|
|
|
"Return the list of all the packages, public or private, omitting only
|
|
|
|
|
superseded packages."
|
|
|
|
|
(fold-packages (lambda (package lst)
|
|
|
|
|
(match (package-replacement package)
|
|
|
|
|
(#f (cons package lst))
|
|
|
|
|
(replacement
|
|
|
|
|
(append (list replacement package) lst))))
|
|
|
|
|
'()
|
|
|
|
|
#:select? (negate package-superseded)))
|
|
|
|
|
|
|
|
|
|
(define (all-origins) ;XXX: copied form 'etc/source-manifest.scm'
|
|
|
|
|
"Return the list of origins referred to by all the packages."
|
|
|
|
|
(let loop ((packages (all-packages))
|
|
|
|
|
(origins '())
|
|
|
|
|
(visited vlist-null))
|
|
|
|
|
(match packages
|
|
|
|
|
((head . tail)
|
|
|
|
|
(let ((new (remove (cut vhash-assq <> visited)
|
|
|
|
|
(package-direct-sources head))))
|
|
|
|
|
(loop tail (append new origins)
|
|
|
|
|
(fold (cut vhash-consq <> #t <>)
|
|
|
|
|
visited new))))
|
|
|
|
|
(()
|
|
|
|
|
origins))))
|
|
|
|
|
|
|
|
|
|
;;; Required by 'origin->json' for 'computed-origin-method' corner cases
|
|
|
|
|
(define gexp-references (@@ (guix gexp) gexp-references))
|
|
|
|
|
|
2023-04-25 14:43:31 +02:00
|
|
|
|
(define %content-addressed-mirrors
|
|
|
|
|
;; List of content-addressed mirrors.
|
|
|
|
|
;; XXX: somewhat duplicated from (guix download)
|
|
|
|
|
(let ((guix-publish
|
|
|
|
|
(lambda (host)
|
|
|
|
|
(lambda (file hash)
|
|
|
|
|
;; Files served by 'guix publish'.
|
|
|
|
|
(string-append "https://" host "/file/"
|
|
|
|
|
file "/" (symbol->string
|
|
|
|
|
(content-hash-algorithm hash))
|
|
|
|
|
"/" (bytevector->nix-base32-string
|
|
|
|
|
(content-hash-value hash)))))))
|
|
|
|
|
|
|
|
|
|
(list (guix-publish "bordeaux.guix.gnu.org")
|
|
|
|
|
(guix-publish "ci.guix.gnu.org")
|
|
|
|
|
(lambda (file hash)
|
|
|
|
|
(string-append "https://tarballs.nixos.org/"
|
|
|
|
|
(symbol->string (content-hash-algorithm hash))
|
|
|
|
|
"/" (bytevector->nix-base32-string
|
|
|
|
|
(content-hash-value hash)))))))
|
|
|
|
|
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(define (origin->json origin)
|
|
|
|
|
"Return a list of JSON representations (an alist) of ORIGIN."
|
|
|
|
|
(define method
|
|
|
|
|
(origin-method origin))
|
|
|
|
|
|
|
|
|
|
(define uri
|
|
|
|
|
(origin-uri origin))
|
|
|
|
|
|
2023-04-25 14:43:31 +02:00
|
|
|
|
(define (resolve urls hash)
|
|
|
|
|
(append (map uri->string
|
|
|
|
|
(append-map (cut maybe-expand-mirrors <> %mirrors)
|
|
|
|
|
(map string->uri urls)))
|
|
|
|
|
(if hash
|
|
|
|
|
(let ((file (origin-actual-file-name origin))
|
|
|
|
|
(hash (origin-hash origin)))
|
|
|
|
|
(map (lambda (make-url)
|
|
|
|
|
(make-url file hash))
|
|
|
|
|
%content-addressed-mirrors))
|
|
|
|
|
'())))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
|
|
|
|
|
(if (eq? method (@@ (guix packages) computed-origin-method))
|
|
|
|
|
;; Packages in gnu/packages/gnuzilla.scm and gnu/packages/linux.scm
|
|
|
|
|
;; represent their 'uri' as 'promise'.
|
|
|
|
|
(match uri
|
|
|
|
|
((? promise? promise)
|
|
|
|
|
(match (force promise)
|
|
|
|
|
((? gexp? g)
|
|
|
|
|
(append-map origin->json
|
|
|
|
|
(filter-map (match-lambda
|
|
|
|
|
((? gexp-input? thing)
|
|
|
|
|
(match (gexp-input-thing thing)
|
|
|
|
|
((? origin? o) o)
|
|
|
|
|
(_ #f)))
|
|
|
|
|
(_ #f))
|
|
|
|
|
(gexp-references g))))
|
|
|
|
|
(_ `((type . #nil))))))
|
|
|
|
|
;; Regular packages represent 'uri' as string.
|
|
|
|
|
`((("type" . ,(cond ((or (eq? url-fetch method)
|
|
|
|
|
(eq? url-fetch/tarbomb method)
|
|
|
|
|
(eq? url-fetch/zipbomb method)) 'url)
|
|
|
|
|
((eq? git-fetch method) 'git)
|
|
|
|
|
((or (eq? svn-fetch method)
|
|
|
|
|
(eq? svn-multi-fetch method)) 'svn)
|
|
|
|
|
((eq? hg-fetch method) 'hg)
|
|
|
|
|
(else #nil)))
|
|
|
|
|
,@(cond ((or (eq? url-fetch method)
|
|
|
|
|
(eq? url-fetch/tarbomb method)
|
|
|
|
|
(eq? url-fetch/zipbomb method))
|
|
|
|
|
`(("urls" . ,(list->vector
|
|
|
|
|
(resolve
|
|
|
|
|
(match uri
|
|
|
|
|
((? string? url) (list url))
|
2023-04-25 14:43:31 +02:00
|
|
|
|
((urls ...) urls))
|
|
|
|
|
(origin-hash origin))))))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
((eq? git-fetch method)
|
|
|
|
|
`(("git_url" . ,(git-reference-url uri))))
|
|
|
|
|
((eq? svn-fetch method)
|
|
|
|
|
`(("svn_url" . ,(svn-reference-url uri))))
|
|
|
|
|
((eq? svn-multi-fetch method)
|
|
|
|
|
`(("svn_url" . ,(svn-multi-reference-url uri))))
|
|
|
|
|
((eq? hg-fetch method)
|
|
|
|
|
`(("hg_url" . ,(hg-reference-url uri))))
|
|
|
|
|
(else '()))
|
|
|
|
|
,@(if (or (eq? url-fetch method)
|
|
|
|
|
(eq? url-fetch/tarbomb method)
|
2023-05-12 11:45:04 +02:00
|
|
|
|
(eq? url-fetch/zipbomb method)
|
|
|
|
|
(eq? git-fetch method)
|
|
|
|
|
(eq? svn-fetch method)
|
|
|
|
|
(eq? svn-multi-fetch method)
|
|
|
|
|
(eq? hg-fetch method))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(let* ((content-hash (origin-hash origin))
|
|
|
|
|
(hash-value (content-hash-value content-hash))
|
|
|
|
|
(hash-algorithm (content-hash-algorithm content-hash))
|
|
|
|
|
(algorithm-string (symbol->string hash-algorithm)))
|
|
|
|
|
`(("integrity" . ,(string-append algorithm-string "-"
|
2023-05-12 11:45:04 +02:00
|
|
|
|
(base64-encode hash-value)))
|
|
|
|
|
("outputHashAlgo" . ,algorithm-string)
|
|
|
|
|
("outputHashMode" . ,(if (or (eq? url-fetch method)
|
|
|
|
|
(eq? url-fetch/tarbomb method)
|
|
|
|
|
(eq? url-fetch/zipbomb method))
|
|
|
|
|
"flat"
|
|
|
|
|
"recursive"))))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
'())
|
|
|
|
|
,@(if (eq? method git-fetch)
|
2023-10-17 13:58:24 +02:00
|
|
|
|
`(("git_ref" . ,(git-reference-commit uri))
|
|
|
|
|
,@(if (git-reference-recursive? uri)
|
|
|
|
|
'(("submodule" . #true))
|
|
|
|
|
'()))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
'())
|
|
|
|
|
,@(if (eq? method svn-fetch)
|
|
|
|
|
`(("svn_revision" . ,(svn-reference-revision uri)))
|
|
|
|
|
'())
|
|
|
|
|
,@(if (eq? method svn-multi-fetch)
|
|
|
|
|
`(("svn_revision" . ,(svn-multi-reference-revision uri)))
|
|
|
|
|
'())
|
|
|
|
|
,@(if (eq? method hg-fetch)
|
|
|
|
|
`(("hg_changeset" . ,(hg-reference-changeset uri)))
|
|
|
|
|
'())))))
|
|
|
|
|
|
2023-10-09 14:26:10 +02:00
|
|
|
|
(define (package-variable-name package)
|
|
|
|
|
"Return the name of the variable whose value is PACKAGE in the module that
|
|
|
|
|
defines it, or #f if this could not be determined."
|
|
|
|
|
(match (package-location package)
|
|
|
|
|
(#f #f)
|
|
|
|
|
((= location-file file)
|
|
|
|
|
(let* ((name (file-name->module-name file))
|
|
|
|
|
(module (false-if-exception (resolve-interface name))))
|
|
|
|
|
(let/ec return
|
|
|
|
|
(module-for-each (lambda (symbol variable)
|
|
|
|
|
(when (eq? package (variable-ref variable))
|
|
|
|
|
(return symbol)))
|
|
|
|
|
module)
|
|
|
|
|
#f)))))
|
|
|
|
|
|
2023-01-04 18:18:21 +01:00
|
|
|
|
(define (package->json package)
|
|
|
|
|
(define cpe-name
|
|
|
|
|
(assoc-ref (package-properties package) 'cpe-name))
|
|
|
|
|
(define cpe-version
|
|
|
|
|
(assoc-ref (package-properties package) 'cpe-version))
|
2023-10-09 14:26:10 +02:00
|
|
|
|
(define variable
|
|
|
|
|
(package-variable-name package))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
|
|
|
|
|
`(("name" . ,(package-name package))
|
|
|
|
|
("version" . ,(package-version package))
|
2023-10-09 14:26:10 +02:00
|
|
|
|
,@(if variable `(("variable_name" . ,variable)) '())
|
2023-01-04 18:18:21 +01:00
|
|
|
|
,@(if cpe-name `(("cpe_name" . ,cpe-name)) '())
|
|
|
|
|
,@(if cpe-version `(("cpe_version" . ,cpe-version)) '())
|
|
|
|
|
,@(if (origin? (package-source package))
|
|
|
|
|
`(("source" . ,(list->vector
|
|
|
|
|
(origin->json (package-source package)))))
|
|
|
|
|
'())
|
|
|
|
|
("synopsis" . ,(package-synopsis package))
|
|
|
|
|
,@(if (package-home-page package)
|
|
|
|
|
`(("homepage" . ,(package-home-page package)))
|
|
|
|
|
'())
|
|
|
|
|
,@(match (package-location package)
|
|
|
|
|
((? location? location)
|
|
|
|
|
`(("location"
|
|
|
|
|
. ,(string-append (location-file location) ":"
|
|
|
|
|
(number->string
|
|
|
|
|
(+ 1 (location-line location)))))))
|
|
|
|
|
(#f
|
|
|
|
|
'()))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(define (sources-json)
|
|
|
|
|
"Return JSON (an alist) listing all the sources."
|
|
|
|
|
;; The Software Heritage format is described here:
|
|
|
|
|
;; https://forge.softwareheritage.org/source/swh-loader-core/browse/master/swh/loader/package/nixguix/tests/data/https_nix-community.github.io/nixpkgs-swh_sources.json
|
|
|
|
|
;; And the loader is implemented here:
|
|
|
|
|
;; https://forge.softwareheritage.org/source/swh-loader-core/browse/master/swh/loader/package/nixguix/
|
|
|
|
|
`(("sources" . ,(list->vector (append-map origin->json (all-origins))))
|
|
|
|
|
("version" . "1")
|
|
|
|
|
("revision" .
|
|
|
|
|
,(match (current-profile)
|
|
|
|
|
(#f %guix-version) ;for lack of a better ID
|
|
|
|
|
(profile
|
|
|
|
|
(let ((channel (find guix-channel? (profile-channels profile))))
|
|
|
|
|
(channel-commit channel)))))))
|
|
|
|
|
|
|
|
|
|
(define (packages-json)
|
|
|
|
|
"Return JSON (an alist) listing all the packages."
|
|
|
|
|
(list->vector (map package->json (all-packages))))
|
|
|
|
|
|
|
|
|
|
(define (write-json json file)
|
2023-10-09 10:57:31 +02:00
|
|
|
|
"Serialize and compress JSON to FILE."
|
|
|
|
|
(let ((pivot (string-append file ".tmp")))
|
|
|
|
|
;; Note: 'with-atomic-file-output' doesn't work well with
|
|
|
|
|
;; 'call-with-gzip-output-port' since the latter closes its underlying
|
|
|
|
|
;; port.
|
|
|
|
|
(call-with-output-file pivot
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(chmod port #o644)
|
|
|
|
|
(call-with-gzip-output-port port
|
|
|
|
|
(lambda (port)
|
|
|
|
|
(set-port-encoding! port "UTF-8")
|
|
|
|
|
(scm->json json port))
|
|
|
|
|
#:buffer-size (expt 2 16)
|
|
|
|
|
#:level 9)))
|
|
|
|
|
(rename-file pivot file)))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;
|
|
|
|
|
;;; Entry point.
|
|
|
|
|
;;;
|
|
|
|
|
|
|
|
|
|
(define-public (main . args)
|
|
|
|
|
(match args
|
|
|
|
|
((_ directory)
|
|
|
|
|
(info (G_ "package metadata will be written to '~a'~%") directory)
|
|
|
|
|
(for-each (lambda (thunk file)
|
|
|
|
|
(write-json (thunk)
|
|
|
|
|
(string-append directory "/" file)))
|
|
|
|
|
(list packages-json sources-json)
|
2023-10-09 10:57:31 +02:00
|
|
|
|
'("packages.json.gz" "sources.json.gz")))
|
2023-01-04 18:18:21 +01:00
|
|
|
|
((command . _)
|
|
|
|
|
(leave (G_ "Usage: ~a DIRECTORY
|
|
|
|
|
|
|
|
|
|
Write 'packages.json' and 'sources.json' files to DIRECTORY.\n")
|
|
|
|
|
(basename command)))))
|
|
|
|
|
|
|
|
|
|
(apply main (command-line))
|