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