summaryrefslogtreecommitdiff
path: root/src/native-libs/install.rkt
blob: 638c507e5324f1944b58eceecd9a73781767e8fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
#lang racket/base
(require racket/path
         racket/system
         racket/format
         racket/file
         racket/pretty
         racket/list
         "cmdline.rkt")

(define sign-as #f)
(define only-meta? #f)

(define dest-dir
  (build-command-line
   #:once-each
   [("--sign-as") id "Sign Mac OS X libraries"
    (set! sign-as id)]
   [("--only-meta") "only generate \"info.rkt\" and \"LICENSE.txt\" files"
    (set! only-meta? #t)]
   #:args (dest-dir)
   dest-dir))

(when (and mac? aarch64? (not sign-as))
  (error "supply `--sign-as` for AArch64 Mac OS"))

;; Hack to make AArch64 Mac OS and Windows libraries look like other Macs:
(define aarch64-renames
  `(("libffi.7" "libffi.6")))

(define libs
  `("libffi.6"
    "libgio-2.0.0"
    "libgmodule-2.0.0"
    "libgthread-2.0.0"
    "libglib-2.0.0"
    "libgobject-2.0.0"
    "libintl.9"
    "libharfbuzz.0"
    "libfribidi.0"
    "libpango-1.0.0"
    "libpangocairo-1.0.0"
    "libpangoft2-1.0.0"
    "libatk-1.0.0"
    "libexpat.1"
    "libfontconfig.1"
    "libfreetype.6"
    "libcairo.2"
    "libpixman-1.0"
    "libpng16.16"
    "libgmp.10"
    "libmpfr.4"
    "libjpeg.9"
    "libpoppler.44"
    "libpoppler-glib.8"))

(define win-libs
  (append
   '("libiconv-2"
     "libeay32"
     "ssleay32"
     "sqlite3"
     "zlib1"
     "libpangowin32-1.0.0")
   (if aarch64?
       null
       '("longdouble"))))

(define mac-libs
  '("libedit.0"))

(define mac64-libs
  '("MMTabBarView.framework"))

(define macx86-libs
  '("PSMTabBarControl.framework"))

(define nonwin-libs
  '("libcrypto.1.1"
    "libssl.1.1"
    "libuuid.1"))

(define no-copy-libs
  '("PSMTabBarControl.framework"
    "MMTabBarView.framework"))

(define linux-libs
  (append
   '("libXau.6"
     "libxcb-shm.0"
     "libxcb-render.0"
     "libxcb.1"
     "libX11.6"
     "libXext.6"
     "libXrender.1"
     "fonts")
   '("libz.1"
     "libsqlite3.0")
   '("libgtk-x11-2.0.0"
     "libgdk-x11-2.0.0"
     "libgdk_pixbuf-2.0.0")))
(define linux-remove-libs
  '("libintl.9"))

(struct pkg-spec (name
                  suffix ; (increment after "-" when library versions change)
                  subdir
                  license-txt-extra ; string to use in "LICENSE.txt"
                  info-rkt-comments ; list of strings for comments in the package "info.rkt" file
                  lib? ; dynamic libraries (as opposed to shared files)
                  for-pkg ; name (e.g., "base"), of #f if the same as the pkg name
                  version ; string or #f
                  libs) ; libs is a:
  ;; (listof (cons/c base-name (cons/c (or/c #f license-desc) (non-empty-listof license-sexp))))
  #:transparent)

(define-syntax-rule (package-mapping-qq ([x ...] ...))
  ;; extra parentheses to avoid reindenting everything
  (list (pkg-spec `x ...) ...))

(define package-mapping
  (package-mapping-qq
   (["draw"        ; pkg name
     "-3"          ; pkg suffix (increment after "-" when library versions change)
     "racket/draw" ; subdir
     "" ; extra for "LICENSE.txt"
     ("additionally, fontconfig/src/fcmd5.h and"
      "fontconfig/src/ftglue.{c,h} are placed in the public domain"
      "using non-standardized language.")
     #t ; dynamic libraries (as opposed to shared files)
     #f ; for-pkg name (e.g., "base"), of #f if the same as the pkg name
     #f ; version
     (["libffi" ,(~a "libffi - Copyright (c) 1996-2014  Anthony Green, Red Hat, Inc and others.\n"
                     "libffi is released under the MIT license.")
                MIT]
      ["libglib" "GLib is released under the GNU Library General Public License (GNU LGPL)."
                 LGPL-2.1-or-later]
      ["libgio" #f LGPL-2.1-or-later]
      ["libgmodule" #f LGPL-2.1-or-later]
      ["libgobject" #f LGPL-2.1-or-later]
      ["libgthread" #f LGPL-2.1-or-later]
      ["libintl" "libintl is released under the GNU Library General Public License (GNU LGPL)."
                 LGPL-2.0-or-later ; some files
                 LGPL-2.1-or-later]
      ["libharfbuzz" "HarfBuzz is released under a MIT license."
                     MIT-Modern-Variant]
      ["libfribidi" "FriBidi is released under the GNU Library General Public License (GNU LGPL)."
                    LGPL-2.1-or-later]
      ["libpango" "Pango is released under the GNU Library General Public License (GNU LGPL)."
                  LGPL-2.1-or-later]
      ["libpangocairo" #f LGPL-2.1-or-later]
      ["libpangoft2" #f LGPL-2.1-or-later]
      ["libpangowin32" #f LGPL-2.1-or-later]
      ["libexpat" #f MIT]
      ["libuuid" "libuuid is released under a Modified BSD license."
                 BSD-3-clause]
      ["libfontconfig" ,(~a "FontConfig:\n"
                            " Copyright © 2000,2001,2002,2003,2004,2006,2007 Keith Packard\n"
                            " Copyright © 2005 Patrick Lam\n"
                            " Copyright © 2009 Roozbeh Pournader\n"
                            " Copyright © 2008,2009 Red Hat, Inc.\n"
                            " Copyright © 2008 Danilo Šegan\n"
                            " Copyright © 2012 Google, Inc.")
                       MIT
                       MIT-Modern-Variant
                       HPND-sell-variant
                       Unicode-DFS-2016]
      ["libfreetype" "FreeType is released under the FreeType project license."
                     FTL]
      ["libcairo" "Cairo is released under the GNU Library General Public License (GNU LGPL)."
                  (LGPL-2.1-only OR MPL-1.1)]
      ["libpixman" "Pixman is released under a MIT license."
                   MIT
                   FTL ; has this part been replaced?
                   HPND-sell-variant]
      ["libpng" "Libpng is released under the libpng license."
                Libpng]
      ["libjpeg" "This software is based in part on the work of the Independent JPEG Group."
                 IJG]
      ["zlib1" "zlib is by Jean-loup Gailly and Mark Adler."
               Zlib]
      ["libz" "zlib is by Jean-loup Gailly and Mark Adler."
              Zlib])]
    ["racket"
     "-3"
     "racket"
     ""
     ()
     #t
     "base"
     "1.2"
     (["libeay32" ,(~a "This product includes software developed by the OpenSSL Project for\n"
                       "use in the OpenSSL Toolkit (https://www.openssl.org/).\n"
                       "\n"
                       "Eric Young is the author of libeay and ssleay.")
                  OpenSSL]
      ["ssleay32" #f OpenSSL]
      ["libssl" ,(~a "This product includes software developed by the OpenSSL Project for\n"
                       "use in the OpenSSL Toolkit (https://www.openssl.org/).\n")
                OpenSSL]
      ["libcrypto" #f OpenSSL]
      ["libiconv-2" "libiconv is released under the GNU Lesser General Public License (GNU LGPL)."
                    LGPL-3.0-or-later]
      ["longdouble" ,(~a "The source to longdouble is included with the Racket source code,\n"
                         "which is available from\n"
                         "  https://www.racket-lang.org/")
                    (Apache-2.0 OR MIT)]
      ["libedit" ,(~a "This package includes libedit software developed for NetBSD under the\n"
                      "NetBSD license.")
                 BSD-3-clause])]

    ["math"
     ""
     "math"
     ""
     ()
     #t
     #f
     #f
     (["libgmp" ,(~a "GNU MP is dual licensed under under the conditions of the GNU Lesser\n"
                     "General Public License (LGPL), version 3, or the GNU General Public\n"
                     "License (GPL), version 2. This is the recipient’s choice, and the\n"
                     "recipient also has the additional option of applying later versions of\n"
                     "these licenses.")
                (LGPL-3.0-or-later OR GPL-2.0-or-later)]
      ["libmpfr" ,(~a "MPFR is released under the GNU Lesser General Public License (GNU LGPL),\n"
                      "either version 3 of the license, or (at your option) any later version.")
                 LGPL-3.0-or-later])]

    ["draw-x11"
     ""
     "racket/draw/x11"
     ""
     ()
     #t
     "draw"
     #f
     (["libX11.6" "libX11 is released under the X.Org Foundation license."
                  X11]
      ["libXau.6" "libXau - Copyright 1988, 1993, 1994, 1998  The Open Group"
                  X11]
      ["libxcb-shm.0" "libxcb - Copyright (C) 2001-2006 Bart Massey, Jamey Sharp, and Josh Triplett."
                      X11]
      ["libxcb-render.0" #f X11]
      ["libxcb.1" #f X11]
      ["libXext.6" "libXext - Copyright 1986, 1987, 1988, 1989, 1994, 1998  The Open Group"
                   X11]
      ["libXrender.1" "libXrender - Copyright © 2001,2003 Keith Packard"
                      X11])]
    ["draw-ttf"
     ""
     "racket/draw/ttf"
     ""
     ()
     #f
     "draw"
     #f
     (["fonts" ,(~a "GNU FreeFont\n"
                    "Copyleft 2002, 2003, 2005, 2008, 2009, 2010 Free Software Foundation.\n"
                    "\n"
                    "Free UCS scalable fonts is free software; you can redistribute it and/or\n"
                    "modify it under the terms of the GNU General Public License as published\n"
                    "by the Free Software Foundation; either version 3 of the License, or\n"
                    "(at your option) any later version.\n"
                    "\n"
                    "The fonts are distributed in the hope that they will be useful, but\n"
                    "WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n"
                    "or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License\n"
                    "for more details.\n"
                    "\n"
                    "You should have received a copy of the GNU General Public License along\n"
                    "with this program; if not, write to the Free Software Foundation, Inc.,\n"
                    "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
                    "\n"
                    "As a special exception, if you create a document which uses this font, and\n"
                    "embed this font or unaltered portions of this font into the document, this\n"
                    "font does not by itself cause the resulting document to be covered by the\n"
                    "GNU General Public License. This exception does not however invalidate any\n"
                    "other reasons why the document might be covered by the GNU General Public\n"
                    "License. If you modify this font, you may extend this exception to your\n"
                    "version of the font, but you are not obligated to do so.  If you do not\n"
                    "wish to do so, delete this exception statement from your version.")
               (GPL-3.0-or-later WITH Font-exception-2.0)])]

    ["gui"
     ""
     "racket/gui"
     ""
     ()
     #t
     #f
     "1.3" ; version
     (["libgtk-x11-2.0.0" "GTK+ is released under the GNU Library General Public License (GNU LGPL)."
                          LGPL-2.1-or-later]
      ["libatk" "ATK is released under the GNU Library General Public License (GNU LGPL)."
                LGPL-2.1-or-later]
      ["libgdk-x11-2.0.0" #f LGPL-2.1-or-later]
      ["libgdk_pixbuf-2.0.0" #f LGPL-2.1-or-later]
      ["PSMTabBarControl.framework" ,(~a "PSMTabBarControl is BSD licensed.\n"
                                         ;; no https
                                         "See: http://www.positivespinmedia.com/dev/PSMTabBarControl.html")
                                    BSD-3-clause]
      ["MMTabBarView.framework" ,(~a "MMTabBarView is BSD licensed.\n"
                                     "See: https://mimo42.github.io/MMTabBarView/")
                                BSD-3-clause])]

    ["db"
     ""
     "db"
     ""
     ()
     #t
     "base"
     #f
     (["libsqlite3.0" "SQLite3 is in the public domain."
                      blessing]
      ["sqlite3" "SQLite3 is in the public domain."
                 blessing])]
    
    ["poppler"
     ""
     "racket-poppler"
     ""
     ()
     #t
     "racket-poppler"
     #f
     (["libpoppler"
       ;; Note: Poppler is GPL and *not* in the main Racket distribution (which is LGPL)
       "Poppler is released under the GNU General Public License (GNU GPL)."
       GPL-2.0-or-later])])))

(define (framework? p)
  (regexp-match? #rx"[.]framework" p))

(define (plain-path? p)
  (or (equal? p "fonts")
      (framework? p)))

(define (revert-name p renames)
  (or (for/or ([pr (in-list renames)])
	(and (equal? (cadr pr) p)
	     (car pr)))
      p))

(define from (build-path (current-directory) "dest" (if win? "bin" "lib")))

(define (find-pkg lib)
  (define pkg (for/or ([p (in-list package-mapping)])
                (define nl
                  (for/or ([nl (in-list (pkg-spec-libs p))])
                    (define n (car nl))
                    (cond
                     [(equal? n lib) nl]
                     [else
                      (define len (string-length n))
                      (and ((string-length lib) . > . (add1 len))
                           (string=? n (substring lib 0 len))
                           (regexp-match? #rx"[-.0-9]" (string (string-ref lib len)))
                           nl)])))
                (and nl
                     (list (pkg-spec-name p)
                           (pkg-spec-suffix p)
                           (pkg-spec-subdir p)
                           (cadr nl)
                           (cddr nl)))))
  (unless pkg
    (error 'install "cannot find package for library: ~e" lib))
  (apply values pkg))

(define (write-setup-infotab o)
  (for-each (λ (s) (displayln s o))
            `("#lang setup/infotab"
              ";; SPDX-License-Identifier: (Apache-2.0 OR MIT)"
              ";; THIS FILE IS AUTO-GENERATED FROM racket/src/native-libs/install.rkt")))

(define (gen-info platform i-platform for-pkg pkg-name subdir libs lic-sexps lics lic-end comments lib? vers)
  (define dest (build-path dest-dir pkg-name))
  (define lib-path (build-path dest subdir "info.rkt"))
  (define top-path (build-path dest "info.rkt"))
  (define same? (equal? lib-path top-path))
  (define (write-libs o)
    (newline o)
    (pretty-write `(define install-platform ,i-platform) o)
    (newline o)
    (pretty-write `(define ,(if lib?
                                'copy-foreign-libs
                                'copy-shared-files)
                    (quote ,libs))
                  o)
    (define dirs (filter (lambda (lib)
                           (or (framework? lib)
                               (directory-exists? (build-path dest subdir lib))))
                         libs))
    (unless (null? dirs)
      (newline o)
      (pretty-write `(define compile-omit-paths (quote ,dirs)) o)))
  (define (write-pkg o)
    (newline o)
    (pretty-write `(define collection 'multi) o)
    (pretty-write `(define deps '("base")) o)
    (newline o)
    (pretty-write `(define pkg-desc ,(format "~a for \"~a\" package"
                                             (if lib?
                                                 "native libraries"
                                                 "shared files")
                                             for-pkg))
                  o)
    (newline o)
    (pretty-write `(define pkg-authors '(mflatt)) o)
    (when vers
      (newline o)
      (pretty-write `(define version ,vers) o))
    (let* ([lic-sexps (remove-duplicates (cons '(Apache-2.0 OR MIT) lic-sexps))]
           [license
            (let loop ([this (car lic-sexps)]
                       [more (cdr lic-sexps)])
              (if (pair? more)
                  `(,this AND ,(loop (car more) (cdr more)))
                  this))])
      (newline o)
      (pretty-write `(define license ',license) o))
    (unless (null? comments)
      (newline o)
      (for ([comment (in-list comments)])
        (fprintf o ";; ~a\n" comment))))
  (unless same?
    (printf "Write ~a\n" lib-path)
    (call-with-output-file*
     lib-path
     #:exists 'truncate
     (lambda (o)
       (write-setup-infotab o)
       (write-libs o))))
  (printf "Write ~a\n" top-path)
  (call-with-output-file*
   top-path
   #:exists 'truncate
   (lambda (o)
     (write-setup-infotab o)
     (write-pkg o)
     (when same?
       (write-libs o))))
  (define lic-path (build-path dest "LICENSE.txt"))
  (printf "Write ~a\n" lic-path)
  (call-with-output-file*
   lic-path
   #:exists 'truncate
   (lambda (o)
     (displayln pkg-name o)
     (newline o)
     (displayln "The Racket code in this package is distributed under the Apache 2.0" o)
     (displayln "and MIT licenses. The user can choose the license under which they" o)
     (displayln "will be using the software. There may be other licenses within the" o)
     (displayln "distribution with which the user must also comply." o)
     (for ([l (in-list lics)])
       (newline o)
       (displayln l o))
     (display lic-end o))))

(define (install platform i-platform so fixup libs renames)
  (define pkgs (make-hash))
  (define pkgs-lic (make-hash))
  (define pkgs-lic-sexps (make-hash))

  (define (install lib)
    (define-values (p orig-p)
      (let ()
	(define (both v) (values v v))
	(cond
	 [(plain-path? lib) (both lib)]
	 [(procedure? so) (both (so lib))]
	 [else
	  (define (make lib) (format "~a.~a" lib so))
	  (values (make lib) (make (revert-name lib renames)))])))
    (define-values (pkg suffix subdir lic lic-sexps) (find-pkg lib))
    (when (null? lic-sexps)
      (error 'install "missing license for library: ~e\n  package: ~e" lib pkg))
    (define dir (build-path dest-dir
                            (~a pkg "-" platform suffix)
                            subdir))
    (define dest (build-path dir p))
    (let-values ([(base name dir?) (split-path dest)])
      (make-directory* base))
    (unless only-meta?
      (unless (member p no-copy-libs)
        (cond
          [(file-exists? dest) (delete-file dest)]
          [(directory-exists? dest) (delete-directory/files dest)])
        (define src (build-path from orig-p))
        (if (directory-exists? src)
            (copy-directory/files src dest)
            (copy-file src dest)))
      (unless (plain-path? p)
        (fixup p dest)))

    (hash-update! pkgs pkg (lambda (l) (cons p l)) '())
    (hash-update! pkgs-lic-sexps pkg (λ (l) (append lic-sexps l)) '())
    (when lic
      (hash-update! pkgs-lic pkg (lambda (l) (cons lic l)) '())))
  
  (for-each install libs)

  (for ([(pkg libs) (in-hash pkgs)])
    (define a
      (findf (λ (a)
               (equal? pkg (pkg-spec-name a)))
             package-mapping))
    (gen-info platform 
              i-platform
              (or (pkg-spec-for-pkg a) pkg)
              (~a pkg "-" platform (pkg-spec-suffix a))
              (pkg-spec-subdir a)
              libs
              (reverse (hash-ref pkgs-lic-sexps pkg))
              (reverse (hash-ref pkgs-lic pkg null))
              (pkg-spec-license-txt-extra a)
              (pkg-spec-info-rkt-comments a)
              (pkg-spec-lib? a)
              (pkg-spec-version a))))

(define (install-mac)
  (define (fixup p p-new)
    (unless (framework? p)
      (printf "Fixing ~s\n" p-new)
      (when aarch64?
	(system (format "codesign --remove-signature ~a" p-new)))
      (unless (memq 'write (file-or-directory-permissions p-new))
        (file-or-directory-permissions p-new #o744))
      (system (format "install_name_tool -id ~a ~a" (file-name-from-path p-new) p-new))
      (for-each (lambda (s)
                  (system (format "install_name_tool -change ~a @loader_path/~a ~a"
                                  (format "~a/~a.dylib" from (revert-name s renames))
                                  (format "~a.dylib" s)
                                  p-new)))
                (append libs nonwin-libs))
      (system (format "strip -S ~a" p-new))
      (when sign-as
	(system (format "codesign -s ~s --timestamp ~a" sign-as p-new)))))

  (define platform (~a (if m32? 
                           (if ppc? "ppc" "i386")
			   (if aarch64? "aarch64" "x86_64"))
                       "-macosx"))

  (define renames (if aarch64?
                      aarch64-renames
                      null))

  (install platform platform "dylib" fixup
           (append libs
                   (cond
                     [ppc? '()]
                     [else mac-libs])
                   (cond
                     [m32? '()]
                     [else mac64-libs])
                   (cond
                     [aarch64? '()]
                     [else macx86-libs])
                   nonwin-libs)
           renames))

(define (install-win)
  (define exe-prefix (cond
                       [m32? "i686-w64-mingw32"]
                       [aarch64? "aarch64-w64-mingw32"]
                       [else "x86_64-w64-mingw32"]))

  (define renames (if aarch64?
                      aarch64-renames
                      null))
  
  (define (rename-one s)
    (regexp-replace #rx"!"
                    (regexp-replace* #rx"[.]"
                                     (regexp-replace #rx"[.](?=.*[.])" s "!")
                                     "-")
                    "."))

  (define win-renames
    (map (lambda (p) (list (rename-one (car p)) (rename-one (cadr p))))
         renames))

  (define (fixup p p-new)
    (printf "Fixing ~s\n" p-new)
    (system (~a exe-prefix "-strip -S " p-new))
    (define-values (i o) (open-input-output-file p-new #:exists 'update))
    (for-each (lambda (p)
                (let loop ()
                  (file-position i 0)
                  (define m (regexp-match-positions (regexp-quote (car p)) i))
                  (when m
                    (file-position o (caar m))
                    (display (cadr p) o)
                    (flush-output o)
                    (loop))))
              win-renames)
    (close-input-port i)
    (close-output-port o))

  (parameterize ([current-environment-variables
                  (environment-variables-copy
                   (current-environment-variables))])
    (putenv "PATH" (~a (if m32?
                           "/usr/local/mw32/bin:/usr/mw32/bin:"
                           "/usr/local/mw64/bin:/usr/mw64/bin:")
                       (getenv "PATH")))

    (install (~a "win32-" (if m32? "i386" (if aarch64? "arm64" "x86_64")))
             (~a "win32\\" (if m32? "i386" (if aarch64? "arm64" "x86_64")))
             "dll"
             fixup
             (for/list ([s (in-list (append libs
                                            win-libs))])
               (rename-one s))
             win-renames)))

(define (install-linux)
  (define (fixup p p-new)
    (printf "Fixing ~s\n" p-new)
    (file-or-directory-permissions p-new #o755)
    (unless (system (format "strip -S ~a" p-new))
      (error "strip failed"))
    ;; Might fail if there are no external references:
    (system (format "chrpath -r '$ORIGIN' ~a" p-new)))

  (define platform (~a (if m32?
                           "i386"
                           "x86_64")
                       "-linux-natipkg"))

  (define (add-so orig-p)
    (define special-cases
      '("libpangoft2-1.0"
        "libpangocairo-1.0"
        "libpango-1.0"
        "libgobject-2.0"
        "libglib-2.0"
        "libgthread-2.0"
        "libgmodule-2.0"
        "libgio-2.0"
        "libgdk_pixbuf-2.0"
        "libatk-1.0"
        "libgdk-x11-2.0"
        "libgtk-x11-2.0"))
    (let loop ([p orig-p] [suffix ""])
      (define p-so (string-append p ".so" suffix))
      (cond
       [(or (file-exists? (build-path from p-so))
            (and only-meta? (member p special-cases)))
        p-so]
       [else
        (define m (regexp-match #rx"^(.*)[.](.*)$" p))
        (cond
         [m
          (loop (cadr m) (string-append "." (caddr m) suffix))]
         [only-meta?
          p-so]
         [else
          (error 'add-so "not found: ~s" orig-p)])])))

  (install platform platform add-so fixup
           (append (remove* linux-remove-libs
                            libs)
                   nonwin-libs
                   linux-libs)
           null))

(cond
 [win? (install-win)]
 [linux? (install-linux)]
 [else (install-mac)])