]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
Fix the linking of files from lilypond by lilypond-book.
[lilypond.git] / scripts / lilypond-book.py
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
3
4 # This file is part of LilyPond, the GNU music typesetter.
5 #
6 # LilyPond is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # LilyPond is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18
19 '''
20 Example usage:
21
22 test:
23   lilypond-book --filter="tr '[a-z]' '[A-Z]'" BOOK
24
25 convert-ly on book:
26   lilypond-book --filter="convert-ly --no-version --from=1.6.11 -" BOOK
27
28 classic lilypond-book:
29   lilypond-book --process="lilypond" BOOK.tely
30
31 TODO:
32
33   *  ly-options: intertext?
34   *  --line-width?
35   *  eps in latex / eps by lilypond -b ps?
36   *  check latex parameters, twocolumn, multicolumn?
37   *  use --png --ps --pdf for making images?
38
39   *  Converting from lilypond-book source, substitute:
40    @mbinclude foo.itely -> @include foo.itely
41    \mbinput -> \input
42
43 '''
44
45
46 # TODO: Better solve the global_options copying to the snippets...
47
48 import glob
49 import os
50 import re
51 import stat
52 import sys
53 import tempfile
54 import imp
55 from optparse import OptionGroup
56
57
58 """
59 @relocate-preamble@
60 """
61
62 import lilylib as ly
63 import fontextract
64 import langdefs
65 global _;_=ly._
66
67 import book_base as BookBase
68 import book_snippets as BookSnippet
69 import book_html
70 import book_docbook
71 import book_texinfo
72 import book_latex
73
74 ly.require_python_version ()
75
76 original_dir = os.getcwd ()
77 backend = 'ps'
78
79 help_summary = (
80 _ ("Process LilyPond snippets in hybrid HTML, LaTeX, texinfo or DocBook document.")
81 + '\n\n'
82 + _ ("Examples:")
83 + '''
84  $ lilypond-book --filter="tr '[a-z]' '[A-Z]'" %(BOOK)s
85  $ lilypond-book -F "convert-ly --no-version --from=2.0.0 -" %(BOOK)s
86  $ lilypond-book --process='lilypond -I include' %(BOOK)s
87 ''' % {'BOOK': _ ("BOOK")})
88
89 authors = ('Jan Nieuwenhuizen <janneke@gnu.org>',
90            'Han-Wen Nienhuys <hanwen@xs4all.nl>')
91
92 ################################################################
93 def exit (i):
94     if ly.is_verbose ():
95         raise Exception (_ ('Exiting (%d)...') % i)
96     else:
97         sys.exit (i)
98
99 progress = ly.progress
100 warning = ly.warning
101 error = ly.error
102
103 def identify ():
104     progress('%s (GNU LilyPond) %s' % (ly.program_name, ly.program_version))
105
106 def warranty ():
107     identify ()
108     ly.encoded_write (sys.stdout, '''
109 %s
110
111   %s
112
113 %s
114 %s
115 ''' % ( _ ('Copyright (c) %s by') % '2001--2012',
116         '\n  '.join (authors),
117         _ ("Distributed under terms of the GNU General Public License."),
118         _ ("It comes with NO WARRANTY.")))
119
120
121 def get_option_parser ():
122     p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'lilypond-book',
123                               description=help_summary,
124                               conflict_handler="resolve",
125                               add_help_option=False)
126
127     p.add_option ('-F', '--filter', metavar=_ ("FILTER"),
128                   action="store",
129                   dest="filter_cmd",
130                   help=_ ("pipe snippets through FILTER [default: `convert-ly -n -']"),
131                   default=None)
132
133     p.add_option ('-f', '--format',
134                   help=_ ("use output format FORMAT (texi [default], texi-html, latex, html, docbook)"),
135                   metavar=_ ("FORMAT"),
136                   action='store')
137
138     p.add_option("-h", "--help",
139                  action="help",
140                  help=_ ("show this help and exit"))
141
142     p.add_option ("-I", '--include', help=_ ("add DIR to include path"),
143                   metavar=_ ("DIR"),
144                   action='append', dest='include_path',
145                   default=[])
146
147     p.add_option ('--info-images-dir',
148                   help=_ ("format Texinfo output so that Info will "
149                           "look for images of music in DIR"),
150                   metavar=_ ("DIR"),
151                   action='store', dest='info_images_dir',
152                   default='')
153
154     p.add_option ('--left-padding',
155                   metavar=_ ("PAD"),
156                   dest="padding_mm",
157                   help=_ ("pad left side of music to align music inspite of uneven bar numbers (in mm)"),
158                   type="float",
159                   default=3.0)
160
161     p.add_option ('--lily-loglevel',
162                   help=_ ("Print lilypond log messages according to LOGLEVEL"),
163                   metavar=_ ("LOGLEVEL"),
164                   action='store', dest='lily_loglevel',
165                   default=os.environ.get ("LILYPOND_LOGLEVEL", None))
166
167     p.add_option ('--lily-output-dir',
168                   help=_ ("write lily-XXX files to DIR, link into --output dir"),
169                   metavar=_ ("DIR"),
170                   action='store', dest='lily_output_dir',
171                   default=None)
172
173     p.add_option ('--load-custom-package', help=_ ("Load the additional python PACKAGE (containing e.g. a custom output format)"),
174                   metavar=_ ("PACKAGE"),
175                   action='append', dest='custom_packages',
176                   default=[])
177
178     p.add_option ("-l", "--loglevel",
179                   help=_ ("Print log messages according to LOGLEVEL "
180                           "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
181                   metavar=_ ("LOGLEVEL"),
182                   action='callback',
183                   callback=ly.handle_loglevel_option,
184                   type='string')
185
186     p.add_option ("-o", '--output', help=_ ("write output to DIR"),
187                   metavar=_ ("DIR"),
188                   action='store', dest='output_dir',
189                   default='')
190
191     p.add_option ('-P', '--process', metavar=_ ("COMMAND"),
192                   help = _ ("process ly_files using COMMAND FILE..."),
193                   action='store',
194                   dest='process_cmd', default='')
195
196     p.add_option ('--redirect-lilypond-output',
197                   help = _ ("Redirect the lilypond output"),
198                   action='store_true',
199                   dest='redirect_output', default=False)
200
201     p.add_option ('-s', '--safe', help=_ ("Compile snippets in safe mode"),
202                   action="store_true",
203                   default=False,
204                   dest="safe_mode")
205
206     p.add_option ('--skip-lily-check',
207                   help=_ ("do not fail if no lilypond output is found"),
208                   metavar=_ ("DIR"),
209                   action='store_true', dest='skip_lilypond_run',
210                   default=False)
211
212     p.add_option ('--skip-png-check',
213                   help=_ ("do not fail if no PNG images are found for EPS files"),
214                   metavar=_ ("DIR"),
215                   action='store_true', dest='skip_png_check',
216                   default=False)
217
218     p.add_option ('--use-source-file-names',
219                   help=_ ("write snippet output files with the same base name as their source file"),
220                   action='store_true', dest='use_source_file_names',
221                   default=False)
222
223     p.add_option ('-V', '--verbose', help=_ ("be verbose"),
224                   action="callback",
225                   callback=ly.handle_loglevel_option,
226                   callback_args=("DEBUG",))
227
228     p.version = "@TOPLEVEL_VERSION@"
229     p.add_option("--version",
230                  action="version",
231                  help=_ ("show version number and exit"))
232
233     p.add_option ('-w', '--warranty',
234                   help=_ ("show warranty and copyright"),
235                   action='store_true')
236
237     group = OptionGroup (p, "Options only for the latex and texinfo backends")
238     group.add_option ('--latex-program',
239               help=_ ("run executable PROG instead of latex, or in\n\
240 case --pdf option is set instead of pdflatex"),
241               metavar=_ ("PROG"),
242               action='store', dest='latex_program',
243               default='latex')
244     group.add_option ('--texinfo-program',
245               help=_ ("run executable PROG instead of texi2pdf"),
246               metavar=_ ("PROG"),
247               action='store', dest='texinfo_program',
248               default='texi2pdf')
249     group.add_option ('--pdf',
250               action="store_true",
251               dest="create_pdf",
252               help=_ ("create PDF files for use with PDFTeX"),
253               default=False)
254     p.add_option_group (group)
255
256     p.add_option_group ('',
257                         description=(
258         _ ("Report bugs via %s")
259         % ' http://post.gmane.org/post.php'
260         '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
261
262
263     for formatter in BookBase.all_formats:
264       formatter.add_options (p)
265
266     return p
267
268 lilypond_binary = os.path.join ('@bindir@', 'lilypond')
269
270 # If we are called with full path, try to use lilypond binary
271 # installed in the same path; this is needed in GUB binaries, where
272 # @bindir is always different from the installed binary path.
273 if 'bindir' in globals () and bindir:
274     lilypond_binary = os.path.join (bindir, 'lilypond')
275
276 # Only use installed binary when we are installed too.
277 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
278     lilypond_binary = 'lilypond'
279
280 global_options = None
281
282
283
284
285 def find_linestarts (s):
286     nls = [0]
287     start = 0
288     end = len (s)
289     while 1:
290         i = s.find ('\n', start)
291         if i < 0:
292             break
293
294         i = i + 1
295         nls.append (i)
296         start = i
297
298     nls.append (len (s))
299     return nls
300
301 def find_toplevel_snippets (input_string, formatter):
302     res = {}
303     types = formatter.supported_snippet_types ()
304     for t in types:
305         res[t] = re.compile (formatter.snippet_regexp (t))
306
307     snippets = []
308     index = 0
309     found = dict ([(t, None) for t in types])
310
311     line_starts = find_linestarts (input_string)
312     line_start_idx = 0
313     # We want to search for multiple regexes, without searching
314     # the string multiple times for one regex.
315     # Hence, we use earlier results to limit the string portion
316     # where we search.
317     # Since every part of the string is traversed at most once for
318     # every type of snippet, this is linear.
319     while 1:
320         first = None
321         endex = 1 << 30
322         for type in types:
323             if not found[type] or found[type][0] < index:
324                 found[type] = None
325
326                 m = res[type].search (input_string[index:endex])
327                 if not m:
328                     continue
329
330                 klass = global_options.formatter.snippet_class (type)
331
332                 start = index + m.start ('match')
333                 line_number = line_start_idx
334                 while (line_starts[line_number] < start):
335                     line_number += 1
336
337                 line_number += 1
338                 snip = klass (type, m, formatter, line_number, global_options)
339
340                 found[type] = (start, snip)
341
342             if (found[type]
343                 and (not first
344                      or found[type][0] < found[first][0])):
345                 first = type
346
347                 # FIXME.
348
349                 # Limiting the search space is a cute
350                 # idea, but this *requires* to search
351                 # for possible containing blocks
352                 # first, at least as long as we do not
353                 # search for the start of blocks, but
354                 # always/directly for the entire
355                 # @block ... @end block.
356
357                 endex = found[first][0]
358
359         if not first:
360             snippets.append (BookSnippet.Substring (input_string, index, len (input_string), line_start_idx))
361             break
362
363         while (start > line_starts[line_start_idx+1]):
364             line_start_idx += 1
365
366         (start, snip) = found[first]
367         snippets.append (BookSnippet.Substring (input_string, index, start, line_start_idx + 1))
368         snippets.append (snip)
369         found[first] = None
370         index = start + len (snip.match.group ('match'))
371
372     return snippets
373
374 def system_in_directory (cmd, directory, logfile):
375     """Execute a command in a different directory.
376
377     Because of win32 compatibility, we can't simply use subprocess.
378     """
379
380     current = os.getcwd()
381     os.chdir (directory)
382     """NB - ignore_error is deliberately set to the same value
383     as redirect_output - this is not a typo."""
384     retval = ly.system(cmd,
385               be_verbose=ly.is_verbose (),
386               redirect_output=global_options.redirect_output,
387               log_file=logfile,
388               progress_p=1,
389               ignore_error=global_options.redirect_output)
390     if retval != 0:
391         print ("Error trapped by lilypond-book")
392         print ("\nPlease see " + logfile + ".log\n")
393         sys.exit(1)
394
395     os.chdir (current)
396
397
398 def process_snippets (cmd, snippets,
399                       formatter, lily_output_dir):
400     """Run cmd on all of the .ly files from snippets."""
401
402     if not snippets:
403         return
404
405     cmd = formatter.adjust_snippet_command (cmd)
406
407     checksum = snippet_list_checksum (snippets)
408     contents = '\n'.join (['snippet-map-%d.ly' % checksum]
409                           + list (set ([snip.basename() + '.ly' for snip in snippets])))
410     name = os.path.join (lily_output_dir,
411                          'snippet-names-%d.ly' % checksum)
412     logfile = name.replace('.ly', '')
413     file (name, 'wb').write (contents)
414
415     system_in_directory (' '.join ([cmd, ly.mkarg (name)]),
416                          lily_output_dir,
417                          logfile)
418
419 def snippet_list_checksum (snippets):
420     return hash (' '.join([l.basename() for l in snippets]))
421
422 def write_file_map (lys, name):
423     snippet_map = file (os.path.join (
424         global_options.lily_output_dir,
425         'snippet-map-%d.ly' % snippet_list_checksum (lys)), 'w')
426
427     snippet_map.write ("""
428 #(define version-seen #t)
429 #(define output-empty-score-list #f)
430 #(ly:add-file-name-alist '(%s
431     ))\n
432 """ % '\n'.join(['("%s.ly" . "%s")\n' % (ly.basename ().replace('\\','/'), name)
433                  for ly in lys]))
434
435 def split_output_files(directory):
436     """Returns directory entries in DIRECTORY/XX/ , where XX are hex digits.
437
438     Return value is a set of strings.
439     """
440     files = []
441     for subdir in glob.glob (os.path.join (directory, '[a-f0-9][a-f0-9]')):
442         base_subdir = os.path.split (subdir)[1]
443         sub_files = [os.path.join (base_subdir, name)
444                      for name in os.listdir (subdir)]
445         files += sub_files
446     return set (files)
447
448 def do_process_cmd (chunks, input_name, options):
449     snippets = [c for c in chunks if isinstance (c, BookSnippet.LilypondSnippet)]
450
451     output_files = split_output_files (options.lily_output_dir)
452     outdated = [c for c in snippets if c.is_outdated (options.lily_output_dir, output_files)]
453
454     write_file_map (outdated, input_name)
455     progress (_ ("Writing snippets..."))
456     for snippet in outdated:
457         snippet.write_ly()
458
459     if outdated:
460         progress (_ ("Processing..."))
461         process_snippets (options.process_cmd, outdated,
462                           options.formatter, options.lily_output_dir)
463
464     else:
465         progress (_ ("All snippets are up to date..."))
466
467     progress (_ ("Linking files..."))
468     abs_lily_output_dir = os.path.join (options.original_dir, options.lily_output_dir)
469     abs_output_dir = os.path.join (options.original_dir, options.output_dir)
470     if abs_lily_output_dir != abs_output_dir:
471         output_files = split_output_files (abs_lily_output_dir)
472         for snippet in snippets:
473             snippet.link_all_output_files (abs_lily_output_dir,
474                                            output_files,
475                                            abs_output_dir)
476
477
478 ###
479 # Format guessing data
480
481 def guess_format (input_filename):
482     format = None
483     e = os.path.splitext (input_filename)[1]
484     for formatter in BookBase.all_formats:
485       if formatter.can_handle_extension (e):
486         return formatter
487     error (_ ("cannot determine format for: %s" % input_filename))
488     exit (1)
489
490 def write_if_updated (file_name, lines):
491     try:
492         f = file (file_name)
493         oldstr = f.read ()
494         new_str = ''.join (lines)
495         if oldstr == new_str:
496             progress (_ ("%s is up to date.") % file_name)
497
498             # this prevents make from always rerunning lilypond-book:
499             # output file must be touched in order to be up to date
500             os.utime (file_name, None)
501             return
502     except:
503         pass
504
505     output_dir = os.path.dirname (file_name)
506     if not os.path.exists (output_dir):
507         os.makedirs (output_dir)
508
509     progress (_ ("Writing `%s'...") % file_name)
510     file (file_name, 'w').writelines (lines)
511
512
513 def note_input_file (name, inputs=[]):
514     ## hack: inputs is mutable!
515     inputs.append (name)
516     return inputs
517
518 def samefile (f1, f2):
519     try:
520         return os.path.samefile (f1, f2)
521     except AttributeError:                # Windoze
522         f1 = re.sub ("//*", "/", f1)
523         f2 = re.sub ("//*", "/", f2)
524         return f1 == f2
525
526 def do_file (input_filename, included=False):
527     # Ugh.
528     input_absname = input_filename
529     if not input_filename or input_filename == '-':
530         in_handle = sys.stdin
531         input_fullname = '<stdin>'
532     else:
533         if os.path.exists (input_filename):
534             input_fullname = input_filename
535         else:
536             input_fullname = global_options.formatter.input_fullname (input_filename)
537         # Normalize path to absolute path, since we will change cwd to the output dir!
538         # Otherwise, "lilypond-book -o out test.tex" will complain that it is
539         # overwriting the input file (which it is actually not), since the
540         # input filename is relative to the CWD...
541         input_absname = os.path.abspath (input_fullname)
542
543         note_input_file (input_fullname)
544         in_handle = file (input_fullname)
545
546     if input_filename == '-':
547         input_base = 'stdin'
548     elif included:
549         input_base = os.path.splitext (input_filename)[0]
550     else:
551         input_base = os.path.basename (
552             os.path.splitext (input_filename)[0])
553
554     # don't complain when global_options.output_dir is existing
555     if not global_options.output_dir:
556         global_options.output_dir = os.getcwd()
557     else:
558         global_options.output_dir = os.path.abspath(global_options.output_dir)
559
560         if not os.path.isdir (global_options.output_dir):
561             os.mkdir (global_options.output_dir, 0777)
562         os.chdir (global_options.output_dir)
563
564     output_filename = os.path.join(global_options.output_dir,
565                                    input_base + global_options.formatter.default_extension)
566     if (os.path.exists (input_filename)
567         and os.path.exists (output_filename)
568         and samefile (output_filename, input_absname)):
569      error (
570      _ ("Output would overwrite input file; use --output."))
571      exit (2)
572
573     try:
574         progress (_ ("Reading %s...") % input_fullname)
575         source = in_handle.read ()
576
577         if not included:
578             global_options.formatter.init_default_snippet_options (source)
579
580
581         progress (_ ("Dissecting..."))
582         chunks = find_toplevel_snippets (source, global_options.formatter)
583
584         # Let the formatter modify the chunks before further processing
585         chunks = global_options.formatter.process_chunks (chunks)
586
587         if global_options.filter_cmd:
588             write_if_updated (output_filename,
589                      [c.filter_text () for c in chunks])
590         elif global_options.process_cmd:
591             do_process_cmd (chunks, input_fullname, global_options)
592             progress (_ ("Compiling %s...") % output_filename)
593             write_if_updated (output_filename,
594                      [s.replacement_text ()
595                      for s in chunks])
596
597         def process_include (snippet):
598             os.chdir (original_dir)
599             name = snippet.substring ('filename')
600             progress (_ ("Processing include: %s") % name)
601             return do_file (name, included=True)
602
603         include_chunks = map (process_include,
604                               filter (lambda x: isinstance (x, BookSnippet.IncludeSnippet),
605                                       chunks))
606
607         return chunks + reduce (lambda x, y: x + y, include_chunks, [])
608
609     except BookSnippet.CompileError:
610         os.chdir (original_dir)
611         progress (_ ("Removing `%s'") % output_filename)
612         raise BookSnippet.CompileError
613
614 def inverse_relpath (path, relpath):
615     """Given two paths, the second relative to the first,
616     return the first path relative to the second."""
617     if os.path.isabs (relpath):
618         return os.path.abspath (path)
619     relparts = ['']
620     parts = os.path.normpath (path).split (os.path.sep)
621     for part in os.path.normpath (relpath).split (os.path.sep):
622         if part == '..':
623             relparts.append (parts[-1])
624             parts.pop ()
625         else:
626             relparts.append ('..')
627             parts.append (part)
628     return os.path.sep.join (relparts[::-1])
629
630 def do_options ():
631     global global_options
632
633     opt_parser = get_option_parser()
634     (global_options, args) = opt_parser.parse_args ()
635
636     global_options.information = {'program_version': ly.program_version, 'program_name': ly.program_name }
637     global_options.original_dir = original_dir
638
639     if global_options.lily_output_dir:
640         global_options.lily_output_dir = os.path.expanduser (global_options.lily_output_dir)
641         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.lily_output_dir))
642
643     if global_options.output_dir:
644         global_options.output_dir = os.path.expanduser (global_options.output_dir)
645         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.output_dir))
646
647     global_options.include_path.insert (0, ".%s" % os.path.sep)
648
649     # Load the python packages (containing e.g. custom formatter classes)
650     # passed on the command line
651     nr = 0
652     for i in global_options.custom_packages:
653         nr += 1
654         print imp.load_source ("book_custom_package%s" % nr, i)
655
656
657     if global_options.warranty:
658         warranty ()
659         exit (0)
660     if not args or len (args) > 1:
661         opt_parser.print_help ()
662         exit (2)
663
664     return args
665
666 def main ():
667     # FIXME: 85 lines of `main' macramee??
668     if (os.environ.has_key ("LILYPOND_BOOK_LOGLEVEL")):
669         ly.set_loglevel (os.environ["LILYPOND_BOOK_LOGLEVEL"])
670     files = do_options ()
671
672     basename = os.path.splitext (files[0])[0]
673     basename = os.path.split (basename)[1]
674
675     if global_options.format:
676       # Retrieve the formatter for the given format
677       for formatter in BookBase.all_formats:
678         if formatter.can_handle_format (global_options.format):
679           global_options.formatter = formatter
680     else:
681         global_options.formatter = guess_format (files[0])
682         global_options.format = global_options.formatter.format
683
684     # make the global options available to the formatters:
685     global_options.formatter.global_options = global_options
686     formats = global_options.formatter.image_formats
687
688     if global_options.process_cmd == '':
689         global_options.process_cmd = (lilypond_binary
690                                       + ' --formats=%s -dbackend=eps ' % formats)
691
692     if global_options.process_cmd:
693         includes = global_options.include_path
694         if global_options.lily_output_dir:
695             # This must be first, so lilypond prefers to read .ly
696             # files in the other lybookdb dir.
697             includes = [global_options.lily_output_dir] + includes
698         global_options.process_cmd += ' '.join ([' -I %s' % ly.mkarg (p)
699                                                  for p in includes])
700
701     global_options.formatter.process_options (global_options)
702
703     if global_options.lily_loglevel:
704         ly.debug_output (_ ("Setting LilyPond's loglevel to %s") % global_options.lily_loglevel, True)
705         global_options.process_cmd += " --loglevel=%s" % global_options.lily_loglevel
706     elif ly.is_verbose ():
707         if os.environ.get ("LILYPOND_LOGLEVEL", None):
708             ly.debug_output (_ ("Setting LilyPond's loglevel to %s (from environment variable LILYPOND_LOGLEVEL)") % os.environ.get ("LILYPOND_LOGLEVEL", None), True)
709             global_options.process_cmd += " --loglevel=%s" % os.environ.get ("LILYPOND_LOGLEVEL", None)
710         else:
711             ly.debug_output (_ ("Setting LilyPond's output to --verbose, implied by lilypond-book's setting"), True)
712             global_options.process_cmd += " --verbose"
713
714     if global_options.padding_mm:
715         global_options.process_cmd += " -deps-box-padding=%f " % global_options.padding_mm
716
717     global_options.process_cmd += " -dread-file-list -dno-strip-output-dir"
718
719     if global_options.lily_output_dir:
720         global_options.lily_output_dir = os.path.abspath(global_options.lily_output_dir)
721         if not os.path.isdir (global_options.lily_output_dir):
722             os.makedirs (global_options.lily_output_dir)
723     else:
724         global_options.lily_output_dir = os.path.abspath(global_options.output_dir)
725
726     relative_output_dir = global_options.output_dir
727
728     identify ()
729     try:
730         chunks = do_file (files[0])
731     except BookSnippet.CompileError:
732         exit (1)
733
734     inputs = note_input_file ('')
735     inputs.pop ()
736
737     base_file_name = os.path.splitext (os.path.basename (files[0]))[0]
738     dep_file = os.path.join (global_options.output_dir, base_file_name + '.dep')
739     final_output_file = os.path.join (relative_output_dir,
740                      base_file_name + global_options.formatter.default_extension)
741
742     os.chdir (original_dir)
743     file (dep_file, 'w').write ('%s: %s\n'
744                                 % (final_output_file, ' '.join (inputs)))
745
746 if __name__ == '__main__':
747     main ()