import re
import os
import copy
-# TODO: We are using os.popen3, which has been deprecated since python 2.6. The
-# suggested replacement is the Popen function of the subprocess module.
-# Unfortunately, on windows this needs the msvcrt module, which doesn't seem
-# to be available in GUB?!?!?!
-# from subprocess import Popen, PIPE
+import shutil
+import subprocess
+import sys
progress = ly.progress
warning = ly.warning
error = ly.error
+debug = ly.debug_output
FILTER = 'filter'
FRAGMENT = 'fragment'
LAYOUT = 'layout'
-LILYQUOTE = 'lilyquote'
LINE_WIDTH = 'line-width'
NOFRAGMENT = 'nofragment'
NOGETTEXT = 'nogettext'
NOINDENT = 'noindent'
-NOQUOTE = 'noquote'
INDENT = 'indent'
NORAGGED_RIGHT = 'noragged-right'
NOTES = 'body'
# 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,
},
##
+ # TODO: Remove the 1mm additional padding in the line-width
+ # once lilypond creates tighter cropped images!
PAPER: {
PAPERSIZE: r'''#(set-paper-size "%(papersize)s")''',
INDENT: r'''indent = %(indent)s''',
- LINE_WIDTH: r'''line-width = %(line-width)s''',
- QUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s''',
- LILYQUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s''',
+ LINE_WIDTH: r'''line-width = %(line-width)s
+ %% offset the left padding, also add 1mm as lilypond creates cropped
+ %% images with a little space on the right
+ line-width = #(- line-width (* mm %(padding_mm)f) (* mm 1))''',
+ QUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s
+ %% offset the left padding, also add 1mm as lilypond creates cropped
+ %% images with a little space on the right
+ line-width = #(- line-width (* mm %(padding_mm)f) (* mm 1))''',
RAGGED_RIGHT: r'''ragged-right = ##t''',
NORAGGED_RIGHT: r'''ragged-right = ##f''',
},
def classic_lilypond_book_compatibility (key, value):
+ if key == 'lilyquote':
+ return (QUOTE, value)
if key == 'singleline' and value == None:
return (RAGGED_RIGHT, None)
\paper {
%(paper_string)s
- line-width = #(- line-width (* mm %(padding_mm)f))
}
\layout {
class LilypondSnippet (Snippet):
def __init__ (self, type, match, formatter, line_number, global_options):
Snippet.__init__ (self, type, match, formatter, line_number, global_options)
+ self.filename = ''
+ self.ext = '.ly'
os = match.group ('options')
- self.do_options (os, self.type)
+ self.parse_snippet_options (os, self.type)
def snippet_options (self):
def split_options (self, option_string):
return self.formatter.split_snippet_options (option_string);
- def do_options (self, option_string, type):
- self.option_dict = {}
+ def parse_snippet_options (self, option_string, type):
+ self.snippet_option_dict = {}
+ # Split option string and create raw option_dict from it
options = self.split_options (option_string)
for option in options:
+ (key, value) = (option, None)
if '=' in option:
(key, value) = re.split ('\s*=\s*', option)
- self.option_dict[key] = value
else:
- if option in no_options:
- if no_options[option] in self.option_dict:
- del self.option_dict[no_options[option]]
+ # a no... option removes a previous option if present!
+ if key in no_options:
+ if no_options[key] in self.option_dict:
+ del self.snippet_option_dict[no_options[key]]
+ key = None
+ # Check for deprecated options, replace them by new ones
+ (c_key, c_value) = classic_lilypond_book_compatibility (key, value)
+ if c_key:
+ if c_value:
+ warning (
+ _ ("deprecated ly-option used: %s=%s") % (key, value))
+ warning (
+ _ ("compatibility mode translation: %s=%s") % (c_key, c_value))
else:
- self.option_dict[option] = None
-
+ warning (
+ _ ("deprecated ly-option used: %s") % key)
+ warning (
+ _ ("compatibility mode translation: %s") % c_key)
+ (key, value) = (c_key, c_value)
+ # Finally, insert the option:
+ if key:
+ self.snippet_option_dict[key] = value
# If LINE_WIDTH is used without parameter, set it to default.
- has_line_width = self.option_dict.has_key (LINE_WIDTH)
- if has_line_width and self.option_dict[LINE_WIDTH] == None:
- has_line_width = False
- del self.option_dict[LINE_WIDTH]
-
- # TODO: Can't we do that more efficiently (built-in python func?)
- for k in self.formatter.default_snippet_options:
- if k not in self.option_dict:
- self.option_dict[k] = self.formatter.default_snippet_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
-
- if type == 'lilypond':
- if LINE_WIDTH in self.option_dict:
- del self.option_dict[LINE_WIDTH]
+ has_line_width = self.snippet_option_dict.has_key (LINE_WIDTH)
+ if has_line_width and self.snippet_option_dict[LINE_WIDTH] == None:
+ del self.snippet_option_dict[LINE_WIDTH]
+
+ # RELATIVE does not work without FRAGMENT, so imply that
+ if self.snippet_option_dict.has_key (RELATIVE):
+ self.snippet_option_dict[FRAGMENT] = None
+
+ # Now get the default options from the formatter object (HTML, latex,
+ # texinfo, etc.) and insert the explicit snippet options to get the
+ # list of all options for this snippet
+ # first, make sure we have an INDENT value as a fallback
+ self.option_dict = {INDENT: '0\\mm'};
+ self.option_dict.update (self.formatter.default_snippet_options);
+ self.option_dict.update (self.snippet_option_dict);
+
+ # also construct a list of all options (as strings) that influence the
+ # visual appearance of the snippet
+ lst = filter (lambda (x,y): x not in PROCESSING_INDEPENDENT_OPTIONS,
+ self.option_dict.iteritems ());
+ option_list = []
+ for (key, value) in lst:
+ if value == None:
+ option_list.append (key)
else:
- if RAGGED_RIGHT in self.option_dict:
- if LINE_WIDTH in self.option_dict:
- del self.option_dict[LINE_WIDTH]
-
- if QUOTE in self.option_dict or type == 'lilypond':
- if LINE_WIDTH in self.option_dict:
- del self.option_dict[LINE_WIDTH]
+ option_list.append (key + "=" + value)
+ option_list.sort ()
+ self.outputrelevant_option_list = option_list
+ #print ("self.outputrelevant_option_list: %s\n" % self.outputrelevant_option_list);
- if not INDENT in self.option_dict:
- self.option_dict[INDENT] = '0\\mm'
# 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
+ #if not LINE_WIDTH in self.option_dict:
+ #if not QUOTE in self.option_dict:
+ #self.option_dict[LINE_WIDTH] = "#(- paper-width \
+#left-margin-default right-margin-default)"
+
+ # Get a list of all options (as string) that influence the snippet appearance
+ def get_outputrelevant_option_strings (self):
+ return self.outputrelevant_option_list
def compose_ly (self, code):
#
# To set @exampleindent locally to zero, we use the @format
# environment for non-quoted snippets.
+ #
+ # Update: since July 2011 we are running texinfo on a test file
+ # to detect the default exampleindent, so we might reintroduce
+ # further usage of exampleindent again.
+ #
+ # First, make sure we have some falback default value, auto-detected
+ # values and explicitly specified values will always override them:
override[EXAMPLEINDENT] = r'0.4\in'
- override[LINE_WIDTH] = '5\\in' # = texinfo_line_widths['@smallbook']
+ override[LINE_WIDTH] = '5\\in'
override.update (self.formatter.default_snippet_options)
+ override['padding_mm'] = self.global_options.padding_mm
- option_list = []
- for option in self.get_option_list ():
- for name in PROCESSING_INDEPENDENT_OPTIONS:
- if option.startswith (name):
- break
- else:
- option_list.append (option)
- option_string = ','.join (option_list)
+ option_string = ','.join (self.get_outputrelevant_option_strings ())
compose_dict = {}
compose_types = [NOTES, PREAMBLE, LAYOUT, PAPER]
for a in compose_types:
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:
- warning (
- _ ("deprecated ly-option used: %s=%s") % (key, value))
- warning (
- _ ("compatibility mode translation: %s=%s") % (c_key, c_value))
- else:
- warning (
- _ ("deprecated ly-option used: %s") % key)
- warning (
- _ ("compatibility mode translation: %s") % c_key)
-
- (key, value) = (c_key, c_value)
if value:
override[key] = value
# 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)
+ for option in self.get_outputrelevant_option_strings ():
+ hash.update (option)
## let's not create too long names.
self.checksum = hash.hexdigest ()[:10]
def basename (self):
cs = self.get_checksum ()
- name = '%s/lily-%s' % (cs[:2], cs[2:])
+ name = os.path.join (cs[:2], 'lily-%s' % cs[2:])
return name
final_basename = basename
existing = open (filename, 'r').read ()
if self.relevant_contents (existing) != self.relevant_contents (self.full_ly ()):
- warning ("%s: duplicate filename but different contents of orginal file,\n\
+ warning ("%s: duplicate filename but different contents of original file,\n\
printing diff against existing file." % filename)
ly.stderr_write (self.filter_pipe (self.full_ly (), 'diff -u %s -' % filename))
else:
def link_all_output_files (self, output_dir, output_dir_files, destination):
existing, missing = self.all_output_files (output_dir, output_dir_files)
if missing:
- print '\nMissing', missing
+ error (_ ('Missing files: %s') % ', '.join (missing))
raise CompileError(self.basename())
for name in existing:
if (self.global_options.use_source_file_names
dst_path = os.path.split(dst)[0]
if not os.path.isdir (dst_path):
os.makedirs (dst_path)
- os.link (src, dst)
+ try:
+ if (self.global_options.use_source_file_names
+ and isinstance (self, LilypondFileSnippet)):
+ fout = open (dst, 'w')
+ fin = open (src, 'r')
+ for line in fin.readlines ():
+ fout.write (line.replace (self.basename (), self.final_basename ()))
+ fout.close ()
+ fin.close ()
+ else:
+ try:
+ os.link (src, dst)
+ except AttributeError:
+ shutil.copyfile (src, dst)
+ except (IOError, OSError):
+ error (_ ('Could not overwrite file %s') % dst)
+ raise CompileError(self.basename())
def additional_files_to_consider (self, base, full):
return []
def additional_files_required (self, base, full):
- return []
+ result = [];
+ if self.ext != '.ly':
+ result.append (base + self.ext)
+ return result
def all_output_files (self, output_dir, output_dir_files):
map (consider_file, [base + '.tex',
base + '.eps',
+ base + '.pdf',
base + '.texidoc',
base + '.doctitle',
base + '-systems.texi',
def filter_pipe (self, input, cmd):
"""Pass input through cmd, and return the result."""
- if self.global_options.verbose:
- progress (_ ("Running through filter `%s'\n") % cmd)
+ debug (_ ("Running through filter `%s'") % cmd, True)
- # TODO: Use Popen once we resolve the problem with msvcrt in Windows:
- (stdin, stdout, stderr) = os.popen3 (cmd)
- # p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
- # (stdin, stdout, stderr) = (p.stdin, p.stdout, p.stderr)
+ closefds = True
+ if (sys.platform == "mingw32"):
+ closefds = False
+ p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=closefds)
+ (stdin, stdout, stderr) = (p.stdin, p.stdout, p.stderr)
stdin.write (input)
status = stdin.close ()
ly.stderr_write (stderr.read ())
exit (status)
- if self.global_options.verbose:
- progress ('\n')
+ debug ('\n')
return output
def __init__ (self, type, match, formatter, line_number, global_options):
LilypondSnippet.__init__ (self, type, match, formatter, line_number, global_options)
self.filename = self.substring ('filename')
- self.ext = os.path.splitext (os.path.basename (self.filename))[1]
- self.contents = file (BookBase.find_file (self.filename, global_options.include_path)).read ()
+ self.contents = file (BookBase.find_file (self.filename,
+ global_options.include_path, global_options.original_dir)).read ()
def get_snippet_code (self):
return self.contents;
LilypondFileSnippet.__init__ (self, type, match, formatter, line_number, global_options)
self.compressed = False
self.converted_ly = None
+ self.ext = os.path.splitext (os.path.basename (self.filename))[1]
self.musicxml_options_dict = {
'verbose': '--verbose',
'lxml': '--lxml',
return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s'
% (name, self.converted_ly))
- def additional_files_required (self, base, full):
- result = [];
- if self.compressed:
- result.append (base + '.mxl')
- else:
- result.append (base + '.xml')
- return result
-
def write_ly (self):
base = self.basename ()
path = os.path.join (self.global_options.lily_output_dir, base)
if os.path.exists (xmlfilename):
diff_against_existing = self.filter_pipe (self.contents, 'diff -u %s - ' % xmlfilename)
if diff_against_existing:
- warning (_ ("%s: duplicate filename but different contents of orginal file,\n\
+ warning (_ ("%s: duplicate filename but different contents of original file,\n\
printing diff against existing file.") % xmlfilename)
ly.stderr_write (diff_against_existing)
else: