]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
Add '-dcrop' option to ps and svg backends
[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--2015',
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 in spite 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.replace (os.path.sep, '/'))]),
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     def globquote(x):
447         return re.sub ("[][*?]", r"[\g<0>]", x)
448     for subdir in glob.glob (os.path.join (globquote (directory),
449                                            '[a-f0-9][a-f0-9]')):
450         base_subdir = os.path.split (subdir)[1]
451         sub_files = [os.path.join (base_subdir, name)
452                      for name in os.listdir (subdir)]
453         files += sub_files
454     return set (files)
455
456 def do_process_cmd (chunks, input_name, options):
457     snippets = [c for c in chunks if isinstance (c, BookSnippet.LilypondSnippet)]
458
459     output_files = split_output_files (options.lily_output_dir)
460     outdated = [c for c in snippets if c.is_outdated (options.lily_output_dir, output_files)]
461
462     write_file_map (outdated, input_name)
463     progress (_ ("Writing snippets..."))
464     for snippet in outdated:
465         snippet.write_ly()
466
467     if outdated:
468         progress (_ ("Processing..."))
469         process_snippets (options.process_cmd, outdated,
470                           options.formatter, options.lily_output_dir)
471
472     else:
473         progress (_ ("All snippets are up to date..."))
474
475     progress (_ ("Linking files..."))
476     abs_lily_output_dir = os.path.join (options.original_dir, options.lily_output_dir)
477     abs_output_dir = os.path.join (options.original_dir, options.output_dir)
478     if abs_lily_output_dir != abs_output_dir:
479         output_files = split_output_files (abs_lily_output_dir)
480         for snippet in snippets:
481             snippet.link_all_output_files (abs_lily_output_dir,
482                                            output_files,
483                                            abs_output_dir)
484
485
486 ###
487 # Format guessing data
488
489 def guess_format (input_filename):
490     format = None
491     e = os.path.splitext (input_filename)[1]
492     for formatter in BookBase.all_formats:
493       if formatter.can_handle_extension (e):
494         return formatter
495     error (_ ("cannot determine format for: %s" % input_filename))
496     exit (1)
497
498 def write_if_updated (file_name, lines):
499     try:
500         f = file (file_name)
501         oldstr = f.read ()
502         new_str = ''.join (lines)
503         if oldstr == new_str:
504             progress (_ ("%s is up to date.") % file_name)
505
506             # this prevents make from always rerunning lilypond-book:
507             # output file must be touched in order to be up to date
508             os.utime (file_name, None)
509             return
510     except:
511         pass
512
513     output_dir = os.path.dirname (file_name)
514     if not os.path.exists (output_dir):
515         os.makedirs (output_dir)
516
517     progress (_ ("Writing `%s'...") % file_name)
518     file (file_name, 'w').writelines (lines)
519
520
521 def note_input_file (name, inputs=[]):
522     ## hack: inputs is mutable!
523     inputs.append (name)
524     return inputs
525
526 def samefile (f1, f2):
527     try:
528         return os.path.samefile (f1, f2)
529     except AttributeError:                # Windoze
530         f1 = re.sub ("//*", "/", f1)
531         f2 = re.sub ("//*", "/", f2)
532         return f1 == f2
533
534 def do_file (input_filename, included=False):
535     # Ugh.
536     input_absname = input_filename
537     if not input_filename or input_filename == '-':
538         in_handle = sys.stdin
539         input_fullname = '<stdin>'
540     else:
541         if os.path.exists (input_filename):
542             input_fullname = input_filename
543         else:
544             input_fullname = global_options.formatter.input_fullname (input_filename)
545         # Normalize path to absolute path, since we will change cwd to the output dir!
546         # Otherwise, "lilypond-book -o out test.tex" will complain that it is
547         # overwriting the input file (which it is actually not), since the
548         # input filename is relative to the CWD...
549         input_absname = os.path.abspath (input_fullname)
550
551         note_input_file (input_fullname)
552         in_handle = file (input_fullname)
553
554     if input_filename == '-':
555         global_options.input_dir = os.getcwd ()
556         input_base = 'stdin'
557     elif included:
558         input_base = os.path.splitext (input_filename)[0]
559     else:
560         global_options.input_dir = os.path.split (input_absname)[0]
561         input_base = os.path.basename (
562             os.path.splitext (input_filename)[0])
563
564     # don't complain when global_options.output_dir is existing
565     if not global_options.output_dir:
566         global_options.output_dir = os.getcwd()
567     else:
568         global_options.output_dir = os.path.abspath(global_options.output_dir)
569
570         if not os.path.isdir (global_options.output_dir):
571             os.mkdir (global_options.output_dir, 0777)
572         os.chdir (global_options.output_dir)
573
574     output_filename = os.path.join(global_options.output_dir,
575                                    input_base + global_options.formatter.default_extension)
576     if (os.path.exists (input_filename)
577         and os.path.exists (output_filename)
578         and samefile (output_filename, input_absname)):
579      error (
580      _ ("Output would overwrite input file; use --output."))
581      exit (2)
582
583     try:
584         progress (_ ("Reading %s...") % input_fullname)
585         source = in_handle.read ()
586
587         if not included:
588             global_options.formatter.init_default_snippet_options (source)
589
590
591         progress (_ ("Dissecting..."))
592         chunks = find_toplevel_snippets (source, global_options.formatter)
593
594         # Let the formatter modify the chunks before further processing
595         chunks = global_options.formatter.process_chunks (chunks)
596
597         if global_options.filter_cmd:
598             write_if_updated (output_filename,
599                      [c.filter_text () for c in chunks])
600         elif global_options.process_cmd:
601             do_process_cmd (chunks, input_fullname, global_options)
602             progress (_ ("Compiling %s...") % output_filename)
603             write_if_updated (output_filename,
604                      [s.replacement_text ()
605                      for s in chunks])
606
607         def process_include (snippet):
608             os.chdir (original_dir)
609             name = snippet.substring ('filename')
610             progress (_ ("Processing include: %s") % name)
611             return do_file (name, included=True)
612
613         include_chunks = map (process_include,
614                               filter (lambda x: isinstance (x, BookSnippet.IncludeSnippet),
615                                       chunks))
616
617         return chunks + reduce (lambda x, y: x + y, include_chunks, [])
618
619     except BookSnippet.CompileError:
620         os.chdir (original_dir)
621         progress (_ ("Removing `%s'") % output_filename)
622         raise BookSnippet.CompileError
623
624 def adjust_include_path (path, outpath):
625     """Rewrite an include path relative to the dir where lilypond is launched.
626     Always use forward slashes since this is what lilypond expects."""
627     path = os.path.expanduser (path)
628     path = os.path.expandvars (path)
629     path = os.path.normpath (path)
630     if os.path.isabs (outpath):
631         return os.path.abspath (path).replace (os.path.sep, '/')
632     if os.path.isabs (path):
633         return path.replace (os.path.sep, '/')
634     return os.path.join (inverse_relpath (original_dir, outpath), path).replace (os.path.sep, '/')
635
636 def inverse_relpath (path, relpath):
637     """Given two paths, the second relative to the first,
638     return the first path relative to the second.
639     Always use forward slashes since this is what lilypond expects."""
640     if os.path.isabs (relpath):
641         return os.path.abspath (path).replace (os.path.sep, '/')
642     relparts = ['']
643     parts = os.path.normpath (path).split (os.path.sep)
644     for part in os.path.normpath (relpath).split (os.path.sep):
645         if part == '..':
646             relparts.append (parts[-1])
647             parts.pop ()
648         else:
649             relparts.append ('..')
650             parts.append (part)
651     return '/'.join (relparts[::-1])
652
653 def do_options ():
654     global global_options
655
656     opt_parser = get_option_parser()
657     (global_options, args) = opt_parser.parse_args ()
658
659     global_options.information = {'program_version': ly.program_version, 'program_name': ly.program_name }
660     global_options.original_dir = original_dir
661
662     if global_options.lily_output_dir:
663         global_options.lily_output_dir = os.path.expanduser (global_options.lily_output_dir)
664         for i, path in enumerate(global_options.include_path):
665             global_options.include_path[i] = adjust_include_path (path, global_options.lily_output_dir)
666         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.lily_output_dir))
667
668     elif global_options.output_dir:
669         global_options.output_dir = os.path.expanduser (global_options.output_dir)
670         for i, path in enumerate(global_options.include_path):
671             global_options.include_path[i] = adjust_include_path (path, global_options.output_dir)
672         global_options.include_path.insert (0, inverse_relpath (original_dir, global_options.output_dir))
673
674     global_options.include_path.insert (0, "./")
675
676     # Load the python packages (containing e.g. custom formatter classes)
677     # passed on the command line
678     nr = 0
679     for i in global_options.custom_packages:
680         nr += 1
681         progress (str(imp.load_source ("book_custom_package%s" % nr, i)))
682
683
684     if global_options.warranty:
685         warranty ()
686         exit (0)
687     if not args or len (args) > 1:
688         opt_parser.print_help ()
689         exit (2)
690
691     return args
692
693 def main ():
694     # FIXME: 85 lines of `main' macramee??
695     if (os.environ.has_key ("LILYPOND_BOOK_LOGLEVEL")):
696         ly.set_loglevel (os.environ["LILYPOND_BOOK_LOGLEVEL"])
697     files = do_options ()
698
699     basename = os.path.splitext (files[0])[0]
700     basename = os.path.split (basename)[1]
701
702     if global_options.format:
703       # Retrieve the formatter for the given format
704       for formatter in BookBase.all_formats:
705         if formatter.can_handle_format (global_options.format):
706           global_options.formatter = formatter
707     else:
708         global_options.formatter = guess_format (files[0])
709         global_options.format = global_options.formatter.format
710
711     # make the global options available to the formatters:
712     global_options.formatter.global_options = global_options
713     formats = global_options.formatter.image_formats
714
715     if global_options.process_cmd == '':
716         global_options.process_cmd = (lilypond_binary
717                                       + ' --formats=%s -dbackend=eps ' % formats)
718
719     if global_options.process_cmd:
720         includes = global_options.include_path
721         global_options.process_cmd += ' '.join ([' -I %s' % ly.mkarg (p)
722                                                  for p in includes])
723
724     global_options.formatter.process_options (global_options)
725
726     if global_options.lily_loglevel:
727         ly.debug_output (_ ("Setting LilyPond's loglevel to %s") % global_options.lily_loglevel, True)
728         global_options.process_cmd += " --loglevel=%s" % global_options.lily_loglevel
729     elif ly.is_verbose ():
730         if os.environ.get ("LILYPOND_LOGLEVEL", None):
731             ly.debug_output (_ ("Setting LilyPond's loglevel to %s (from environment variable LILYPOND_LOGLEVEL)") % os.environ.get ("LILYPOND_LOGLEVEL", None), True)
732             global_options.process_cmd += " --loglevel=%s" % os.environ.get ("LILYPOND_LOGLEVEL", None)
733         else:
734             ly.debug_output (_ ("Setting LilyPond's output to --verbose, implied by lilypond-book's setting"), True)
735             global_options.process_cmd += " --verbose"
736
737     if global_options.padding_mm:
738         global_options.process_cmd += " -deps-box-padding=%f " % global_options.padding_mm
739
740     global_options.process_cmd += " -dread-file-list -dno-strip-output-dir"
741
742     if global_options.lily_output_dir:
743         global_options.lily_output_dir = os.path.abspath(global_options.lily_output_dir)
744         if not os.path.isdir (global_options.lily_output_dir):
745             os.makedirs (global_options.lily_output_dir)
746     else:
747         global_options.lily_output_dir = os.path.abspath(global_options.output_dir)
748
749     relative_output_dir = global_options.output_dir
750
751     identify ()
752     try:
753         chunks = do_file (files[0])
754     except BookSnippet.CompileError:
755         exit (1)
756
757     inputs = note_input_file ('')
758     inputs.pop ()
759
760     base_file_name = os.path.splitext (os.path.basename (files[0]))[0]
761     dep_file = os.path.join (global_options.output_dir, base_file_name + '.dep')
762     final_output_file = os.path.join (relative_output_dir,
763                      base_file_name + global_options.formatter.default_extension)
764
765     os.chdir (original_dir)
766     file (dep_file, 'w').write ('%s: %s\n'
767                                 % (final_output_file, ' '.join (inputs)))
768
769 if __name__ == '__main__':
770     main ()