]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
4e8a5322532f044ea290b63e2e4f11d22ffa57cb
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3 # Run lilypond, latex, dvips.
4 #
5 # This is the third incarnation of ly2dvi.
6 #
7 # Earlier incarnations of ly2dvi were written by
8 # Jeffrey B. Reed<daboys@austin.rr.com> (Python version)
9 # Jan Arne Fagertun <Jan.A.Fagertun@@energy.sintef.no> (Bourne shell script)
10 #
11
12 #
13 # Note: gettext work best if we use ' for docstrings and "
14 #       for gettextable strings.
15 #       --> DO NOT USE """ for docstrings.
16
17 '''
18 TODO:
19
20   * figure out which set of command line options should make ly2dvi:
21
22       na: create tex only?  
23       na: create latex only? 
24       na: create tex and latex
25       default: create dvi only
26       na: create tex, latex and dvi
27       -P: create dvi and ps
28       na: * create ps only
29
30      etc.
31
32      for foo.ly, rename ly2dvi.dir to out-ly2dvi, foo.ly2dvi, foo.dir ?
33      
34   * move versatile taglines, 
35   
36      \header {
37         beginfooter=\mutopiaPD
38         endfooter=\tagline  -> 'lily was here <version>'
39      }
40
41      lilytagline (->lily was here), usertagline, copyright etc.
42
43   * head/header tagline/endfooter
44
45   * dvi from lilypond .tex output?  This is hairy, because we create dvi
46     from lilypond .tex *and* header output.
47
48   * multiple \score blocks?
49   
50 '''
51
52
53
54
55
56 import os
57 import stat
58 import string
59 import re
60 import getopt
61 import sys
62 import shutil
63 import __main__
64 import operator
65 import tempfile
66 import traceback
67
68
69 # Handle bug in Python 1.6-2.1
70 #
71 # there are recursion limits for some patterns in Python 1.6 til 2.1. 
72 # fix this by importing pre instead. Fix by Mats.
73
74 # todo: should check Python version first.
75 try:
76         import pre
77         re = pre
78         del pre
79 except ImportError:
80         import re
81
82
83 ################################################################
84 # lilylib.py -- options and stuff
85
86 # source file of the GNU LilyPond music typesetter
87
88 try:
89         import gettext
90         gettext.bindtextdomain ('lilypond', localedir)
91         gettext.textdomain ('lilypond')
92         _ = gettext.gettext
93 except:
94         def _ (s):
95                 return s
96
97 program_version = '@TOPLEVEL_VERSION@'
98 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
99         program_version = '1.5.17'
100
101 def identify ():
102         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
103
104 def warranty ():
105         identify ()
106         sys.stdout.write ('\n')
107         sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
108         sys.stdout.write ('\n')
109         sys.stdout.write ('  Han-Wen Nienhuys')
110         sys.stdout.write ('  Jan Nieuwenhuizen')
111         sys.stdout.write ('\n')
112         sys.stdout.write (_ (r'''
113 Distributed under terms of the GNU General Public License. It comes with
114 NO WARRANTY.'''))
115         sys.stdout.write ('\n')
116
117 def progress (s):
118         errorport.write (s + '\n')
119
120 def warning (s):
121         progress (_ ("warning: ") + s)
122
123 def user_error (s, e=1):
124         errorport.write (program_name + ":" + _ ("error: ") + s + '\n')
125         sys.exit (e)
126         
127 def error (s):
128
129
130         '''Report the error S.  Exit by raising an exception. Please
131         do not abuse by trying to catch this error. If you do not want
132         a stack trace, write to the output directly.
133
134         RETURN VALUE
135
136         None
137         
138         '''
139         
140         progress (_ ("error: ") + s)
141         raise _ ("Exiting ... ")
142
143 def getopt_args (opts):
144         '''Construct arguments (LONG, SHORT) for getopt from  list of options.'''
145         short = ''
146         long = []
147         for o in opts:
148                 if o[1]:
149                         short = short + o[1]
150                         if o[0]:
151                                 short = short + ':'
152                 if o[2]:
153                         l = o[2]
154                         if o[0]:
155                                 l = l + '='
156                         long.append (l)
157         return (short, long)
158
159 def option_help_str (o):
160         '''Transform one option description (4-tuple ) into neatly formatted string'''
161         sh = '  '       
162         if o[1]:
163                 sh = '-%s' % o[1]
164
165         sep = ' '
166         if o[1] and o[2]:
167                 sep = ','
168                 
169         long = ''
170         if o[2]:
171                 long= '--%s' % o[2]
172
173         arg = ''
174         if o[0]:
175                 if o[2]:
176                         arg = '='
177                 arg = arg + o[0]
178         return '  ' + sh + sep + long + arg
179
180
181 def options_help_str (opts):
182         '''Convert a list of options into a neatly formatted string'''
183         w = 0
184         strs =[]
185         helps = []
186
187         for o in opts:
188                 s = option_help_str (o)
189                 strs.append ((s, o[3]))
190                 if len (s) > w:
191                         w = len (s)
192
193         str = ''
194         for s in strs:
195                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
196         return str
197
198 def help ():
199         ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
200                 ('\n\n'),
201                 (help_summary),
202                 ('\n\n'),
203                 (_ ("Options:")),
204                 ('\n'),
205                 (options_help_str (option_definitions)),
206                 ('\n\n'),
207                 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
208                 ('\n')]
209         map (sys.stdout.write, ls)
210         
211 def setup_temp ():
212         """
213         Create a temporary directory, and return its name. 
214         """
215         global temp_dir
216         if not keep_temp_dir_p:
217                 temp_dir = tempfile.mktemp (program_name)
218         try:
219                 os.mkdir (temp_dir, 0777)
220         except OSError:
221                 pass
222
223         return temp_dir
224
225
226 def system (cmd, ignore_error = 0):
227         """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
228
229         RETURN VALUE
230
231         Exit status of CMD
232         """
233         
234         if verbose_p:
235                 progress (_ ("Invoking `%s\'") % cmd)
236
237         st = os.system (cmd)
238         if st:
239                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
240                 msg = name + ': ' + _ ("command exited with value %d") % st
241                 if ignore_error:
242                         warning (msg + ' ' + _ ("(ignored)") + ' ')
243                 else:
244                         error (msg)
245
246         return st
247
248
249 def cleanup_temp ():
250         if not keep_temp_dir_p:
251                 if verbose_p:
252                         progress (_ ("Cleaning %s...") % temp_dir)
253                 shutil.rmtree (temp_dir)
254
255
256 def strip_extension (f, ext):
257         (p, e) = os.path.splitext (f)
258         if e == ext:
259                 e = ''
260         return p + e
261
262 ################################################################
263 # END Library
264
265
266
267
268
269
270 # if set, LILYPONDPREFIX must take prevalence
271 # if datadir is not set, we're doing a build and LILYPONDPREFIX 
272 datadir = '@datadir@'
273
274
275 if os.environ.has_key ('LILYPONDPREFIX') :
276         datadir = os.environ['LILYPONDPREFIX']
277 else:
278         datadir = '@datadir@'
279
280
281 while datadir[-1] == os.sep:
282         datadir= datadir[:-1]
283
284 program_name = 'ly2dvi'
285
286 original_dir = os.getcwd ()
287 temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
288 errorport = sys.stderr
289 keep_temp_dir_p = 0
290 verbose_p = 0
291 preview_p = 0
292
293 try:
294         import gettext
295         gettext.bindtextdomain ('lilypond', '@localedir@')
296         gettext.textdomain ('lilypond')
297         _ = gettext.gettext
298 except:
299         def _ (s):
300                 return s
301
302 # Attempt to fix problems with limited stack size set by Python!
303 # Sets unlimited stack size. Note that the resource module only
304 # is available on UNIX.
305 try:
306        import resource
307        resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
308 except:
309        pass
310
311 help_summary = _ ("Generate .dvi with LaTeX for LilyPond")
312
313 option_definitions = [
314         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
315         ('', 'h', 'help', _ ("this help")),
316         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
317         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
318         ('', '', 'no-lily', _ ("don't run LilyPond")),
319         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
320         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
321         (_ ("FILE"), 'f', 'find-pfa', _ ("find pfa fonts used in FILE")),
322         # why capital P?
323         ('', '', 'preview', _("Make a picture of the first system.")),
324         ('', 'P', 'postscript', _ ("generate PostScript output")),
325         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
326         ('', 'V', 'verbose', _ ("verbose")),
327         ('', 'v', 'version', _ ("print version number")),
328         ('', 'w', 'warranty', _ ("show warranty and copyright")),
329         ]
330
331 layout_fields = ['dedication', 'title', 'subtitle', 'subsubtitle',
332           'footer', 'head', 'composer', 'arranger', 'instrument',
333           'opus', 'piece', 'metre', 'meter', 'poet', 'texttranslator']
334
335
336 # init to empty; values here take precedence over values in the file
337
338 ## TODO: change name.
339 extra_init = {
340         'language' : [],
341         'latexheaders' : [],
342         'latexpackages' :  ['geometry'],
343         'latexoptions' : [],
344         'papersize' : [],
345         'pagenumber' : [1],
346         'textheight' : [], 
347         'linewidth' : [],
348         'orientation' : [],
349         'unit' : ['pt'],
350 }
351
352 extra_fields = extra_init.keys ()
353 fields = layout_fields + extra_fields
354
355 include_path = ['.']
356 lily_p = 1
357 paper_p = 1
358
359 output_name = ''
360
361 ## docme: what does this do?
362 targets = [ 'DVI', 'LATEX', 'MIDI', 'TEX']
363
364 track_dependencies_p = 0
365 dependency_files = []
366
367
368
369 kpse = os.popen ('kpsexpand \$TEXMF').read()
370 kpse = re.sub('[ \t\n]+$','', kpse)
371 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
372
373 environment = {
374         ## todo: prevent multiple addition.
375         'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
376         'GS_FONTPATH' : type1_paths,
377         'GS_LIB' : datadir + '/ps',
378 }
379
380 # tex needs lots of memory, more than it gets by default on Debian
381 non_path_environment = {
382         'extra_mem_top' : '1000000',
383         'extra_mem_bottom' : '1000000',
384         'pool_size' : '250000',
385 }
386
387 def setup_environment ():
388         # $TEXMF is special, previous value is already taken care of
389         if os.environ.has_key ('TEXM'):
390                 del os.environ['TEXMF']
391  
392
393         for key in environment.keys ():
394                 val = environment[key]
395                 if os.environ.has_key (key):
396                         val = os.environ[key] + os.pathsep + val 
397                 os.environ[key] = val
398
399         for key in non_path_environment.keys ():
400                 val = non_path_environment[key]
401                 os.environ[key] = val
402
403 #what a name.
404 def set_setting (dict, key, val):
405         try:
406                 val = string.atof (val)
407         except ValueError:
408                 #warning (_ ("invalid value: %s") % `val`)
409                 pass
410
411         try:
412                 dict[key].append (val)
413         except KeyError:
414                 warning (_ ("no such setting: %s") % `key`)
415                 dict[key] = [val]
416
417
418 def print_environment ():
419         for (k,v) in os.environ.items ():
420                 sys.stderr.write ("%s=\"%s\"\n" % (k,v)) 
421
422 def quiet_system (cmd, name):
423         if not verbose_p:
424                 progress ( _("Running %s...") % name)
425                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
426
427         return system (cmd)
428
429
430 def run_lilypond (files, outbase, dep_prefix):
431         opts = ''
432 #       opts = opts + '--output=%s.tex' % outbase
433         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
434                                               include_path))
435         if paper_p:
436                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
437                                                       fields))
438         else:
439                 opts = opts + ' --no-paper'
440                 
441         if track_dependencies_p:
442                 opts = opts + " --dependencies"
443                 if dep_prefix:
444                         opts = opts + ' --dep-prefix=%s' % dep_prefix
445
446         fs = string.join (files)
447
448         if not verbose_p:
449                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
450                 progress ( _("Running %s...") % 'LilyPond')
451         else:
452                 opts = opts + ' --verbose'
453
454                 # for better debugging!
455                 print_environment ()
456         print opts, fs  
457         system ('lilypond %s %s ' % (opts, fs), 'lilypond')
458
459 def analyse_lilypond_output (filename, extra):
460         
461         # urg
462         '''Grep FILENAME for interesting stuff, and
463         put relevant info into EXTRA.'''
464         filename = filename+'.tex'
465         progress (_ ("Analyzing %s...") % filename)
466         s = open (filename).read ()
467
468         # search only the first 10k
469         s = s[:10240]
470         for x in extra_fields:
471                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
472                 if m:
473                         set_setting (extra, x, m.group (1))
474
475 def find_tex_files_for_base (base, extra):
476
477         """
478         Find the \header fields dumped from BASE.
479         """
480         
481         headerfiles = {}
482         for f in layout_fields:
483                 if os.path.exists (base + '.' + f):
484                         headerfiles[f] = base+'.'+f
485
486         if os.path.exists (base  +'.dep'):
487                 dependency_files.append (base + '.dep')
488
489         for f in extra_fields:
490                 if os.path.exists (base + '.' + f):
491                         extra[f].append (open (base + '.' + f).read ())
492         
493         return (base  +'.tex',headerfiles)
494          
495
496 def find_tex_files (files, extra):
497         """
498         Find all .tex files whose prefixes start with some name in FILES. 
499
500         """
501         
502         tfiles = []
503         
504         for f in files:
505                 x = 0
506                 while 1:
507                         fname = os.path.basename (f)
508                         fname = strip_extension (fname, '.ly')
509                         if x:
510                                 fname = fname + '-%d' % x
511
512                         if os.path.exists (fname + '.tex'):
513                                 tfiles.append (find_tex_files_for_base (fname, extra))
514                                 analyse_lilypond_output (fname, extra)
515                         else:
516                                 break
517
518                         x = x + 1
519         if not x:
520                 fstr = string.join (files, ', ')
521                 warning (_ ("no lilypond output found for %s") % fstr)
522         return tfiles
523
524 def one_latex_definition (defn, first):
525         s = '\n'
526         for (k,v) in defn[1].items ():
527                 val = open (v).read ()
528                 if (string.strip (val)):
529                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
530                 else:
531                         s = s + r'''\let\lilypond%s\relax''' % k
532                 s = s + '\n'
533
534         if first:
535                 s = s + '\\def\\mustmakelilypondtitle{}\n'
536         else:
537                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
538                 
539         s = s + '\\input %s\n' % defn[0] # The final \n seems important here. It ensures that the footers and taglines end up on the right page.
540         return s
541
542
543 ly_paper_to_latexpaper =  {
544         'a4' : 'a4paper',
545         'letter' : 'letterpaper', 
546 }
547
548 #TODO: should set textheight (enlarge) depending on papersize. 
549 def global_latex_preamble (extra):
550         '''construct preamble from EXTRA,'''
551         s = ""
552         s = s + '% generation tag\n'
553
554         options = ''
555
556
557         if extra['papersize']:
558                 try:
559                         options = ly_paper_to_latexpaper[extra['papersize'][0]]
560                 except KeyError:
561                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
562                         pass
563
564         if extra['latexoptions']:
565                 options = options + ',' + extra['latexoptions'][-1]
566
567         s = s + '\\documentclass[%s]{article}\n' % options
568
569         if extra['language']:
570                 s = s + r'\usepackage[%s]{babel}\n' % extra['language'][-1]
571
572
573         s = s + '\\usepackage{%s}\n' \
574                 % string.join (extra['latexpackages'], ',')
575
576         if extra['latexheaders']:
577                 s = s + '\\include{%s}\n' \
578                         % string.join (extra['latexheaders'], '}\n\\include{')
579
580         unit = extra['unit'][-1]
581
582         textheight = ''
583         if extra['textheight']:
584                 textheight = ',textheight=%f%s' % (extra['textheight'][0], unit)
585
586         orientation = 'portrait'
587         if extra['orientation']:
588                 orientation = extra['orientation'][0]
589
590         # set sane geometry width (a4-width) for linewidth = -1.
591         maxlw = max (extra['linewidth'] + [-1])
592         if maxlw < 0:
593                 # who the hell is 597 ?
594                 linewidth = '597pt'
595         else:
596                 linewidth = '%d%s' % (maxlw, unit)
597         s = s + '\geometry{width=%s%s,headheight=2mm,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
598
599         if extra['latexoptions']:
600                 s = s + '\geometry{twosideshift=4mm}\n'
601
602         s = s + r'''
603 \usepackage[latin1]{inputenc}
604 \input{titledefs}
605 '''
606         
607         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
608                 s = s + '\setcounter{page}{%s}\n' % (extra['pagenumber'][-1])
609                 s = s + '\\pagestyle{plain}\n'
610         else:
611                 s = s + '\\pagestyle{empty}\n'
612
613
614         return s
615
616         
617 def global_latex_definition (tfiles, extra):
618         '''construct preamble from EXTRA, dump Latex stuff for each
619 lily output file in TFILES after that, and return the Latex file constructed.  '''
620
621         
622         s = global_latex_preamble (extra) + '\\begin{document}\n'
623         s = s + '\\thispagestyle{firstpage}\n'
624
625         first = 1
626         for t in tfiles:
627                 s = s + one_latex_definition (t, first)
628                 first = 0
629
630
631         s = s + '\\thispagestyle{lastpage}\n'
632         s = s + '\\end{document}'
633
634         return s
635
636 def run_latex (files, outbase, extra):
637
638         """Construct latex file, for FILES and EXTRA, dump it into
639 OUTBASE.latex. Run LaTeX on it.
640
641 RETURN VALUE
642
643 None
644         """
645         latex_fn = outbase + '.latex'
646         
647         wfs = find_tex_files (files, extra)
648         s = global_latex_definition (wfs, extra)
649
650         f = open (latex_fn, 'w')
651         f.write (s)
652         f.close ()
653
654         cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
655         quiet_system (cmd, 'LaTeX')
656
657         if preview_p:
658                 # make a preview by rendering only the 1st line.
659                 preview_fn = outbase + '.preview.tex'
660                 f = open(preview_fn, 'w')
661                 f.write (r'''
662 %s
663 \input lilyponddefs
664 \pagestyle{empty}
665 \begin{document}
666 \def\interscoreline{\endinput}
667 \input %s
668 \end{document}
669 ''' % (global_latex_preamble (extra), outbase))
670
671                 f.close()
672                 cmd = 'latex \\\\nonstopmode \\\\input %s' % preview_fn
673                 quiet_system (cmd, "LaTeX for preview")
674         
675
676 def run_dvips (outbase, extra):
677
678
679         """Run dvips using the correct options taken from EXTRA,
680 leaving a PS file in OUTBASE.ps
681
682 RETURN VALUE
683
684 None.
685 """
686         opts = ''
687         if extra['papersize']:
688                 opts = opts + ' -t%s' % extra['papersize'][0]
689
690         if extra['orientation'] and extra['orientation'][0] == 'landscape':
691                 opts = opts + ' -tlandscape'
692
693         cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
694         quiet_system (cmd, 'dvips')
695
696         if preview_p:
697                 cmd = 'dvips -E -o%s %s' % ( outbase + '.preview.ps', outbase + '.preview.dvi')         
698                 quiet_system (cmd, 'dvips for preview')
699
700 def get_bbox (filename):
701         # cut & paste 
702         system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
703
704         box = open (filename + '.bbox').read()
705         m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
706         gr = []
707         if m:
708                 gr = map (string.atoi, m.groups ())
709         
710         return gr
711
712 #
713 # cut & paste from lilypond-book.
714 #
715 def make_preview (name, extra):
716         bbox = get_bbox (name + '.preview.ps')
717         margin = 0
718         fo = open (name + '.trans.eps' , 'w')
719         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
720         fo.close ()
721         
722         res = 90
723
724         x = (2* margin + bbox[2] - bbox[0]) * res / 72.
725         y = (2* margin + bbox[3] - bbox[1]) * res / 72.
726
727         cmd = r'''gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s'''
728         
729         cmd = cmd % (x, y, res, name + '.trans.eps', name + '.preview.ps',name + '.png')
730         quiet_system (cmd, 'gs')
731
732         try:
733                 status = system (cmd)
734         except:
735                 os.unlink (name + '.png')
736                 error ("Removing output file")
737
738
739
740 def generate_dependency_file (depfile, outname):
741         df = open (depfile, 'w')
742         df.write (outname + ':' )
743         
744         for d in dependency_files:
745                 s = open (d).read ()
746                 s = re.sub ('#[^\n]*\n', '', s)
747                 s = re.sub (r'\\\n', ' ', s)
748                 m = re.search ('.*:(.*)\n', s)
749
750                 # ugh. Different targets?
751                 if m:
752                         df.write ( m.group (1)  + ' ' )
753
754         df.write ('\n')
755         df.close ();
756
757 def find_file_in_path (path, name):
758         for d in string.split (path, os.pathsep):
759                 if name in os.listdir (d):
760                         return os.path.join (d, name)
761
762 # Added as functionality to ly2dvi, because ly2dvi may well need to do this
763 # in future too.
764 PS = '%!PS-Adobe'
765 def find_pfa_fonts (name):
766         s = open (name).read ()
767         if s[:len (PS)] != PS:
768                 # no ps header?
769                 errorport.write (_( "error: ") + _ ("not a PostScript file: `%s\'" % name))
770                 errorport.write ('\n')
771                 sys.exit (1)
772         here = 0
773         m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
774         pfa = []
775         while m:
776                 here = m.end (1)
777                 pfa.append (m.group (1))
778                 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
779         return pfa
780
781         
782 (sh, long) = getopt_args (option_definitions)
783 try:
784         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
785 except getopt.error, s:
786         errorport.write ('\n')
787         errorport.write (_ ("error: ") + _ ("getopt says: `%s\'" % s))
788         errorport.write ('\n')
789         errorport.write ('\n')
790         help ()
791         sys.exit (2)
792         
793 for opt in options:     
794         o = opt[0]
795         a = opt[1]
796
797         if 0:
798                 pass
799         elif o == '--help' or o == '-h':
800                 help ()
801                 sys.exit (0)
802         elif o == '--find-pfa' or o == '-f':
803                 fonts = map (lambda x: x + '.pfa', find_pfa_fonts (a))
804                 files = map (lambda x:
805                              find_file_in_path (os.environ['GS_FONTPATH'], x),
806                              fonts)
807                 print string.join (files, ' ')
808                 sys.exit (0)
809         elif o == '--include' or o == '-I':
810                 include_path.append (a)
811         elif o == '--postscript' or o == '-P':
812                 targets.append ('PS')
813         elif o == '--keep' or o == '-k':
814                 keep_temp_dir_p = 1
815         elif o == '--no-lily':
816                 lily_p = 0
817         elif o == '--preview':
818                 preview_p = 1
819                 targets.append ('PNG')
820                 
821         elif o == '--no-paper' or o == '-m':
822
823                 targets = ['MIDI'] 
824                 paper_p = 0
825         elif o == '--output' or o == '-o':
826                 output_name = a
827         elif o == '--set' or o == '-s':
828                 ss = string.split (a, '=')
829                 set_setting (extra_init, ss[0], ss[1])
830         elif o == '--dependencies' or o == '-d':
831                 track_dependencies_p = 1
832         elif o == '--verbose' or o == '-V':
833                 verbose_p = 1
834         elif o == '--version' or o == '-v':
835                 identify ()
836                 sys.exit (0)
837         elif o == '--warranty' or o == '-w':
838                 status = system ('lilypond -w', ignore_error = 1)
839                 if status:
840                         warranty ()
841
842                 sys.exit (0)
843
844
845 def cp_to_dir (pattern, dir):
846         "Copy files matching re PATTERN from cwd to DIR"
847         # Duh.  Python style portable: cp *.EXT OUTDIR
848         # system ('cp *.%s %s' % (ext, outdir), 1)
849         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
850         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
851
852 # Python < 1.5.2 compatibility
853 #
854 # On most platforms, this is equivalent to
855 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
856 if os.path.__dict__.has_key ('abspath'):
857         abspath = os.path.abspath
858 else:
859         def abspath (path):
860                 return os.path.normpath (os.path.join (os.getcwd (), path))
861
862 if os.__dict__.has_key ('makedirs'):
863         makedirs = os.makedirs
864 else:
865         def makedirs (dir, mode=0777):
866                 system ('mkdir -p %s' % dir)
867
868 def mkdir_p (dir, mode=0777):
869         if not os.path.isdir (dir):
870                 makedirs (dir, mode)
871
872 include_path = map (abspath, include_path)
873
874 original_output = output_name
875
876
877 if files and files[0] != '-':
878         
879         # Ugh, maybe make a setup () function
880         files = map (lambda x: strip_extension (x, '.ly'), files)
881
882         # hmmm. Wish I'd 've written comments when I wrote this.
883         # now it looks complicated.
884         
885         (outdir, outbase) = ('','')
886         if not output_name:
887                 outbase = os.path.basename (files[0])
888                 outdir = abspath('.')
889         elif output_name[-1] == os.sep:
890                 outdir = abspath (output_name)
891                 outbase = os.path.basename (files[0])
892         else:
893                 (outdir, outbase) = os.path.split (abspath (output_name))
894
895         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
896                 output_name = strip_extension (output_name, i)
897                 outbase = strip_extension (outbase, i)
898         files = map (abspath, files)
899
900         for i in files[:] + [output_name]:
901                 if string.find (i, ' ') >= 0:
902                         user_error (_ ("filename should not contain spaces: `%s'") % i)
903                         
904         if os.path.dirname (output_name) != '.':
905                 dep_prefix = os.path.dirname (output_name)
906         else:
907                 dep_prefix = 0
908
909         reldir = os.path.dirname (output_name)
910         if outdir != '.' and (track_dependencies_p or targets):
911                 mkdir_p (outdir, 0777)
912
913         setup_environment ()
914         tmpdir = setup_temp ()
915
916         # to be sure, add tmpdir *in front* of inclusion path.
917         #os.environ['TEXINPUTS'] =  tmpdir + ':' + os.environ['TEXINPUTS']
918         os.chdir (tmpdir)
919         
920         if lily_p:
921                 try:
922                         run_lilypond (files, outbase, dep_prefix)
923                 except:
924                         # TODO: friendly message about LilyPond setup/failing?
925                         #
926                         # TODO: lilypond should fail with different
927                         # error codes for:
928                         #   - guile setup/startup failure
929                         #   - font setup failure
930                         #   - init.ly setup failure
931                         #   - parse error in .ly
932                         #   - unexpected: assert/core dump
933                         targets = []
934                         traceback.print_exc ()
935
936         if 'PNG' in targets and 'PS' not in targets:
937                 targets.append ('PS')
938         if 'PS' in targets and 'DVI' not in targets:
939                 targets.append('DVI')
940
941         if 'DVI' in targets:
942                 try:
943                         run_latex (files, outbase, extra_init)
944                         # unless: add --tex, or --latex?
945                         targets.remove ('TEX')
946                         targets.remove('LATEX')
947                 except:
948                         # TODO: friendly message about TeX/LaTeX setup,
949                         # trying to run tex/latex by hand
950                         if 'DVI' in targets:
951                                 targets.remove ('DVI')
952                         if 'PS' in targets:
953                                 targets.remove ('PS')
954                         traceback.print_exc ()
955
956         if 'PS' in targets:
957                 try:
958                         run_dvips (outbase, extra_init)
959                 except: 
960                         if 'PS' in targets:
961                                 targets.remove ('PS')
962                         traceback.print_exc ()
963
964         if 'PNG' in  targets:
965                 make_preview (outbase, extra_init)
966                 
967         # add DEP to targets?
968         if track_dependencies_p:
969                 depfile = os.path.join (outdir, outbase + '.dep')
970                 generate_dependency_file (depfile, depfile)
971                 if os.path.isfile (depfile):
972                         progress (_ ("dependencies output to `%s'...") % depfile)
973
974         # Hmm, if this were a function, we could call it the except: clauses
975         for i in targets:
976                 ext = string.lower (i)
977                 cp_to_dir ('.*\.%s$' % ext, outdir)
978                 outname = outbase + '.' + string.lower (i)
979                 abs = os.path.join (outdir, outname)
980                 if reldir != '.':
981                         outname = os.path.join (reldir, outname)
982                 if os.path.isfile (abs):
983                         progress (_ ("%s output to `%s'...") % (i, outname))
984                 elif verbose_p:
985                         warning (_ ("can't find file: `%s'") % outname)
986                         
987         os.chdir (original_dir)
988         cleanup_temp ()
989         
990 else:
991         # FIXME: read from stdin when files[0] = '-'
992         help ()
993         user_error (_ ("no files specified on command line."), 2)
994
995
996