* scripts/lilypond-book.py (output_dict): remove support for the
[lilypond.git] / scripts / lilypond-book.py
blobfbba449b9de758b90111fb48a2568287d34cdf7d
1 #!@PYTHON@
2 # vim: set noexpandtab:
4 """
6 TODO:
7 * junk --outdir for--output
8 * Figure out clean set of options.
10 * texinfo: add support for @pagesize
12 todo: dimension handling (all the x2y) is clumsy. (tca: Thats
13 because the values are taken directly from texinfo.tex,
14 geometry.sty and article.cls. Give me a hint, and I'll
15 fix it.)
18 TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
22 This is a slightly hairy program. The general approach is as follows
23 The input string is chopped up in chunks, i.e. , a list of tuples
25 with the format (TAG_STR, MAIN_STR, OPTIONS, TODO, BASE)
27 This list is built step by step: first ignore and verbatim commands
28 are handled, delivering a list of chunks.
30 then all chunks containing lilypond commands are chopped up
32 when all chunks have their final form, all bodies from lilypond blocks are
33 extracted, and if applicable, written do disk and run through lilypond.
36 tags supported
38 ignore
39 lilypond
40 input
41 verb
42 verbatim
43 multicols
44 numcols
49 """
51 # This is was the idea for handling of comments:
52 # Multiline comments, @ignore .. @end ignore is scanned for
53 # in read_doc_file, and the chunks are marked as 'ignore', so
54 # lilypond-book will not touch them any more. The content of the
55 # chunks are written to the output file. Also 'include' and 'input'
56 # regex has to check if they are commented out.
59 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
60 # These three regex's has to check if they are on a commented line,
61 # % for latex, @c for texinfo.
63 # Then lines that are commented out with % (latex) and @c (Texinfo)
64 # are put into chunks marked 'ignore'. This cannot be done before
65 # searching for the lilypond-blocks because % is also the comment character
66 # for lilypond.
68 # The the rest of the rexeces are searched for. They don't have to test
69 # if they are on a commented out line.
71 import glob
72 import stat
73 import string
76 ################################################################
77 # Users of python modules should include this snippet
78 # and customize variables below.
80 # We'll suffer this path init stuff as long as we don't install our
81 # python packages in <prefix>/lib/pythonx.y (and don't kludge around
82 # it as we do with teTeX on Red Hat Linux: set some environment var
83 # (PYTHONPATH) in profile)
85 # If set, LILYPONDPREFIX must take prevalence
86 # if datadir is not set, we're doing a build and LILYPONDPREFIX
87 import getopt, os, sys
88 datadir = '@local_lilypond_datadir@'
89 if not os.path.isdir (datadir):
90 datadir = '@lilypond_datadir@'
91 if os.environ.has_key ('LILYPONDPREFIX') :
92 datadir = os.environ['LILYPONDPREFIX']
93 while datadir[-1] == os.sep:
94 datadir= datadir[:-1]
96 sys.path.insert (0, os.path.join (datadir, 'python'))
98 # Customize these
99 #if __name__ == '__main__':
101 import lilylib as ly
102 global _;_=ly._
103 global re;re = ly.re
105 # lilylib globals
106 program_version = '@TOPLEVEL_VERSION@'
107 program_name = 'lilypond-book'
108 verbose_p = 0
109 pseudo_filter_p = 0
110 original_dir = os.getcwd ()
113 preview_resolution = 90
115 ## FIXME
116 ## ly2dvi: silly name?
117 ## do -P or -p by default?
118 ##help_summary = _ ("Run LilyPond using LaTeX for titling")
119 help_summary = _ ("Process LilyPond snippets in hybrid html, LaTeX or texinfo document")
120 copyright = ('Tom Cato Amundsen <tca@gnu.org>',
121 'Han-Wen Nienhuys <hanwen@cs.uu.nl>')
123 option_definitions = [
124 (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")),
125 (_ ("DIM"), '', 'default-music-fontsize', _ ("default fontsize for music. DIM is assumed to be in points")),
126 (_ ("DIM"), '', 'default-lilypond-fontsize', _ ("deprecated, use --default-music-fontsize")),
127 (_ ("OPT"), '', 'extra-options', _ ("pass OPT quoted to the lilypond command line")),
128 (_ ("DIM"), '', 'force-music-fontsize', _ ("force fontsize for all inline lilypond. DIM is assumed to be in points")),
129 (_ ("DIM"), '', 'force-lilypond-fontsize', _ ("deprecated, use --force-music-fontsize")),
130 ('', 'h', 'help', _ ("print this help")),
131 (_ ("DIR"), 'I', 'include', _ ("include path")),
132 ('', 'M', 'dependencies', _ ("write dependencies")),
133 (_ ("PREF"), '', 'dep-prefix', _ ("prepend PREF before each -M dependency")),
134 ('', 'n', 'no-lily', _ ("don't run lilypond")),
135 ('', '', 'no-pictures', _ ("don't generate pictures")),
136 ('', '', 'no-music', _ ("strip all lilypond blocks from output")),
137 (_ ("FILE"), 'o', 'outname', _ ("filename main output file")),
138 (_ ("FILE"), '', 'outdir', _ ("where to place generated files")),
139 (_ ('RES'), '', 'preview-resolution',
140 _ ("set the resolution of the preview to RES")),
141 ('', 'V', 'verbose', _ ("be verbose")),
142 ('', 'v', 'version', _ ("print version information")),
143 ('', 'w', 'warranty', _ ("show warranty and copyright")),
146 # format specific strings, ie. regex-es for input, and % strings for output
148 # global variables
150 include_path = [os.getcwd ()]
152 #lilypond_binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
154 lilypond_binary = os.path.join ('@bindir@', 'lilypond-bin')
156 # only use installed binary when we're installed too.
157 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
158 lilypond_binary = 'lilypond-bin'
162 ly2dvi_binary = os.path.join ('@bindir@', 'ly2dvi')
164 # only use installed binary when we're installed too.
165 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
166 ly2dvi_binary = 'ly2dvi'
170 g_extra_opts = ''
171 g_here_dir = os.getcwd ()
172 g_dep_prefix = ''
173 g_outdir = ''
174 g_force_music_fontsize = 0
175 g_do_pictures = 1
176 g_do_music = 1
177 g_make_html = 0
179 format = ''
180 g_run_lilypond = 1
181 no_match = 'a\ba'
183 default_music_fontsize = 16
184 default_text_fontsize = 12
185 paperguru = None
187 ################################################################
188 # Dimension handling for LaTeX.
190 class LatexPaper:
191 def __init__ (self):
192 self.m_document_preamble = []
193 self.m_num_cols = 1
194 self.m_multicols = 1
196 def find_latex_dims (self):
197 if g_outdir:
198 fname = os.path.join (g_outdir, "lily-tmp.tex")
199 else:
200 fname = "lily-tmp.tex"
201 try:
202 f = open (fname, "w")
203 except IOError:
204 error ("Error creating temporary file '%s'" % fname)
206 for s in self.m_document_preamble:
207 f.write (s)
208 f.write (r"""
209 \begin{document}
210 \typeout{---}
211 \typeout{\columnsep \the\columnsep}
212 \typeout{\textwidth \the\textwidth}
213 \typeout{---}
214 \end{document}
215 """)
216 f.close ()
217 re_dim = re.compile (r"\\(\w+)\s+(\d+\.\d+)")
219 cmd = "latex '\\nonstopmode \input %s'" % fname
220 # Ugh. (La)TeX writes progress and error messages on stdout
221 # Redirect to stderr
222 cmd = '(( %s >&2 ) >&- )' % cmd
223 status = ly.system (cmd, ignore_error = 1)
224 signal = 0xf & status
225 exit_status = status >> 8
227 if status:
228 ly.error (_ ("LaTeX failed."))
229 ly.error (_ ("The error log is as follows:"))
231 #URG see ly2dvi
232 try:
233 lns = open ('lily-tmp.log').readlines ()
234 except:
235 lns = ''
236 countdown = -3
237 for ln in lns:
238 sys.stderr.write (ln)
239 if re.match ('^!', ln):
240 countdown = 3
242 if countdown == 0:
243 break
245 if countdown > 0:
246 countdown = countdown -1
248 sys.stderr.write (" ... (further messages elided)...\n")
249 sys.exit (1)
251 lns = open ('lily-tmp.log').readlines ()
252 for ln in lns:
253 ln = string.strip (ln)
254 m = re_dim.match (ln)
255 if m:
256 if m.groups ()[0] in ('textwidth', 'columnsep'):
257 self.__dict__['m_%s' % m.groups ()[0]] = float (m.groups ()[1])
259 try:
260 os.remove (fname)
261 os.remove (os.path.splitext (fname)[0]+".aux")
262 os.remove (os.path.splitext (fname)[0]+".log")
263 except:
264 pass
266 if not self.__dict__.has_key ('m_textwidth'):
267 raise 'foo!'
269 def get_linewidth (self):
270 if self.m_num_cols == 1:
271 w = self.m_textwidth
272 else:
273 w = (self.m_textwidth - self.m_columnsep)/2
274 if self.m_multicols > 1:
275 return (w - self.m_columnsep* (self.m_multicols-1)) \
276 / self.m_multicols
277 return w
280 class HtmlPaper:
281 def __init__ (self):
282 self.m_papersize = 'letterpaper'
283 self.m_fontsize = 12
284 def get_linewidth (self):
285 return html_linewidths[self.m_papersize][self.m_fontsize]
287 class TexiPaper:
288 def __init__ (self):
289 self.m_papersize = 'letterpaper'
290 self.m_fontsize = 12
291 def get_linewidth (self):
292 return texi_linewidths[self.m_papersize][self.m_fontsize]
294 def mm2pt (x):
295 return x * 2.8452756
296 def in2pt (x):
297 return x * 72.26999
298 def em2pt (x, fontsize = 10):
299 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
300 def ex2pt (x, fontsize = 10):
301 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
303 def pt2pt (x):
304 return x
306 dimension_conversion_dict ={
307 'mm': mm2pt,
308 'cm': lambda x: mm2pt (10*x),
309 'in': in2pt,
310 'em': em2pt,
311 'ex': ex2pt,
312 'pt': pt2pt
315 # Convert numeric values, with or without specific dimension, to floats.
316 # Keep other strings
317 def conv_dimen_to_float (value):
318 if type (value) == type (""):
319 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
320 if m:
321 unit = m.group (2)
322 num = string.atof (m.group (1))
323 conv = dimension_conversion_dict[m.group (2)]
325 value = conv (num)
327 elif re.match ("^[0-9.]+$",value):
328 value = float (value)
330 return value
332 texi_linewidths = {
333 'afourpaper': {12: mm2pt (160)},
334 'afourwide': {12: in2pt (6.5)},
335 'afourlatex': {12: mm2pt (150)},
336 'smallbook': {12: in2pt (5)},
337 'letterpaper': {12: in2pt (6)}}
339 html_linewidths = {
340 'afourpaper': {12: mm2pt (160)},
341 'afourwide': {12: in2pt (6.5)},
342 'afourlatex': {12: mm2pt (150)},
343 'smallbook': {12: in2pt (5)},
344 'letterpaper': {12: in2pt (6)}}
347 ################################################################
348 # How to output various structures.
349 output_dict= {
352 'html' : {
354 'output-filename' : r'''
355 <!-- %s >
356 <a href="%s">
357 <pre>%s</pre></a>:''',
358 'output-lilypond-fragment': '''<lilypond%s>
359 \context Staff\context Voice{ %s }
360 </lilypond>''',
361 'output-noinline': r'''
362 <!-- generated: %(fn)s.png !-->
363 ''',
364 ## maybe <hr> ?
365 'pagebreak': None,
366 # Verbatim text is always finished with \n. FIXME: For HTML,
367 # this newline should be removed.
368 'output-verbatim': r'''<pre>
369 %s</pre>''',
370 # Verbatim text is always finished with \n. FIXME: For HTML,
371 # this newline should be removed.
372 'output-small-verbatim': r'''<font size=-1><pre>
373 %s</pre></font>''',
374 ## Ugh we need to differentiate on origin:
375 ## lilypond-block origin wants an extra <p>, but
376 ## inline music doesn't.
377 ## possibly other center options?
378 'output-html': r'''
379 %(htmlimages)s''',
383 'latex': {
385 'output-lilypond-fragment' : r'''\begin[singleline,%s]{lilypond}
386 \context Voice{
389 \end{lilypond}''',
390 'output-filename' : r'''\verb+%s+:\\
391 %% %s
392 %% %s
393 ''',
395 # verbatim text is always finished with \n
396 'output-verbatim': r'''\begin{verbatim}
397 %s\end{verbatim}
398 ''',
399 # verbatim text is always finished with \n
400 'output-small-verbatim': r'''{\small\begin{verbatim}
401 %s\end{verbatim}}
402 ''',
403 'output-default-post': "\\def\postLilyPondExample{}\n",
404 'output-default-pre': "\\def\preLilyPondExample{}\n",
405 'usepackage-graphics': '\\usepackage{graphics}\n',
406 'output-noinline': r'''
407 %% generated: %(fn)s.eps
408 ''',
409 'output-latex-quoted': r'''{\preLilyPondExample
410 \input %(fn)s.tex
411 \postLilyPondExample}''',
412 'output-latex-noquote': r'''{\parindent 0pt
413 \preLilyPondExample
414 \input %(fn)s.tex
415 \postLilyPondExample}''',
416 'pagebreak': r'\pagebreak',
420 'texi' : {
423 'output-filename' : r'''
424 @ifnothtml
425 @file{%s}:@*
426 @end ifnothtml
427 @ifhtml
428 @uref{%s,@file{%s}}
429 @end ifhtml
430 ''',
431 'output-lilypond-fragment': '''@lilypond[%s]
432 \context Staff\context Voice{ %s }
433 @end lilypond ''',
434 'output-noinline': r'''
435 @c generated: %(fn)s.png
436 ''',
437 'pagebreak': None,
438 # verbatim text is always finished with \n
439 'output-small-verbatim': r'''@smallexample
440 %s@end smallexample
441 ''',
442 # verbatim text is always finished with \n
443 'output-verbatim': r'''@example
444 %s@end example
445 ''',
446 # do some tweaking: @ is needed in some ps stuff.
448 # ugh, the <p> below breaks inline images...
449 'output-texi-noquote': r'''@tex
450 \catcode`\@=12
451 \parindent 0pt
452 \def\lilypondbook{}
453 \input %(fn)s.tex
454 \catcode`\@=0
455 @end tex
456 @html
457 <p>%(htmlimages)s
459 @end html
460 ''',
461 'output-texi-quoted': r'''@quotation
462 @tex
463 \catcode`\@=12
464 \def\lilypondbook{}
465 \input %(fn)s.tex
466 \catcode`\@=0
467 @end tex
468 @html
469 <p>%(htmlimages)s
471 @end html
472 @end quotation
473 ''',
478 def output_verbatim (body, small):
479 global format
480 if format == 'html':
481 body = re.sub ('&', '&amp;', body)
482 body = re.sub ('>', '&gt;', body)
483 body = re.sub ('<', '&lt;', body)
484 elif format == 'texi':
485 # clumsy workaround for python 2.2 pre bug.
486 body = re.sub ('@', '@@', body)
487 body = re.sub ('{', '@{', body)
488 body = re.sub ('}', '@}', body)
490 if small:
491 key = 'output-small-verbatim'
492 else:
493 key = 'output-verbatim'
494 return get_output (key) % body
497 ################################################################
498 # Recognize special sequences in the input
501 # Warning: This uses extended regular expressions. Tread with care.
503 # legenda
505 # (?P<name>regex) -- assign result of REGEX to NAME
506 # *? -- match non-greedily.
507 # (?m) -- multiline regex: make ^ and $ match at each line
508 # (?s) -- make the dot match all characters including newline
509 re_dict = {
510 'html': {
511 'include': no_match,
512 'input': no_match,
513 'header': no_match,
514 'preamble-end': no_match,
515 'landscape': no_match,
516 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
517 'verb': r'''(?P<code><pre>.*?</pre>)''',
518 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
519 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
520 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
521 'option-sep' : '\s*',
522 'intertext': r',?\s*intertext=\".*?\"',
523 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
524 'singleline-comment': no_match,
525 'numcols': no_match,
526 'multicols': no_match,
527 'ly2dvi': r'(?m)(?P<match><ly2dvifile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</ly2dvifile>)',
530 'latex': {
531 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
532 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
533 'option-sep' : ',\s*',
534 'header': r"\n*\\documentclass\s*(\[.*?\])?",
535 'preamble-end': r'(?P<code>\\begin\s*{document})',
536 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
537 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
538 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
539 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
540 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
541 'def-post-re': r"\\def\\postLilyPondExample",
542 'def-pre-re': r"\\def\\preLilyPondExample",
543 'usepackage-graphics': r"\usepackage\s*{graphics}",
544 'intertext': r',?\s*intertext=\".*?\"',
545 'multiline-comment': no_match,
546 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
547 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
548 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
549 'ly2dvi': no_match,
553 # why do we have distinction between @mbinclude and @include?
555 'texi': {
556 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
557 'input': no_match,
558 'header': no_match,
559 'preamble-end': no_match,
560 'landscape': no_match,
561 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
562 'verb': r'''(?P<code>@code{.*?})''',
563 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
564 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
565 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s''',
566 'option-sep' : ',\s*',
567 'intertext': r',?\s*intertext=\".*?\"',
568 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
569 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
570 'numcols': no_match,
571 'multicols': no_match,
572 'ly2dvi': no_match,
577 for r in re_dict.keys ():
578 olddict = re_dict[r]
579 newdict = {}
580 for k in olddict.keys ():
581 try:
582 newdict[k] = re.compile (olddict[k])
583 except:
584 print 'invalid regexp: %s' % olddict[k]
586 ## we'd like to catch and reraise a more
587 ## detailed error, but alas, the exceptions
588 ## changed across the 1.5/2.1 boundary.
590 raise "Invalid re"
591 re_dict[r] = newdict
594 def uniq (list):
595 list.sort ()
596 s = list
597 list = []
598 for x in s:
599 if x not in list:
600 list.append (x)
601 return list
604 def get_output (name):
605 return output_dict[format][name]
607 def get_re (name):
608 return re_dict[format][name]
610 def bounding_box_dimensions (fname):
611 if g_outdir:
612 fname = os.path.join (g_outdir, fname)
613 try:
614 fd = open (fname)
615 except IOError:
616 error ("Error opening `%s'" % fname)
617 str = fd.read ()
618 s = re.search ('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
619 if s:
621 gs = map (lambda x: string.atoi (x), s.groups ())
622 return (int (gs[2] - gs[0] + 0.5),
623 int (gs[3] - gs[1] + 0.5))
624 else:
625 return (0,0)
627 def error (str):
628 sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
629 raise 'Exiting.'
632 def compose_full_body (body, opts):
633 '''Construct the lilypond code to send to LilyPond.
634 Add stuff to BODY using OPTS as options.'''
635 music_size = default_music_fontsize
636 if g_force_music_fontsize:
637 music_size = g_force_music_fontsize
638 indent = ''
639 linewidth = ''
640 notime = ''
641 for o in opts:
642 if not g_force_music_fontsize:
643 m = re.match ('([0-9]+)pt', o)
644 if m:
645 music_size = string.atoi (m.group (1))
647 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
648 if m:
649 f = float (m.group (1))
650 indent = 'indent = %f\\%s' % (f, m.group (2))
652 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
653 if m:
654 f = float (m.group (1))
655 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
657 if re.search ('\\\\score', body):
658 is_fragment = 0
659 else:
660 is_fragment = 1
661 if 'fragment' in opts:
662 is_fragment = 1
663 if 'nofragment' in opts:
664 is_fragment = 0
666 if is_fragment and not 'multiline' in opts:
667 opts.append ('singleline')
669 if 'raggedright' in opts or 'singleline' in opts:
670 if not linewidth:
671 linewidth = 'raggedright = ##t'
672 if not indent:
673 indent = 'indent = 0.0\mm'
674 elif not linewidth:
675 global paperguru
676 l = paperguru.get_linewidth ()
677 linewidth = 'linewidth = %f\pt' % l
679 if 'noindent' in opts:
680 indent = 'indent = 0.0\mm'
682 if 'notime' in opts:
683 notime = r'''
684 \translator {
685 \StaffContext
686 \remove Time_signature_engraver
690 orig_name = ''
691 for o in opts:
692 m= re.search ('relative(.*)', o)
693 v = 0
694 if m:
695 try:
696 v = string.atoi (m.group (1))
697 except ValueError:
698 pass
700 v = v + 1
701 pitch = 'c'
702 if v < 0:
703 pitch = pitch + '\,' * v
704 elif v > 0:
705 pitch = pitch + '\'' * v
707 body = '\\relative %s { %s }' % (pitch, body)
708 m =re.search ("filename=(.*)", o)
709 if m:
710 orig_name = m.group (1)
712 if is_fragment:
713 body = r'''
714 \score {
715 \notes {
719 ''' % body
721 opts = uniq (opts)
722 optstring = string.join (opts, ' ')
723 optstring = re.sub ('\n', ' ', optstring)
724 body = r'''
725 %% Generated automatically by: lilypond-book.py
726 %% options are %s
727 \include "paper%d.ly"
728 \paper {
733 ''' % (optstring, music_size, linewidth, indent, notime) + body
735 if orig_name:
736 body = '\\renameinput \"%s\"\n%s' % (orig_name, body)
739 # ughUGH not original options
740 return body
742 def scan_html_preamble (chunks):
743 return
745 def scan_latex_preamble (chunks):
746 # First we want to scan the \documentclass line
747 # it should be the first non-comment line.
748 # The only thing we really need to know about the \documentclass line
749 # is if there are one or two columns to begin with.
750 idx = 0
751 while 1:
752 if chunks[idx][0] == 'ignore':
753 idx = idx + 1
754 continue
755 m = get_re ('header').match (chunks[idx][1])
756 if not m:
757 error ("Latex documents must start with a \documentclass command")
758 if m.group (1):
759 options = re.split (',[\n \t]*', m.group (1)[1:-1])
760 else:
761 options = []
762 if 'twocolumn' in options:
763 paperguru.m_num_cols = 2
764 break
767 # Then we add everything before \begin{document} to
768 # paperguru.m_document_preamble so that we can later write this header
769 # to a temporary file in find_latex_dims() to find textwidth.
770 while idx < len (chunks) and chunks[idx][0] != 'preamble-end':
771 if chunks[idx] == 'ignore':
772 idx = idx + 1
773 continue
774 paperguru.m_document_preamble.append (chunks[idx][1])
775 idx = idx + 1
777 if len (chunks) == idx:
778 error ("Didn't find end of preamble (\\begin{document})")
780 paperguru.find_latex_dims ()
782 def scan_texi_preamble (chunks):
783 # this is not bulletproof..., it checks the first 10 chunks
784 for c in chunks[:10]:
785 if c[0] == 'input':
786 for s in ('afourpaper', 'afourwide', 'letterpaper',
787 'afourlatex', 'smallbook'):
788 if string.find (c[1], "@%s" % s) != -1:
789 paperguru.m_papersize = s
792 def scan_preamble (chunks):
793 global format
794 if format == 'html':
795 scan_html_preamble (chunks)
796 elif format == 'latex':
797 scan_latex_preamble (chunks)
798 elif format == 'texi':
799 scan_texi_preamble (chunks)
802 def completize_preamble (chunks):
803 global format
804 if format != 'latex':
805 return chunks
806 pre_b = post_b = graphics_b = None
807 for chunk in chunks:
808 if chunk[0] == 'preamble-end':
809 break
810 if chunk[0] == 'input':
811 m = get_re ('def-pre-re').search (chunk[1])
812 if m:
813 pre_b = 1
814 if chunk[0] == 'input':
815 m = get_re ('def-post-re').search (chunk[1])
816 if m:
817 post_b = 1
819 if chunk[0] == 'input':
820 m = get_re ('usepackage-graphics').search (chunk[1])
821 if m:
822 graphics_b = 1
823 x = 0
824 while x < len (chunks) and chunks[x][0] != 'preamble-end':
825 x = x + 1
827 if x == len (chunks):
828 return chunks
830 if not pre_b:
831 chunks.insert (x, ('input', get_output ('output-default-pre')))
832 if not post_b:
833 chunks.insert (x, ('input', get_output ('output-default-post')))
834 if not graphics_b:
835 chunks.insert (x, ('input', get_output ('usepackage-graphics')))
837 return chunks
840 read_files = []
841 def find_file (name):
843 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
846 if name == '-':
847 return (sys.stdin.read (), '<stdin>')
848 f = None
849 nm = ''
850 for a in include_path:
851 try:
852 nm = os.path.join (a, name)
853 f = open (nm)
854 global read_files
855 read_files.append (nm)
856 break
857 except IOError:
858 pass
859 if f:
860 sys.stderr.write ("Reading `%s'\n" % nm)
861 return (f.read (), nm)
862 else:
863 error ("File not found `%s'\n" % name)
864 return ('', '')
866 def do_ignore (match_object):
867 return [('ignore', match_object.group ('code'))]
868 def do_preamble_end (match_object):
869 return [('preamble-end', match_object.group ('code'))]
871 def make_verbatim (match_object):
872 return [('verbatim', match_object.group ('code'))]
874 def make_verb (match_object):
875 return [('verb', match_object.group ('code'))]
877 def do_include_file (m):
878 "m: MatchObject"
879 return [('input', get_output ('pagebreak'))] \
880 + read_doc_file (m.group ('filename')) \
881 + [('input', get_output ('pagebreak'))]
883 def do_input_file (m):
884 return read_doc_file (m.group ('filename'))
886 def make_lilypond (m):
887 if m.group ('options'):
888 options = m.group ('options')
889 else:
890 options = ''
891 return [('input', get_output ('output-lilypond-fragment') %
892 (options, m.group ('code')))]
894 def make_lilypond_file (m):
897 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
898 into a @lilypond .. @end lilypond block.
902 if m.group ('options'):
903 options = get_re ('option-sep').split (m.group ('options'))
904 else:
905 options = []
906 (content, nm) = find_file (m.group ('filename'))
907 options.append ("filename=%s" % nm)
908 (path, base) = os.path.split (nm)
910 if path not in include_path:
911 include_path.append (path)
913 return [('lilypond', content, options)]
916 def make_ly2dvi_block (m):
919 Find <ly2dvifile .. >
922 return [('ly2dvi', m.group ('filename'), m.group ('options'))]
925 def make_lilypond_block (m):
926 if not g_do_music:
927 return []
929 if m.group ('options'):
930 options = get_re ('option-sep').split (m.group ('options'))
931 else:
932 options = []
933 options = filter (lambda s: s != '', options)
934 return [('lilypond', m.group ('code'), options)]
937 def do_columns (m):
938 global format
939 if format != 'latex':
940 return []
941 if m.group ('num') == 'one':
942 return [('numcols', m.group ('code'), 1)]
943 if m.group ('num') == 'two':
944 return [('numcols', m.group ('code'), 2)]
946 def do_multicols (m):
947 global format
948 if format != 'latex':
949 return []
950 if m.group ('be') == 'begin':
951 return [('multicols', m.group ('code'), int (m.group ('num')))]
952 else:
953 return [('multicols', m.group ('code'), 1)]
954 return []
956 def chop_chunks (chunks, re_name, func, use_match=0):
957 newchunks = []
958 for c in chunks:
959 if c[0] == 'input':
960 str = c[1]
961 while str:
962 m = get_re (re_name).search (str)
963 if m == None:
964 newchunks.append (('input', str))
965 str = ''
966 else:
967 if use_match:
968 newchunks.append (('input', str[:m.start ('match')]))
969 else:
970 newchunks.append (('input', str[:m.start (0)]))
971 #newchunks.extend (func (m))
972 # python 1.5 compatible:
973 newchunks = newchunks + func (m)
974 str = str [m.end (0):]
975 else:
976 newchunks.append (c)
977 return newchunks
979 def determine_format (str):
982 SIDE EFFECT! This sets FORMAT and PAPERGURU
986 global format
987 if format == '':
988 html = re.search ('(?i)<[dh]tml', str[:200])
989 latex = re.search (r'''\\document''', str[:200])
990 texi = re.search ('@node|@setfilename', str[:200])
992 f = ''
993 g = None
995 if html and not latex and not texi:
996 f = 'html'
997 elif latex and not html and not texi:
998 f = 'latex'
999 elif texi and not html and not latex:
1000 f = 'texi'
1001 else:
1002 error ("can't determine format, please specify")
1003 format = f
1005 global paperguru
1006 if paperguru == None:
1007 if format == 'html':
1008 g = HtmlPaper ()
1009 elif format == 'latex':
1010 g = LatexPaper ()
1011 elif format == 'texi':
1012 g = TexiPaper ()
1014 paperguru = g
1017 def read_doc_file (filename):
1018 '''Read the input file, find verbatim chunks and do \input and \include
1020 (str, path) = find_file (filename)
1021 determine_format (str)
1023 chunks = [('input', str)]
1025 # we have to check for verbatim before doing include,
1026 # because we don't want to include files that are mentioned
1027 # inside a verbatim environment
1028 chunks = chop_chunks (chunks, 'verbatim', make_verbatim)
1030 chunks = chop_chunks (chunks, 'verb', make_verb)
1031 chunks = chop_chunks (chunks, 'multiline-comment', do_ignore)
1032 #ugh fix input
1033 chunks = chop_chunks (chunks, 'include', do_include_file, 1)
1034 chunks = chop_chunks (chunks, 'input', do_input_file, 1)
1035 return chunks
1038 taken_file_names = {}
1040 def unique_file_name (body):
1041 return 'lily-' + `abs (hash (body))`
1043 def schedule_lilypond_block (chunk):
1044 '''Take the body and options from CHUNK, figure out how the
1045 real .ly should look. The .ly is written, and scheduled in
1046 TODO.
1048 Return: a single chunk.
1050 The chunk pertaining to the lilypond output
1051 has the format (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE),
1052 where TODO has format [basename, extension, extension, ... ]
1055 (type, body, opts) = chunk
1056 assert type == 'lilypond'
1057 file_body = compose_full_body (body, opts)
1058 ## Hmm, we should hash only lilypond source, and skip the
1059 ## %options are ...
1060 ## comment line
1061 basename = unique_file_name (file_body)
1062 for o in opts:
1063 m = re.search ('filename="(.*?)"', o)
1064 if m:
1065 basename = m.group (1)
1066 if not taken_file_names.has_key (basename):
1067 taken_file_names[basename] = 0
1068 else:
1069 taken_file_names[basename] = taken_file_names[basename] + 1
1070 basename = basename + "-t%i" % taken_file_names[basename]
1071 update_file (file_body, os.path.join (g_outdir, basename) + '.ly')
1072 needed_filetypes = ['tex']
1074 if format == 'html' or g_make_html:
1075 needed_filetypes.append ('eps')
1076 needed_filetypes.append ('png')
1077 if 'eps' in opts and not ('eps' in needed_filetypes):
1078 needed_filetypes.append ('eps')
1080 pathbase = os.path.join (g_outdir, basename)
1081 def must_rebuild (base, ext1, ext2):
1083 f2 = base + ext2
1084 f1 = base + ext1
1085 fp2 = base + '-page1' + ext2
1087 isfile2 = os.path.isfile (f2)
1089 if not isfile2 and os.path.isfile (fp2):
1090 f2 = fp2
1091 isfile2 = os.path.isfile (fp2)
1093 if (os.path.isfile (f2) and isfile2 and
1094 os.stat (f1)[stat.ST_MTIME] >
1095 os.stat (f2)[stat.ST_MTIME]) or \
1096 not isfile2:
1098 return 1
1100 todo = []
1101 if 'tex' in needed_filetypes and must_rebuild (pathbase, '.ly', '.tex'):
1102 todo.append ('tex')
1103 if 'eps' in needed_filetypes and must_rebuild (pathbase, '.tex', '.eps'):
1104 todo.append ('eps')
1105 if 'png' in needed_filetypes and must_rebuild (pathbase, '.eps', '.png'):
1106 todo.append ('png')
1108 return ('lilypond', body, opts, todo, basename)
1110 def format_lilypond_block (chunk):
1113 Figure out what should be left MAIN_STR (meant
1114 for the main file) from a lilypond chunk: process
1115 verbatim, and other options. Return: multiple chunks.
1121 return_chunks = []
1123 (type, body, opts, todo, basename) = chunk
1124 assert type == 'lilypond'
1127 newbody = ''
1128 filename_chunk = None
1129 if 'printfilename' in opts:
1130 for o in opts:
1131 m= re.match ("filename=(.*)", o)
1132 if m:
1133 template = get_output ("output-filename")
1134 b = basename + '.ly'
1135 human_base = os.path.basename (m.group (1))
1137 ## todo: include path, but strip
1138 ## first part of the path.
1139 filename_chunk = ('input', template % (human_base, b,human_base))
1140 break
1143 if 'smallverbatim' in opts:
1144 newbody += output_verbatim (body, 1)
1145 elif 'verbatim' in opts:
1146 newbody += output_verbatim (body, 0)
1148 for o in opts:
1149 m = re.search ('intertext="(.*?)"', o)
1150 if m:
1151 newbody = newbody + "\n"
1152 if format == 'texi':
1153 newbody = newbody + "@noindent\n"
1154 elif format == 'latex':
1155 newbody = newbody + "\\noindent\n"
1156 newbody = newbody + m.group (1) + "\n"
1158 if 'noinline' in opts:
1159 s = 'output-noinline'
1160 elif format == 'latex':
1161 if 'quote' in opts:
1162 s = 'output-latex-quoted'
1163 else:
1164 s = 'output-latex-noquote'
1165 elif format == 'texi':
1166 if 'quote' in opts:
1167 s = 'output-texi-quoted'
1168 else:
1169 s = 'output-texi-noquote'
1170 else: # format == 'html'
1171 s = 'output-html'
1173 def html_pages (basename):
1174 pat = os.path.join (g_outdir, "%s-page*.png"% basename)
1176 files = glob.glob (pat)
1179 template = '''<img align="center" valign="center"
1180 border="0" src="%s" alt="[picture of music]">'''
1182 str = ''
1183 if files == []:
1184 files = [basename+'.png' ]
1185 else:
1186 files = map (os.path.basename, files)
1188 for f in files:
1189 str += template % f
1191 str = '<a href="%s.ly">%s</a>' % (basename, str)
1193 return str
1196 newbody = newbody + get_output (s) % {'fn': basename,
1197 'htmlimages': html_pages(basename)
1200 if filename_chunk:
1201 return_chunks += [filename_chunk]
1203 return_chunks += [('lilypond', newbody, opts, todo, basename)]
1205 return return_chunks
1207 def format_lilypond_output_bodies (chunks):
1208 newchunks = []
1209 for c in chunks:
1211 if c[0] == 'lilypond':
1212 newchunks += format_lilypond_block (c)
1213 else:
1214 newchunks.append (c)
1216 return newchunks
1220 def process_lilypond_blocks (chunks):#ugh rename
1221 newchunks = []
1222 # Count sections/chapters.
1223 for c in chunks:
1224 if c[0] == 'lilypond':
1225 c = schedule_lilypond_block (c)
1226 elif c[0] == 'numcols':
1227 paperguru.m_num_cols = c[2]
1228 elif c[0] == 'multicols':
1229 paperguru.m_multicols = c[2]
1231 newchunks.append (c)
1233 return newchunks
1235 def process_ly2dvi_blocks (chunks):
1237 def process_ly2dvi_block (chunk):
1240 Run ly2dvi script on filename specified in CHUNK.
1241 This is only supported for HTML output.
1243 In HTML output it will leave a download menu with ps/pdf/midi etc. in
1244 a separate HTML file, and a title + preview in the main html file,
1245 linking to the menu.
1248 (tag, name, opts) = chunk
1249 assert format == 'html'
1250 (content, original_name) = find_file (name)
1252 original_name = os.path.basename (original_name)
1254 base = unique_file_name (content)
1255 outname = base + '.ly'
1256 changed = update_file (content, outname)
1258 preview = base + ".preview.png"
1259 preview_page = base + '-page1.png'
1261 if changed or not (os.path.isfile (preview) or
1262 os.path.isfile (preview_page)):
1264 ly.system ('%s --preview --postscript --verbose %s ' % (ly2dvi_binary, base) )
1266 ly.make_ps_images (base + '.ps')
1267 ly.system ('gzip -9 - < %s.ps > %s.ps.gz' % (base, base))
1269 def size_str (fn):
1270 b = os.stat(fn)[stat.ST_SIZE]
1271 if b < 1024:
1272 return '%d bytes' % b
1273 elif b < (2 << 20):
1274 return '%d kb' % (b >> 10)
1275 else:
1276 return '%d mb' % (b >> 20)
1278 exts = {
1279 'pdf' : "Print (PDF, %s)",
1280 'ps.gz' : "Print (gzipped PostScript, %s)",
1281 'png' : "View (PNG, %s)",
1282 'midi' : "Listen (MIDI, %s)",
1283 'ly' : "View source code (%s)",
1286 menu = ''
1287 page_files = glob.glob ('%s-page*.png' % base)
1289 for p in page_files:
1290 p = p.strip()
1291 if os.path.isfile (p):
1292 sz = size_str (p)
1293 page = re.sub ('.*page([0-9])+.*', 'View page \\1 (PNG picture, %s)\n', p)
1294 page = page % sz
1295 menu += '<li><a href="%s">%s</a>' % (p, page)
1297 ext_order = ['ly', 'pdf', 'ps.gz', 'midi']
1298 for e in ext_order:
1299 fn = base + '.' + e
1300 print 'checking,' , fn
1301 if not os.path.isfile (fn):
1302 continue
1304 entry = exts[e] % size_str (fn)
1306 ## TODO: do something like
1307 ## this for texinfo/latex as well ?
1309 menu += '<li><a href="%s">%s</a>\n\n' % (fn, entry)
1312 explanatory_para = """The pictures are 90dpi
1313 anti-aliased snapshots of the printed output, in PNG format. Both PDF and PS
1314 use scalable fonts and should look OK at any resolution."""
1316 separate_menu =r'''
1317 <title>LilyPond example %s</title>
1319 <h1>%s</h1>
1320 <p><img src="%s">
1321 <p>%s
1323 <ul>%s</ul>''' % (original_name,original_name, preview, explanatory_para, menu)
1325 open (base + '.html','w'). write (separate_menu)
1327 inline_menu = '<p/><a href="%s.html"><img alt="%s" src="%s"></a><p/>' % (base, original_name, preview)
1329 return ('ly2dvi', inline_menu)
1331 newchunks = []
1332 for c in chunks:
1333 if c[0] == 'ly2dvi':
1334 c = process_ly2dvi_block (c)
1335 newchunks.append (c)
1337 return newchunks
1339 def compile_all_files (chunks):
1340 global foutn
1341 eps = []
1342 tex = []
1343 png = []
1345 for c in chunks:
1346 if c[0] != 'lilypond':
1347 continue
1349 base = c[4]
1350 exts = c[3]
1351 for e in exts:
1352 if e == 'eps':
1353 eps.append (base)
1354 elif e == 'tex':
1355 #ugh
1356 if base + '.ly' not in tex:
1357 tex.append (base + '.ly')
1358 elif e == 'png' and g_do_pictures:
1359 png.append (base)
1360 d = os.getcwd ()
1361 if g_outdir:
1362 os.chdir (g_outdir)
1363 if tex:
1364 # fixme: be sys-independent.
1365 def incl_opt (x):
1366 if g_outdir and x[0] != '/' :
1367 x = os.path.join (g_here_dir, x)
1368 return ' -I %s' % x
1370 incs = map (incl_opt, include_path)
1371 lilyopts = string.join (incs)
1372 if do_deps:
1373 lilyopts += ' --dependencies'
1374 if g_outdir:
1375 lilyopts += ' --dep-prefix=' + g_outdir + '/'
1376 lilyopts += ' --header=texidoc'
1377 texfiles = string.join (tex)
1378 cmd = string.join ((lilypond_binary, lilyopts, g_extra_opts,
1379 texfiles))
1381 ly.lilypond_version_check (lilypond_binary, '@TOPLEVEL_VERSION@')
1383 ly.system (cmd, ignore_error = 0, progress_p = 1)
1386 # Ugh, fixing up dependencies for .tex generation
1388 if do_deps:
1389 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep',
1390 x), tex)
1392 for i in depfiles:
1393 f =open (i)
1394 text=f.read ()
1395 f.close ()
1396 text=re.sub ('\n([^:\n]*):',
1397 '\n' + foutn + ':', text)
1398 f = open (i, 'w')
1399 f.write (text)
1400 f.close ()
1402 def to_eps (file):
1403 cmd = r"latex '\nonstopmode \input %s'" % file
1404 # Ugh. (La)TeX writes progress and error messages on stdout
1405 # Redirect to stderr
1406 cmd = '(( %s >&2 ) >&- )' % cmd
1408 ly.system (cmd)
1409 ly.system ("dvips -E -o %s.eps %s" % (file, file))
1410 map (to_eps, eps)
1412 map (ly.make_ps_images, map (lambda x: x + '.eps', png))
1413 os.chdir (d)
1416 def update_file (body, name):
1418 write the body if it has changed. Return whether BODY has changed.
1420 same = 0
1421 try:
1422 f = open (name)
1423 fs = f.read (-1)
1424 same = (fs == body)
1425 except:
1426 pass
1428 if not same:
1429 f = open (name , 'w')
1430 f.write (body)
1431 f.close ()
1433 return not same
1436 def write_deps (fn, target, chunks):
1437 global read_files
1438 sys.stderr.write ('Writing `%s\'\n' % os.path.join (g_outdir, fn))
1439 f = open (os.path.join (g_outdir, fn), 'w')
1440 f.write ('%s%s: ' % (g_dep_prefix, target))
1441 for d in read_files:
1442 f.write ('%s ' % d)
1445 ## There used to be code to write .tex dependencies, but
1446 ## that is silly: lilypond-book has its own dependency scheme
1447 ## to ensure that all lily-XXX.tex files are there
1450 f.write ('\n')
1451 f.close ()
1452 read_files = []
1454 def check_texidoc (chunks):
1455 ## TODO: put file name in front of texidoc.
1457 n = []
1458 for c in chunks:
1459 if c[0] == 'lilypond':
1460 (type, body, opts, todo, basename) = c;
1461 pathbase = os.path.join (g_outdir, basename)
1462 if os.path.isfile (pathbase + '.texidoc') \
1463 and 'notexidoc' not in opts:
1464 n.append( ('input', '\n@include %s.texidoc\n\n' % basename))
1465 n.append (c)
1466 return n
1469 ## what's this? Docme --hwn
1471 def fix_epswidth (chunks):
1472 newchunks = []
1473 for c in chunks:
1474 if c[0] != 'lilypond' or 'eps' not in c[2]:
1475 newchunks.append (c)
1476 continue
1478 mag = 1.0
1479 for o in c[2]:
1480 m = re.match ('magnification=([0-9.]+)', o)
1481 if m:
1482 mag = string.atof (m.group (1))
1484 def replace_eps_dim (match, lmag = mag):
1485 filename = match.group (1)
1486 dims = bounding_box_dimensions (filename)
1488 return '%fpt' % (dims[0] *lmag)
1490 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1491 newchunks.append (('lilypond', body, c[2], c[3], c[4]))
1493 return newchunks
1496 ##docme: why global?
1497 foutn=""
1499 def do_file (input_filename):
1500 chunks = read_doc_file (input_filename)
1501 chunks = chop_chunks (chunks, 'ly2dvi', make_ly2dvi_block, 1)
1502 chunks = chop_chunks (chunks, 'lilypond', make_lilypond, 1)
1503 chunks = chop_chunks (chunks, 'lilypond-file', make_lilypond_file, 1)
1504 chunks = chop_chunks (chunks, 'lilypond-block', make_lilypond_block, 1)
1505 chunks = chop_chunks (chunks, 'singleline-comment', do_ignore, 1)
1506 chunks = chop_chunks (chunks, 'preamble-end', do_preamble_end)
1507 chunks = chop_chunks (chunks, 'numcols', do_columns)
1508 chunks = chop_chunks (chunks, 'multicols', do_multicols)
1510 scan_preamble (chunks)
1511 chunks = process_lilypond_blocks (chunks)
1512 chunks = process_ly2dvi_blocks (chunks)
1514 # Do It.
1515 global g_run_lilypond
1516 if g_run_lilypond:
1517 compile_all_files (chunks)
1518 chunks = fix_epswidth (chunks)
1521 chunks = format_lilypond_output_bodies (chunks)
1522 global format
1523 if format == 'texi':
1524 chunks = check_texidoc (chunks)
1527 x = 0
1528 chunks = completize_preamble (chunks)
1530 global foutn
1532 if outname:
1533 my_outname = outname
1534 elif input_filename == '-' or input_filename == "/dev/stdin":
1535 my_outname = '-'
1536 else:
1537 my_outname = os.path.basename (os.path.splitext (input_filename)[0]) + '.' + format
1538 my_depname = my_outname + '.dep'
1540 if my_outname == '-' or my_outname == '/dev/stdout':
1541 fout = sys.stdout
1542 foutn = "<stdout>"
1543 global do_deps
1544 do_deps = 0
1545 else:
1546 foutn = os.path.join (g_outdir, my_outname)
1547 sys.stderr.write ("Writing `%s'\n" % foutn)
1548 fout = open (foutn, 'w')
1549 for c in chunks:
1550 fout.write (c[1])
1551 fout.close ()
1552 # should chmod -w
1554 if do_deps:
1555 write_deps (my_depname, foutn, chunks)
1557 outname = ''
1558 try:
1559 (sh, long) = ly.getopt_args (option_definitions)
1560 (options, files) = getopt.getopt (sys.argv[1:], sh, long)
1562 except getopt.error, msg:
1563 sys.stderr.write ('\n')
1564 ly.error (_ ("getopt says: `%s\'" % s))
1565 sys.stderr.write ('\n')
1566 ly.help ()
1567 ly.exit (2)
1569 do_deps = 0
1570 for opt in options:
1571 o = opt[0]
1572 a = opt[1]
1574 if o == '--include' or o == '-I':
1575 include_path.append (a)
1576 elif o == '--version' or o == '-v':
1577 ly.identify (sys.stdout)
1578 sys.exit (0)
1579 elif o == '--verbose' or o == '-V':
1580 verbose_p = 1
1581 elif o == '--format' or o == '-f':
1582 format = a
1583 if a == 'texi-html':
1584 format = 'texi'
1585 g_make_html = 1
1586 elif o == '--outname' or o == '-o':
1587 if len (files) > 1:
1588 #HACK
1589 sys.stderr.write ("lilypond-book is confused by --outname on multiple files")
1590 sys.exit (1)
1591 outname = a
1592 elif o == '--help' or o == '-h':
1593 ly.help ()
1594 sys.exit (0)
1595 elif o == '--no-lily' or o == '-n':
1596 g_run_lilypond = 0
1597 elif o == '--preview-resolution':
1598 preview_resolution = string.atoi (a)
1599 elif o == '--dependencies' or o == '-M':
1600 do_deps = 1
1601 elif o == '--default-music-fontsize':
1602 default_music_fontsize = string.atoi (a)
1603 elif o == '--default-lilypond-fontsize':
1604 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1605 default_music_fontsize = string.atoi (a)
1606 elif o == '--extra-options':
1607 g_extra_opts = a
1608 elif o == '--force-music-fontsize':
1609 g_force_music_fontsize = string.atoi (a)
1610 elif o == '--force-lilypond-fontsize':
1611 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1612 g_force_music_fontsize = string.atoi (a)
1613 elif o == '--dep-prefix':
1614 g_dep_prefix = a
1615 elif o == '--no-pictures':
1616 g_do_pictures = 0
1617 elif o == '--no-music':
1618 g_do_music = 0
1619 elif o == '--outdir':
1620 g_outdir = a
1621 elif o == '--warranty' or o == '-w':
1622 #status = os.system ('lilypond -w')
1623 if 1 or status:
1624 ly.warranty ()
1625 sys.exit (0)
1627 ly.identify (sys.stderr)
1629 if g_outdir:
1630 if os.path.isfile (g_outdir):
1631 error ("outdir is a file: %s" % g_outdir)
1632 if not os.path.exists (g_outdir):
1633 os.mkdir (g_outdir)
1635 if not files:
1636 ly.help ()
1637 ly.error (_ ("no files specified on command line"))
1638 ly.exit (2)
1640 ly.setup_environment ()
1643 for input_filename in files:
1644 do_file (input_filename)
1648 # Petr, ik zou willen dat ik iets zinvoller deed,
1649 # maar wat ik kan ik doen, het verandert toch niets?
1650 # --hwn 20/aug/99