]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
9c49508d7a85bfba912400108a31d345be678b97
[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 # Need to shell-quote, issue 3468
281
282 import pipes
283 lilypond_binary = pipes.quote (lilypond_binary)
284
285 global_options = None
286
287
288
289
290 def find_linestarts (s):
291     nls = [0]
292     start = 0
293     end = len (s)
294     while 1:
295         i = s.find ('\n', start)
296         if i < 0:
297             break
298
299         i = i + 1
300         nls.append (i)
301         start = i
302
303     nls.append (len (s))
304     return nls
305
306 def find_toplevel_snippets (input_string, formatter):
307     res = {}
308     types = formatter.supported_snippet_types ()
309     for t in types:
310         res[t] = re.compile (formatter.snippet_regexp (t))
311
312     snippets = []
313     index = 0
314     found = dict ([(t, None) for t in types])
315
316     line_starts = find_linestarts (input_string)
317     line_start_idx = 0
318     # We want to search for multiple regexes, without searching
319     # the string multiple times for one regex.
320     # Hence, we use earlier results to limit the string portion
321     # where we search.
322     # Since every part of the string is traversed at most once for
323     # every type of snippet, this is linear.
324     while 1:
325         first = None
326         endex = 1 << 30
327         for type in types:
328             if not found[type] or found[type][0] < index:
329                 found[type] = None
330
331                 m = res[type].search (input_string[index:endex])
332                 if not m:
333                     continue
334
335                 klass = global_options.formatter.snippet_class (type)
336
337                 start = index + m.start ('match')
338                 line_number = line_start_idx
339                 while (line_starts[line_number] < start):
340                     line_number += 1
341
342                 line_number += 1
343                 snip = klass (type, m, formatter, line_number, global_options)
344
345                 found[type] = (start, snip)
346
347             if (found[type]
348                 and (not first
349                      or found[type][0] < found[first][0])):
350                 first = type
351
352                 # FIXME.
353
354                 # Limiting the search space is a cute
355                 # idea, but this *requires* to search
356                 # for possible containing blocks
357                 # first, at least as long as we do not
358                 # search for the start of blocks, but
359                 # always/directly for the entire
360                 # @block ... @end block.
361
362                 endex = found[first][0]
363
364         if not first:
365             snippets.append (BookSnippet.Substring (input_string, index, len (input_string), line_start_idx))
366             break
367
368         while (start > line_starts[line_start_idx+1]):
369             line_start_idx += 1
370
371         (start, snip) = found[first]
372         snippets.append (BookSnippet.Substring (input_string, index, start, line_start_idx + 1))
373         snippets.append (snip)
374         found[first] = None
375         index = start + len (snip.match.group ('match'))
376
377     return snippets
378
379 def system_in_directory (cmd, directory, logfile):
380     """Execute a command in a different directory.
381
382     Because of win32 compatibility, we can't simply use subprocess.
383     """
384
385     current = os.getcwd()
386     os.chdir (directory)
387     """NB - ignore_error is deliberately set to the same value
388     as redirect_output - this is not a typo."""
389     retval = ly.system(cmd,
390               be_verbose=ly.is_verbose (),
391               redirect_output=global_options.redirect_output,
392               log_file=logfile,
393               progress_p=1,
394               ignore_error=global_options.redirect_output)
395     if retval != 0:
396         print ("Error trapped by lilypond-book")
397         print ("\nPlease see " + logfile + ".log\n")
398         sys.exit(1)
399
400     os.chdir (current)
401
402
403 def process_snippets (cmd, snippets,
404                       formatter, lily_output_dir):
405     """Run cmd on all of the .ly files from snippets."""
406
407     if not snippets:
408         return
409
410     cmd = formatter.adjust_snippet_command (cmd)
411
412     checksum = snippet_list_checksum (snippets)
413     contents = '\n'.join (['snippet-map-%d.ly' % checksum]
414                           + list (set ([snip.basename() + '.ly' for snip in snippets])))
415     name = os.path.join (lily_output_dir,
416                          'snippet-names-%d.ly' % checksum)
417     logfile = name.replace('.ly', '')
418     file (name, 'wb').write (contents)
419
420     system_in_directory (' '.join ([cmd, ly.mkarg (name)]),
421                          lily_output_dir,
422                          logfile)
423
424 def snippet_list_checksum (snippets):
425     return hash (' '.join([l.basename() for l in snippets]))
426
427 def write_file_map (lys, name):
428     snippet_map = file (os.path.join (
429         global_options.lily_output_dir,
430         'snippet-map-%d.ly' % snippet_list_checksum (lys)), 'w')
431
432     snippet_map.write ("""
433 #(define version-seen #t)
434 #(define output-empty-score-list #f)
435 #(ly:add-file-name-alist '(%s
436     ))\n
437 """ % '\n'.join(['("%s.ly" . "%s")\n' % (ly.basename ().replace('\\','/'), name)
438                  for ly in lys]))
439
440 def split_output_files(directory):
441     """Returns directory entries in DIRECTORY/XX/ , where XX are hex digits.
442
443     Return value is a set of strings.
444     """
445     files = []
446     for subdir in glob.glob (os.path.join (directory, '[a-f0-9][a-f0-9]')):
447         base_subdir = os.path.split (subdir)[1]
448         sub_files = [os.path.join (base_subdir, name)
449                      for name in os.listdir (subdir)]
450         files += sub_files
451     return set (files)
452
453 def do_process_cmd (chunks, input_name, options):
454     snippets = [c for c in chunks if isinstance (c, BookSnippet.LilypondSnippet)]
455
456     output_files = split_output_files (options.lily_output_dir)
457     outdated = [c for c in snippets if c.is_outdated (options.lily_output_dir, output_files)]
458
459     write_file_map (outdated, input_name)
460     progress (_ ("Writing snippets..."))
461     for snippet in outdated:
462         snippet.write_ly()
463
464     if outdated:
465         progress (_ ("Processing..."))
466         process_snippets (options.process_cmd, outdated,
467                           options.formatter, options.lily_output_dir)
468
469     else:
470         progress (_ ("All snippets are up to date..."))
471
472     progress (_ ("Linking files..."))
473     abs_lily_output_dir = os.path.join (options.original_dir, options.lily_output_dir)
474     abs_output_dir = os.path.join (options.original_dir, options.output_dir)
475     if abs_lily_output_dir != abs_output_dir:
476         output_files = split_output_files (abs_lily_output_dir)
477         for snippet in snippets:
478             snippet.link_all_output_files (abs_lily_output_dir,
479                                            output_files,
480                                            abs_output_dir)
481
482
483 ###
484 # Format guessing data
485
486 def guess_format (input_filename):
487     format = None
488     e = os.path.splitext (input_filename)[1]
489     for formatter in BookBase.all_formats:
490       if formatter.can_handle_extension (e):
491         return formatter
492     error (_ ("cannot determine format for: %s" % input_filename))
493     exit (1)
494
495 def write_if_updated (file_name, lines):
496     try:
497         f = file (file_name)
498         oldstr = f.read ()
499         new_str = ''.join (lines)
500         if oldstr == new_str:
501             progress (_ ("%s is up to date.") % file_name)
502
503             # this prevents make from always rerunning lilypond-book:
504             # output file must be touched in order to be up to date
505             os.utime (file_name, None)
506             return
507     except:
508         pass
509
510     output_dir = os.path.dirname (file_name)
511     if not os.path.exists (output_dir):
512         os.makedirs (output_dir)
513
514     progress (_ ("Writing `%s'...") % file_name)
515     file (file_name, 'w').writelines (lines)
516
517
518 def note_input_file (name, inputs=[]):
519     ## hack: inputs is mutable!
520     inputs.append (name)
521     return inputs
522
523 def samefile (f1, f2):
524     try:
525         return os.path.samefile (f1, f2)
526     except AttributeError:                # Windoze
527         f1 = re.sub ("//*", "/", f1)
528         f2 = re.sub ("//*", "/", f2)
529         return f1 == f2
530
531 def do_file (input_filename, included=False):
532     # Ugh.
533     input_absname = input_filename
534     if not input_filename or input_filename == '-':
535         in_handle = sys.stdin
536         input_fullname = '<stdin>'
537     else:
538         if os.path.exists (input_filename):
539             input_fullname = input_filename
540         else:
541             input_fullname = global_options.formatter.input_fullname (input_filename)
542         # Normalize path to absolute path, since we will change cwd to the output dir!
543         # Otherwise, "lilypond-book -o out test.tex" will complain that it is
544         # overwriting the input file (which it is actually not), since the
545         # input filename is relative to the CWD...
546         input_absname = os.path.abspath (input_fullname)
547
548         note_input_file (input_fullname)
549         in_handle = file (input_fullname)
550
551     if input_filename == '-':
552         global_options.input_dir = os.getcwd ()
553         input_base = 'stdin'
554     elif included:
555         input_base = os.path.splitext (input_filename)[0]
556     else:
557         global_options.input_dir = os.path.split (input_absname)[0]
558         input_base = os.path.basename (
559             os.path.splitext (input_filename)[0])
560
561     # don't complain when global_options.output_dir is existing
562     if not global_options.output_dir:
563         global_options.output_dir = os.getcwd()
564     else:
565         global_options.output_dir = os.path.abspath(global_options.output_dir)
566
567         if not os.path.isdir (global_options.output_dir):
568             os.mkdir (global_options.output_dir, 0777)
569         os.chdir (global_options.output_dir)
570
571     output_filename = os.path.join(global_options.output_dir,
572                                    input_base + global_options.formatter.default_extension)
573     if (os.path.exists (input_filename)
574         and os.path.exists (output_filename)
575         and samefile (output_filename, input_absname)):
576      error (
577      _ ("Output would overwrite input file; use --output."))
578      exit (2)
579
580     try:
581         progress (_ ("Reading %s...") % input_fullname)
582         source = in_handle.read ()
583
584         if not included:
585             global_options.formatter.init_default_snippet_options (source)
586
587
588         progress (_ ("Dissecting..."))
589         chunks = find_toplevel_snippets (source, global_options.formatter)
590
591         # Let the formatter modify the chunks before further processing
592         chunks = global_options.formatter.process_chunks (chunks)
593
594         if global_options.filter_cmd:
595             write_if_updated (output_filename,
596                      [c.filter_text () for c in chunks])
597         elif global_options.process_cmd:
598             do_process_cmd (chunks, input_fullname, global_options)
599             progress (_ ("Compiling %s...") % output_filename)
600             write_if_updated (output_filename,
601                      [s.replacement_text ()
602                      for s in chunks])
603
604         def process_include (snippet):
605             os.chdir (original_dir)
606             name = snippet.substring ('filename')
607             progress (_ ("Processing include: %s") % name)
608             return do_file (name, included=True)
609
610         include_chunks = map (process_include,
611                               filter (lambda x: isinstance (x, BookSnippet.IncludeSnippet),
612                                       chunks))
613
614         return chunks + reduce (lambda x, y: x + y, include_chunks, [])
615
616     except BookSnippet.CompileError:
617         os.chdir (original_dir)
618         progress (_ ("Removing `%s'") % output_filename)
619         raise BookSnippet.CompileError
620
621 def inverse_relpath (path, relpath):
622     """Given two paths, the second relative to the first,
623     return the first path relative to the second."""
624     if os.path.isabs (relpath):
625         return os.path.abspath (path)
626     relparts = []
627     parts = os.path.normpath (path).split (os.path.sep)
628     for part in os.path.normpath (relpath).split (os.path.sep):
629         if part == '..':
630             relparts.append (parts[-1])
631             parts.pop ()
632         else:
633             relparts.append ('..')
634             parts.append (part)
635     return os.path.sep.join (relparts[::-1])
636
637 def do_options ():
638     global global_options
639
640     opt_parser = get_option_parser()
641     (global_options, args) = opt_parser.parse_args ()
642
643     global_options.information = {'program_version': ly.program_version, 'program_name': ly.program_name }
644     global_options.original_dir = original_dir
645
646     if global_options.lily_output_dir:
647         global_options.lily_output_dir = os.path.expanduser (global_options.lily_output_dir)
648         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.lily_output_dir))
649
650     if global_options.output_dir:
651         global_options.output_dir = os.path.expanduser (global_options.output_dir)
652         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.output_dir))
653
654     global_options.include_path.insert (0, ".")
655
656     # Load the python packages (containing e.g. custom formatter classes)
657     # passed on the command line
658     nr = 0
659     for i in global_options.custom_packages:
660         nr += 1
661         progress (str(imp.load_source ("book_custom_package%s" % nr, i)))
662
663
664     if global_options.warranty:
665         warranty ()
666         exit (0)
667     if not args or len (args) > 1:
668         opt_parser.print_help ()
669         exit (2)
670
671     return args
672
673 def main ():
674     # FIXME: 85 lines of `main' macramee??
675     if (os.environ.has_key ("LILYPOND_BOOK_LOGLEVEL")):
676         ly.set_loglevel (os.environ["LILYPOND_BOOK_LOGLEVEL"])
677     files = do_options ()
678
679     basename = os.path.splitext (files[0])[0]
680     basename = os.path.split (basename)[1]
681
682     if global_options.format:
683       # Retrieve the formatter for the given format
684       for formatter in BookBase.all_formats:
685         if formatter.can_handle_format (global_options.format):
686           global_options.formatter = formatter
687     else:
688         global_options.formatter = guess_format (files[0])
689         global_options.format = global_options.formatter.format
690
691     # make the global options available to the formatters:
692     global_options.formatter.global_options = global_options
693     formats = global_options.formatter.image_formats
694
695     if global_options.process_cmd == '':
696         global_options.process_cmd = (lilypond_binary
697                                       + ' --formats=%s -dbackend=eps ' % formats)
698
699     if global_options.process_cmd:
700         includes = global_options.include_path
701         if global_options.lily_output_dir:
702             # This must be first, so lilypond prefers to read .ly
703             # files in the other lybookdb dir.
704             includes = [global_options.lily_output_dir] + includes
705         global_options.process_cmd += ' '.join ([' -I %s' % ly.mkarg (p)
706                                                  for p in includes])
707
708     global_options.formatter.process_options (global_options)
709
710     if global_options.lily_loglevel:
711         ly.debug_output (_ ("Setting LilyPond's loglevel to %s") % global_options.lily_loglevel, True)
712         global_options.process_cmd += " --loglevel=%s" % global_options.lily_loglevel
713     elif ly.is_verbose ():
714         if os.environ.get ("LILYPOND_LOGLEVEL", None):
715             ly.debug_output (_ ("Setting LilyPond's loglevel to %s (from environment variable LILYPOND_LOGLEVEL)") % os.environ.get ("LILYPOND_LOGLEVEL", None), True)
716             global_options.process_cmd += " --loglevel=%s" % os.environ.get ("LILYPOND_LOGLEVEL", None)
717         else:
718             ly.debug_output (_ ("Setting LilyPond's output to --verbose, implied by lilypond-book's setting"), True)
719             global_options.process_cmd += " --verbose"
720
721     if global_options.padding_mm:
722         global_options.process_cmd += " -deps-box-padding=%f " % global_options.padding_mm
723
724     global_options.process_cmd += " -dread-file-list -dno-strip-output-dir"
725
726     if global_options.lily_output_dir:
727         global_options.lily_output_dir = os.path.abspath(global_options.lily_output_dir)
728         if not os.path.isdir (global_options.lily_output_dir):
729             os.makedirs (global_options.lily_output_dir)
730     else:
731         global_options.lily_output_dir = os.path.abspath(global_options.output_dir)
732
733     relative_output_dir = global_options.output_dir
734
735     identify ()
736     try:
737         chunks = do_file (files[0])
738     except BookSnippet.CompileError:
739         exit (1)
740
741     inputs = note_input_file ('')
742     inputs.pop ()
743
744     base_file_name = os.path.splitext (os.path.basename (files[0]))[0]
745     dep_file = os.path.join (global_options.output_dir, base_file_name + '.dep')
746     final_output_file = os.path.join (relative_output_dir,
747                      base_file_name + global_options.formatter.default_extension)
748
749     os.chdir (original_dir)
750     file (dep_file, 'w').write ('%s: %s\n'
751                                 % (final_output_file, ' '.join (inputs)))
752
753 if __name__ == '__main__':
754     main ()