]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/lilypond-book.py
Add lilypond-book ly verbatim gettext quirk
[lilypond.git] / scripts / lilypond-book.py
index ebea918cb46640396ff4f1a0938d36ce0706ba73..b4bd319a690d3b35aa27b969a7d0b4edd2d0435a 100644 (file)
@@ -1,5 +1,20 @@
 #!@TARGET_PYTHON@
 
+# This file is part of LilyPond, the GNU music typesetter.
+#
+# LilyPond is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# LilyPond is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+
 '''
 Example usage:
 
@@ -111,7 +126,7 @@ def warranty ():
 
 %s
 %s
-''' % ( _ ('Copyright (c) %s by') % '2001--2009',
+''' % ( _ ('Copyright (c) %s by') % '2001--2010',
         '\n  '.join (authors),
         _ ("Distributed under terms of the GNU General Public License."),
         _ ("It comes with NO WARRANTY.")))
@@ -149,7 +164,8 @@ def get_option_parser ():
                   default='')
 
     p.add_option ('--latex-program',
-                  help=_ ("run executable PROG instead of latex"),
+                  help=_ ("run executable PROG instead of latex, or in\n\
+case --pdf option is set instead of pdflatex"),
                   metavar=_ ("PROG"),
                   action='store', dest='latex_program',
                   default='latex')
@@ -161,11 +177,28 @@ def get_option_parser ():
                   type="float",
                   default=3.0)
 
+    p.add_option ('--lily-output-dir',
+                  help=_ ("write lily-XXX files to DIR, link into --output dir"),
+                  metavar=_ ("DIR"),
+                  action='store', dest='lily_output_dir',
+                  default=None)
+
     p.add_option ("-o", '--output', help=_ ("write output to DIR"),
                   metavar=_ ("DIR"),
                   action='store', dest='output_dir',
                   default='')
 
+    p.add_option ('--pdf',
+                  action="store_true",
+                  dest="create_pdf",
+                  help=_ ("create PDF files for use with PDFTeX"),
+                  default=False)
+
+    p.add_option ('-P', '--process', metavar=_ ("COMMAND"),
+                  help = _ ("process ly_files using COMMAND FILE..."),
+                  action='store',
+                  dest='process_cmd', default='')
+
     p.add_option ('--skip-lily-check',
                   help=_ ("do not fail if no lilypond output is found"),
                   metavar=_ ("DIR"),
@@ -178,21 +211,9 @@ def get_option_parser ():
                   action='store_true', dest='skip_png_check',
                   default=False)
 
-    p.add_option ('--lily-output-dir',
-                  help=_ ("write lily-XXX files to DIR, link into --output dir"),
-                  metavar=_ ("DIR"),
-                  action='store', dest='lily_output_dir',
-                  default=None)
-
-    p.add_option ('-P', '--process', metavar=_ ("COMMAND"),
-                  help = _ ("process ly_files using COMMAND FILE..."),
-                  action='store',
-                  dest='process_cmd', default='')
-
-    p.add_option ('--pdf',
-                  action="store_true",
-                  dest="create_pdf",
-                  help=_ ("create PDF files for use with PDFTeX"),
+    p.add_option ('--use-source-file-names',
+                  help=_ ("write snippet output files with the same base name as their source file"),
+                  action='store_true', dest='use_source_file_names',
                   default=False)
 
     p.add_option ('-V', '--verbose', help=_ ("be verbose"),
@@ -272,18 +293,23 @@ TEXIDOC = 'texidoc'
 TEXINFO = 'texinfo'
 VERBATIM = 'verbatim'
 VERSION = 'lilypondversion'
-FONTLOAD = 'fontload'
 FILENAME = 'filename'
 ALT = 'alt'
 
 
-# NOTIME has no opposite so it isn't part of this dictionary.
+# NOTIME and NOGETTEXT have no opposite so they aren't part of this
+# dictionary.
 # NOQUOTE is used internally only.
 no_options = {
     NOFRAGMENT: FRAGMENT,
     NOINDENT: INDENT,
 }
 
+# Options that have no impact on processing by lilypond (or --process
+# argument)
+PROCESSING_INDEPENDENT_OPTIONS = (
+    ALT, NOGETTEXT, VERBATIM, ADDVERSION,
+    TEXIDOC, DOCTITLE, VERSION, PRINTFILENAME)
 
 # Recognize special sequences in the input.
 #
@@ -364,9 +390,9 @@ snippet_res = {
          r'''(?mx)
           (?P<match>
           <lilypond
-           (\s*(?P<options>.*?)\s*:)?\s*
+           (\s+(?P<options>.*?))?\s*:\s*
            (?P<code>.*?)
-          />)''',
+          \s*/>)''',
 
         'lilypond_block':
          r'''(?msx)
@@ -375,7 +401,7 @@ snippet_res = {
            \s*(?P<options>.*?)\s*
           >
           (?P<code>.*?)
-          </lilypond>)''',
+          </lilypond\s*>)''',
 
         'lilypond_file':
          r'''(?mx)
@@ -384,7 +410,7 @@ snippet_res = {
            \s*(?P<options>.*?)\s*
           >
           \s*(?P<filename>.*?)\s*
-          </lilypondfile>)''',
+          </lilypondfile\s*>)''',
 
         'multiline_comment':
          r'''(?smx)
@@ -600,7 +626,6 @@ simple_options = [
     TEXIDOC,
     LANG,
     VERBATIM,
-    FONTLOAD,
     FILENAME,
     ALT,
     ADDVERSION
@@ -830,8 +855,6 @@ PREAMBLE_LY = '''%%%% Generated by %(program_name)s
 %(preamble_string)s
 
 \paper {
-  #(define dump-extents #t)
-  %(font_dump_setting)s
   %(paper_string)s
   force-assignment = #""
   line-width = #(- line-width (* mm  %(padding_mm)f))
@@ -937,7 +960,7 @@ def verb_ly_gettext (s):
 
     if langdefs.LANGDICT[document_language].enable_ly_identifier_l10n:
         for v in ly_var_def_re.findall (s):
-            s = re.sub (r"(?m)(^|[' \\#])%s([^a-zA-Z])" % v,
+            s = re.sub (r"(?m)(?<!\\clef)(^|[' \\#])%s([^a-zA-Z])" % v,
                         "\\1" + t (v) + "\\2",
                         s)
         for id in ly_context_id_re.findall (s):
@@ -1036,10 +1059,12 @@ class LilypondSnippet (Snippet):
         self.do_options (os, self.type)
 
     def verb_ly (self):
-        if NOGETTEXT in self.option_dict:
-            return self.substring ('code')
-        else:
-            return verb_ly_gettext (self.substring ('code'))
+        verb_text = self.substring ('code')
+        if not NOGETTEXT in self.option_dict:
+            verb_text = verb_ly_gettext (verb_text)
+        if not verb_text.endswith ('\n'):
+            verb_text += '\n'
+        return verb_text
 
     def ly (self):
         contents = self.substring ('code')
@@ -1118,13 +1143,28 @@ class LilypondSnippet (Snippet):
         if not INDENT in self.option_dict:
             self.option_dict[INDENT] = '0\\mm'
 
-        # The QUOTE pattern from ly_options only emits the `line-width'
-        # keyword.
-        if has_line_width and QUOTE in self.option_dict:
-            if no_line_width_value:
-                del self.option_dict[LINE_WIDTH]
-            else:
-                del self.option_dict[QUOTE]
+        # Set a default line-width if there is none. We need this, because
+        # lilypond-book has set left-padding by default and therefore does
+        # #(define line-width (- line-width (* 3 mm)))
+        # TODO: Junk this ugly hack if the code gets rewritten to concatenate
+        # all settings before writing them in the \paper block.
+        if not LINE_WIDTH in self.option_dict:
+            if not QUOTE in self.option_dict:
+                if not LILYQUOTE in self.option_dict:
+                    self.option_dict[LINE_WIDTH] = "#(- paper-width \
+left-margin-default right-margin-default)"
+
+    def get_option_list (self):
+        if not 'option_list' in self.__dict__:
+            option_list = []
+            for (key, value) in self.option_dict.items ():
+                if value == None:
+                    option_list.append (key)
+                else:
+                    option_list.append (key + '=' + value)
+            option_list.sort ()
+            self.option_list = option_list
+        return self.option_list
 
     def compose_ly (self, code):
         if FRAGMENT in self.option_dict:
@@ -1163,19 +1203,22 @@ class LilypondSnippet (Snippet):
         override.update (default_ly_options)
 
         option_list = []
-        for (key, value) in self.option_dict.items ():
-            if value == None:
-                option_list.append (key)
+        for option in self.get_option_list ():
+            for name in PROCESSING_INDEPENDENT_OPTIONS:
+                if option.startswith (name):
+                    break
             else:
-                option_list.append (key + '=' + value)
+                option_list.append (option)
         option_string = ','.join (option_list)
-
         compose_dict = {}
         compose_types = [NOTES, PREAMBLE, LAYOUT, PAPER]
         for a in compose_types:
             compose_dict[a] = []
 
-        for (key, value) in self.option_dict.items ():
+        option_names = self.option_dict.keys ()
+        option_names.sort ()
+        for key in option_names:
+            value = self.option_dict[key]
             (c_key, c_value) = classic_lilypond_book_compatibility (key, value)
             if c_key:
                 if c_value:
@@ -1224,9 +1267,6 @@ class LilypondSnippet (Snippet):
         notes_string = '\n  '.join (compose_dict[NOTES]) % vars ()
         preamble_string = '\n  '.join (compose_dict[PREAMBLE]) % override
         padding_mm = global_options.padding_mm
-        font_dump_setting = ''
-        if FONTLOAD in self.option_dict:
-            font_dump_setting = '#(define-public force-eps-font-include #t)\n'
 
         d = globals().copy()
         d.update (locals())
@@ -1240,7 +1280,16 @@ class LilypondSnippet (Snippet):
             except ImportError:
                 from md5 import md5
 
-            hash = md5 (self.relevant_contents (self.full_ly ()))
+            # We only want to calculate the hash based on the snippet
+            # code plus fragment options relevant to processing by
+            # lilypond, not the snippet + preamble
+            hash = md5 (self.relevant_contents (self.ly ()))
+            for option in self.get_option_list ():
+                for name in PROCESSING_INDEPENDENT_OPTIONS:
+                    if option.startswith (name):
+                        break
+                else:
+                    hash.update (option)
 
             ## let's not create too long names.
             self.checksum = hash.hexdigest ()[:10]
@@ -1249,22 +1298,31 @@ class LilypondSnippet (Snippet):
 
     def basename (self):
         cs = self.get_checksum ()
-        name = '%s/lily-%s' % (cs[:2], cs[2:10])
+        name = '%s/lily-%s' % (cs[:2], cs[2:])
         return name
 
+    final_basename = basename
+
     def write_ly (self):
         base = self.basename ()
         path = os.path.join (global_options.lily_output_dir, base)
         directory = os.path.split(path)[0]
         if not os.path.isdir (directory):
             os.makedirs (directory)
-        out = file (path + '.ly', 'w')
-        out.write (self.full_ly ())
-        file (path + '.txt', 'w').write ('image of music')
+        filename = path + '.ly'
+        if os.path.exists (filename):
+            diff_against_existing = filter_pipe (self.full_ly (), 'diff -u %s -' % filename)
+            if diff_against_existing:
+                warning ("%s: duplicate filename but different contents of orginal file,\n\
+printing diff against existing file." % filename)
+                ly.stderr_write (diff_against_existing)
+        else:
+            out = file (filename, 'w')
+            out.write (self.full_ly ())
+            file (path + '.txt', 'w').write ('image of music')
 
     def relevant_contents (self, ly):
-        return re.sub (r'\\(version|sourcefileline|sourcefilename)[^\n]*\n|' +
-                       NOGETTEXT + '[,\]]', '', ly)
+        return re.sub (r'\\(version|sourcefileline|sourcefilename)[^\n]*\n', '', ly)
 
     def link_all_output_files (self, output_dir, output_dir_files, destination):
         existing, missing = self.all_output_files (output_dir, output_dir_files)
@@ -1272,13 +1330,25 @@ class LilypondSnippet (Snippet):
             print '\nMissing', missing
             raise CompileError(self.basename())
         for name in existing:
+            if (global_options.use_source_file_names
+                and isinstance (self, LilypondFileSnippet)):
+                base, ext = os.path.splitext (name)
+                components = base.split ('-')
+                # ugh, assume filenames with prefix with one dash (lily-xxxx)
+                if len (components) > 2:
+                    base_suffix = '-' + components[-1]
+                else:
+                    base_suffix = ''
+                final_name = self.final_basename () + base_suffix + ext
+            else:
+                final_name = name
             try:
-                os.unlink (os.path.join (destination, name))
+                os.unlink (os.path.join (destination, final_name))
             except OSError:
                 pass
 
             src = os.path.join (output_dir, name)
-            dst = os.path.join (destination, name)
+            dst = os.path.join (destination, final_name)
             dst_path = os.path.split(dst)[0]
             if not os.path.isdir (dst_path):
                 os.makedirs (dst_path)
@@ -1379,7 +1449,7 @@ class LilypondSnippet (Snippet):
         return func (self)
 
     def get_images (self):
-        base = self.basename ()
+        base = self.final_basename ()
 
         single = '%(base)s.png' % vars ()
         multiple = '%(base)s-page1.png' % vars ()
@@ -1396,7 +1466,7 @@ class LilypondSnippet (Snippet):
 
     def output_docbook (self):
         str = ''
-        base = self.basename ()
+        base = self.final_basename ()
         for image in self.get_images ():
             (base, ext) = os.path.splitext (image)
             str += output[DOCBOOK][OUTPUT] % vars ()
@@ -1412,7 +1482,7 @@ class LilypondSnippet (Snippet):
 
     def output_html (self):
         str = ''
-        base = self.basename ()
+        base = self.final_basename ()
         if self.format == HTML:
             str += self.output_print_filename (HTML)
             if VERBATIM in self.option_dict:
@@ -1441,13 +1511,13 @@ class LilypondSnippet (Snippet):
             info_image_path = os.path.join (global_options.info_images_dir, base)
             str += output[TEXINFO][OUTPUTIMAGE] % vars ()
 
-        base = self.basename ()
+        base = self.final_basename ()
         str += output[self.format][OUTPUT] % vars ()
         return str
 
     def output_latex (self):
         str = ''
-        base = self.basename ()
+        base = self.final_basename ()
         if self.format == LATEX:
             str += self.output_print_filename (LATEX)
             if VERBATIM in self.option_dict:
@@ -1468,7 +1538,7 @@ class LilypondSnippet (Snippet):
     def output_print_filename (self, format):
         str = ''
         if PRINTFILENAME in self.option_dict:
-            base = self.basename ()
+            base = self.final_basename ()
             filename = os.path.basename (self.substring ('filename'))
             str = output[format][PRINTFILENAME] % vars ()
 
@@ -1476,7 +1546,7 @@ class LilypondSnippet (Snippet):
 
     def output_texinfo (self):
         str = self.output_print_filename (TEXINFO)
-        base = self.basename ()
+        base = self.final_basename ()
         if DOCTITLE in self.option_dict:
             doctitle = base + '.doctitle'
             translated_doctitle = doctitle + document_language
@@ -1528,13 +1598,24 @@ class LilypondFileSnippet (LilypondSnippet):
         s = self.contents
         s = re_begin_verbatim.split (s)[-1]
         s = re_end_verbatim.split (s)[0]
-        return verb_ly_gettext (s)
+        if not NOGETTEXT in self.option_dict:
+            s = verb_ly_gettext (s)
+        if not s.endswith ('\n'):
+            s += '\n'
+        return s
 
     def ly (self):
         name = self.substring ('filename')
         return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s'
                 % (name, self.contents))
 
+    def final_basename (self):
+        if global_options.use_source_file_names:
+            base = os.path.splitext (os.path.basename (self.substring ('filename')))[0]
+            return base
+        else:
+            return self.basename ()
+
 
 class LilyPondVersionString (Snippet):
     """A string that does not require extra memory."""
@@ -1703,7 +1784,7 @@ def process_snippets (cmd, snippets,
 
     checksum = snippet_list_checksum (snippets)
     contents = '\n'.join (['snippet-map-%d.ly' % checksum]
-                          + [snip.basename() + '.ly' for snip in snippets])
+                          + list (set ([snip.basename() + '.ly' for snip in snippets])))
     name = os.path.join (lily_output_dir,
                          'snippet-names-%d.ly' % checksum)
     file (name, 'wb').write (contents)
@@ -1752,17 +1833,17 @@ def get_latex_textwidth (source):
     os.unlink (logfile)
 
     columns = 0
-    m = re.search ('columns=([0-9.]*)', parameter_string)
+    m = re.search ('columns=([0-9.]+)', parameter_string)
     if m:
         columns = int (m.group (1))
 
     columnsep = 0
-    m = re.search ('columnsep=([0-9.]*)pt', parameter_string)
+    m = re.search ('columnsep=([0-9.]+)pt', parameter_string)
     if m:
         columnsep = float (m.group (1))
 
     textwidth = 0
-    m = re.search ('textwidth=([0-9.]*)pt', parameter_string)
+    m = re.search ('textwidth=([0-9.]+)pt', parameter_string)
     if m:
         textwidth = float (m.group (1))
         if columns:
@@ -2073,6 +2154,8 @@ def main ():
         global_options.process_cmd += ' --formats=eps '
         if global_options.create_pdf:
             global_options.process_cmd += "--pdf -dinclude-eps-fonts -dgs-load-fonts "
+            if global_options.latex_program == 'latex':
+                global_options.latex_program = 'pdflatex'
 
     if global_options.verbose:
         global_options.process_cmd += " --verbose "