X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Flilypond-book.py;h=63f075a1492db94649491416c7884e019eae86f2;hb=d461778d6fd14bf62f726fe795e2ef542e2133c1;hp=86e78e2449427b2218bd99d6b7dd8c67a9973fad;hpb=92eb006cff3771c5f76058de84ae9e0ad9e74eb4;p=lilypond.git diff --git a/scripts/lilypond-book.py b/scripts/lilypond-book.py index 86e78e2449..63f075a149 100644 --- a/scripts/lilypond-book.py +++ b/scripts/lilypond-book.py @@ -28,6 +28,7 @@ TODO: ''' +import glob import md5 import os import re @@ -41,6 +42,7 @@ import tempfile import lilylib as ly import fontextract +import langdefs global _;_=ly._ @@ -134,6 +136,12 @@ def get_option_parser (): action='store', dest='info_images_dir', default='') + p.add_option ('--latex-program', + help=_ ("run executable PROG instead of latex"), + metavar=_ ("PROG"), + action='store', dest='latex_program', + default='latex') + p.add_option ('--left-padding', metavar=_ ("PAD"), dest="padding_mm", @@ -175,11 +183,6 @@ def get_option_parser (): help=_ ("create PDF files for use with PDFTeX"), default=False) - p.add_option ('', '--psfonts', action="store_true", dest="psfonts", - help=_ ('''extract all PostScript fonts into INPUT.psfonts for LaTeX -must use this with dvips -h INPUT.psfonts'''), - default=None) - p.add_option ('-V', '--verbose', help=_ ("be verbose"), action="store_true", default=False, @@ -210,6 +213,8 @@ global_options = None default_ly_options = { 'alt': "[image of music]" } +document_language = '' + # # Is this pythonic? Personally, I find this rather #define-nesque. --hwn # @@ -242,6 +247,7 @@ QUOTE = 'quote' RAGGED_RIGHT = 'ragged-right' RELATIVE = 'relative' STAFFSIZE = 'staffsize' +DOCTITLE = 'doctitle' TEXIDOC = 'texidoc' TEXINFO = 'texinfo' VERBATIM = 'verbatim' @@ -528,6 +534,7 @@ simple_options = [ NOFRAGMENT, NOINDENT, PRINTFILENAME, + DOCTITLE, TEXIDOC, LANG, VERBATIM, @@ -820,9 +827,31 @@ def verbatim_html (s): re.sub ('<', '<', re.sub ('&', '&', s))) +ly_var_def_re = re.compile (r'^([a-zA-Z]+)[\t ]*=', re.M) +ly_comment_re = re.compile (r'(%+[\t ]*)(.*)$', re.M) + +def ly_comment_gettext (t, m): + return m.group (1) + t (m.group (2)) + +def verb_ly_gettext (s): + if not document_language: + return s + try: + t = langdefs.translation[document_language] + except: + return s + + s = ly_comment_re.sub (lambda m: ly_comment_gettext (t, m), s) + + for v in ly_var_def_re.findall (s): + s = re.sub (r"(?m)(^|[' \\#])%s([^a-zA-Z])" % v, + "\\1" + t (v) + "\\2", + s) + return s texinfo_lang_re = re.compile ('(?m)^@documentlanguage (.*?)( |$)') def set_default_options (source, default_ly_options, format): + global document_language if LINE_WIDTH not in default_ly_options: if format == LATEX: textwidth = get_latex_textwidth (source) @@ -830,9 +859,9 @@ def set_default_options (source, default_ly_options, format): elif format == TEXINFO: m = texinfo_lang_re.search (source) if m and not m.group (1).startswith ('en'): - default_ly_options[LANG] = m.group (1) + document_language = m.group (1) else: - default_ly_options[LANG] = '' + document_language = '' for regex in texinfo_line_widths: # FIXME: @layout is usually not in # chunk #0: @@ -910,7 +939,7 @@ class LilypondSnippet (Snippet): self.do_options (os, self.type) def verb_ly (self): - return self.substring ('code') + return verb_ly_gettext (self.substring ('code')) def ly (self): contents = self.substring ('code') @@ -964,6 +993,12 @@ class LilypondSnippet (Snippet): if k not in self.option_dict: self.option_dict[k] = default_ly_options[k] + # RELATIVE does not work without FRAGMENT; + # make RELATIVE imply FRAGMENT + has_relative = self.option_dict.has_key (RELATIVE) + if has_relative and not self.option_dict.has_key (FRAGMENT): + self.option_dict[FRAGMENT] = None + if not has_line_width: if type == 'lilypond' or FRAGMENT in self.option_dict: self.option_dict[RAGGED_RIGHT] = None @@ -1107,19 +1142,16 @@ class LilypondSnippet (Snippet): return self.checksum def basename (self): - if FILENAME in self.option_dict: - return self.option_dict[FILENAME] - cs = self.get_checksum () - - # TODO: use xx/xxxxx directory layout. - name = 'lily-%s' % cs[:10] + name = '%s/lily-%s' % (cs[:2], cs[2:10]) return name 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') @@ -1140,6 +1172,9 @@ class LilypondSnippet (Snippet): src = os.path.join (output_dir, name) dst = os.path.join (destination, name) + dst_path = os.path.split(dst)[0] + if not os.path.isdir (dst_path): + os.makedirs (dst_path) os.link (src, dst) @@ -1170,12 +1205,22 @@ class LilypondSnippet (Snippet): if not skip_lily: require_file (base + '-systems.count') + if 'ddump-profile' in global_options.process_cmd: + require_file (base + '.profile') + if 'dseparate-log-file' in global_options.process_cmd: + require_file (base + '.log') + map (consider_file, [base + '.tex', base + '.eps', base + '.texidoc', + base + '.doctitle', base + '-systems.texi', base + '-systems.tex', base + '-systems.pdftexi']) + if document_language: + map (consider_file, + [base + '.texidoc' + document_language, + base + '.doctitle' + document_language]) # UGH - junk global_options if (base + '.eps' in result and self.format in (HTML, TEXINFO) @@ -1190,11 +1235,18 @@ class LilypondSnippet (Snippet): system_count = 0 if not skip_lily and not missing: system_count = int(file (full + '-systems.count').read()) + for number in range(1, system_count + 1): systemfile = '%s-%d' % (base, number) require_file (systemfile + '.eps') consider_file (systemfile + '.pdf') - + + # We can't require signatures, since books and toplevel + # markups do not output a signature. + if 'ddump-signature' in global_options.process_cmd: + consider_file (systemfile + '.signature') + + return (result, missing) def is_outdated (self, output_dir, current_files): @@ -1318,9 +1370,16 @@ class LilypondSnippet (Snippet): def output_texinfo (self): str = self.output_print_filename (TEXINFO) base = self.basename () + if DOCTITLE in self.option_dict: + doctitle = base + '.doctitle' + translated_doctitle = doctitle + document_language + if os.path.exists (translated_doctitle): + str += '@lydoctitle %s\n' % open (translated_doctitle).read () + elif os.path.exists (doctitle): + str += '@lydoctitle %s\n' % open (doctitle).read () if TEXIDOC in self.option_dict: texidoc = base + '.texidoc' - translated_texidoc = texidoc + default_ly_options[LANG] + translated_texidoc = texidoc + document_language if os.path.exists (translated_texidoc): str += '@include %(translated_texidoc)s\n\n' % vars () elif os.path.exists (texidoc): @@ -1362,7 +1421,7 @@ class LilypondFileSnippet (LilypondSnippet): s = self.contents s = re_begin_verbatim.split (s)[-1] s = re_end_verbatim.split (s)[0] - return s + return verb_ly_gettext (s) def ly (self): name = self.substring ('filename') @@ -1527,9 +1586,9 @@ def process_snippets (cmd, snippets, checksum = snippet_list_checksum (snippets) contents = '\n'.join (['snippet-map-%d.ly' % checksum] - + [snip.basename() for snip in snippets]) + + [snip.basename() + '.ly' for snip in snippets]) name = os.path.join (lily_output_dir, - 'snippet-names-%d' % checksum) + 'snippet-names-%d.ly' % checksum) file (name, 'wb').write (contents) system_in_directory (' '.join ([cmd, name]), @@ -1568,7 +1627,8 @@ def get_latex_textwidth (source): tmp_handle.write (latex_document) tmp_handle.close () - ly.system ('latex %s' % tmpfile, be_verbose=global_options.verbose) + ly.system ('%s %s' % (global_options.latex_program, tmpfile), + be_verbose=global_options.verbose) parameter_string = file (logfile).read() os.unlink (tmpfile) @@ -1631,11 +1691,23 @@ def write_file_map (lys, name): """ % '\n'.join(['("%s.ly" . "%s")\n' % (ly.basename (), name) for ly in lys])) +def split_output_files(directory): + """Returns directory entries in DIRECTORY/XX/ , where XX are hex digits. + + Return value is a set of strings. + """ + files = [] + for subdir in glob.glob (os.path.join (directory, '[a-f0-9][a-f0-9]')): + base_subdir = os.path.split (subdir)[1] + sub_files = [os.path.join (base_subdir, name) + for name in os.listdir (subdir)] + files += sub_files + return set (files) + def do_process_cmd (chunks, input_name, options): snippets = [c for c in chunks if isinstance (c, LilypondSnippet)] - - output_files = set(os.listdir(options.lily_output_dir)) + output_files = split_output_files (options.lily_output_dir) outdated = [c for c in snippets if c.is_outdated (options.lily_output_dir, output_files)] write_file_map (outdated, input_name) @@ -1654,7 +1726,7 @@ def do_process_cmd (chunks, input_name, options): progress (_ ("All snippets are up to date...")) if options.lily_output_dir != options.output_dir: - output_files = set(os.listdir(options.lily_output_dir)) + output_files = split_output_files (options.lily_output_dir) for snippet in snippets: snippet.link_all_output_files (options.lily_output_dir, output_files, @@ -1702,9 +1774,14 @@ def write_if_updated (file_name, lines): # this prevents make from always rerunning lilypond-book: # output file must be touched in order to be up to date os.utime (file_name, None) + return except: pass + output_dir = os.path.dirname (file_name) + if not os.path.exists (output_dir): + os.makedirs (output_dir) + progress (_ ("Writing `%s'...") % file_name) file (file_name, 'w').writelines (lines) progress ('\n') @@ -1723,7 +1800,7 @@ def samefile (f1, f2): f2 = re.sub ("//*", "/", f2) return f1 == f2 -def do_file (input_filename): +def do_file (input_filename, included=False): # Ugh. if not input_filename or input_filename == '-': in_handle = sys.stdin @@ -1741,6 +1818,8 @@ def do_file (input_filename): if input_filename == '-': input_base = 'stdin' + elif included: + input_base = os.path.splitext (input_filename)[0] else: input_base = os.path.basename ( os.path.splitext (input_filename)[0]) @@ -1811,7 +1890,7 @@ def do_file (input_filename): name = snippet.substring ('filename') progress (_ ("Processing include: %s") % name) progress ('\n') - return do_file (name) + return do_file (name, included=True) include_chunks = map (process_include, filter (lambda x: isinstance (x, IncludeSnippet), @@ -1863,8 +1942,13 @@ def main (): + ' --formats=%s -dbackend=eps ' % formats) if global_options.process_cmd: - global_options.process_cmd += ' '.join ([(' -I %s' % ly.mkarg (p)) - for p in global_options.include_path]) + includes = global_options.include_path + if global_options.lily_output_dir: + # This must be first, so lilypond prefers to read .ly + # files in the other lybookdb dir. + includes = [os.path.abspath(global_options.lily_output_dir)] + includes + global_options.process_cmd += ' '.join ([' -I %s' % ly.mkarg (p) + for p in includes]) if global_options.format in (TEXINFO, LATEX): ## prevent PDF from being switched on by default. @@ -1878,7 +1962,7 @@ def main (): if global_options.padding_mm: global_options.process_cmd += " -deps-box-padding=%f " % global_options.padding_mm - global_options.process_cmd += " -dread-file-list " + global_options.process_cmd += " -dread-file-list -dno-strip-output-dir" if global_options.lily_output_dir: global_options.lily_output_dir = os.path.abspath(global_options.lily_output_dir)