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