]> git.donarmstrong.com Git - lilypond.git/blobdiff - scm/lily.scm
Add '-dcrop' option to ps and svg backends
[lilypond.git] / scm / lily.scm
index 35a16019a1e74df912b0166dbf1cf28e71594a19..5e727f1a5892cbb4de4d067f53e719ab3e9fcaf8 100644 (file)
@@ -1,6 +1,6 @@
 ;;;; This file is part of LilyPond, the GNU music typesetter.
 ;;;;
-;;;; Copyright (C) 1998--2012 Jan Nieuwenhuizen <janneke@gnu.org>
+;;;; Copyright (C) 1998--2015 Jan Nieuwenhuizen <janneke@gnu.org>
 ;;;; Han-Wen Nienhuys <hanwen@xs4all.nl>
 ;;;;
 ;;;; LilyPond is free software: you can redistribute it and/or modify
 (define-public PLATFORM
   (string->symbol
    (string-downcase
-    (car (string-tokenize (utsname:sysname (uname)))))))
+    (car (string-tokenize (utsname:sysname (uname)) char-set:letter)))))
+
+;; We don't use (srfi srfi-39) (parameter objects) here because that
+;; does not give us a name/handle to the underlying fluids themselves.
+
+(define %parser (make-fluid))
+(define %location (make-fluid))
+;; No public setters: should not get overwritten in action
+(define-public (*parser*) (fluid-ref %parser))
+(define-public (*location*) (fluid-ref %location))
+;; but properly scoped location should be fine
+(defmacro-public with-location (loc . body)
+  `(with-fluids ((,%location ,loc)) ,@body))
+
+;; It would be nice to convert occurences of parser/location to
+;; (*parser*)/(*location*) using the syncase module but it is utterly
+;; broken in GUILE 1 and would require changing a lot of unrelated
+;; innocuous constructs which just happen to fall apart with
+;; inscrutable error messages.
+
+;;
+;; Session-handling variables and procedures.
+;;
+;;  A "session" corresponds to one .ly file processed on a LilyPond
+;;  command line.  Every session gets to see a reasonably fresh state
+;;  of LilyPond and should work independently from previous files.
+;;
+;;  Session management relies on cooperation, namely the user not
+;;  trying to change variables and data structures internal to
+;;  LilyPond.  It is not proof against in-place modification of data
+;;  structures (as they are just reinitialized with the original
+;;  identities), and it is not proof against tampering with internals.
+;;
+;;  As a consequence, session management is not sufficient for
+;;  separating multiple independent .ly files in "-dsafe" mode: you
+;;  should give each its own LilyPond process when reliable separation
+;;  is mandatory.
+;;
+;;  For standard tasks and programming practices, multiple sessions in
+;;  the same LilyPond job should work reasonably independently and
+;;  without "bleed-over" while still loading and compiling the
+;;  relevant .scm and .ly files only once.
+;;
+
+(define lilypond-declarations '())
+(define lilypond-exports '())
+(define after-session-hook (make-hook))
+
+(define-public (call-after-session thunk)
+  (if (ly:undead? lilypond-declarations)
+      (ly:error (_ "call-after-session used after session start")))
+  (add-hook! after-session-hook thunk #t))
+
+(define (make-session-variable name value)
+  (if (ly:undead? lilypond-declarations)
+      (ly:error (_ "define-session used after session start")))
+  (let ((var (module-make-local-var! (current-module) name)))
+    (if (variable-bound? var)
+        (ly:error (_ "symbol ~S redefined") name))
+    (variable-set! var value)
+    var))
+
+(defmacro define-session (name value)
+  "This defines a variable @var{name} with the starting value
+@var{value} that is reinitialized at the start of each session.
+A@tie{}session basically corresponds to one LilyPond file on the
+command line.  The value is recorded at the start of the first session
+after loading all initialization files and before loading the user
+file and is reinstated for all of the following sessions.  This
+happens just by replacing the value, not by copying structures, so you
+should not destructively modify them.  For example, lists defined in
+this manner should be changed within a session only be adding material
+to their front or replacing them altogether, not by modifying parts of
+them.  It is an error to call @code{define-session} after the first
+session has started."
+  (define (add-session-variable name value)
+    (set! lilypond-declarations
+          (cons (make-session-variable name value) lilypond-declarations)))
+  `(,add-session-variable ',name ,value))
+
+(defmacro define-session-public (name value)
+  "Like @code{define-session}, but also exports @var{name} into parser modules."
+  (define (add-session-variable name value)
+    (set! lilypond-exports
+          (acons name (make-session-variable name value) lilypond-exports)))
+  `(begin
+     ;; this is a bit icky: we place the variable right into every
+     ;; parser module so that both set! and define will affect the
+     ;; original variable in the (lily) module.  However, we _also_
+     ;; export it normally from (lily) for the sake of other modules
+     ;; not sharing the name space of the parser.
+     (,add-session-variable ',name ,value)
+     (export ,name)))
+
+(define (session-terminate)
+  (if (ly:undead? lilypond-declarations)
+      (begin
+        (for-each
+         (lambda (p) (variable-set! (cadr p) (cddr p)))
+         (ly:get-undead lilypond-declarations))
+        (run-hook after-session-hook))))
+
+(define lilypond-interfaces #f)
+
+(define-public (session-initialize thunk)
+  "Initialize this session.  The first session in a LilyPond run is
+initialized by calling @var{thunk}, then recording the values of all
+variables in the current module as well as those defined with
+@code{define-session}.  Subsequent calls of @code{session-initialize}
+ignore @var{thunk} and instead just reinitialize all recorded
+variables to their value after the initial call of @var{thunk}."
+
+  ;; We need to save the variables of the current module along with
+  ;; their values: functions defined in the module might refer to the
+  ;; variables.
+
+  ;; The entries in lilypond-declarations consist of a cons* consisting
+  ;; of symbol, variable, and value.  Variables defined with
+  ;; define-session have the symbol set to #f.
+
+  (if (ly:undead? lilypond-declarations)
+      (begin
+        (module-use-interfaces! (current-module) (reverse lilypond-interfaces))
+        (for-each
+         (lambda (p)
+           (let ((var (cadr p))
+                 (val (cddr p)))
+             (variable-set! var val)
+             (if (car p)
+                 (module-add! (current-module) (car p) var))))
+         (ly:get-undead lilypond-declarations)))
+      (begin
+        ;; import all public session variables natively into parser
+        ;; module.  That makes them behave identically under define/set!
+        (for-each (lambda (v)
+                    (module-add! (current-module) (car v) (cdr v)))
+                  lilypond-exports)
+        ;; Initialize first session
+        (thunk)
+        ;; lilypond-exports is no longer needed since we will grab its
+        ;; values from (current-module).
+        (set! lilypond-exports #f)
+        (set! lilypond-interfaces
+              (filter (lambda (m) (eq? 'interface (module-kind m)))
+                      (module-uses (current-module))))
+        (let ((decl (map! (lambda (v)
+                            (cons* #f v (variable-ref v)))
+                          lilypond-declarations)))
+          (module-for-each
+           (lambda (s v)
+             (let ((val (variable-ref v)))
+               (if (not (ly:lily-parser? val))
+                   (set! decl
+                         (cons
+                          (cons* s v val)
+                          decl)))))
+           (current-module))
+          (set! lilypond-declarations (ly:make-undead decl))))))
 
 (define scheme-options-definitions
   `(
     ;; be longer than 48 characters per line.
 
     (anti-alias-factor 1
-"Render at higher resolution (using given factor)
+                       "Render at higher resolution (using given factor)
 and scale down result to prevent jaggies in
 PNG images.")
-    (aux-files #t
-"Create .tex, .texi, .count files in the
+    (aux-files
+     #t
+     "Create .tex, .texi, .count files in the
 EPS backend.")
-    (backend ps
-"Select backend.  Possible values: 'eps, 'null,
+    (backend
+     ps
+     "Select backend.  Possible values: 'eps, 'null,
 'ps, 'scm, 'socket, 'svg.")
-    (check-internal-types #f
-"Check every property assignment for types.")
-    (clip-systems #f
-"Generate cut-out snippets of a score.")
-    (datadir #f
-"LilyPond prefix for data files (read-only).")
-    (debug-gc #f
-"Dump memory debugging statistics.")
-    (debug-gc-assert-parsed-dead #f
-"For memory debugging: Ensure that all
+    (check-internal-types
+     #f
+     "Check every property assignment for types.")
+    (clip-systems
+     #f
+     "Generate cut-out snippets of a score.")
+    (crop
+     #f
+     "Match the size of the normal output to the typeset image.")
+    (datadir
+     #f
+     "LilyPond prefix for data files (read-only).")
+    (debug-gc
+     #f
+     "Dump memory debugging statistics.")
+    (debug-gc-assert-parsed-dead
+     #f
+     "For memory debugging: Ensure that all
 references to parsed objects are dead.  This is
 an internal option, and is switched on
 automatically for `-ddebug-gc'.")
-    (debug-lexer #f
-"Debug the flex lexer.")
-    (debug-page-breaking-scoring #f
-"Dump scores for many different page breaking
+    (debug-lexer
+     #f
+     "Debug the flex lexer.")
+    (debug-page-breaking-scoring
+     #f
+     "Dump scores for many different page breaking
 configurations.")
-    (debug-parser #f
-"Debug the bison parser.")
-    (debug-property-callbacks #f
-"Debug cyclic callback chains.")
-    (debug-skylines #f
-"Debug skylines.")
-    (delete-intermediate-files #t
-"Delete unusable, intermediate PostScript files.")
-    (dump-profile #f
-"Dump memory and time information for each file.")
-    (dump-cpu-profile #f
-"Dump timing information (system-dependent).")
-    (dump-signatures #f
-"Dump output signatures of each system.  Used for
+    (debug-parser
+     #f
+     "Debug the bison parser.")
+    (debug-property-callbacks
+     #f
+     "Debug cyclic callback chains.")
+    (debug-skylines
+     #f
+     "Debug skylines.")
+    (delete-intermediate-files
+     #t
+     "Delete unusable, intermediate PostScript files.")
+    (dump-profile
+     #f
+     "Dump memory and time information for each file.")
+    (dump-cpu-profile
+     #f
+     "Dump timing information (system-dependent).")
+    (dump-signatures
+     #f
+     "Dump output signatures of each system.  Used for
 regression testing.")
-    (eps-box-padding #f
-"Pad left edge of the output EPS bounding box by
+    (embed-source-code
+     #f
+     "Embed the source files inside the generated PDF document.")
+    (eps-box-padding
+     #f
+     "Pad left edge of the output EPS bounding box by
 given amount (in mm).")
-    (gs-load-fonts #f
-"Load fonts via Ghostscript.")
-    (gs-load-lily-fonts #f
-"Load only LilyPond fonts via Ghostscript.")
-    (gui #f
-"Run LilyPond from a GUI and redirect stderr to
+    (font-export-dir
+     #f
+     "Directory for exporting fonts as PostScript files.")
+    (gs-load-fonts
+     #f
+     "Load fonts via Ghostscript.")
+    (gs-load-lily-fonts
+     #f
+     "Load only LilyPond fonts via Ghostscript.")
+    (gs-never-embed-fonts
+     #f
+     "Make Ghostscript embed only TrueType fonts and no other font format.")
+    (gui
+     #f
+     "Run LilyPond from a GUI and redirect stderr to
 a log file.")
-    (help #f
-"Show this help.")
-    (include-book-title-preview #t
-"Include book titles in preview images.")
-    (include-eps-fonts #t
-"Include fonts in separate-system EPS files.")
-    (include-settings #f
-"Include file for global settings, included before the score is processed.")
-    (job-count #f
-"Process in parallel, using the given number of
+    (help
+     #f
+     "Show this help.")
+    (include-book-title-preview
+     #t
+     "Include book titles in preview images.")
+    (include-eps-fonts
+     #t
+     "Include fonts in separate-system EPS files.")
+    (include-settings
+     #f
+     "Include file for global settings, included before the score is processed.")
+    (job-count
+     #f
+     "Process in parallel, using the given number of
 jobs.")
-    (log-file #f
-"If string FOO is given as argument, redirect
+    (log-file
+     #f
+     "If string FOO is given as argument, redirect
 output to log file `FOO.log'.")
-    (max-markup-depth 1024
-"Maximum depth for the markup tree. If a markup has more levels,
+    (max-markup-depth
+     1024
+     "Maximum depth for the markup tree. If a markup has more levels,
 assume it will not terminate on its own, print a warning and return a
 null markup instead.")
     (midi-extension ,(if (eq? PLATFORM 'windows)
                          "mid"
                          "midi")
-"Set the default file extension for MIDI output
+                    "Set the default file extension for MIDI output
 file to given string.")
-    (music-strings-to-paths #f
-"Convert text strings to paths when glyphs belong
+    (music-strings-to-paths
+     #f
+     "Convert text strings to paths when glyphs belong
 to a music font.")
-    (old-relative #f
-"Make \\relative mode for simultaneous music work
-similar to chord syntax.")
-    (point-and-click #t
-"Add point & click links to PDF output.")
-    (paper-size "a4"
-"Set default paper size.")
-    (pixmap-format "png16m"
-"Set GhostScript's output format for pixel images.")
-    (preview #f
-"Create preview images also.")
-    (print-pages #t
-"Print pages in the normal way.")
-    (protected-scheme-parsing #t
-"Continue when errors in inline scheme are caught
+    (point-and-click
+     #t
+     "Add point & click links to PDF and SVG output.")
+    (paper-size
+     "a4"
+     "Set default paper size.")
+    (pixmap-format
+     "png16m"
+     "Set GhostScript's output format for pixel images.")
+    (preview
+     #f
+     "Create preview images also.")
+    (print-pages
+     #t
+     "Print pages in the normal way.")
+    (protected-scheme-parsing
+     #t
+     "Continue when errors in inline scheme are caught
 in the parser.  If #f, halt on errors and print
 a stack trace.")
-    (profile-property-accesses #f
-"Keep statistics of get_property() calls.")
-    (resolution 101
-"Set resolution for generating PNG pixmaps to
+    (profile-property-accesses
+     #f
+     "Keep statistics of get_property() calls.")
+    (resolution
+     101
+     "Set resolution for generating PNG pixmaps to
 given value (in dpi).")
-    (read-file-list #f
-"Specify name of a file which contains a list of
+    (read-file-list
+     #f
+     "Specify name of a file which contains a list of
 input files to be processed.")
-    (relative-includes #f
-"When processing an \\include command, look for
-the included file relative to the current file
-(instead of the root file)")
-    (safe #f
-"Run in safer mode.")
-    (separate-log-files #f
-"For input files `FILE1.ly', `FILE2.ly', ...
+    (relative-includes
+     #f
+     "When processing an \\include command, look for
+the included file relative to the current file\
+\n(instead of the root file)")
+    (safe
+     #f
+     "Run in safer mode.")
+    (separate-log-files
+     #f
+     "For input files `FILE1.ly', `FILE2.ly', ...
 output log data to files `FILE1.log',
 `FILE2.log', ...")
-    (show-available-fonts #f
-"List available font names.")
-    (strict-infinity-checking #f
-"Force a crash on encountering Inf and NaN
+    (show-available-fonts
+     #f
+     "List available font names.")
+    (strict-infinity-checking
+     #f
+     "Force a crash on encountering Inf and NaN
 floating point exceptions.")
-    (strip-output-dir #t
-"Don't use directories from input files while
+    (strip-output-dir
+     #t
+     "Don't use directories from input files while
 constructing output file names.")
-    (svg-woff #f
-"Use woff font files in SVG backend.")
-    (trace-memory-frequency #f
-"Record Scheme cell usage this many times per
+    (strokeadjust
+     #f
+     "Set the PostScript strokeadjust operator explicitly.
+This employs different drawing primitives, resulting in
+large PDF file size increases but often markedly better
+PDF previews.")
+    (svg-woff
+     #f
+     "Use woff font files in SVG backend.")
+    (trace-memory-frequency
+     #f
+     "Record Scheme cell usage this many times per
 second.  Dump results to `FILE.stacks' and
 `FILE.graph'.")
-    (trace-scheme-coverage #f
-"Record coverage of Scheme files in `FILE.cov'.")
+    (trace-scheme-coverage
+     #f
+     "Record coverage of Scheme files in `FILE.cov'.")
     (verbose ,(ly:verbose-output?)
-"Verbose output, i.e. loglevel at least DEBUG (read-only).")
-    (warning-as-error #f
-"Change all warning and programming_error
+             "Verbose output, i.e. loglevel at least DEBUG (read-only).")
+    (warning-as-error
+     #f
+     "Change all warning and programming_error
 messages into errors.")
     ))
 
@@ -220,7 +439,7 @@ messages into errors.")
              (scm clip-region)
              (scm memory-trace)
              (scm coverage)
-            (scm safe-utility-defs))
+             (scm safe-utility-defs))
 
 (define-public _ gettext)
 ;;; There are new modules defined in Guile V2.0 which we need to use.
@@ -230,11 +449,11 @@ messages into errors.")
 ;;
 
 (cond
 ((guile-v2)
-   (ly:debug (_ "Using (ice-9 curried-definitions) module\n"))
-   (use-modules (ice-9 curried-definitions)))
 (else
-    (ly:debug (_ "Guile 1.8\n"))))
+ ((guile-v2)
+  (ly:debug (_ "Using (ice-9 curried-definitions) module\n"))
+  (use-modules (ice-9 curried-definitions)))
+ (else
+  (ly:debug (_ "Guile 1.8\n"))))
 
 ;; TODO add in modules for V1.8.7 deprecated in V2.0 and integrated
 ;; into Guile base code, like (ice-9 syncase).
@@ -246,8 +465,8 @@ messages into errors.")
 (define-public (ergonomic-simple-format dest . rest)
   "Like ice-9's @code{format}, but without the memory consumption."
   (if (string? dest)
-      (apply simple-format (cons #f (cons dest rest)))
-      (apply simple-format (cons dest rest))))
+      (apply simple-format #f dest rest)
+      (apply simple-format dest rest)))
 
 (define format
   ergonomic-simple-format)
@@ -261,7 +480,7 @@ messages into errors.")
   v)
 
 (define-public (print . args)
-  (apply format (cons (current-output-port) args)))
+  (apply format (current-output-port) args))
 
 
 ;;; General settings.
@@ -281,8 +500,6 @@ messages into errors.")
 (if (ly:get-option 'trace-scheme-coverage)
     (coverage:enable))
 
-(define-public parser #f)
-
 (define music-string-to-path-backends
   '(svg))
 
@@ -310,8 +527,8 @@ messages into errors.")
   (if (string-index x #\\)
       x
       (string-regexp-substitute
-        "//*" "/"
-        (string-regexp-substitute "\\\\" "/" x))))
+       "//*" "/"
+       (string-regexp-substitute "\\\\" "/" x))))
 
 (define-public (ly-getcwd)
   (if (eq? PLATFORM 'windows)
@@ -326,7 +543,8 @@ messages into errors.")
             (and (eq? PLATFORM 'windows)
                  (> file-name-length 2)
                  (eq? (string-ref file-name 1) #\:)
-                 (eq? (string-ref file-name 2) #\/))))))
+                 (or (eq? (string-ref file-name 2) #\\)
+                     (eq? (string-ref file-name 2) #\/)))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; If necessary, emulate Guile V2 module_export_all! for Guile V1.8.n
@@ -381,9 +599,10 @@ messages into errors.")
     "define-note-names.scm"
     "c++.scm"
     "chord-entry.scm"
-    "stencil.scm"
-    "define-markup-commands.scm"
+    "skyline.scm"
     "markup.scm"
+    "define-markup-commands.scm"
+    "stencil.scm"
     "modal-transforms.scm"
     "chord-generic-names.scm"
     "chord-ignatzek-names.scm"
@@ -391,11 +610,11 @@ messages into errors.")
     "part-combiner.scm"
     "autochange.scm"
     "define-music-properties.scm"
+    "time-signature.scm"
     "time-signature-settings.scm"
     "auto-beam.scm"
     "chord-name.scm"
     "bezier-tools.scm"
-    "ly-syntax-constructors.scm"
 
     "define-context-properties.scm"
     "translation-functions.scm"
@@ -419,6 +638,7 @@ messages into errors.")
     "define-grobs.scm"
     "define-grob-interfaces.scm"
     "define-stencil-commands.scm"
+    "scheme-engravers.scm"
     "titling.scm"
     "text.scm"
 
@@ -427,7 +647,7 @@ messages into errors.")
     "x11-color.scm"))
 ;;  - Files to be loaded last
 (define init-scheme-files-tail
-;;  - must be after everything has been defined
+  ;;  - must be after everything has been defined
   '("safe-lily.scm"))
 ;;
 ;; Now construct the load list
@@ -482,7 +702,7 @@ messages into errors.")
 
 (define-public guile-predicates
   `((,hash-table? . "hash table")
-  ))
+    ))
 
 (define-public lilypond-scheme-predicates
   `((,boolean-or-symbol? . "boolean or symbol")
@@ -491,21 +711,30 @@ messages into errors.")
     (,fraction? . "fraction, as pair")
     (,grob-list? . "list of grobs")
     (,index? . "non-negative integer")
-    ;; this is built on cheap-list
-    (,list-or-symbol? . "list or symbol")
+    (,key? . "index or symbol")
+    (,key-list? . "list of indexes or symbols")
+    (,key-list-or-music? . "key list or music")
+    (,key-list-or-symbol? . "key list or symbol")
     (,markup? . "markup")
     (,markup-command-list? . "markup command list")
     (,markup-list? . "markup list")
     (,moment-pair? . "pair of moment objects")
     (,number-list? . "number list")
     (,number-or-grob? . "number or grob")
+    (,number-or-markup? . "number or markup")
     (,number-or-pair? . "number or pair")
     (,number-or-string? . "number or string")
     (,number-pair? . "pair of numbers")
+    (,number-pair-list? . "list of number pairs")
+    (,rational-or-procedure? . "an exact rational or procedure")
     (,rhythmic-location? . "rhythmic location")
     (,scheme? . "any type")
     (,string-or-pair? . "string or pair")
+    (,string-or-music? . "string or music")
     (,string-or-symbol? . "string or symbol")
+    (,symbol-list? . "symbol list")
+    (,symbol-list-or-music? . "symbol list or music")
+    (,symbol-list-or-symbol? . "symbol list or symbol")
     (,void? . "void")
     ))
 
@@ -523,6 +752,7 @@ messages into errors.")
     (,ly:font-metric? . "font metric")
     (,ly:grob? . "graphical (layout) object")
     (,ly:grob-array? . "array of grobs")
+    (,ly:grob-properties? . "grob properties")
     (,ly:input-location? . "input location")
     (,ly:item? . "item")
     (,ly:iterator? . "iterator")
@@ -543,7 +773,6 @@ messages into errors.")
     (,ly:pitch? . "pitch")
     (,ly:prob? . "property object")
     (,ly:score? . "score")
-    (,ly:simple-closure? . "simple closure")
     (,ly:skyline? . "skyline")
     (,ly:skyline-pair? . "pair of skylines")
     (,ly:source-file? . "source file")
@@ -553,6 +782,7 @@ messages into errors.")
     (,ly:stream-event? . "stream event")
     (,ly:translator? . "translator")
     (,ly:translator-group? . "translator group")
+    (,ly:undead? . "undead container")
     (,ly:unpure-pure-container? . "unpure/pure container")
     ))
 
@@ -578,8 +808,8 @@ messages into errors.")
 
 (define (dump-profile base last this)
   (let* ((outname (format #f "~a.profile" (dir-basename base ".ly")))
-         (diff (map (lambda (y) (apply - y)) (zip this last))))
-    (ly:progress "\nWriting timing to ~a..." outname)
+         (diff (map - this last)))
+    (ly:progress "\nWriting timing to ~a...\n" outname)
     (format (open-file outname "w")
             "time: ~a\ncells: ~a\n"
             (if (ly:get-option 'dump-cpu-profile)
@@ -613,10 +843,10 @@ messages into errors.")
                          (lambda (a b)
                            (< (object-address (car a))
                               (object-address (car b))))))
-        (out-file-name (string-append
-                       "gcstat-" (number->string gc-protect-stat-count)
-                       ".scm"))
-        (outfile (open-file out-file-name "w")))
+         (out-file-name (string-append
+                         "gcstat-" (number->string gc-protect-stat-count)
+                         ".scm"))
+         (outfile (open-file out-file-name "w")))
     (set! gc-dumping #t)
     (ly:progress "Dumping GC statistics ~a...\n" out-file-name)
     (for-each (lambda (y)
@@ -646,13 +876,13 @@ messages into errors.")
           (ly:set-option 'debug-gc-assert-parsed-dead #t)
           (gc)
           (ly:set-option 'debug-gc-assert-parsed-dead #f)
-         (for-each
-          (lambda (x)
-            (if (not (hashq-ref gc-zombies x))
-                (begin
-                  (ly:programming-error "Parsed object should be dead: ~a" x)
-                  (hashq-set! gc-zombies x #t))))
-          (ly:parsed-undead-list!))
+          (for-each
+           (lambda (x)
+             (if (not (hashq-ref gc-zombies x))
+                 (begin
+                   (ly:programming-error "Parsed object should be dead: ~a" x)
+                   (hashq-set! gc-zombies x #t))))
+           (ly:parsed-undead-list!))
           (set! stats (gc-live-object-stats))
           (ly:progress "Dumping live object statistics.\n")
           (dump-live-object-stats outfile)))
@@ -677,11 +907,10 @@ messages into errors.")
 
   (let* ((stat (gulp-file "/proc/self/status"))
          (lines (string-split stat #\newline))
-         (interesting (filter identity
-                              (map
-                               (lambda (l)
-                                 (string-match "^VmData:[ \t]*([0-9]*) kB" l))
-                               lines)))
+         (interesting (filter-map
+                       (lambda (l)
+                         (string-match "^VmData:[ \t]*([0-9]*) kB" l))
+                       lines))
          (mem (string->number (match:substring (car interesting) 1))))
     (format #t "VMDATA: ~a\n" mem)
     (display (gc-stats))
@@ -698,9 +927,9 @@ PIDs or the number of the process."
   (define (helper count acc)
     (if (> count 0)
         (let* ((pid (primitive-fork)))
-              (if (= pid 0)
-                  (1- count)
-                  (helper (1- count) (cons pid acc))))
+          (if (= pid 0)
+              (1- count)
+              (helper (1- count) (cons pid acc))))
         acc))
 
   (helper count '()))
@@ -734,12 +963,15 @@ PIDs or the number of the process."
              (ly:exit 2 #t)))
   (if (ly:get-option 'read-file-list)
       (set! files
-            (filter (lambda (s)
-                      (> (string-length s) 0))
-                    (apply append
-                           (map (lambda (f)
-                                  (string-split (ly:gulp-file f) #\nl))
-                                files)))))
+            (remove string-null?
+                    (append-map
+                     (lambda (f)
+                       (string-split
+                         (if (guile-v2)
+                             (string-delete #\cr (ly:gulp-file f))
+                             (string-delete (ly:gulp-file f) #\cr))
+                         #\nl))
+                     files))))
   (if (and (number? (ly:get-option 'job-count))
            (>= (length files) (ly:get-option 'job-count)))
       (let* ((count (ly:get-option 'job-count))
@@ -752,7 +984,7 @@ PIDs or the number of the process."
             (begin (ly:set-option
                     'log-file (format #f "~a-~a"
                                       (ly:get-option 'log-file) joblist))
-                    (set! files (vector-ref split-todo joblist)))
+                   (set! files (vector-ref split-todo joblist)))
             (begin (ly:progress "\nForking into jobs:  ~a\n" joblist)
                    (for-each
                     (lambda (pid)
@@ -760,7 +992,7 @@ PIDs or the number of the process."
                         (if (not (= stat 0))
                             (set! errors
                                   (acons (list-element-index joblist pid)
-                                 stat errors)))))
+                                         stat errors)))))
                     joblist)
                    (for-each
                     (lambda (x)
@@ -779,17 +1011,17 @@ PIDs or the number of the process."
                             (ly:message
                              (_ "logfile ~a (exit ~a):\n~a")
                              logfile (status:exit-val state) tail))))
-                      errors)
-                     (if (pair? errors)
-                         (ly:error "Children ~a exited with errors."
-                                   (map car errors)))
-                     ;; must overwrite individual entries
-                     (if (ly:get-option 'dump-profile)
-                         (dump-profile "lily-run-total"
-                                       '(0 0) (profile-measurements)))
-                     (if (null? errors)
-                         (ly:exit 0 #f)
-                         (ly:exit 1 #f))))))
+                    errors)
+                   (if (pair? errors)
+                       (ly:error "Children ~a exited with errors."
+                                 (map car errors)))
+                   ;; must overwrite individual entries
+                   (if (ly:get-option 'dump-profile)
+                       (dump-profile "lily-run-total"
+                                     '(0 0) (profile-measurements)))
+                   (if (null? errors)
+                       (ly:exit 0 #f)
+                       (ly:exit 1 #f))))))
 
   (if (string-or-symbol? (ly:get-option 'log-file))
       (ly:stderr-redirect (format #f "~a.log" (ly:get-option 'log-file)) "w"))
@@ -809,10 +1041,11 @@ PIDs or the number of the process."
   (let* ((failed '())
          (separate-logs (ly:get-option 'separate-log-files))
          (ping-log
-          (if separate-logs
-              (open-file (if (string-or-symbol? (ly:get-option 'log-file))
-                             (format #f "~a.log" (ly:get-option 'log-file))
-                     "/dev/stderr") "a") #f))
+          (and separate-logs
+               (if (string-or-symbol? (ly:get-option 'log-file))
+                   (open-file (format #f "~a.log" (ly:get-option 'log-file))
+                              "a")
+                   (fdes->outport 2))))
          (do-measurements (ly:get-option 'dump-profile))
          (handler (lambda (key failed-file)
                     (set! failed (append (list failed-file) failed)))))
@@ -832,6 +1065,7 @@ PIDs or the number of the process."
              (mtrace:start-trace  (ly:get-option 'trace-memory-frequency)))
          (lilypond-file handler x)
          (ly:check-expected-warnings)
+         (session-terminate)
          (if start-measurements
              (dump-profile x start-measurements (profile-measurements)))
          (if (ly:get-option 'trace-memory-frequency)
@@ -843,10 +1077,13 @@ PIDs or the number of the process."
          (ly:set-option 'debug-gc-assert-parsed-dead #t)
          (gc)
          (ly:set-option 'debug-gc-assert-parsed-dead #f)
-        (for-each
-         (lambda (x)
-           (ly:programming-error "Parsed object should be dead: ~a" x))
-         (ly:parsed-undead-list!))
+         (for-each
+          (lambda (x)
+            (if (not (hashq-ref gc-zombies x))
+                (begin
+                  (ly:programming-error "Parsed object should be dead: ~a" x)
+                  (hashq-set! gc-zombies x #t))))
+          (ly:parsed-undead-list!))
          (if (ly:get-option 'debug-gc)
              (dump-gc-protects)
              (ly:reset-all-fonts))
@@ -860,8 +1097,6 @@ PIDs or the number of the process."
         (dump-profile "lily-run-total" '(0 0) (profile-measurements)))
     failed))
 
-(define-public lilypond-declarations '())
-
 (define (lilypond-file handler file-name)
   (catch 'ly-file-failed
          (lambda () (ly:parse-file file-name))