gnu: waybar: Fix build.
[jackhill/guix/guix.git] / guix / git-download.scm
CommitLineData
9b5b5c17 1;;; GNU Guix --- Functional package management for GNU
b5eb901a 2;;; Copyright © 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
6554be68 3;;; Copyright © 2017 Mathieu Lirzin <mthl@gnu.org>
f135b4ae 4;;; Copyright © 2017 Christopher Baines <mail@cbaines.net>
bbf6bc1a 5;;; Copyright © 2020 Jakub Kądziołka <kuba@kadziolka.net>
9b5b5c17
LC
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
22(define-module (guix git-download)
6119ebf1 23 #:use-module (guix gexp)
e87f0591 24 #:use-module (guix store)
6119ebf1 25 #:use-module (guix monads)
9b5b5c17 26 #:use-module (guix records)
9b5b5c17 27 #:use-module (guix packages)
37ce440d 28 #:use-module (guix modules)
0d5a559f 29 #:autoload (guix build-system gnu) (standard-packages)
aed0a594 30 #:use-module (git)
9b5b5c17 31 #:use-module (ice-9 match)
f135b4ae 32 #:use-module (ice-9 vlist)
6554be68 33 #:use-module (srfi srfi-1)
bbf6bc1a
JK
34 #:use-module (srfi srfi-34)
35 #:use-module (srfi srfi-35)
9b5b5c17
LC
36 #:export (git-reference
37 git-reference?
38 git-reference-url
39 git-reference-commit
6750877f 40 git-reference-recursive?
9b5b5c17 41
ee17a9e0
DC
42 git-fetch
43 git-version
6554be68
ML
44 git-file-name
45 git-predicate))
9b5b5c17
LC
46
47;;; Commentary:
48;;;
49;;; An <origin> method that fetches a specific commit from a Git repository.
50;;; The repository URL and commit hash are specified with a <git-reference>
51;;; object.
52;;;
53;;; Code:
54
55(define-record-type* <git-reference>
56 git-reference make-git-reference
57 git-reference?
6750877f
LC
58 (url git-reference-url)
59 (commit git-reference-commit)
60 (recursive? git-reference-recursive? ; whether to recurse into sub-modules
61 (default #f)))
9b5b5c17 62
6119ebf1
LC
63(define (git-package)
64 "Return the default Git package."
65 (let ((distro (resolve-interface '(gnu packages version-control))))
3b5829bb 66 (module-ref distro 'git-minimal)))
6119ebf1 67
f220a838 68(define* (git-fetch ref hash-algo hash
9b5b5c17 69 #:optional name
f220a838 70 #:key (system (%current-system)) (guile (default-guile))
6119ebf1 71 (git (git-package)))
f220a838
LC
72 "Return a fixed-output derivation that fetches REF, a <git-reference>
73object. The output is expected to have recursive hash HASH of type
74HASH-ALGO (a symbol). Use NAME as the file name, or a generic name if #f."
6750877f
LC
75 (define inputs
76 ;; When doing 'git clone --recursive', we need sed, grep, etc. to be
77 ;; available so that 'git submodule' works.
78 (if (git-reference-recursive? ref)
0d5a559f 79 (standard-packages)
608d3dca
LC
80
81 ;; The 'swh-download' procedure requires tar and gzip.
82 `(("gzip" ,(module-ref (resolve-interface '(gnu packages compression))
83 'gzip))
84 ("tar" ,(module-ref (resolve-interface '(gnu packages base))
85 'tar)))))
6750877f 86
608d3dca 87 (define guile-json
b5eb901a 88 (module-ref (resolve-interface '(gnu packages guile)) 'guile-json-4))
608d3dca 89
e9f8a7e2
MO
90 (define guile-zlib
91 (module-ref (resolve-interface '(gnu packages guile)) 'guile-zlib))
92
608d3dca
LC
93 (define gnutls
94 (module-ref (resolve-interface '(gnu packages tls)) 'gnutls))
95
37ce440d 96 (define modules
e9f8a7e2
MO
97 (delete '(guix config)
98 (source-module-closure '((guix build git)
99 (guix build utils)
100 (guix build download-nar)
101 (guix swh)))))
37ce440d 102
6119ebf1 103 (define build
37ce440d 104 (with-imported-modules modules
e9f8a7e2
MO
105 (with-extensions (list guile-json gnutls ;for (guix swh)
106 guile-zlib)
608d3dca
LC
107 #~(begin
108 (use-modules (guix build git)
109 (guix build utils)
110 (guix build download-nar)
111 (guix swh)
112 (ice-9 match))
113
114 (define recursive?
115 (call-with-input-string (getenv "git recursive?") read))
116
117 ;; The 'git submodule' commands expects Coreutils, sed,
118 ;; grep, etc. to be in $PATH.
119 (set-path-environment-variable "PATH" '("bin")
120 (match '#+inputs
121 (((names dirs outputs ...) ...)
122 dirs)))
123
124 (setvbuf (current-output-port) 'line)
125 (setvbuf (current-error-port) 'line)
126
127 (or (git-fetch (getenv "git url") (getenv "git commit")
128 #$output
129 #:recursive? recursive?
130 #:git-command (string-append #+git "/bin/git"))
131 (download-nar #$output)
132
133 ;; As a last resort, attempt to download from Software Heritage.
a7696b97
LC
134 ;; Disable X.509 certificate verification to avoid depending
135 ;; on nss-certs--we're authenticating the checkout anyway.
608d3dca
LC
136 ;; XXX: Currently recursive checkouts are not supported.
137 (and (not recursive?)
a7696b97 138 (parameterize ((%verify-swh-certificate? #f))
b8815c5e
LC
139 (format (current-error-port)
140 "Trying to download from Software Heritage...~%")
141 (swh-download (getenv "git url") (getenv "git commit")
142 #$output))))))))
6750877f 143
f220a838 144 (mlet %store-monad ((guile (package->derivation guile system)))
6119ebf1 145 (gexp->derivation (or name "git-checkout") build
c0b2d08b
LC
146
147 ;; Use environment variables and a fixed script name so
148 ;; there's only one script in store for all the
149 ;; downloads.
150 #:script-name "git-download"
151 #:env-vars
152 `(("git url" . ,(git-reference-url ref))
153 ("git commit" . ,(git-reference-commit ref))
154 ("git recursive?" . ,(object->string
155 (git-reference-recursive? ref))))
67c2db17
LC
156 #:leaked-env-vars '("http_proxy" "https_proxy"
157 "LC_ALL" "LC_MESSAGES" "LANG"
158 "COLUMNS")
c0b2d08b 159
6119ebf1 160 #:system system
6b44a097 161 #:local-build? #t ;don't offload repo cloning
6119ebf1
LC
162 #:hash-algo hash-algo
163 #:hash hash
164 #:recursive? #t
5c6a30c5 165 #:guile-for-build guile)))
9b5b5c17 166
ee17a9e0
DC
167(define (git-version version revision commit)
168 "Return the version string for packages using git-download."
bbf6bc1a
JK
169 ;; git-version is almost exclusively executed while modules are being loaded.
170 ;; This makes any errors hide their backtrace. Avoid the mysterious error
171 ;; "Value out of range 0 to N: 7" when the commit ID is too short, which
172 ;; can happen, for example, when the user swapped the revision and commit
173 ;; arguments by mistake.
174 (when (< (string-length commit) 7)
e84d9dcd
JK
175 (raise
176 (condition
177 (&message (message "git-version: commit ID unexpectedly short")))))
ee17a9e0
DC
178 (string-append version "-" revision "." (string-take commit 7)))
179
180(define (git-file-name name version)
181 "Return the file-name for packages using git-download."
182 (string-append name "-" version "-checkout"))
183
f135b4ae
CB
184\f
185;;;
186;;; 'git-predicate'.
187;;;
188
aed0a594
LC
189(define (git-file-list directory)
190 "Return the list of files checked in in the Git repository at DIRECTORY.
191The result is similar to that of the 'git ls-files' command, except that it
192also includes directories, not just regular files. The returned file names
193are relative to DIRECTORY, which is not necessarily the root of the checkout."
280fc835
MB
194 (let* (;; 'repository-working-directory' always returns a trailing "/",
195 ;; so add one here to ease the comparisons below.
196 (directory (string-append (canonicalize-path directory) "/"))
aed0a594 197 (dot-git (repository-discover directory))
aed0a594 198 (repository (repository-open dot-git))
5d9fe431 199 (workdir (repository-working-directory repository))
aed0a594
LC
200 (head (repository-head repository))
201 (oid (reference-target head))
202 (commit (commit-lookup repository oid))
203 (tree (commit-tree commit))
204 (files (tree-list tree)))
205 (repository-close! repository)
280fc835 206 (if (string=? workdir directory)
aed0a594 207 files
280fc835 208 (let ((relative (string-drop directory (string-length workdir))))
aed0a594
LC
209 (filter-map (lambda (file)
210 (and (string-prefix? relative file)
211 (string-drop file (string-length relative))))
212 files)))))
f135b4ae 213
6554be68
ML
214(define (git-predicate directory)
215 "Return a predicate that returns true if a file is part of the Git checkout
13512e1b
LC
216living at DIRECTORY. If DIRECTORY does not lie within a Git checkout, and
217upon Git errors, return #f instead of a predicate.
6554be68
ML
218
219The returned predicate takes two arguments FILE and STAT where FILE is an
220absolute file name and STAT is the result of 'lstat'."
13512e1b
LC
221 (catch 'git-error
222 (lambda ()
223 (let* ((files (git-file-list directory))
224 (inodes (fold (lambda (file result)
225 (let ((stat
226 (lstat (string-append directory "/"
227 file))))
228 (vhash-consv (stat:ino stat) (stat:dev stat)
229 result)))
230 vlist-null
231 files)))
232 (lambda (file stat)
233 ;; Comparing file names is always tricky business so we rely on inode
234 ;; numbers instead.
235 (match (vhash-assv (stat:ino stat) inodes)
236 ((_ . dev) (= dev (stat:dev stat)))
237 (#f #f)))))
238 (const #f)))
6554be68 239
9b5b5c17 240;;; git-download.scm ends here