~ chicken-core (master) /egg-compile.scm
Trap1;;;; egg-info processing and compilation
2;
3; Copyright (c) 2017-2022, The CHICKEN Team
4; All rights reserved.
5;
6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
7; conditions are met:
8;
9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10; disclaimer.
11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
12; disclaimer in the documentation and/or other materials provided with the distribution.
13; Neither the name of the author nor the names of its contributors may be used to endorse or promote
14; products derived from this software without specific prior written permission.
15;
16; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24; POSSIBILITY OF SUCH DAMAGE.
25
26
27(define default-extension-options '())
28(define default-program-options '())
29(define default-static-program-link-options '())
30(define default-dynamic-program-link-options '())
31(define default-static-extension-link-options '())
32(define default-dynamic-extension-link-options '())
33(define default-static-compilation-options '("-O2" "-d1"))
34(define default-dynamic-compilation-options '("-O2" "-d1"))
35(define default-import-library-compilation-options '("-O2" "-d0"))
36
37(define default-program-linkage
38 (if staticbuild '(static) '(dynamic)))
39
40(define default-extension-linkage
41 (if staticbuild '(static) '(static dynamic)))
42
43(define +unix-executable-extension+ "")
44(define +windows-executable-extension+ ".exe")
45(define +unix-object-extension+ ".o")
46(define +unix-archive-extension+ ".a")
47(define +windows-object-extension+ ".obj")
48(define +windows-archive-extension+ ".a")
49(define +link-file-extension+ ".link")
50
51(define keep-generated-files #f)
52(define dependency-targets '())
53
54
55;;; some utilities
56
57(define override-prefix
58 (let ((prefix (get-environment-variable "CHICKEN_INSTALL_PREFIX")))
59 (lambda (dir default)
60 (if prefix
61 (string-append prefix dir)
62 default))))
63
64(define (object-extension platform)
65 (case platform
66 ((unix) +unix-object-extension+)
67 ((windows) +windows-object-extension+)))
68
69(define (archive-extension platform)
70 (case platform
71 ((unix) +unix-archive-extension+)
72 ((windows) +windows-archive-extension+)))
73
74(define (executable-extension platform)
75 (case platform
76 ((unix) +unix-executable-extension+)
77 ((windows) +windows-executable-extension+)))
78
79(define (copy-directory-command platform)
80 "cp -r")
81
82(define (copy-file-command platform)
83 "cp")
84
85(define (mkdir-command platform)
86 "mkdir -p")
87
88(define (install-executable-command platform)
89 (string-append default-install-program " "
90 default-install-program-executable-flags))
91
92(define (install-file-command platform)
93 (string-append default-install-program " "
94 default-install-program-data-flags))
95
96(define (remove-file-command platform)
97 "rm -f")
98
99(define (cd-command platform)
100 "cd")
101
102(define (uses-compiled-import-library? mode)
103 (not (and (eq? mode 'host) staticbuild)))
104
105;; this one overrides "destination-repository" in egg-environment to allow use of
106;; CHICKEN_INSTALL_PREFIX (via "override-prefix")
107(define (effective-destination-repository mode #!optional run)
108 (if (eq? 'target mode)
109 (if run target-run-repo target-repo)
110 (or (get-environment-variable "CHICKEN_INSTALL_REPOSITORY")
111 (override-prefix (string-append "/lib/chicken/" (number->string binary-version))
112 host-repo))))
113
114;;; topological sort with cycle check
115
116(define (sort-dependencies dag eq)
117 (condition-case (topological-sort dag eq)
118 ((exn runtime cycle)
119 (error "cyclic dependencies" dag))))
120
121
122;;; collect import libraries for all modules
123
124(define (import-libraries mods dest rtarget mode)
125 (define (implib name)
126 (conc dest "/" name ".import."
127 (if (uses-compiled-import-library? mode)
128 "so"
129 "scm")))
130 (if mods
131 (map implib mods)
132 (list (implib rtarget))))
133
134
135;;; normalize target path for "random files" (data, c-include, scheme-include)
136
137(define (normalize-destination dest mode)
138 (let ((dest* (normalize-pathname dest)))
139 (if (irregex-search '(: bos ".." ("\\/")) dest*)
140 (error "destination must be relative to CHICKEN install prefix" dest)
141 (normalize-pathname
142 (make-pathname (if (eq? mode 'target)
143 default-prefix
144 (override-prefix "/" host-prefix))
145 dest*)))))
146
147
148;;; check condition in conditional clause
149
150(define (check-condition tst mode link)
151 (define (fail x)
152 (error "invalid conditional expression in `cond-expand' clause"
153 x))
154 (let walk ((x tst))
155 (cond ((and (list? x) (pair? x))
156 (cond ((and (eq? (car x) 'not) (= 2 (length x)))
157 (not (walk (cadr x))))
158 ((eq? 'and (car x)) (every walk (cdr x)))
159 ((eq? 'or (car x)) (any walk (cdr x)))
160 (else (fail x))))
161 ((memq x '(dynamic static)) (memq x link))
162 ((memq x '(target host)) (memq x mode))
163 ((symbol? x) (feature? x))
164 (else (fail x)))))
165
166
167;;; parse custom configuration information from script
168
169(define (parse-custom-config arg)
170 (define (read-all)
171 (let loop ((lst '()))
172 (let ((x (read)))
173 (if (eof-object? x)
174 (reverse lst)
175 (loop (append (reverse (flatten x)) lst))))))
176 (if (and (list? arg) (eq? 'custom-config (car arg)))
177 (let* ((arg (cdr arg))
178 (in (with-input-from-pipe
179 (conc default-csi " -s "
180 (if (list? arg)
181 (string-intersperse (map ->string arg) " ")
182 (->string arg)))
183 read-all)))
184 (map ->string in))
185 (list arg)))
186
187
188;;; compile an egg-information tree into abstract build/install operations
189
190(define (compile-egg-info eggfile info version platform mode)
191 (let ((exts '())
192 (prgs '())
193 (objs '())
194 (data '())
195 (genfiles '())
196 (cinc '())
197 (scminc '())
198 (target #f)
199 (src #f)
200 (files '())
201 (ifiles '())
202 (cbuild #f)
203 (oname #f)
204 (link '())
205 (dest #f)
206 (sdeps '())
207 (cdeps '())
208 (lopts '())
209 (opts '())
210 (mods #f)
211 (lobjs '())
212 (tfile #f)
213 (ptfile #f)
214 (ifile #f)
215 (install #t)
216 (eggfile (locate-egg-file eggfile))
217 (objext (object-extension platform))
218 (arcext (archive-extension platform))
219 (exeext (executable-extension platform)))
220 (define (check-target t lst)
221 (when (member t lst)
222 (error "target multiply defined" t))
223 t)
224 (define (addfiles . filess)
225 (set! ifiles (concatenate (cons ifiles filess)))
226 files)
227 (define (checkfiles files target)
228 (when (null? files)
229 (warning "target has no files" target)))
230 (define (compile-component info)
231 (case (car info)
232 ((extension)
233 (fluid-let ((target (check-target (cadr info) exts))
234 (cdeps '())
235 (sdeps '())
236 (src #f)
237 (cbuild #f)
238 (link (if (null? link) default-extension-linkage link))
239 (tfile #f)
240 (ptfile #f)
241 (ifile #f)
242 (lopts lopts)
243 (lobjs '())
244 (oname #f)
245 (mods #f)
246 (opts opts))
247 (for-each compile-extension/program (cddr info))
248 (let ((dest (effective-destination-repository mode #t))
249 ;; Respect install-name if specified
250 (rtarget (or oname target)))
251 (when (eq? #t tfile) (set! tfile rtarget))
252 (when (eq? #t ifile) (set! ifile rtarget))
253 (addfiles
254 (if (memq 'static link)
255 (list (conc dest "/" rtarget
256 (if (null? lobjs)
257 objext
258 arcext))
259 (conc dest "/" rtarget +link-file-extension+))
260 '())
261 (if (memq 'dynamic link) (list (conc dest "/" rtarget ".so")) '())
262 (if tfile
263 (list (conc dest "/" tfile ".types"))
264 '())
265 (if ifile
266 (list (conc dest "/" ifile ".inline"))
267 '())
268 (import-libraries mods dest rtarget mode))
269 (set! exts
270 (cons (list target
271 dependencies: cdeps
272 source: src options: opts
273 link-options: lopts linkage: link custom: cbuild
274 mode: mode types-file: tfile inline-file: ifile
275 predefined-types: ptfile eggfile: eggfile
276 modules: (or mods (list rtarget))
277 source-dependencies: sdeps
278 link-objects: lobjs
279 output-file: rtarget)
280 exts)))))
281 ((installed-c-object c-object)
282 (fluid-let ((target (check-target (cadr info) exts))
283 (cdeps '())
284 (sdeps '())
285 (src #f)
286 (cbuild #f)
287 (link (if (null? link) default-extension-linkage link))
288 (oname #f)
289 (mods #f)
290 (install (eq? 'installed-c-object (car info)))
291 (opts opts))
292 (for-each compile-extension/program (cddr info))
293 (let ((dest (effective-destination-repository mode #t))
294 ;; Respect install-name if specified
295 (rtarget (or oname target)))
296 (when install
297 (addfiles (list (conc dest "/" rtarget objext))))
298 (set! objs
299 (cons (list target dependencies: cdeps source: src
300 options: opts
301 linkage: link custom: cbuild
302 mode: mode
303 eggfile: eggfile
304 source-dependencies: sdeps
305 output-file: rtarget)
306 objs)))))
307 ((data)
308 (fluid-let ((target (check-target (cadr info) data))
309 (dest #f)
310 (files '()))
311 (for-each compile-data/include (cddr info))
312 (checkfiles files target)
313 (let* ((dest (or (and dest (normalize-destination dest mode))
314 (if (eq? mode 'target)
315 default-sharedir
316 (override-prefix "/share" host-sharedir))))
317 (dest (normalize-pathname (conc dest "/"))))
318 (addfiles (map (cut conc dest <>) files)))
319 (set! data
320 (cons (list target dependencies: '() files: files
321 destination: dest mode: mode)
322 data))))
323 ((generated-source-file)
324 (fluid-let ((target (check-target (cadr info) data))
325 (src #f)
326 (cbuild #f)
327 (sdeps '())
328 (cdeps '()))
329 (for-each compile-extension/program (cddr info))
330 (unless cbuild
331 (error "generated source files need a custom build step" target))
332 (set! genfiles
333 (cons (list target dependencies: cdeps source: src
334 custom: cbuild source-dependencies: sdeps
335 eggfile: eggfile)
336 genfiles))))
337 ((c-include)
338 (fluid-let ((target (check-target (cadr info) cinc))
339 (dest #f)
340 (files '()))
341 (for-each compile-data/include (cddr info))
342 (checkfiles files target)
343 (let* ((dest (or (and dest (normalize-destination dest mode))
344 (if (eq? mode 'target)
345 default-incdir
346 (override-prefix "/include" host-incdir))))
347 (dest (normalize-pathname (conc dest "/"))))
348 (addfiles (map (cut conc dest <>) files)))
349 (set! cinc
350 (cons (list target dependencies: '() files: files
351 destination: dest mode: mode)
352 cinc))))
353 ((scheme-include)
354 (fluid-let ((target (check-target (cadr info) scminc))
355 (dest #f)
356 (files '()))
357 (checkfiles files target)
358 (for-each compile-data/include (cddr info))
359 (let* ((dest (or (and dest (normalize-destination dest mode))
360 (if (eq? mode 'target)
361 default-sharedir
362 (override-prefix "/share" host-sharedir))))
363 (dest (normalize-pathname (conc dest "/"))))
364 (addfiles (map (cut conc dest <>) files)))
365 (set! scminc
366 (cons (list target dependencies: '() files: files
367 destination: dest mode: mode)
368 scminc))))
369 ((program)
370 (fluid-let ((target (check-target (cadr info) prgs))
371 (cdeps '())
372 (sdeps '())
373 (cbuild #f)
374 (src #f)
375 (link (if (null? link) default-program-linkage link))
376 (lobjs '())
377 (lopts lopts)
378 (oname #f)
379 (opts opts))
380 (for-each compile-extension/program (cddr info))
381 (let ((dest (if (eq? mode 'target)
382 default-bindir
383 (override-prefix "/bin" host-bindir)))
384 ;; Respect install-name if specified
385 (rtarget (or oname target)))
386 (addfiles (list (conc dest "/" rtarget exeext)))
387 (set! prgs
388 (cons (list target dependencies: cdeps
389 source: src options: opts
390 link-options: lopts linkage: link
391 custom: cbuild
392 mode: mode output-file: rtarget
393 source-dependencies: sdeps
394 link-objects: lobjs
395 eggfile: eggfile)
396 prgs)))))
397 (else (compile-common info compile-component 'component))))
398 (define (compile-extension/program info)
399 (case (car info)
400 ((linkage)
401 (set! link (cdr info)))
402 ((types-file)
403 (set! tfile
404 (cond ((null? (cdr info)) #t)
405 ((not (pair? (cadr info)))
406 (arg info 1 name?))
407 (else
408 (set! ptfile #t)
409 (set! tfile
410 (or (null? (cdadr info))
411 (arg (cadr info) 1 name?)))))))
412 ((objects)
413 (let ((los (map ->string (cdr info))))
414 (set! lobjs (append lobjs los))
415 (set! cdeps (append cdeps (map ->dep los)))))
416 ((inline-file)
417 (set! ifile (or (null? (cdr info)) (arg info 1 name?))))
418 ((custom-build)
419 (set! cbuild (->string (arg info 1 name?))))
420 ((csc-options)
421 (set! opts
422 (apply append
423 opts
424 (map parse-custom-config (cdr info)))))
425 ((link-options)
426 (set! lopts
427 (apply append
428 lopts
429 (map parse-custom-config (cdr info)))))
430 ((source)
431 (set! src (->string (arg info 1 name?))))
432 ((install-name)
433 (set! oname (->string (arg info 1 name?))))
434 ((modules)
435 (set! mods (map library-id (cdr info))))
436 ((component-dependencies)
437 (set! cdeps (append cdeps (map ->dep (cdr info)))))
438 ((source-dependencies)
439 (set! sdeps (append sdeps (map ->dep (cdr info)))))
440 (else (compile-common info compile-extension/program 'extension/program))))
441 (define (compile-common info walk context)
442 (case (car info)
443 ((target)
444 (when (eq? mode 'target)
445 (for-each walk (cdr info))))
446 ((host)
447 (when (eq? mode 'host)
448 (for-each walk (cdr info))))
449 ((error)
450 (apply error (cdr info)))
451 ((cond-expand)
452 (compile-cond-expand info walk))
453 (else
454 (fprintf (current-error-port) "\nWarning (~a): property `~a' invalid or in wrong context (~a)\n\n" eggfile (car info) context))))
455 (define (compile-data/include info)
456 (case (car info)
457 ((destination)
458 (set! dest (->string (arg info 1 name?))))
459 ((files)
460 (set! files (append files (map ->string (cdr info)))))
461 (else (compile-common info compile-data/include 'data/include))))
462 (define (compile-options info)
463 (define (custom info)
464 (map parse-custom-config info))
465 (case (car info)
466 ((csc-options) (set! opts (apply append opts (custom (cdr info)))))
467 ((link-options) (set! lopts (apply append lopts (custom (cdr info)))))
468 ((linkage) (set! link (apply append link (custom (cdr info)))))
469 (else (error "invalid component-options specification" info))))
470 (define (compile-cond-expand info walk)
471 (let loop ((clauses (cdr info)))
472 (cond ((null? clauses)
473 (error "no matching clause in `cond-expand' form"
474 info))
475 ((or (eq? 'else (caar clauses))
476 (check-condition (caar clauses) mode link))
477 (for-each walk (cdar clauses)))
478 (else (loop (cdr clauses))))))
479 (define (->dep x)
480 (if (name? x)
481 (if (symbol? x) x (string->symbol x))
482 (error "invalid dependency" x)))
483 (define (compile info)
484 (case (car info)
485 ((synopsis dependencies test-dependencies category version author maintainer
486 license build-dependencies foreign-dependencies platform
487 distribution-files) #f)
488 ((components) (for-each compile-component (cdr info)))
489 ((component-options)
490 (for-each compile-options (cdr info)))
491 (else (compile-common info compile 'toplevel))))
492 (define (arg info n #!optional (pred (constantly #t)))
493 (when (< (length info) n)
494 (error "missing argument" info n))
495 (let ((x (list-ref info n)))
496 (unless (pred x)
497 (error "argument has invalid type" x))
498 x))
499 (define (name? x) (or (string? x) (symbol? x)))
500 (define dep=? equal?)
501 (define (filter pred lst)
502 (cond ((null? lst) '())
503 ((pred (car lst)) (cons (car lst) (filter pred (cdr lst))))
504 (else (filter pred (cdr lst)))))
505 (define (filter-deps name deps)
506 (filter (lambda (dep)
507 (and (symbol? dep)
508 (or (assq dep exts)
509 (assq dep objs)
510 (assq dep data)
511 (assq dep cinc)
512 (assq dep scminc)
513 (assq dep genfiles)
514 (assq dep prgs)
515 (error "unknown component dependency" dep))))
516 deps))
517 ;; collect information
518 (for-each compile info)
519 ;; sort topologically, by dependencies
520 (let* ((all (append prgs exts objs genfiles))
521 (order (reverse (sort-dependencies
522 (map (lambda (dep)
523 (cons (car dep)
524 (filter-deps (car dep)
525 (get-keyword dependencies: (cdr dep)))))
526 all)
527 dep=?))))
528 ;; generate + return build/install commands
529 (values
530 ;; build commands
531 (append-map
532 (lambda (id)
533 (cond ((assq id exts) =>
534 (lambda (data)
535 (let ((link (get-keyword linkage: (cdr data)))
536 (mods (get-keyword modules: (cdr data))))
537 (append (if (memq 'dynamic link)
538 (list (apply compile-dynamic-extension data))
539 '())
540 (if (memq 'static link)
541 ;; if compiling both static + dynamic, override
542 ;; modules/types-file/inline-file properties to
543 ;; avoid generating things twice:
544 (list (apply compile-static-extension
545 (if (memq 'dynamic link)
546 (cons (car data)
547 (append '(modules: #f
548 types-file: #f
549 inline-file: #f)
550 (cdr data)))
551 data)))
552 '())
553 (if (uses-compiled-import-library? mode)
554 (map (lambda (mod)
555 (apply compile-import-library
556 mod (cdr data))) ; override name
557 mods)
558 '())))))
559 ((assq id prgs) =>
560 (lambda (data)
561 (let ((link (get-keyword linkage: (cdr data))))
562 (append (if (memq 'dynamic link)
563 (list (apply compile-dynamic-program data))
564 '())
565 (if (memq 'static link)
566 (list (apply compile-static-program data))
567 '())))))
568 ((assq id objs) =>
569 (lambda (data)
570 (let ((link (get-keyword linkage: (cdr data))))
571 (append (if (memq 'dynamic link)
572 (list (apply compile-dynamic-object data))
573 '())
574 (if (memq 'static link)
575 (list (apply compile-static-object data))
576 '())))))
577 ((assq id genfiles) =>
578 (lambda (data)
579 (list (apply compile-generated-file data))))
580 ((or (assq id data)
581 (assq id cinc)
582 (assq id scminc))
583 '()) ;; nothing to build for data components
584 (else (error "Error in chicken-install, don't know how to build component" id))))
585 order)
586 ;; installation commands
587 (append
588 (append-map
589 (lambda (ext)
590 (let ((link (get-keyword linkage: (cdr ext)))
591 (mods (get-keyword modules: (cdr ext))))
592 (append
593 (if (memq 'static link)
594 (list (apply install-static-extension ext))
595 '())
596 (if (memq 'dynamic link)
597 (list (apply install-dynamic-extension ext))
598 '())
599 (if (and (memq 'dynamic link)
600 (uses-compiled-import-library? (get-keyword mode: ext)))
601 (map (lambda (mod)
602 (apply install-import-library
603 mod (cdr ext))) ; override name
604 mods)
605 (map (lambda (mod)
606 (apply install-import-library-source
607 mod (cdr ext))) ; s.a.
608 mods))
609 (if (get-keyword types-file: (cdr ext))
610 (list (apply install-types-file ext))
611 '())
612 (if (get-keyword inline-file: (cdr ext))
613 (list (apply install-inline-file ext))
614 '()))))
615 exts)
616 (map (lambda (obj) (apply install-object obj)) objs)
617 (map (lambda (prg) (apply install-program prg)) prgs)
618 (map (lambda (data) (apply install-data data)) data)
619 (map (lambda (cinc) (apply install-c-include cinc)) cinc)
620 (map (lambda (scminc) (apply install-data scminc)) scminc))
621 ;; augmented egg-info
622 (append `((installed-files ,@ifiles))
623 (if version `((version ,version)) '())
624 info)))))
625
626
627;;; shell code generation - build operations
628
629(define ((compile-static-extension name #!key mode dependencies
630 source-dependencies
631 source (options '())
632 predefined-types eggfile
633 link-objects modules
634 custom types-file inline-file)
635 srcdir platform)
636 (let* ((cmd (or (custom-cmd custom srcdir platform)
637 default-csc))
638 (sname (prefix srcdir name))
639 (tfile (prefix srcdir (conc types-file ".types")))
640 (ifile (prefix srcdir (conc inline-file ".inline")))
641 (lfile (conc sname +link-file-extension+))
642 (opts (append (if (null? options)
643 default-static-compilation-options
644 options)
645 (if (and types-file
646 (not predefined-types))
647 (list "-emit-types-file" tfile)
648 '())
649 (if inline-file
650 (list "-emit-inline-file" ifile)
651 '())))
652 (out1 (conc sname ".static"))
653 (out2 (target-file (conc out1
654 (object-extension platform))
655 mode))
656 (out3 (if (null? link-objects)
657 out2
658 (target-file (conc out1
659 (archive-extension platform))
660 mode)))
661 (imps (map (lambda (m)
662 (prefix srcdir (conc m ".import.scm")))
663 (or modules '())))
664 (targets (append (list out3 lfile)
665 (maybe types-file tfile)
666 (maybe inline-file ifile)
667 imps))
668 (src (or source (conc name ".scm"))))
669 (when custom
670 (prepare-custom-command cmd platform))
671 (print-build-command targets
672 `(,@(filelist srcdir source-dependencies) ,src ,eggfile
673 ,@(if custom (list cmd) '())
674 ,@(get-dependency-targets dependencies))
675 `(,@(if custom '("sh") '())
676 ,cmd ,@(if keep-generated-files '("-k") '())
677 "-regenerate-import-libraries"
678 ,@(if modules '("-J") '()) "-M"
679 "-setup-mode" "-static" "-I" ,srcdir
680 "-emit-link-file" ,lfile
681 ,@(if (eq? mode 'host) '("-host") '())
682 "-D" "compiling-extension"
683 "-c" "-unit" ,name
684 "-D" "compiling-static-extension"
685 "-C" ,(conc "-I" srcdir)
686 ,@opts ,src "-o" ,out2)
687 platform)
688 (when (pair? link-objects)
689 (let ((lobjs (filelist srcdir
690 (map (cut conc <> ".static" (object-extension platform))
691 link-objects))))
692 (print-build-command (list out3)
693 `(,out2 ,@lobjs)
694 `(,target-librarian ,target-librarian-options ,out3 ,out2 ,@lobjs)
695 platform)))
696 (print-end-command platform)))
697
698(define ((compile-dynamic-extension name #!key mode mode dependencies
699 source (options '())
700 (link-options '())
701 predefined-types eggfile
702 link-objects
703 source-dependencies modules
704 custom types-file inline-file)
705 srcdir platform)
706 (let* ((cmd (or (custom-cmd custom srcdir platform)
707 default-csc))
708 (sname (prefix srcdir name))
709 (tfile (prefix srcdir (conc types-file ".types")))
710 (ifile (prefix srcdir (conc inline-file ".inline")))
711 (opts (append (if (null? options)
712 default-dynamic-compilation-options
713 options)
714 (if (and types-file
715 (not predefined-types))
716 (list "-emit-types-file" tfile)
717 '())
718 (if inline-file
719 (list "-emit-inline-file" ifile)
720 '())))
721 (out (target-file (conc sname ".so") mode))
722 (src (or source (conc name ".scm")))
723 (lobjs (map (lambda (lo)
724 (target-file (conc lo
725 (object-extension platform))
726 mode))
727 link-objects))
728 (imps (map (lambda (m)
729 (prefix srcdir (conc m ".import.scm")))
730 modules))
731 (targets (append (list out)
732 (maybe inline-file ifile)
733 (maybe (and types-file
734 (not predefined-types)) tfile)
735 imps)))
736 (add-dependency-target name out)
737 (when custom
738 (prepare-custom-command cmd platform))
739 (print-build-command targets
740 `(,src ,eggfile ,@(if custom (list cmd) '())
741 ,@(filelist srcdir lobjs)
742 ,@(filelist srcdir source-dependencies)
743 ,@(get-dependency-targets dependencies))
744 `(,@(if custom '("sh") '())
745 ,cmd ,@(if keep-generated-files '("-k") '())
746 ,@(if (eq? mode 'host) '("-host") '())
747 "-D" "compiling-extension"
748 "-J" "-s" "-regenerate-import-libraries"
749 "-setup-mode" "-I" ,srcdir
750 "-C" ,(conc "-I" srcdir)
751 ,@opts
752 ,@link-options
753 ,src
754 ,@(filelist srcdir lobjs)
755 "-o" ,out)
756 platform)
757 (print-end-command platform)))
758
759(define ((compile-import-library name #!key mode
760 source-dependencies
761 (options '()) (link-options '()))
762 srcdir platform)
763 (let* ((cmd default-csc)
764 (sname (prefix srcdir name))
765 (opts (if (null? options)
766 default-import-library-compilation-options
767 options))
768 (out (target-file (conc sname ".import.so") mode))
769 (src (conc name ".import.scm")))
770 (print-build-command (list out)
771 ;; TODO: eggfile not part of dependencies?
772 `(,src #;,eggfile ,@(filelist srcdir source-dependencies))
773 `(,cmd ,@(if keep-generated-files '("-k") '())
774 "-setup-mode" "-s"
775 ,@(if (eq? mode 'host) '("-host") '())
776 "-I" ,srcdir "-C" ,(conc "-I" srcdir)
777 ,@opts ,@link-options
778 ,src
779 "-o" ,out)
780 platform)
781 (print-end-command platform)))
782
783(define ((compile-static-object name #!key mode dependencies
784 source-dependencies
785 source (options '())
786 eggfile custom)
787 srcdir platform)
788 (let* ((cmd (or (custom-cmd custom srcdir platform)
789 default-csc))
790 (sname (prefix srcdir name))
791 (ssname (and source (prefix srcdir source)))
792 (opts (if (null? options)
793 default-static-compilation-options
794 options))
795 (out (target-file (conc sname
796 ".static"
797 (object-extension platform))
798 mode))
799 (src (or ssname (conc sname ".c"))))
800 (when custom
801 (prepare-custom-command cmd platform))
802 (print-build-command (list out)
803 `(,@(filelist srcdir source-dependencies) ,src ,eggfile
804 ,@(if custom (list cmd) '())
805 ,@(get-dependency-targets dependencies))
806 `(,@(if custom '("sh") '())
807 ,cmd "-setup-mode" "-static" "-I" ,srcdir
808 ,@(if (eq? mode 'host) '("-host") '())
809 "-c" "-C" ,(conc "-I" srcdir)
810 ,@opts ,src "-o" ,out)
811 platform)
812 (print-end-command platform)))
813
814(define ((compile-dynamic-object name #!key mode mode dependencies
815 source (options '())
816 eggfile
817 source-dependencies
818 custom)
819 srcdir platform)
820 (let* ((cmd (or (custom-cmd custom srcdir platform)
821 default-csc))
822 (opts (if (null? options)
823 default-dynamic-compilation-options
824 options))
825 (sname (prefix srcdir name))
826 (ssname (and source (prefix srcdir source)))
827 (out (target-file (conc sname
828 (object-extension platform))
829 mode))
830 (src (or ssname (conc sname ".c"))))
831 (add-dependency-target name out)
832 (when custom
833 (prepare-custom-command cmd platform))
834 (print-build-command (list out)
835 `(,src ,eggfile ,@(if custom (list cmd) '())
836 ,@(filelist srcdir source-dependencies)
837 ,@(get-dependency-targets dependencies))
838 `(,@(if custom '("sh") '())
839 ,cmd ,@(if (eq? mode 'host) '("-host") '())
840 "-s" "-c" "-C" ,(conc "-I" srcdir)
841 ,@opts ,src "-o" ,out)
842 platform)
843 (print-end-command platform)))
844
845(define ((compile-dynamic-program name #!key source mode dependencies
846 (options '()) (link-options '())
847 source-dependencies
848 custom eggfile link-objects)
849 srcdir platform)
850 (let* ((cmd (or (custom-cmd custom srcdir platform)
851 default-csc))
852 (sname (prefix srcdir name))
853 (opts (if (null? options)
854 default-dynamic-compilation-options
855 options))
856 (out (target-file (conc sname
857 (executable-extension platform))
858 mode))
859 (lobjs (map (lambda (lo)
860 (target-file (conc lo
861 (object-extension platform))
862 mode))
863 link-objects))
864 (src (or source (conc name ".scm"))))
865 (when custom
866 (prepare-custom-command cmd platform))
867 (print-build-command (list out)
868 `(,src ,eggfile ,@(if custom (list cmd) '())
869 ,@(filelist srcdir source-dependencies)
870 ,@(filelist srcdir lobjs)
871 ,@(get-dependency-targets dependencies))
872 `(,@(if custom '("sh") '())
873 ,cmd ,@(if keep-generated-files '("-k") '())
874 "-setup-mode"
875 ,@(if (eq? mode 'host) '("-host") '())
876 "-I" ,srcdir
877 "-C" ,(conc "-I" srcdir)
878 ,@opts ,@link-options ,src
879 ,@(filelist srcdir lobjs)
880 "-o" ,out)
881 platform)
882 (print-end-command platform)))
883
884(define ((compile-static-program name #!key source dependencies
885 (options '()) (link-options '())
886 source-dependencies
887 custom mode eggfile link-objects)
888 srcdir platform)
889 (let* ((cmd (or (custom-cmd custom srcdir platform)
890 default-csc))
891 (sname (prefix srcdir name))
892 (opts (if (null? options)
893 default-static-compilation-options
894 options))
895 (out (target-file (conc sname
896 (executable-extension platform))
897 mode))
898 (lobjs (map (lambda (lo)
899 (target-file (conc lo
900 (object-extension platform))
901 mode))
902 link-objects))
903 (src (or source (conc name ".scm"))))
904 (when custom
905 (prepare-custom-command cmd platform))
906 (print-build-command (list out)
907 `(,src ,eggfile ,@(if custom (list cmd) '())
908 ,@(filelist srcdir lobjs)
909 ,@(filelist srcdir source-dependencies)
910 ,@(get-dependency-targets dependencies))
911 `(,@(if custom '("sh") '())
912 ,cmd ,@(if keep-generated-files '("-k") '())
913 ,@(if (eq? mode 'host) '("-host") '())
914 "-static" "-setup-mode" "-I" ,srcdir
915 "-C" ,(conc "-I" srcdir)
916 ,@opts ,@link-options ,src
917 ,@(filelist srcdir lobjs)
918 "-o" ,out)
919 platform)
920 (print-end-command platform)))
921
922(define ((compile-generated-file name #!key source custom dependencies
923 source-dependencies eggfile)
924 srcdir platform)
925 (let ((cmd (custom-cmd custom srcdir platform))
926 (out (or source name)))
927 (add-dependency-target name out)
928 (prepare-custom-command cmd platform)
929 (print-build-command (list out)
930 (append
931 (filelist srcdir source-dependencies)
932 (get-dependency-targets dependencies))
933 `("sh" ,cmd ,eggfile)
934 platform)
935 (print-end-command platform)))
936
937
938;; installation operations
939
940(define ((install-static-extension name #!key mode output-file
941 link-objects)
942 srcdir platform)
943 (let* ((cmd (install-file-command platform))
944 (mkdir (mkdir-command platform))
945 (ext (if (null? link-objects)
946 (object-extension platform)
947 (archive-extension platform)))
948 (sname (prefix srcdir name))
949 (out (qs* (target-file (conc sname ".static" ext) mode)
950 platform #t))
951 (outlnk (qs* (conc sname +link-file-extension+) platform #t))
952 (dest (effective-destination-repository mode))
953 (dfile (qs* dest platform #t))
954 (ddir (shell-variable "DESTDIR" platform)))
955 (print "\n" mkdir " " ddir dfile)
956 (print cmd " " out " " ddir
957 (qs* (conc dest "/" output-file ext) platform #t))
958 (print cmd " " outlnk " " ddir
959 (qs* (conc dest "/" output-file +link-file-extension+)
960 platform #t))
961 (print-end-command platform)))
962
963(define ((install-dynamic-extension name #!key mode (ext ".so")
964 output-file)
965 srcdir platform)
966 (let* ((cmd (install-executable-command platform))
967 (mkdir (mkdir-command platform))
968 (sname (prefix srcdir name))
969 (out (qs* (target-file (conc sname ext) mode) platform #t))
970 (dest (effective-destination-repository mode))
971 (dfile (qs* dest platform #t))
972 (ddir (shell-variable "DESTDIR" platform))
973 (destf (qs* (conc dest "/" output-file ext) platform #t)))
974 (print "\n" mkdir " " ddir dfile)
975 (print cmd " " out " " ddir destf)
976 (print-end-command platform)))
977
978(define ((install-import-library name #!key mode)
979 srcdir platform)
980 ((install-dynamic-extension name mode: mode ext: ".import.so"
981 output-file: name)
982 srcdir platform))
983
984(define ((install-import-library-source name #!key mode)
985 srcdir platform)
986 (let* ((cmd (install-file-command platform))
987 (mkdir (mkdir-command platform))
988 (sname (prefix srcdir name))
989 (out (qs* (target-file (conc sname ".import.scm") mode)
990 platform #t))
991 (dest (effective-destination-repository mode))
992 (dfile (qs* dest platform #t))
993 (ddir (shell-variable "DESTDIR" platform)))
994 (print "\n" mkdir " " ddir dfile)
995 (print cmd " " out " " ddir
996 (qs* (conc dest "/" name ".import.scm") platform #t))
997 (print-end-command platform)))
998
999(define ((install-types-file name #!key mode types-file)
1000 srcdir platform)
1001 (let* ((cmd (install-file-command platform))
1002 (mkdir (mkdir-command platform))
1003 (out (qs* (prefix srcdir (conc types-file ".types"))
1004 platform #t))
1005 (dest (effective-destination-repository mode))
1006 (dfile (qs* dest platform #t))
1007 (ddir (shell-variable "DESTDIR" platform)))
1008 (print "\n" mkdir " " ddir dfile)
1009 (print cmd " " out " " ddir
1010 (qs* (conc dest "/" types-file ".types") platform #t))
1011 (print-end-command platform)))
1012
1013(define ((install-inline-file name #!key mode inline-file)
1014 srcdir platform)
1015 (let* ((cmd (install-file-command platform))
1016 (mkdir (mkdir-command platform))
1017 (out (qs* (prefix srcdir (conc inline-file ".inline"))
1018 platform #t))
1019 (dest (effective-destination-repository mode))
1020 (dfile (qs* dest platform #t))
1021 (ddir (shell-variable "DESTDIR" platform)))
1022 (print "\n" mkdir " " ddir dfile)
1023 (print cmd " " out " " ddir
1024 (qs* (conc dest "/" inline-file ".inline") platform #t))
1025 (print-end-command platform)))
1026
1027(define ((install-program name #!key mode output-file) srcdir platform)
1028 (let* ((cmd (install-executable-command platform))
1029 (mkdir (mkdir-command platform))
1030 (ext (executable-extension platform))
1031 (sname (prefix srcdir name))
1032 (out (qs* (target-file (conc sname ext) mode) platform #t))
1033 (dest (if (eq? mode 'target)
1034 default-bindir
1035 (override-prefix "/bin" host-bindir)))
1036 (dfile (qs* dest platform #t))
1037 (ddir (shell-variable "DESTDIR" platform))
1038 (destf (qs* (conc dest "/" output-file ext) platform #t)))
1039 (print "\n" mkdir " " ddir dfile)
1040 (print cmd " " out " " ddir destf)
1041 (print-end-command platform)))
1042
1043(define ((install-object name #!key mode output-file) srcdir platform)
1044 (let* ((cmd (install-file-command platform))
1045 (mkdir (mkdir-command platform))
1046 (ext (object-extension platform))
1047 (sname (prefix srcdir name))
1048 (out (qs* (target-file (conc sname ext) mode)
1049 platform #t))
1050 (dest (effective-destination-repository mode))
1051 (dfile (qs* dest platform #t))
1052 (ddir (shell-variable "DESTDIR" platform)))
1053 (print "\n" mkdir " " ddir dfile)
1054 (print cmd " " out " " ddir
1055 (qs* (conc dest "/" output-file ext) platform #t))
1056 (print-end-command platform)))
1057
1058(define (install-random-files dest files mode srcdir platform)
1059 (let* ((fcmd (install-file-command platform))
1060 (dcmd (copy-directory-command platform))
1061 (root (string-append srcdir "/"))
1062 (mkdir (mkdir-command platform))
1063 (sfiles (map (cut prefix srcdir <>) files))
1064 (dfile (qs* dest platform #t))
1065 (ddir (shell-variable "DESTDIR" platform)))
1066 (print "\n" mkdir " " ddir dfile)
1067 (let-values (((ds fs) (partition directory? sfiles)))
1068 (for-each
1069 (lambda (d)
1070 (let* ((ds (strip-dir-prefix srcdir d))
1071 (fdir (pathname-directory ds)))
1072 (when fdir
1073 (print mkdir " " ddir
1074 (qs* (make-pathname dest fdir) platform #t)))
1075 (print dcmd " " (qs* d platform #t)
1076 " " ddir
1077 (if fdir
1078 (qs* (make-pathname dest fdir) platform #t)
1079 dfile))
1080 (print-end-command platform)))
1081 ds)
1082 (when (pair? fs)
1083 (for-each
1084 (lambda (f)
1085 (let* ((fs (strip-dir-prefix srcdir f))
1086 (fdir (pathname-directory fs)))
1087 (when fdir
1088 (print mkdir " " ddir
1089 (qs* (make-pathname dest fdir) platform #t)))
1090 (print fcmd " " (qs* f platform)
1091 " " ddir
1092 (if fdir
1093 (qs* (make-pathname dest fdir) platform #t)
1094 dfile)))
1095 (print-end-command platform))
1096 fs)))))
1097
1098(define ((install-data name #!key files destination mode)
1099 srcdir platform)
1100 (install-random-files (or destination
1101 (if (eq? mode 'target)
1102 default-sharedir
1103 (override-prefix "/share"
1104 host-sharedir)))
1105 files mode srcdir platform))
1106
1107(define ((install-c-include name #!key deps files destination mode)
1108 srcdir platform)
1109 (install-random-files (or destination
1110 (if (eq? mode 'target)
1111 default-incdir
1112 (override-prefix "/include"
1113 host-incdir)))
1114 files mode srcdir platform))
1115
1116;; manage dependency-targets
1117
1118(define (add-dependency-target target output)
1119 (cond ((assq target dependency-targets) =>
1120 (lambda (a)
1121 (set-cdr! a output)))
1122 (else (set! dependency-targets
1123 (cons (cons target output) dependency-targets)))))
1124
1125(define (get-dependency-targets targets)
1126 (append-map
1127 (lambda (t)
1128 (cond ((assq t dependency-targets) => (lambda (a) (list (cdr a))))
1129 (else '())))
1130 targets))
1131
1132
1133;;; Generate shell or batch commands from abstract build/install operations
1134
1135(define (generate-shell-commands platform cmds dest srcdir prefix suffix keep)
1136 (fluid-let ((keep-generated-files keep))
1137 (with-output-to-file dest
1138 (lambda ()
1139 (prefix platform)
1140 (print (cd-command platform) " " (qs* srcdir platform #t))
1141 (for-each
1142 (lambda (cmd) (cmd srcdir platform))
1143 cmds)
1144 (suffix platform)))))
1145
1146
1147;;; affixes for build- and install-scripts
1148
1149(define ((build-prefix mode name info) platform)
1150 (case platform
1151 ((unix)
1152 (printf #<<EOF
1153#!/bin/sh~%
1154set -e
1155PATH=~a:$PATH
1156export CHICKEN_CC=~a
1157export CHICKEN_CXX=~a
1158export CHICKEN_CSC=~a
1159export CHICKEN_CSI=~a
1160
1161EOF
1162 (qs* default-bindir platform) (qs* default-cc platform)
1163 (qs* default-cxx platform) (qs* default-csc platform)
1164 (qs* default-csi platform)))))
1165
1166(define ((build-suffix mode name info) platform)
1167 (case platform
1168 ((unix)
1169 (printf #<<EOF
1170EOF
1171 ))))
1172
1173(define ((install-prefix mode name info) platform)
1174 (case platform
1175 ((unix)
1176 (printf #<<EOF
1177#!/bin/sh~%
1178set -e
1179
1180EOF
1181 ))))
1182
1183(define ((install-suffix mode name info) platform)
1184 (let* ((infostr (with-output-to-string (cut pp info)))
1185 (dcmd (remove-file-command platform))
1186 (mkdir (mkdir-command platform))
1187 (dir (destination-repository mode))
1188 (qdir (qs* dir platform #t))
1189 (dest (qs* (make-pathname dir name +egg-info-extension+)
1190 platform #t))
1191 (ddir (shell-variable "DESTDIR" platform)))
1192 (case platform
1193 ((unix)
1194 (printf #<<EOF
1195
1196~a ~a~a
1197~a ~a~a
1198cat >~a~a <<'ENDINFO'
1199~aENDINFO~%
1200EOF
1201 mkdir ddir qdir
1202 dcmd ddir dest
1203 ddir dest infostr)))))
1204
1205;;; some utilities for mangling + quoting
1206
1207;; The qs procedure quotes for mingw or other platforms. We
1208;; "normalised" the platform to "windows" in chicken-install, so we
1209;; have to undo that here again. It can also convert slashes to
1210;; backslashes on Windows, which is necessary in many cases when
1211;; running programs via "cmd".
1212;;
1213;; It also supports already-quoted arguments which can be taken as-is.
1214(define (qs* arg platform #!optional slashify?)
1215 (let* ((arg (->string arg))
1216 (path arg))
1217 (qs path (if (eq? platform 'windows) 'mingw platform))))
1218
1219(define (prefix dir name)
1220 (make-pathname dir (->string name)))
1221
1222;; Workaround for obscure behaviour of "system" on Windows: If a
1223;; string starts with double quotes, you _must_ wrap the whole string
1224;; in an extra set of quotes to avoid the outer quotes being stripped.
1225;; Don't ask.
1226(define (system+ str platform)
1227 (system (if (and (eq? platform 'windows)
1228 (positive? (string-length str))
1229 (char=? #\" (string-ref str 0)))
1230 (string-append "\"" str "\"")
1231 str)))
1232
1233(define (target-file fname mode)
1234 (if (eq? mode 'target) (string-append fname ".target") fname))
1235
1236(define (joins strs platform)
1237 (string-intersperse (map (cut qs* <> platform) strs) " "))
1238
1239(define (filelist dir lst)
1240 (map (cut prefix dir <>) lst))
1241
1242(define (shell-variable var platform)
1243 (string-append "\"${" var "}\""))
1244
1245(define prepare-custom-command void)
1246
1247(define (custom-cmd custom srcdir platform)
1248 (and custom (prefix srcdir custom)))
1249
1250(define (print-build-command targets sources command-and-args platform)
1251 (print "\n" (qs* default-builder platform) " "
1252 (joins targets platform)
1253 " : " (joins sources platform) " "
1254 " : " (joins command-and-args platform)))
1255
1256(define print-end-command void)
1257
1258(define (strip-dir-prefix prefix fname)
1259 (let* ((plen (string-length prefix))
1260 (p1 (substring fname 0 plen)))
1261 (assert (string=? prefix p1) "wrong prefix")
1262 (substring fname (add1 plen))))
1263
1264(define (maybe f x) (if f (list x) '()))
1265
1266(define (caretize str)
1267 (string-translate* str '(("&" . "^&") ("^" . "^^") ("|" . "^|")
1268 ("<" . "^<") (">" . "^>"))))
1269
1270(define (ensure-line-limit str lim)
1271 (when (>= (string-length str) lim)
1272 (error "line length exceeds platform limit: " str))
1273 str)