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