]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
*** empty log message ***
[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         st = os.system (cmd)
237         if st:
238                 name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
239                 msg = name + ': ' + _ ("command exited with value %d") % st
240                 if ignore_error:
241                         warning (msg + ' ' + _ ("(ignored)") + ' ')
242                 else:
243                         error (msg)
244
245         return st
246
247
248 def cleanup_temp ():
249         if not keep_temp_dir_p:
250                 if verbose_p:
251                         progress (_ ("Cleaning %s...") % temp_dir)
252                 shutil.rmtree (temp_dir)
253
254
255 def strip_extension (f, ext):
256         (p, e) = os.path.splitext (f)
257         if e == ext:
258                 e = ''
259         return p + e
260
261 ################################################################
262 # END Library
263
264
265
266
267
268
269 # if set, LILYPONDPREFIX must take prevalence
270 # if datadir is not set, we're doing a build and LILYPONDPREFIX 
271 datadir = '@datadir@'
272
273
274 if os.environ.has_key ('LILYPONDPREFIX') :
275         datadir = os.environ['LILYPONDPREFIX']
276 else:
277         datadir = '@datadir@'
278
279
280 while datadir[-1] == os.sep:
281         datadir= datadir[:-1]
282
283 program_name = 'ly2dvi'
284
285 original_dir = os.getcwd ()
286 temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
287 errorport = sys.stderr
288 keep_temp_dir_p = 0
289 verbose_p = 0
290
291 try:
292         import gettext
293         gettext.bindtextdomain ('lilypond', '@localedir@')
294         gettext.textdomain ('lilypond')
295         _ = gettext.gettext
296 except:
297         def _ (s):
298                 return s
299
300 # Attempt to fix problems with limited stack size set by Python!
301 # Sets unlimited stack size. Note that the resource module only
302 # is available on UNIX.
303 try:
304        import resource
305        resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
306 except:
307        pass
308
309 help_summary = _ ("Generate .dvi with LaTeX for LilyPond")
310
311 option_definitions = [
312         ('', 'd', 'dependencies', _ ("write Makefile dependencies for every input file")),
313         ('', 'h', 'help', _ ("this help")),
314         (_ ("DIR"), 'I', 'include', _ ("add DIR to LilyPond's search path")),
315         ('', 'k', 'keep', _ ("keep all output, and name the directory %s.dir") % program_name),
316         ('', '', 'no-lily', _ ("don't run LilyPond")),
317         ('', 'm', 'no-paper', _ ("produce MIDI output only")),
318         (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
319         (_ ("FILE"), 'f', 'find-pfa', _ ("find pfa fonts used in FILE")),
320         # why capital P?
321         ('', 'P', 'postscript', _ ("generate PostScript output")),
322         (_ ("KEY=VAL"), 's', 'set', _ ("change global setting KEY to VAL")),
323         ('', 'V', 'verbose', _ ("verbose")),
324         ('', 'v', 'version', _ ("print version number")),
325         ('', 'w', 'warranty', _ ("show warranty and copyright")),
326         ]
327
328 layout_fields = ['dedication', 'title', 'subtitle', 'subsubtitle',
329           'footer', 'head', 'composer', 'arranger', 'instrument',
330           'opus', 'piece', 'metre', 'meter', 'poet', 'texttranslator']
331
332
333 # init to empty; values here take precedence over values in the file
334
335 ## TODO: change name.
336 extra_init = {
337         'language' : [],
338         'latexheaders' : [],
339         'latexpackages' :  ['geometry'],
340         'latexoptions' : [],
341         'papersize' : [],
342         'pagenumber' : [1],
343         'textheight' : [], 
344         'linewidth' : [],
345         'orientation' : [],
346         'unit' : ['pt'],
347 }
348
349 extra_fields = extra_init.keys ()
350 fields = layout_fields + extra_fields
351
352 include_path = ['.']
353 lily_p = 1
354 paper_p = 1
355
356 output_name = ''
357 targets = {
358         'DVI' : 0,
359         'LATEX' : 0,
360         'MIDI' : 0,
361         'TEX' : 0,
362         }
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 run_lilypond (files, outbase, dep_prefix):
423         opts = ''
424 #       opts = opts + '--output=%s.tex' % outbase
425         opts = opts + ' ' + string.join (map (lambda x : '-I ' + x,
426                                               include_path))
427         if paper_p:
428                 opts = opts + ' ' + string.join (map (lambda x : '-H ' + x,
429                                                       fields))
430         else:
431                 opts = opts + ' --no-paper'
432                 
433         if track_dependencies_p:
434                 opts = opts + " --dependencies"
435                 if dep_prefix:
436                         opts = opts + ' --dep-prefix=%s' % dep_prefix
437
438         fs = string.join (files)
439
440         if not verbose_p:
441                 # cmd = cmd + ' 1> /dev/null 2> /dev/null'
442                 progress ( _("Running %s...") % 'LilyPond')
443         else:
444                 opts = opts + ' --verbose'
445
446                 # for better debugging!
447                 print_environment ()
448         print opts, fs  
449         system ('lilypond %s %s ' % (opts, fs))
450
451 def analyse_lilypond_output (filename, extra):
452         
453         # urg
454         '''Grep FILENAME for interesting stuff, and
455         put relevant info into EXTRA.'''
456         filename = filename+'.tex'
457         progress (_ ("Analyzing %s...") % filename)
458         s = open (filename).read ()
459
460         # search only the first 10k
461         s = s[:10240]
462         for x in extra_fields:
463                 m = re.search (r'\\def\\lilypondpaper%s{([^}]*)}'%x, s)
464                 if m:
465                         set_setting (extra, x, m.group (1))
466
467 def find_tex_files_for_base (base, extra):
468
469         """
470         Find the \header fields dumped from BASE.
471         """
472         
473         headerfiles = {}
474         for f in layout_fields:
475                 if os.path.exists (base + '.' + f):
476                         headerfiles[f] = base+'.'+f
477
478         if os.path.exists (base  +'.dep'):
479                 dependency_files.append (base + '.dep')
480
481         for f in extra_fields:
482                 if os.path.exists (base + '.' + f):
483                         extra[f].append (open (base + '.' + f).read ())
484         
485         return (base  +'.tex',headerfiles)
486          
487
488 def find_tex_files (files, extra):
489         """
490         Find all .tex files whose prefixes start with some name in FILES. 
491
492         """
493         
494         tfiles = []
495         
496         for f in files:
497                 x = 0
498                 while 1:
499                         fname = os.path.basename (f)
500                         fname = strip_extension (fname, '.ly')
501                         if x:
502                                 fname = fname + '-%d' % x
503
504                         if os.path.exists (fname + '.tex'):
505                                 tfiles.append (find_tex_files_for_base (fname, extra))
506                                 analyse_lilypond_output (fname, extra)
507                         else:
508                                 break
509
510                         x = x + 1
511         if not x:
512                 fstr = string.join (files, ', ')
513                 warning (_ ("no lilypond output found for %s") % fstr)
514         return tfiles
515
516 def one_latex_definition (defn, first):
517         s = '\n'
518         for (k,v) in defn[1].items ():
519                 val = open (v).read ()
520                 if (string.strip (val)):
521                         s = s + r'''\def\lilypond%s{%s}''' % (k, val)
522                 else:
523                         s = s + r'''\let\lilypond%s\relax''' % k
524                 s = s + '\n'
525
526         if first:
527                 s = s + '\\def\\mustmakelilypondtitle{}\n'
528         else:
529                 s = s + '\\def\\mustmakelilypondpiecetitle{}\n'
530                 
531         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.
532         return s
533
534
535 ly_paper_to_latexpaper =  {
536         'a4' : 'a4paper',
537         'letter' : 'letterpaper', 
538 }
539
540 def global_latex_definition (tfiles, extra):
541
542         '''construct preamble from EXTRA, dump Latex stuff for each
543 lily output file in TFILES after that, and return the Latex file constructed.  '''
544
545
546         s = ""
547         s = s + '% generation tag\n'
548
549         options = ''
550
551         if extra['papersize']:
552                 try:
553                         options = '%s' % ly_paper_to_latexpaper[extra['papersize'][0]]
554                 except KeyError:
555                         warning (_ ("invalid value: %s") % `extra['papersize'][0]`)
556                         pass
557
558         if extra['latexoptions']:
559                 options = options + ',' + extra['latexoptions'][-1]
560
561         s = s + '\\documentclass[%s]{article}\n' % options
562
563         if extra['language']:
564                 s = s + r'\usepackage[%s]{babel}\n' % extra['language'][-1]
565
566
567         s = s + '\\usepackage{%s}\n' \
568                 % string.join (extra['latexpackages'], ',')
569
570         if extra['latexheaders']:
571                 s = s + '\\include{%s}\n' \
572                         % string.join (extra['latexheaders'], '}\n\\include{')
573
574         unit = extra['unit'][-1]
575
576         textheight = ''
577         if extra['textheight']:
578                 textheight = ',textheight=%f%s' % (extra['textheight'][0], unit)
579
580         orientation = 'portrait'
581         if extra['orientation']:
582                 orientation = extra['orientation'][0]
583
584         # set sane geometry width (a4-width) for linewidth = -1.
585         maxlw = max (extra['linewidth'] + [-1])
586         if maxlw < 0:
587                 # who the hell is 597 ?
588                 linewidth = '597pt'
589         else:
590                 linewidth = '%d%s' % (maxlw, unit)
591         s = s + '\geometry{width=%s%s,headheight=2mm,footskip=2mm,%s}\n' % (linewidth, textheight, orientation)
592
593         if extra['latexoptions']:
594                 s = s + '\geometry{twosideshift=4mm}\n'
595
596         s = s + r'''
597 \usepackage[latin1]{inputenc}
598 \input{titledefs}
599 '''
600         
601         if extra['pagenumber'] and extra['pagenumber'][-1] and extra['pagenumber'][-1] != 'no':
602                 s = s + '\setcounter{page}{%s}\n' % (extra['pagenumber'][-1])
603                 s = s + '\\pagestyle{plain}\n'
604         else:
605                 s = s + '\\pagestyle{empty}\n'
606
607         s = s + '\\begin{document}\n'
608         s = s + '\\thispagestyle{firstpage}\n'
609
610         first = 1
611         for t in tfiles:
612                 s = s + one_latex_definition (t, first)
613                 first = 0
614
615
616         s = s + '\\thispagestyle{lastpage}\n'
617         s = s + '\\end{document}'
618
619         return s
620
621 def run_latex (files, outbase, extra):
622
623         """Construct latex file, for FILES and EXTRA, dump it into
624 OUTBASE.latex. Run LaTeX on it.
625
626 RETURN VALUE
627
628 None
629         """
630         latex_fn = outbase + '.latex'
631         
632         wfs = find_tex_files (files, extra)
633         s = global_latex_definition (wfs, extra)
634
635         f = open (latex_fn, 'w')
636         f.write (s)
637         f.close ()
638
639         cmd = 'latex \\\\nonstopmode \\\\input %s' % latex_fn
640
641         if not verbose_p:
642                 progress ( _("Running %s...") % 'LaTeX')
643                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
644
645         system (cmd)
646
647 def run_dvips (outbase, extra):
648
649
650         """Run dvips using the correct options taken from EXTRA,
651 leaving a PS file in OUTBASE.ps
652
653 RETURN VALUE
654
655 None.
656 """
657         opts = ''
658         if extra['papersize']:
659                 opts = opts + ' -t%s' % extra['papersize'][0]
660
661         if extra['orientation'] and extra['orientation'][0] == 'landscape':
662                 opts = opts + ' -tlandscape'
663
664         cmd = 'dvips %s -o%s %s' % (opts, outbase + '.ps', outbase + '.dvi')
665         
666         if not verbose_p:
667                 progress ( _("Running %s...") % 'dvips')
668                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
669                 
670         system (cmd)
671
672 def generate_dependency_file (depfile, outname):
673         df = open (depfile, 'w')
674         df.write (outname + ':' )
675         
676         for d in dependency_files:
677                 s = open (d).read ()
678                 s = re.sub ('#[^\n]*\n', '', s)
679                 s = re.sub (r'\\\n', ' ', s)
680                 m = re.search ('.*:(.*)\n', s)
681
682                 # ugh. Different targets?
683                 if m:
684                         df.write ( m.group (1)  + ' ' )
685
686         df.write ('\n')
687         df.close ();
688
689 def find_file_in_path (path, name):
690         for d in string.split (path, os.pathsep):
691                 if name in os.listdir (d):
692                         return os.path.join (d, name)
693
694 # Added as functionality to ly2dvi, because ly2dvi may well need to do this
695 # in future too.
696 PS = '%!PS-Adobe'
697 def find_pfa_fonts (name):
698         s = open (name).read ()
699         if s[:len (PS)] != PS:
700                 # no ps header?
701                 errorport.write (_( "error: ") + _ ("not a PostScript file: `%s\'" % name))
702                 errorport.write ('\n')
703                 sys.exit (1)
704         here = 0
705         m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
706         pfa = []
707         while m:
708                 here = m.end (1)
709                 pfa.append (m.group (1))
710                 m = re.match ('.*?/(feta[-a-z0-9]+) +findfont', s[here:], re.DOTALL)
711         return pfa
712
713         
714 (sh, long) = getopt_args (option_definitions)
715 try:
716         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
717 except getopt.error, s:
718         errorport.write ('\n')
719         errorport.write (_ ("error: ") + _ ("getopt says: `%s\'" % s))
720         errorport.write ('\n')
721         errorport.write ('\n')
722         help ()
723         sys.exit (2)
724         
725 for opt in options:     
726         o = opt[0]
727         a = opt[1]
728
729         if 0:
730                 pass
731         elif o == '--help' or o == '-h':
732                 help ()
733                 sys.exit (0)
734         elif o == '--find-pfa' or o == '-f':
735                 fonts = map (lambda x: x + '.pfa', find_pfa_fonts (a))
736                 files = map (lambda x:
737                              find_file_in_path (os.environ['GS_FONTPATH'], x),
738                              fonts)
739                 print string.join (files, ' ')
740                 sys.exit (0)
741         elif o == '--include' or o == '-I':
742                 include_path.append (a)
743         elif o == '--postscript' or o == '-P':
744                 targets['PS'] = 0
745         elif o == '--keep' or o == '-k':
746                 keep_temp_dir_p = 1
747         elif o == '--no-lily':
748                 lily_p = 0
749         elif o == '--no-paper' or o == '-m':
750                 targets = {}
751                 targets['MIDI'] = 0
752                 paper_p = 0
753         elif o == '--output' or o == '-o':
754                 output_name = a
755         elif o == '--set' or o == '-s':
756                 ss = string.split (a, '=')
757                 set_setting (extra_init, ss[0], ss[1])
758         elif o == '--dependencies' or o == '-d':
759                 track_dependencies_p = 1
760         elif o == '--verbose' or o == '-V':
761                 verbose_p = 1
762         elif o == '--version' or o == '-v':
763                 identify ()
764                 sys.exit (0)
765         elif o == '--warranty' or o == '-w':
766                 status = system ('lilypond -w', ignore_error = 1)
767                 if status:
768                         warranty ()
769
770                 sys.exit (0)
771
772
773 def cp_to_dir (pattern, dir):
774         "Copy files matching re PATTERN from cwd to DIR"
775         # Duh.  Python style portable: cp *.EXT OUTDIR
776         # system ('cp *.%s %s' % (ext, outdir), 1)
777         files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
778         map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
779
780 # Python < 1.5.2 compatibility
781 #
782 # On most platforms, this is equivalent to
783 #`normpath(join(os.getcwd()), PATH)'.  *Added in Python version 1.5.2*
784 if os.path.__dict__.has_key ('abspath'):
785         abspath = os.path.abspath
786 else:
787         def abspath (path):
788                 return os.path.normpath (os.path.join (os.getcwd (), path))
789
790 if os.__dict__.has_key ('makedirs'):
791         makedirs = os.makedirs
792 else:
793         def makedirs (dir, mode=0777):
794                 system ('mkdir -p %s' % dir)
795
796 def mkdir_p (dir, mode=0777):
797         if not os.path.isdir (dir):
798                 makedirs (dir, mode)
799
800 include_path = map (abspath, include_path)
801
802 original_output = output_name
803
804
805 if files and files[0] != '-':
806         
807         # Ugh, maybe make a setup () function
808         files = map (lambda x: strip_extension (x, '.ly'), files)
809
810         # hmmm. Wish I'd 've written comments when I wrote this.
811         # now it looks complicated.
812         
813         (outdir, outbase) = ('','')
814         if not output_name:
815                 outbase = os.path.basename (files[0])
816                 outdir = abspath('.')
817         elif output_name[-1] == os.sep:
818                 outdir = abspath (output_name)
819                 outbase = os.path.basename (files[0])
820         else:
821                 (outdir, outbase) = os.path.split (abspath (output_name))
822
823         for i in ('.dvi', '.latex', '.ly', '.ps', '.tex'):
824                 output_name = strip_extension (output_name, i)
825                 outbase = strip_extension (outbase, i)
826         files = map (abspath, files)
827
828         for i in files[:] + [output_name]:
829                 if string.find (i, ' ') >= 0:
830                         user_error (_ ("filename should not contain spaces: `%s'") % i)
831                         
832         if os.path.dirname (output_name) != '.':
833                 dep_prefix = os.path.dirname (output_name)
834         else:
835                 dep_prefix = 0
836
837         reldir = os.path.dirname (output_name)
838         if outdir != '.' and (track_dependencies_p or targets.keys ()):
839                 mkdir_p (outdir, 0777)
840
841         setup_environment ()
842         tmpdir = setup_temp ()
843
844         # to be sure, add tmpdir *in front* of inclusion path.
845         #os.environ['TEXINPUTS'] =  tmpdir + ':' + os.environ['TEXINPUTS']
846         os.chdir (tmpdir)
847         
848         if lily_p:
849                 try:
850                         run_lilypond (files, outbase, dep_prefix)
851                 except:
852                         # TODO: friendly message about LilyPond setup/failing?
853                         #
854                         # TODO: lilypond should fail with different
855                         # error codes for:
856                         #   - guile setup/startup failure
857                         #   - font setup failure
858                         #   - init.ly setup failure
859                         #   - parse error in .ly
860                         #   - unexpected: assert/core dump
861                         targets = {}
862                         traceback.print_exc ()
863
864         if targets.has_key ('DVI') or targets.has_key ('PS'):
865                 try:
866                         run_latex (files, outbase, extra_init)
867                         # unless: add --tex, or --latex?
868                         del targets['TEX']
869                         del targets['LATEX']
870                 except:
871                         # TODO: friendly message about TeX/LaTeX setup,
872                         # trying to run tex/latex by hand
873                         if targets.has_key ('DVI'):
874                                 del targets['DVI']
875                         if targets.has_key ('PS'):
876                                 del targets['PS']
877                         traceback.print_exc ()
878
879         if targets.has_key ('PS'):
880                 try:
881                         run_dvips (outbase, extra_init)
882                 except: 
883                         if targets.has_key ('PS'):
884                                 del targets['PS']
885                         traceback.print_exc ()
886
887         # add DEP to targets?
888         if track_dependencies_p:
889                 depfile = os.path.join (outdir, outbase + '.dep')
890                 generate_dependency_file (depfile, depfile)
891                 if os.path.isfile (depfile):
892                         progress (_ ("dependencies output to `%s'...") % depfile)
893
894         # Hmm, if this were a function, we could call it the except: clauses
895         for i in targets.keys ():
896                 ext = string.lower (i)
897                 cp_to_dir ('.*\.%s$' % ext, outdir)
898                 outname = outbase + '.' + string.lower (i)
899                 abs = os.path.join (outdir, outname)
900                 if reldir != '.':
901                         outname = os.path.join (reldir, outname)
902                 if os.path.isfile (abs):
903                         progress (_ ("%s output to `%s'...") % (i, outname))
904                 elif verbose_p:
905                         warning (_ ("can't find file: `%s'") % outname)
906                         
907         os.chdir (original_dir)
908         cleanup_temp ()
909         
910 else:
911         # FIXME: read from stdin when files[0] = '-'
912         help ()
913         user_error (_ ("no files specified on command line."), 2)
914
915
916