offload: Fix potential file descriptor and memory leak.
[jackhill/guix/guix.git] / guix / git-download.scm
CommitLineData
9b5b5c17 1;;; GNU Guix --- Functional package management for GNU
e9b046fd 2;;; Copyright © 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
6554be68 3;;; Copyright © 2017 Mathieu Lirzin <mthl@gnu.org>
9b5b5c17
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (guix git-download)
6554be68 21 #:use-module (guix build utils)
6119ebf1 22 #:use-module (guix gexp)
e87f0591 23 #:use-module (guix store)
6119ebf1 24 #:use-module (guix monads)
9b5b5c17 25 #:use-module (guix records)
9b5b5c17 26 #:use-module (guix packages)
0d5a559f 27 #:autoload (guix build-system gnu) (standard-packages)
9b5b5c17 28 #:use-module (ice-9 match)
6554be68
ML
29 #:use-module (ice-9 popen)
30 #:use-module (ice-9 rdelim)
31 #:use-module (srfi srfi-1)
9b5b5c17
LC
32 #:export (git-reference
33 git-reference?
34 git-reference-url
35 git-reference-commit
6750877f 36 git-reference-recursive?
9b5b5c17 37
ee17a9e0
DC
38 git-fetch
39 git-version
6554be68
ML
40 git-file-name
41 git-predicate))
9b5b5c17
LC
42
43;;; Commentary:
44;;;
45;;; An <origin> method that fetches a specific commit from a Git repository.
46;;; The repository URL and commit hash are specified with a <git-reference>
47;;; object.
48;;;
49;;; Code:
50
51(define-record-type* <git-reference>
52 git-reference make-git-reference
53 git-reference?
6750877f
LC
54 (url git-reference-url)
55 (commit git-reference-commit)
56 (recursive? git-reference-recursive? ; whether to recurse into sub-modules
57 (default #f)))
9b5b5c17 58
6119ebf1
LC
59(define (git-package)
60 "Return the default Git package."
61 (let ((distro (resolve-interface '(gnu packages version-control))))
62 (module-ref distro 'git)))
63
f220a838 64(define* (git-fetch ref hash-algo hash
9b5b5c17 65 #:optional name
f220a838 66 #:key (system (%current-system)) (guile (default-guile))
6119ebf1 67 (git (git-package)))
f220a838
LC
68 "Return a fixed-output derivation that fetches REF, a <git-reference>
69object. The output is expected to have recursive hash HASH of type
70HASH-ALGO (a symbol). Use NAME as the file name, or a generic name if #f."
6750877f
LC
71 (define inputs
72 ;; When doing 'git clone --recursive', we need sed, grep, etc. to be
73 ;; available so that 'git submodule' works.
74 (if (git-reference-recursive? ref)
0d5a559f 75 (standard-packages)
6750877f
LC
76 '()))
77
6119ebf1 78 (define build
e9b046fd
LC
79 (with-imported-modules '((guix build git)
80 (guix build utils))
81 #~(begin
82 (use-modules (guix build git)
83 (guix build utils)
84 (ice-9 match))
6119ebf1 85
e9b046fd
LC
86 ;; The 'git submodule' commands expects Coreutils, sed,
87 ;; grep, etc. to be in $PATH.
88 (set-path-environment-variable "PATH" '("bin")
89 (match '#+inputs
90 (((names dirs) ...)
91 dirs)))
6750877f 92
c0b2d08b 93 (git-fetch (getenv "git url") (getenv "git commit")
e9b046fd 94 #$output
c0b2d08b
LC
95 #:recursive? (call-with-input-string
96 (getenv "git recursive?")
97 read)
e9b046fd 98 #:git-command (string-append #+git "/bin/git")))))
6750877f 99
f220a838 100 (mlet %store-monad ((guile (package->derivation guile system)))
6119ebf1 101 (gexp->derivation (or name "git-checkout") build
c0b2d08b
LC
102
103 ;; Use environment variables and a fixed script name so
104 ;; there's only one script in store for all the
105 ;; downloads.
106 #:script-name "git-download"
107 #:env-vars
108 `(("git url" . ,(git-reference-url ref))
109 ("git commit" . ,(git-reference-commit ref))
110 ("git recursive?" . ,(object->string
111 (git-reference-recursive? ref))))
112
6119ebf1 113 #:system system
6b44a097 114 #:local-build? #t ;don't offload repo cloning
6119ebf1
LC
115 #:hash-algo hash-algo
116 #:hash hash
117 #:recursive? #t
5c6a30c5 118 #:guile-for-build guile)))
9b5b5c17 119
ee17a9e0
DC
120(define (git-version version revision commit)
121 "Return the version string for packages using git-download."
122 (string-append version "-" revision "." (string-take commit 7)))
123
124(define (git-file-name name version)
125 "Return the file-name for packages using git-download."
126 (string-append name "-" version "-checkout"))
127
6554be68
ML
128(define (git-predicate directory)
129 "Return a predicate that returns true if a file is part of the Git checkout
130living at DIRECTORY. Upon Git failure, return #f instead of a predicate.
131
132The returned predicate takes two arguments FILE and STAT where FILE is an
133absolute file name and STAT is the result of 'lstat'."
134 (define (parent-directory? thing directory)
135 ;; Return #t if DIRECTORY is the parent of THING.
136 (or (string-suffix? thing directory)
137 (and (string-index thing #\/)
138 (parent-directory? (dirname thing) directory))))
139
140 (let* ((pipe (with-directory-excursion directory
141 (open-pipe* OPEN_READ "git" "ls-files")))
142 (files (let loop ((lines '()))
143 (match (read-line pipe)
144 ((? eof-object?)
145 (reverse lines))
146 (line
147 (loop (cons line lines))))))
ba2260db 148 (inodes (map (lambda (file)
151cb973
CB
149 (let ((stat (lstat
150 (string-append directory "/" file))))
ba2260db
LC
151 (cons (stat:dev stat) (stat:ino stat))))
152 files))
6554be68
ML
153 (status (close-pipe pipe)))
154 (and (zero? status)
155 (lambda (file stat)
156 (match (stat:type stat)
157 ('directory
158 ;; 'git ls-files' does not list directories, only regular files,
159 ;; so we need this special trick.
160 (any (lambda (f) (parent-directory? f file))
161 files))
162 ((or 'regular 'symlink)
ba2260db
LC
163 ;; Comparing file names is always tricky business so we rely on
164 ;; inode numbers instead
165 (member (cons (stat:dev stat) (stat:ino stat))
166 inodes))
6554be68
ML
167 (_
168 #f))))))
169
9b5b5c17 170;;; git-download.scm ends here