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