Merge branch 'master' into staging
[jackhill/guix/guix.git] / tests / gremlin.scm
CommitLineData
15aa2c38 1;;; GNU Guix --- Functional package management for GNU
e7ad192d 2;;; Copyright © 2015, 2018, 2020, 2022 Ludovic Courtès <ludo@gnu.org>
6a2050b1 3;;; Copyright © 2022 Chris Marusich <cmmarusich@gmail.com>
24c3485b 4;;; Copyright © 2022 Pierre Langlois <pierre.langlois@gmx.com>
15aa2c38
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (test-gremlin)
22 #:use-module (guix elf)
6a2050b1 23 #:use-module (guix tests)
24c3485b
PL
24 #:use-module ((guix utils) #:select (call-with-temporary-directory
25 target-aarch64?))
15aa2c38
LC
26 #:use-module (guix build utils)
27 #:use-module (guix build gremlin)
24c3485b 28 #:use-module (gnu packages bootstrap)
15aa2c38 29 #:use-module (srfi srfi-1)
cd91504d 30 #:use-module (srfi srfi-26)
49a1203d 31 #:use-module (srfi srfi-34)
15aa2c38
LC
32 #:use-module (srfi srfi-64)
33 #:use-module (rnrs io ports)
b178fc23 34 #:use-module (ice-9 popen)
53fd256e
LC
35 #:use-module (ice-9 rdelim)
36 #:use-module (ice-9 regex)
15aa2c38
LC
37 #:use-module (ice-9 match))
38
39(define %guile-executable
f7dfda2c
LC
40 (match (false-if-exception (readlink "/proc/self/exe"))
41 ((? string? program)
15aa2c38
LC
42 (and (file-exists? program) (elf-file? program)
43 program))
44 (_
45 #f)))
46
47(define read-elf
48 (compose parse-elf get-bytevector-all))
49
b178fc23
LC
50(define c-compiler
51 (or (which "gcc") (which "cc") (which "g++")))
52
15aa2c38
LC
53\f
54(test-begin "gremlin")
55
56(unless %guile-executable (test-skip 1))
57(test-assert "elf-dynamic-info-needed, executable"
58 (let* ((elf (call-with-input-file %guile-executable read-elf))
59 (dyninfo (elf-dynamic-info elf)))
60 (or (not dyninfo) ;static executable
61 (lset<= string=?
62 (list (string-append "libguile-" (effective-version))
6b974159 63 "libc")
15aa2c38
LC
64 (map (lambda (lib)
65 (string-take lib (string-contains lib ".so")))
66 (elf-dynamic-info-needed dyninfo))))))
67
53fd256e 68(unless (and %guile-executable (not (getenv "LD_LIBRARY_PATH"))
e4b5a238
CM
69 (file-needed %guile-executable) ;statically linked?
70 ;; When Guix has been built on a foreign distro, using a
71 ;; toolchain and libraries from that foreign distro, it is not
72 ;; unusual for the runpath to be empty.
73 (pair? (file-runpath %guile-executable)))
53fd256e
LC
74 (test-skip 1))
75(test-assert "file-needed/recursive"
76 (let* ((needed (file-needed/recursive %guile-executable))
77 (pipe (dynamic-wind
78 (lambda ()
79 ;; Tell ld.so to list loaded objects, like 'ldd' does.
80 (setenv "LD_TRACE_LOADED_OBJECTS" "yup"))
81 (lambda ()
82 (open-pipe* OPEN_READ %guile-executable))
83 (lambda ()
84 (unsetenv "LD_TRACE_LOADED_OBJECTS")))))
85 (define ldd-rx
86 (make-regexp "^[[:blank:]]+([[:graph:]]+ => )?([[:graph:]]+) .*$"))
87
88 (define (read-ldd-output port)
89 ;; Read from PORT output in GNU ldd format.
90 (let loop ((result '()))
91 (match (read-line port)
92 ((? eof-object?)
93 (reverse result))
94 ((= (cut regexp-exec ldd-rx <>) m)
95 (if m
96 (loop (cons (match:substring m 2) result))
97 (loop result))))))
53fd256e 98 (define ground-truth
6a2050b1 99 (remove (lambda (entry)
e7ad192d
LC
100 ;; See vdso(7) for the list of vDSO names across
101 ;; architectures.
6a2050b1 102 (or (string-prefix? "linux-vdso.so" entry)
e7ad192d
LC
103 (string-prefix? "linux-vdso32.so" entry) ;32-bit powerpc
104 (string-prefix? "linux-vdso64.so" entry) ;64-bit powerpc
24c3485b
PL
105 (string-prefix? "linux-gate.so" entry) ;i386
106 ;; FIXME: ELF files on aarch64 do not always include a
107 ;; NEEDED entry for the dynamic linker, and it is unclear
108 ;; if that is OK. See: https://issues.guix.gnu.org/52943
109 (and (target-aarch64?)
110 (string-contains entry (glibc-dynamic-linker)))))
53fd256e
LC
111 (read-ldd-output pipe)))
112
113 (and (zero? (close-pipe pipe))
6a2050b1
CM
114 ;; It's OK if file-needed/recursive returns multiple entries that are
115 ;; different strings referring to the same file. This appears to be a
116 ;; benign edge case. See: https://issues.guix.gnu.org/52940
117 (lset= file=? (pk 'truth ground-truth) (pk 'needed needed)))))
53fd256e 118
cd91504d
LC
119(test-equal "expand-origin"
120 '("OOO/../lib"
121 "OOO"
122 "../OOO/bar/OOO/baz"
123 "ORIGIN/foo")
124 (map (cut expand-origin <> "OOO")
125 '("$ORIGIN/../lib"
126 "${ORIGIN}"
127 "../${ORIGIN}/bar/$ORIGIN/baz"
128 "ORIGIN/foo")))
129
b178fc23
LC
130(unless c-compiler
131 (test-skip 1))
132(test-equal "strip-runpath"
133 "hello\n"
134 (call-with-temporary-directory
135 (lambda (directory)
136 (with-directory-excursion directory
137 (call-with-output-file "t.c"
138 (lambda (port)
139 (display "int main () { puts(\"hello\"); }" port)))
140 (invoke c-compiler "t.c"
6b974159 141 "-Wl,--enable-new-dtags" "-Wl,-rpath=/foo" "-Wl,-rpath=/bar")
b178fc23
LC
142 (let* ((dyninfo (elf-dynamic-info
143 (parse-elf (call-with-input-file "a.out"
144 get-bytevector-all))))
145 (old (elf-dynamic-info-runpath dyninfo))
146 (new (strip-runpath "a.out"))
147 (new* (strip-runpath "a.out")))
148 (validate-needed-in-runpath "a.out")
149 (and (member "/foo" old) (member "/bar" old)
150 (not (member "/foo" new))
151 (not (member "/bar" new))
152 (equal? new* new)
153 (let* ((pipe (open-input-pipe "./a.out"))
154 (str (get-string-all pipe)))
155 (close-pipe pipe)
156 str)))))))
157
49a1203d
LC
158(unless c-compiler
159 (test-skip 1))
160(test-equal "set-file-runpath + file-runpath"
161 "hello\n"
162 (call-with-temporary-directory
163 (lambda (directory)
164 (with-directory-excursion directory
165 (call-with-output-file "t.c"
166 (lambda (port)
167 (display "int main () { puts(\"hello\"); }" port)))
168
169 (invoke c-compiler "t.c"
170 "-Wl,--enable-new-dtags" "-Wl,-rpath=/xxxxxxxxx")
171
172 (let ((original-runpath (file-runpath "a.out")))
173 (and (member "/xxxxxxxxx" original-runpath)
174 (guard (c ((runpath-too-long-error? c)
175 (string=? "a.out" (runpath-too-long-error-file c))))
176 (set-file-runpath "a.out" (list (make-string 777 #\y))))
177 (let ((runpath (delete "/xxxxxxxxx" original-runpath)))
178 (set-file-runpath "a.out" runpath)
179 (equal? runpath (file-runpath "a.out")))
180 (let* ((pipe (open-input-pipe "./a.out"))
181 (str (get-string-all pipe)))
182 (close-pipe pipe)
183 str)))))))
184
10101712
DM
185(unless c-compiler
186 (test-skip 1))
187(test-equal "elf-dynamic-info-soname"
188 "libfoo.so.2"
189 (call-with-temporary-directory
190 (lambda (directory)
191 (with-directory-excursion directory
192 (call-with-output-file "t.c"
193 (lambda (port)
194 (display "// empty file" port)))
195 (invoke c-compiler "t.c"
196 "-shared" "-Wl,-soname,libfoo.so.2")
197 (let* ((dyninfo (elf-dynamic-info
198 (parse-elf (call-with-input-file "a.out"
199 get-bytevector-all))))
200 (soname (elf-dynamic-info-soname dyninfo)))
201 soname)))))
202
15aa2c38 203(test-end "gremlin")