~ chicken-core (chicken-5) /egg-compile.scm


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