]> git.donarmstrong.com Git - lilypond.git/blob - scm/lily.scm
Support tablature slides.
[lilypond.git] / scm / lily.scm
1 ;;;; lily.scm -- toplevel Scheme stuff
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;; 
5 ;;;; (c) 1998--2006 Jan Nieuwenhuizen <janneke@gnu.org>
6 ;;;; Han-Wen Nienhuys <hanwen@xs4all.nl>
7
8
9 (define (define-scheme-options)
10   (for-each (lambda (x)
11               (ly:add-option (car x) (cadr x) (caddr x)))
12           
13             `(
14
15               ;; NAMING: either
16
17               ;; - [subject-]object-object-verb +"ing"
18               ;; - [subject-]-verb-object-object
19
20               (anti-alias-factor 1 "render at higher resolution and scale down result\nto prevent jaggies in PNG")
21               (check-internal-types #f "check every property assignment for types")
22               (clip-systems #f "Generate cut-out snippets of a score")
23               (debug-gc #f "dump memory debugging statistics")
24               (debug-gc-assert-parsed-dead
25                #f "for memory debugging: ensure that all refs to parsed objects are dead.")
26               (debug-lexer #f "debug the flex lexer")
27               (debug-midi #f "generate human readable MIDI")
28               (debug-parser #f "debug the bison parser")
29               (debug-skylines #f "debug skylines")
30               (delete-intermediate-files #f
31                                          "delete unusable PostScript files")
32               (dump-signatures #f "dump output signatures of each system")
33               (dump-tweaks #f "dump page layout and tweaks for each score having the tweak-key layout property set.")
34               (gs-load-fonts #f
35                             "load fonts via Ghostscript.")
36               (include-book-title-preview #t "include book-titles in preview images.")
37               (include-eps-fonts #t "Include fonts in separate-system EPS files.")
38               (job-count #f "Process in parallel") 
39
40               (eps-box-padding #f "Pad EPS bounding box left edge by this much to guarantee alignment between systems")
41
42               (gui #f "running from gui; redirect stderr to log file")
43               (log-file #f "redirect output to log FILE.log")
44               (old-relative #f
45                             "relative for simultaneous music works
46 similar to chord syntax")
47               (object-keys #f
48                            "experimental mechanism for remembering tweaks")
49               (point-and-click #t "use point & click")
50               (paper-size "a4" "the default paper size")
51               (protected-scheme-parsing #t "continue when finding errors in inline
52 scheme are caught in the parser. If off, halt 
53 on errors, and print a stack trace.")
54               (profile-property-accesses #f "keep statistics of get_property() calls.")
55               
56               (resolution 101 "resolution for generating PNG bitmaps")
57               (read-file-list #f "Read files to be processed from command line arguments")
58
59               (safe #f "Run safely")
60               (strict-infinity-checking #f "If yes, crash on encountering Inf/NaN")
61
62               (ttf-verbosity 0
63                              "how much verbosity for TTF font embedding?")
64
65               (show-available-fonts #f
66                                     "List  font names available.")
67
68               (verbose ,(ly:command-line-verbose?) "value for the --verbose flag")
69               )))
70
71
72 ;; need to do this in the beginning. Other parts of the
73 ;; Scheme init depend on these options.
74 ;;
75 (define-scheme-options)
76
77 (debug-set! stack 0)
78
79 (if (defined? 'set-debug-cell-accesses!)
80     (set-debug-cell-accesses! #f))
81
82                                         ;(set-debug-cell-accesses! 1000)
83
84 (use-modules (ice-9 regex)
85              (ice-9 safe)
86              (ice-9 optargs)
87              (oop goops)
88              (srfi srfi-1)
89              (srfi srfi-13)
90              (srfi srfi-14)
91              (scm clip-region)
92
93              )
94
95
96 ;; my display
97 (define-public (myd k v) (display k) (display ": ") (display v) (display ", "))
98
99 (define-public (print . args)
100   (apply format (cons (current-output-port) args)))
101
102
103 ;;; General settings
104 ;;; debugging evaluator is slower.  This should
105 ;;; have a more sensible default.
106
107 (if (ly:get-option 'verbose)
108     (begin
109       (ly:set-option 'protected-scheme-parsing #f)
110       (debug-enable 'debug)
111       (debug-enable 'backtrace)
112       (read-enable 'positions)))
113
114 (define-public tex-backend?
115   (member (ly:output-backend) '("texstr" "tex")))
116
117 (define-public parser #f)
118
119 (define-public (lilypond-version)
120   (string-join
121    (map (lambda (x) (if (symbol? x)
122                         (symbol->string x)
123                         (number->string x)))
124         (ly:version))
125    "."))
126
127
128 ;; TeX C++ code actually hooks into TEX_STRING_HASHLIMIT 
129 (define-public TEX_STRING_HASHLIMIT 10000000)
130
131
132
133 ;; gettext wrapper for guile < 1.7.2
134 (if (defined? 'gettext)
135     (define-public _ gettext)
136     (define-public _ ly:gettext))
137
138 (define-public (ly:load x)
139   (let* ((file-name (%search-load-path x)))
140     (if (ly:get-option 'verbose)
141         (ly:progress "[~A" file-name))
142     (if (not file-name)
143         (ly:error (_ "Can't find ~A") x))
144     (primitive-load file-name)
145     (if (ly:get-option 'verbose)
146         (ly:progress "]"))))
147
148 ;; Cygwin
149 ;; #(CYGWIN_NT-5.1 Hostname 1.5.12(0.116/4/2) 2004-11-10 08:34 i686)
150 ;;
151 ;; Debian
152 ;; #(Linux hostname 2.4.27-1-686 #1 Fri Sep 3 06:28:00 UTC 2004 i686)
153 ;;
154 ;; Mingw
155 ;; #(Windows XP HOSTNAME build 2600 5.01 Service Pack 1 i686)
156 ;;
157
158 ;; ugh, code dup.
159 (define-public PLATFORM
160   (string->symbol
161    (string-downcase
162     (car (string-tokenize (vector-ref (uname) 0) char-set:letter)))))
163
164 (define-public DOS
165   (let ((platform (string-tokenize
166                    (vector-ref (uname) 0) char-set:letter+digit)))
167     (if (null? (cdr platform)) #f
168         (member (string-downcase (cadr platform)) '("95" "98" "me")))))
169
170 (case PLATFORM
171   ((windows)
172    (define native-getcwd getcwd)
173    (define (slashify x)
174      (if (string-index x #\\)
175          x
176          (string-regexp-substitute
177           "//*" "/"
178           (string-regexp-substitute "\\\\" "/" x))))
179    ;; FIXME: this prints a warning.
180    (define-public (ly-getcwd)
181      (slashify (native-getcwd))))
182   (else (define-public ly-getcwd getcwd)))
183
184 (define-public (is-absolute? file-name)
185   (let ((file-name-length (string-length file-name)))
186     (if (= file-name-length 0)
187         #f
188         (or (eq? (string-ref file-name 0) #\/)
189             (and (eq? PLATFORM 'windows)
190                  (> file-name-length 2)
191                  (eq? (string-ref file-name 1) #\:)
192                  (eq? (string-ref file-name 2) #\/))))))
193
194 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
195
196 (define (type-check-list location signature arguments)
197   "Typecheck a list of arguments against a list of type
198 predicates. Print a message at LOCATION if any predicate failed."
199
200   (define (recursion-helper signature arguments count) 
201     (define (helper pred? arg count) 
202       (if (not (pred? arg))
203
204           (begin
205             (ly:input-message
206              location
207              (format
208               #f (_ "wrong type for argument ~a.  Expecting ~a, found ~s")
209               count (type-name pred?) arg))
210             #f)
211           #t))
212
213     (if (null? signature)
214         #t
215         (and (helper (car signature) (car arguments) count)
216              (recursion-helper (cdr signature) (cdr arguments) (1+ count)))))
217
218   (recursion-helper signature arguments 1))
219
220 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
221 ;;  output
222
223
224 ;;(define-public (output-framework) (write "hello\n"))
225
226 (define output-tex-module
227   (make-module 1021 (list (resolve-interface '(scm output-tex)))))
228 (define output-ps-module
229   (make-module 1021 (list (resolve-interface '(scm output-ps)))))
230
231 (define-public (ps-output-expression expr port)
232   (display (eval expr output-ps-module) port))
233
234 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
235 ;; Safe definitions utility
236 (define safe-objects (list))
237
238 (define-macro (define-safe-public arglist . body)
239   "Define a variable, export it, and mark it as safe, ie usable in LilyPond safe mode.
240 The syntax is the same as `define*-public'."
241   (define (get-symbol arg)
242     (if (pair? arg)
243         (get-symbol (car arg))
244         arg))
245   (let ((safe-symbol (get-symbol arglist)))
246     `(begin
247        (define*-public ,arglist
248          ,@body)
249        (set! safe-objects (cons (cons ',safe-symbol ,safe-symbol)
250                                 safe-objects))
251        ,safe-symbol)))
252
253 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
254 ;; init pitch system
255
256 (ly:set-default-scale (ly:make-scale #(0 2 4 5 7 9 11)))
257
258
259
260 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
261 ;; other files.
262
263 (for-each ly:load
264           ;; load-from-path
265           '("lily-library.scm"
266             "file-cache.scm"
267             "define-event-classes.scm"
268             "define-music-types.scm"
269             "output-lib.scm"
270             "c++.scm"
271             "chord-ignatzek-names.scm"
272             "chord-entry.scm"
273             "chord-generic-names.scm"
274             "stencil.scm"
275             "markup.scm"
276             "music-functions.scm"
277             "part-combiner.scm"
278             "autochange.scm"
279             "define-music-properties.scm"
280             "auto-beam.scm"
281             "chord-name.scm"
282
283             "parser-ly-from-scheme.scm"
284             "ly-syntax-constructors.scm"
285             
286             "define-context-properties.scm"
287             "translation-functions.scm"
288             "script.scm"
289             "midi.scm"
290             "layout-beam.scm"
291             "parser-clef.scm"
292             "layout-slur.scm"
293             "font.scm"
294             "encoding.scm"
295             
296             "fret-diagrams.scm"
297             "define-markup-commands.scm"
298             "define-grob-properties.scm"
299             "define-grobs.scm"
300             "define-grob-interfaces.scm"
301             "define-stencil-commands.scm"
302             "titling.scm"
303             
304             "paper.scm"
305             "backend-library.scm"
306             "x11-color.scm"
307
308             ;; must be after everything has been defined
309             "safe-lily.scm"))
310
311
312 (set! type-p-name-alist
313       `(
314         (,boolean-or-symbol? . "boolean or symbol")
315         (,boolean? . "boolean")
316         (,char? . "char")
317         (,grob-list? . "list of grobs")
318         (,hash-table? . "hash table")
319         (,input-port? . "input port")
320         (,integer? . "integer")
321         (,list? . "list")
322         (,ly:context? . "context")
323         (,ly:dimension? . "dimension, in staff space")
324         (,ly:dir? . "direction")
325         (,ly:duration? . "duration")
326         (,ly:grob? . "layout object")
327         (,ly:input-location? . "input location")
328         (,ly:moment? . "moment")
329         (,ly:music? . "music")
330         (,ly:pitch? . "pitch")
331         (,ly:translator? . "translator")
332         (,ly:font-metric? . "font metric")
333         (,ly:simple-closure? . "simple closure")
334         (,markup-list? . "list of markups")
335         (,markup? . "markup")
336         (,ly:music-list? . "list of music")
337         (,number-or-grob? . "number or grob")
338         (,number-or-string? . "number or string")
339         (,number-pair? . "pair of numbers")
340         (,number? . "number")
341         (,output-port? . "output port")   
342         (,pair? . "pair")
343         (,procedure? . "procedure")
344         (,rhythmic-location? . "rhythmic location")
345         (,scheme? . "any type")
346         (,string? . "string")
347         (,symbol? . "symbol")
348         (,vector? . "vector")))
349
350
351 ;; debug mem leaks
352
353 (define gc-protect-stat-count 0)
354 (define-public (dump-gc-protects)
355   (set! gc-protect-stat-count (1+ gc-protect-stat-count))
356   (let* ((protects (sort
357                     (hash-table->alist (ly:protects))
358                     (lambda (a b)
359                       (< (object-address (car a))
360                          (object-address (car b))))))
361
362          (out-file-name (string-append
363                          "gcstat-" (number->string gc-protect-stat-count)
364                          ".scm"))
365          (outfile    (open-file  out-file-name  "w")))
366
367     (display (format "Dumping GC statistics ~a...\n" out-file-name))
368     (display
369      (map (lambda (y)
370             (let ((x (car y))
371                   (c (cdr y)))
372               (display 
373                (format "~a (~a) = ~a\n" (object-address x) c x)
374                outfile)))
375           (filter
376            (lambda (x)
377              (not (symbol? (car x))))
378            protects))
379      outfile)
380
381     (format outfile "\nprotected symbols: ~a\n"
382             (length (filter symbol?  (map car protects))))
383     
384              
385
386     ;; (display (ly:smob-protects))
387     (newline outfile)
388     (if (defined? 'gc-live-object-stats)
389         (let* ((stats #f))
390           (display "Live object statistics: GC'ing\n")
391           (ly:reset-all-fonts)
392           (gc)
393           (gc)
394           (ly:set-option 'debug-gc-assert-parsed-dead #t)
395           (gc)
396           (ly:set-option 'debug-gc-assert-parsed-dead #f)
397
398           (set! stats (gc-live-object-stats))
399           (display "Dumping live object statistics.\n")
400           
401           (for-each
402            (lambda (x)
403              (format outfile "~a: ~a\n" (car x) (cdr x)))
404            (sort (gc-live-object-stats)
405                  (lambda (x y)
406                    (string<? (car x) (car y)))))))
407
408
409     (newline outfile)
410     (let*
411         ((stats (gc-stats)))
412       
413       (for-each
414        (lambda (sym)
415          (display
416           (format "~a ~a ~a\n"
417                   gc-protect-stat-count
418                   sym
419                   (let ((sym-stat (assoc sym stats)))
420                     (if sym-stat 
421                         (cdr sym-stat)
422                         "?")))
423           outfile))
424        '(protected-objects bytes-malloced cell-heap-size
425                            
426                            )))
427     
428     ))
429
430 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
431
432
433 (define (multi-fork count)
434   (define (helper count acc)
435     (if (> count 0)
436       (let*
437           ((pid  (primitive-fork)))
438         (if (= pid 0)
439             (1- count)
440             (helper (1- count) (cons pid acc))))
441       acc))
442   (helper count '()))
443
444
445 (define-public (lilypond-main files)
446   "Entry point for LilyPond."
447   
448   (define (no-files-handler)
449     (ly:usage)
450     (exit 2))
451
452   (if (ly:get-option 'gui)
453       (gui-main files))
454
455   (if (null? files)
456       (no-files-handler))
457
458   (if (ly:get-option 'read-file-list)
459       (set! files
460             (filter (lambda (s)
461                       (> (string-length s) 0))
462                     (apply append
463                            (map (lambda (f) (string-split (ly:gulp-file f) #\nl))
464                                 files)))
465             ))
466   
467   (if (and (number? (ly:get-option 'job-count))
468            (> (length files) (ly:get-option 'job-count)))
469       
470       (let*
471           ((count (ly:get-option 'job-count))
472            (split-todo (split-list files count)) 
473            (joblist (multi-fork count))
474            (errors '()))
475
476         (if (not (string-or-symbol? (ly:get-option 'log-file)))
477             (ly:set-option 'log-file "lilypond-multi-run"))
478         
479         (if (number? joblist)
480             (begin
481               (ly:set-option 'log-file (format "~a-~a"
482                                                (ly:get-option 'log-file) joblist))
483               (set! files (vector-ref split-todo joblist)))
484
485             (begin
486               (ly:progress "\nForking into jobs:  ~a\n" joblist)
487               (for-each
488                (lambda (pid)
489                  (let* ((stat (cdr (waitpid pid))))
490
491                    (if (not (= stat 0))
492                        (set! errors (cons (list-element-index joblist pid) errors)))))
493                joblist)
494
495               (for-each
496                (lambda (x)
497                  (let* ((logfile  (format "~a-~a.log"
498                                           (ly:get-option 'log-file) x))
499                         (log (ly:gulp-file logfile))
500                         (len (string-length log))
501                         (tail (substring  log (max 0 (- len 1024)))))
502
503                    (display (format "\n\nlogfile ~a:\n\n ~a" logfile tail))))
504
505                errors)
506
507               (if (pair? errors)
508                   (ly:error "Children ~a exited with errors." errors))
509
510             (exit (if (null? errors) 0 1))))))
511               
512            
513   (if (string-or-symbol? (ly:get-option 'log-file))
514       (ly:stderr-redirect (format "~a.log" (ly:get-option 'log-file)) "w"))
515   
516   (let ((failed (lilypond-all files)))
517     (if (pair? failed)
518         (begin
519           (ly:error (_ "failed files: ~S") (string-join failed))
520           (exit 1))
521         (begin
522           ;; HACK: be sure to exit with single newline
523           (ly:message "")
524           (exit 0)))))
525
526 (define-public (lilypond-all files)
527
528
529   (if (ly:get-option 'show-available-fonts)
530       (begin
531         (ly:font-config-display-fonts)
532         (exit 0)
533         ))
534   
535   (let* ((failed '())
536          (handler (lambda (key failed-file)
537                     (set! failed (append (list failed-file) failed)))))
538
539     (for-each
540      (lambda (x)
541        (lilypond-file handler x)
542        (ly:clear-anonymous-modules)
543        (if (ly:get-option 'debug-gc)
544            (dump-gc-protects)
545            (if (= (random 40) 1)
546                (ly:reset-all-fonts))))
547
548      files)
549     failed))
550
551 (define (lilypond-file handler file-name)
552   (catch 'ly-file-failed
553          (lambda () (ly:parse-file file-name))
554          (lambda (x . args) (handler x file-name))))
555
556 (use-modules (scm editor))
557
558 (define-public (gui-main files)
559   (if (null? files)
560       (gui-no-files-handler))
561
562   (if (not (string? (ly:get-option 'log-file)))
563       (let* ((base (basename (car files) ".ly"))
564              (log-name (string-append base ".log")))
565         (if (not (ly:get-option 'gui))
566             (ly:message (_ "Redirecting output to ~a...") log-name))
567         (ly:stderr-redirect log-name "w")
568         (ly:message "# -*-compilation-*-"))
569     
570     (let ((failed (lilypond-all files)))
571       (if (pair? failed)
572           (begin
573             ;; ugh
574             (ly:stderr-redirect "foo" "r")
575             (system (get-editor-command log-name 0 0 0))
576             (ly:error (_ "failed files: ~S") (string-join failed))
577             ;; not reached?
578             (exit 1))
579           (exit 0)))))
580
581 (define (gui-no-files-handler)
582   (let* ((ly (string-append (ly:effective-prefix) "/ly/"))
583          ;; FIXME: soft-code, localize
584          (welcome-ly (string-append ly "Welcome_to_LilyPond.ly"))
585          (cmd (get-editor-command welcome-ly 0 0 0)))
586     (ly:message (_ "Invoking `~a'...") cmd)
587     (system cmd)
588     (exit 1)))