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