X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Flilypond-book.py;h=5aec278f0eba5a8c6a6af8c0c6ea1ed27c3b475c;hb=f27a0658b549dc41903280c62388e76df87523f0;hp=e9ed11648ef10c3122351e90192a8edc58bd2758;hpb=1e9b18e75fd10a2d7e60fd3a6c84571bc0fc9277;p=lilypond.git diff --git a/scripts/lilypond-book.py b/scripts/lilypond-book.py index e9ed11648e..5aec278f0e 100644 --- a/scripts/lilypond-book.py +++ b/scripts/lilypond-book.py @@ -1,122 +1,143 @@ #!@PYTHON@ ''' -TODO: - ly-options: intertext, quote ? - --linewidth? - eps in latex? - check latex parameters, twocolumn, multicolumn? - Example usage: test: lilypond-book --filter="tr '[a-z]' '[A-Z]'" BOOK - + convert-ly on book: lilypond-book --filter="convert-ly --no-version --from=1.6.11 -" BOOK classic lilypond-book: - lilypond-book --process="lilypond-bin" BOOK.tely + lilypond-book --process="lilypond" BOOK.tely + +TODO: + * ly-options: intertext? + * --linewidth? + * eps in latex / eps by lilypond -b ps? + * check latex parameters, twocolumn, multicolumn? + * use --png --ps --pdf for making images? + + * Converting from lilypond-book source, substitute: + @mbinclude foo.itely -> @include foo.itely + \mbinput -> \input - must substitute: - @mbinclude foo.itely -> @include foo.itely - \mbinput -> \input - ''' import __main__ import glob +import stat import string -################################################################ # Users of python modules should include this snippet # and customize variables below. -# We'll suffer this path init stuff as long as we don't install our -# python packages in /lib/pythonx.y (and don't kludge around -# it as we do with teTeX on Red Hat Linux: set some environment var -# (PYTHONPATH) in profile) +# We'll suffer this path initialization stuff as long as we don't install +# our python packages in /lib/pythonX.Y (and don't kludge around +# it as we do with teTeX on Red Hat Linux: set some environment variables +# (PYTHONPATH) in `etc/profile'). -# If set, LILYPONDPREFIX must take prevalence -# if datadir is not set, we're doing a build and LILYPONDPREFIX +# If set, LILYPONDPREFIX must take prevalence. +# if datadir is not set, we're doing a build and LILYPONDPREFIX. import getopt, os, sys datadir = '@local_lilypond_datadir@' if not os.path.isdir (datadir): datadir = '@lilypond_datadir@' -if os.environ.has_key ('LILYPONDPREFIX') : +if os.environ.has_key ('LILYPONDPREFIX'): datadir = os.environ['LILYPONDPREFIX'] while datadir[-1] == os.sep: datadir= datadir[:-1] sys.path.insert (0, os.path.join (datadir, 'python')) -# Customize these +# Customize these. #if __name__ == '__main__': import lilylib as ly global _;_=ly._ global re;re = ly.re - -# lilylib globals +# Lilylib globals. program_version = '@TOPLEVEL_VERSION@' -program_name = 'lilypond-book' +program_name = sys.argv[0] verbose_p = 0 pseudo_filter_p = 0 original_dir = os.getcwd () +backend = 'ps' - -help_summary = _ ("""Process LilyPond snippets in hybrid HTML, LaTeX or texinfo document. Example usage: +help_summary = _ ( +'''Process LilyPond snippets in hybrid HTML, LaTeX, or texinfo document. +Example usage: lilypond-book --filter="tr '[a-z]' '[A-Z]'" BOOK lilypond-book --filter="convert-ly --no-version --from=2.0.0 -" BOOK - lilypond-book --process='lilypond-bin -I include' BOOK - -""") + lilypond-book --process='lilypond -I include' BOOK +''') copyright = ('Jan Nieuwenhuizen ', 'Han-Wen Nienhuys ') option_definitions = [ - (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")), - (_ ("FILTER"), 'F', 'filter', _ ("pipe snippets through FILTER [convert-ly -n -]")), - ('', 'h', 'help', _ ("print this help")), - (_ ("DIR"), 'I', 'include', _ ("add DIR to include path")), - (_ ("COMMAND"), 'P', 'process', _ ("process ly_files using COMMAND FILE...")), - (_ ("DIR"), 'o', 'output', _ ("write output to DIR")), - ('', 'V', 'verbose', _ ("be verbose")), - ('', 'v', 'version', _ ("print version information")), - ('', 'w', 'warranty', _ ("show warranty and copyright")), - ] + (_ ("EXT"), 'f', 'format', + _ ('''use output format EXT (texi [default], texi-html, + latex, html)''')), + (_ ("FILTER"), 'F', 'filter', + _ ("pipe snippets through FILTER [convert-ly -n -]")), + ('', 'h', 'help', + _ ("print this help")), + (_ ("DIR"), 'I', 'include', + _ ("add DIR to include path")), + (_ ("DIR"), 'o', 'output', + _ ("write output to DIR")), + (_ ("COMMAND"), 'P', 'process', + _ ("process ly_files using COMMAND FILE...")), + ('', 'V', 'verbose', + _ ("be verbose")), + ('', 'v', 'version', + _ ("print version information")), + ('', 'w', 'warranty', + _ ("show warranty and copyright")), +] include_path = [ly.abspath (os.getcwd ())] -lilypond_binary = os.path.join ('@bindir@', 'lilypond-bin') +lilypond_binary = os.path.join ('@bindir@', 'lilypond') -# only use installed binary when we're installed too. +# Only use installed binary when we are installed too. if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary): - lilypond_binary = 'lilypond-bin' - + lilypond_binary = 'lilypond' use_hash_p = 1 format = 0 output_name = 0 latex_filter_cmd = 'latex "\\nonstopmode \input /dev/stdin"' filter_cmd = 0 -process_cmd = lilypond_binary +process_cmd = '' default_ly_options = {} +# +# Is this pythonic? Personally, I find this rather #define-nesque. --hwn +# AFTER = 'after' BEFORE = 'before' +EXAMPLEINDENT = 'exampleindent' +FILTER = 'filter' FRAGMENT = 'fragment' HTML = 'html' +INDENT = 'indent' LATEX = 'latex' +LAYOUT = 'layout' LINEWIDTH = 'linewidth' NOFRAGMENT = 'nofragment' +NOINDENT = 'noindent' +NOQUOTE = 'noquote' NOTES = 'body' +NOTIME = 'notime' OUTPUT = 'output' PAPER = 'paper' PREAMBLE = 'preamble' PRINTFILENAME = 'printfilename' +QUOTE = 'quote' RAGGEDRIGHT = 'raggedright' RELATIVE = 'relative' STAFFSIZE = 'staffsize' @@ -124,232 +145,577 @@ TEXIDOC = 'texidoc' TEXINFO = 'texinfo' VERBATIM = 'verbatim' -# Recognize special sequences in the input +# NOTIME has no opposite so it isn't part of this dictionary. +# NOQUOTE is used internally only. +no_options = { + NOFRAGMENT: FRAGMENT, + NOINDENT: INDENT, +} + +# Recognize special sequences in the input. # -# (?Pregex) -- assign result of REGEX to NAME -# *? -- match non-greedily. -# (?m) -- multiline regex: make ^ and $ match at each line -# (?s) -- make the dot match all characters including newline +# (?Pregex) -- Assign result of REGEX to NAME. +# *? -- Match non-greedily. +# (?m) -- Multiline regex: Make ^ and $ match at each line. +# (?s) -- Make the dot match all characters including newline. +# (?x) -- Ignore whitespace in patterns. no_match = 'a\ba' snippet_res = { + ## HTML: { - 'include': no_match, - 'lilypond' : '(?m)(?P[^:]*):)(?P.*?)/>)', - 'lilypond_block': r'''(?ms)(?P[^>]+)?>(?P.*?))''', - 'lilypond_file': r'(?m)(?P[^>]+)?>\s*(?P[^<]+)\s*)', - 'multiline_comment': r"(?sm)\s*(?!@c\s+)(?P)\s", - 'singleline_comment': no_match, - 'verb': r'''(?P
.*?
)''', - 'verbatim': r'''(?s)(?P
\s.*?
\s)''', + 'include': + no_match, + + 'lilypond': + r'''(?mx) + (?P + .*?)\s*:)?\s* + (?P.*?) + />)''', + + 'lilypond_block': + r'''(?msx) + (?P + .*?)\s* + > + (?P.*?) + )''', + + 'lilypond_file': + r'''(?mx) + (?P + .*?)\s* + > + \s*(?P.*?)\s* + )''', + + 'multiline_comment': + r'''(?smx) + (?P + \s*(?!@c\s+) + (?P) + \s)''', + + 'singleline_comment': + no_match, + + 'verb': + r'''(?x) + (?P + (?P
.*?
))''', + + 'verbatim': + r'''(?x) + (?s) + (?P + (?P
\s.*?
\s))''', }, + ## LATEX: { - 'include': r'(?m)^[^%\n]*?(?P\\input{(?P[^}]+)})', - 'lilypond' : r'(?m)^[^%\n]*?(?P\\lilypond\s*(\[(?P.*?)\])?\s*{(?P.*?)})', - 'lilypond_block': r"(?sm)^[^%\n]*?(?P\\begin\s*(\[(?P.*?)\])?\s*{lilypond}(?P.*?)\\end{lilypond})", - 'lilypond_file': r'(?m)^[^%\n]*?(?P\\lilypondfile\s*(\[(?P.*?)\])?\s*\{(?P.+)})', - 'multiline_comment': no_match, - 'singleline_comment': r"(?m)^.*?(?P(?P^%.*$\n+))", - 'verb': r"(?P\\verb(?P.).*?(?P=del))", - 'verbatim': r"(?s)(?P\\begin\s*{verbatim}.*?\\end{verbatim})", + 'include': + r'''(?smx) + ^[^%\n]*? + (?P + \\input\s*{ + (?P\S+?) + })''', + + 'lilypond': + r'''(?smx) + ^[^%\n]*? + (?P + \\lilypond\s*( + \[ + \s*(?P.*?)\s* + \])?\s*{ + (?P.*?) + })''', + + 'lilypond_block': + r'''(?smx) + ^[^%\n]*? + (?P + \\begin\s*( + \[ + \s*(?P.*?)\s* + \])?\s*{lilypond} + (?P.*?) + ^[^%\n]*? + \\end\s*{lilypond})''', + + 'lilypond_file': + r'''(?smx) + ^[^%\n]*? + (?P + \\lilypondfile\s*( + \[ + \s*(?P.*?)\s* + \])?\s*\{ + (?P\S+?) + })''', + + 'multiline_comment': + no_match, + + 'singleline_comment': + r'''(?mx) + ^.*? + (?P + (?P + %.*$\n+))''', + + 'verb': + r'''(?mx) + ^[^%\n]*? + (?P + (?P + \\verb(?P.) + .*? + (?P=del)))''', + + 'verbatim': + r'''(?msx) + ^[^%\n]*? + (?P + (?P + \\begin\s*{verbatim} + .*? + \\end\s*{verbatim}))''', }, + ## TEXINFO: { - 'include': '(?m)^[^%\n]*?(?P@include\s+(?P\S+))', - 'lilypond' : '(?m)^(?P@lilypond(\[(?P[^]]*)\])?{(?P.*?)})', - 'lilypond_block': r'''(?ms)^(?P@lilypond(\[(?P[^]]*)\])?\s(?P.*?)@end lilypond)\s''', - 'lilypond_file': '(?m)^(?P@lilypondfile(\[(?P[^]]*)\])?{(?P[^}]+)})', - 'multiline_comment': r'(?sm)^\s*(?!@c\s+)(?P@ignore\s.*?@end\s+ignore)\s', - 'singleline_comment': r'(?m)^.*(?P(?P@c([ \t][^\n]*|)\n))', - -# don't do this: fucks up with @code{@{} -# 'verb': r'''(?P@code{.*?})''', - 'verbatim': r'''(?s)(?P@example\s.*?@end\s+example\s)''', + 'include': + r'''(?mx) + ^(?P + @include\s+ + (?P\S+))''', + + 'lilypond': + r'''(?smx) + ^[^\n]*?(?!@c\s+)[^\n]*? + (?P + @lilypond\s*( + \[ + \s*(?P.*?)\s* + \])?\s*{ + (?P.*?) + })''', + + 'lilypond_block': + r'''(?msx) + ^(?P + @lilypond\s*( + \[ + \s*(?P.*?)\s* + \])?\s+? + ^(?P.*?) + ^@end\s+lilypond)\s''', + + 'lilypond_file': + r'''(?mx) + ^(?P + @lilypondfile\s*( + \[ + \s*(?P.*?)\s* + \])?\s*{ + (?P\S+) + })''', + + 'multiline_comment': + r'''(?smx) + ^(?P + (?P + @ignore\s + .*? + @end\s+ignore))\s''', + + 'singleline_comment': + r'''(?mx) + ^.* + (?P + (?P + @c([ \t][^\n]*|)\n))''', + + # Don't do this: It interferes with @code{@{}. + # 'verb': r'''(?P@code{.*?})''', + + 'verbatim': + r'''(?sx) + (?P + (?P + @example + \s.*? + @end\s+example\s))''', }, - } +} format_res = { HTML: { - 'option-sep' : '\s*', - 'intertext': r',?\s*intertext=\".*?\"', + 'intertext': r',?\s*intertext=\".*?\"', + 'option_sep': '\s*', }, + LATEX: { - 'intertext': r',?\s*intertext=\".*?\"', - 'option-sep' : ',\s*', + 'intertext': r',?\s*intertext=\".*?\"', + 'option_sep': '\s*,\s*', }, + TEXINFO: { - 'intertext': r',?\s*intertext=\".*?\"', - 'option-sep' : ',\s*', + 'intertext': r',?\s*intertext=\".*?\"', + 'option_sep': '\s*,\s*', }, - } +} + +# Options without a pattern in ly_options. +simple_options = [ + EXAMPLEINDENT, + FRAGMENT, + NOFRAGMENT, + NOINDENT, + PRINTFILENAME, + TEXIDOC, + VERBATIM +] ly_options = { + ## NOTES: { - RELATIVE: r'''\relative c%(relative_quotes)s''', + RELATIVE: r'''\relative c%(relative_quotes)s''', }, + + ## PAPER: { - 'indent' : r''' - indent = %(indent)s''', - 'linewidth' : r''' - linewidth = %(linewidth)s''', - 'noindent' : r''' - indent = 0.0\mm''', - 'notime' : r''' - \context { - \StaffContext - \remove Time_signature_engraver - }''', - RAGGEDRIGHT : r''' - indent = 0.0\mm - raggedright = ##t''', + INDENT: r'''indent = %(indent)s''', + + LINEWIDTH: r'''linewidth = %(linewidth)s''', + + QUOTE: r'''linewidth = %(linewidth)s - 2.0 * %(exampleindent)s''', + + RAGGEDRIGHT: r'''raggedright = ##t''', }, + + ## + LAYOUT: { + NOTIME: r'''\context { + \Staff + \remove Time_signature_engraver + }''', + }, + + ## PREAMBLE: { - STAFFSIZE: r''' -#(set-global-staff-size %(staffsize)s)''', + STAFFSIZE: r'''#(set-global-staff-size %(staffsize)s)''', }, - } +} output = { - HTML : { - AFTER: r''' + ## + HTML: { + FILTER: r''' +%(code)s + +''', + + AFTER: r'''

''', - BEFORE: r''' -

+ + BEFORE: r'''

''', - OUTPUT: r''' + + OUTPUT: r''' [image of music]''', - PRINTFILENAME:'

%(filename)s

', - VERBATIM: r'''
+	 border="0" src="%(image)s" alt="[image of music]">''',
+
+		PRINTFILENAME: '

%(filename)s

', + + QUOTE: r'''
+%(str)s +
+''', + + VERBATIM: r'''
 %(verb)s
''', }, - LATEX : { - AFTER: '', - BEFORE: '', - OUTPUT: r'''{\parindent 0pt + ## + LATEX: { + OUTPUT: r'''{%% +\parindent 0pt \catcode`\@=12 -\ifx\preLilyPondExample\preLilyPondExample\fi -\def\lilypondbook{} -\input %(base)s.tex -\ifx\preLilyPondExample\postLilyPondExample\fi -\catcode`\@=0}''', - PRINTFILENAME: '''\\texttt{%(filename)s} - +\ifx\preLilyPondExample \undefined + \relax +\else + \preLilyPondExample +\fi +\def\lilypondbook{}%% +\input %(base)s-systems.tex +\ifx\postLilyPondExample \undefined + \relax +\else + \postLilyPondExample +\fi +}''', + + PRINTFILENAME: '''\\texttt{%(filename)s} ''', - VERBATIM: r'''\begin{verbatim} + + QUOTE: r'''\begin{quotation} +%(str)s +\end{quotation} +''', + + VERBATIM: r'''\noindent +\begin{verbatim} %(verb)s\end{verbatim} ''', + + FILTER: r'''\begin{lilypond}[%(options)s] +%(code)s +\end{lilypond}''', }, - TEXINFO : { - AFTER: '', - BEFORE: '', - OUTPUT: r''' - @image{%(base)s,,,[image of music],%(ext)s} -''', - PRINTFILENAME: '''@file{%(filename)s} + ## + TEXINFO: { + FILTER: r'''@lilypond[%(options)s] +%(code)s +@lilypond''', + + OUTPUT: r'''@noindent +@iftex +@include %(base)s-systems.texi +@end iftex +@ifnottex +@image{%(base)s,,,[image of music],%(ext)s} +@end ifnottex +''', + PRINTFILENAME: '''@file{%(filename)s} ''', - VERBATIM: r'''@example + + QUOTE: r'''@quotation +%(str)s@end quotation +''', + + NOQUOTE: r'''@format +%(str)s@end format +''', + + VERBATIM: r'''@exampleindent 0 +@example %(verb)s@end example ''', }, - } +} + +PREAMBLE_LY = r'''%%%% Generated by %(program_name)s +%%%% Options: [%(option_string)s] -PREAMBLE_LY = r'''%% Generated by %(program_name)s -%% Options: [%(option_string)s] +#(set! toplevel-score-handler ly:parser-print-score) +#(set! toplevel-music-handler (lambda (p m) + (ly:parser-print-score + p (ly:music-scorify m p)))) + +#(define version-seen? #t) %(preamble_string)s -\paper {%(paper_string)s + +\paper { + #(define dump-extents #t) + %(paper_string)s +} + +\layout { + %(layout_string)s +} +''' + +FRAGMENT_LY = r''' +%(notes_string)s +{ +%% ly snippet contents follows: +%(code)s +%% end ly snippet } -''' +''' -FRAGMENT_LY = r'''\score{ - \notes%(notes_string)s{ - %(code)s } -}''' -FULL_LY = '%(code)s' +FULL_LY = ''' +%% ly snippet: +%(code)s +%% end ly snippet +''' texinfo_linewidths = { - '@afourpaper': '160 \\mm', - '@afourwide': '6.5 \\in', - '@afourlatex': '150 \\mm', - '@smallbook': '5 \\in' , + '@afourpaper': '160\\mm', + '@afourwide': '6.5\\in', + '@afourlatex': '150\\mm', + '@smallbook': '5\\in', '@letterpaper': '6\\in', - } +} -def classic_lilypond_book_compatibility (o): - if o == 'singleline': - return RAGGEDRIGHT - m = re.search ('relative\s*([-0-9])', o) - if m: - return 'relative=%s' % m.group (1) - m = re.match ('([0-9]+)pt', o) - if m: - return 'staffsize=%s' % m.group (1) - m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o) +def classic_lilypond_book_compatibility (key, value): + if key == 'singleline' and value == None: + return (RAGGEDRIGHT, None) + + m = re.search ('relative\s*([-0-9])', key) if m: - f = float (m.group (1)) - return 'indent=%f\\%s' % (f, m.group (2)) - m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o) + return ('relative', m.group (1)) + + m = re.match ('([0-9]+)pt', key) if m: - f = float (m.group (1)) - return 'linewidth=%f\\%s' % (f, m.group (2)) - return None + return ('staffsize', m.group (1)) + + if key == 'indent' or key == 'linewidth': + m = re.match ('([-.0-9]+)(cm|in|mm|pt|staffspace)', value) + if m: + f = float (m.group (1)) + return (key, '%f\\%s' % (f, m.group (2))) + + return (None, None) + +def compose_ly (code, options, type): + option_dict = {} + + for i in options: + if string.find (i, '=') > 0: + (key, value) = re.split ('\s*=\s*', i) + option_dict[key] = value + else: + if i in no_options.keys (): + if no_options[i] in option_dict.keys (): + del option_dict[no_options[i]] + else: + option_dict[i] = None + + has_linewidth = option_dict.has_key (LINEWIDTH) + no_linewidth_value = 0 + + # If LINEWIDTH is used without parameter, set it to default. + if has_linewidth and option_dict[LINEWIDTH] == None: + no_linewidth_value = 1 + del option_dict[LINEWIDTH] -def compose_ly (code, options): - #Hmm for i in default_ly_options.keys (): - if i not in options: - options.append (i) - - m = re.search (r'''\\score''', code) - if not m and (not options \ - or not NOFRAGMENT in options or FRAGMENT in options): - if RAGGEDRIGHT not in options: - options.append (RAGGEDRIGHT) + if i not in option_dict.keys (): + option_dict[i] = default_ly_options[i] + + if not has_linewidth: + if type == 'lilypond' or FRAGMENT in option_dict.keys (): + option_dict[RAGGEDRIGHT] = None + + if type == 'lilypond': + if LINEWIDTH in option_dict.keys (): + del option_dict[LINEWIDTH] + else: + if RAGGEDRIGHT in option_dict.keys (): + if LINEWIDTH in option_dict.keys (): + del option_dict[LINEWIDTH] + + if QUOTE in option_dict.keys () or type == 'lilypond': + if LINEWIDTH in option_dict.keys (): + del option_dict[LINEWIDTH] + + if not INDENT in option_dict.keys (): + option_dict[INDENT] = '0\\mm' + + # The QUOTE pattern from ly_options only emits the `linewidth' + # keyword. + if has_linewidth and QUOTE in option_dict.keys (): + if no_linewidth_value: + del option_dict[LINEWIDTH] + else: + del option_dict[QUOTE] + + if FRAGMENT in option_dict.keys (): body = FRAGMENT_LY else: body = FULL_LY - # defaults - relative = 0 - staffsize = 16 + # Defaults. + relative = 1 override = {} + # The original concept of the `exampleindent' option is broken. + # It is not possible to get a sane value for @exampleindent at all + # without processing the document itself. Saying + # + # @exampleindent 0 + # @example + # ... + # @end example + # @exampleindent 5 + # + # causes ugly results with the DVI backend of texinfo since the + # default value for @exampleindent isn't 5em but 0.4in (or a smaller + # value). Executing the above code changes the environment + # indentation to an unknown value because we don't know the amount + # of 1em in advance since it is font-dependent. Modifying + # @exampleindent in the middle of a document is simply not + # supported within texinfo. + # + # As a consequence, the only function of @exampleindent is now to + # specify the amount of indentation for the `quote' option. + # + # To set @exampleindent locally to zero, we use the @format + # environment for non-quoted snippets. + override[EXAMPLEINDENT] = r'0.4\in' + override[LINEWIDTH] = texinfo_linewidths['@smallbook'] override.update (default_ly_options) - option_string = string.join (options, ',') - notes_options = [] - paper_options = [] - preamble_options = [] - for i in options: - c = classic_lilypond_book_compatibility (i) - if c: - ly.warning (_ ("deprecated ly-option used: %s" % i)) - ly.warning (_ ("compatibility mode translation: %s" \ - % c)) - i = c - - if string.find (i, '=') > 0: - key, value = string.split (i, '=') + option_list = [] + for (key, value) in option_dict.items (): + if value == None: + option_list.append (key) + else: + option_list.append (key + '=' + value) + option_string = string.join (option_list, ',') + + compose_dict = {} + compose_types = [NOTES, PREAMBLE, LAYOUT, PAPER] + for a in compose_types: + compose_dict[a] = [] + + for (key, value) in option_dict.items(): + (c_key, c_value) = \ + classic_lilypond_book_compatibility (key, value) + if c_key: + if c_value: + ly.warning \ + (_ ("deprecated ly-option used: %s=%s" \ + % (key, value))) + ly.warning \ + (_ ("compatibility mode translation: %s=%s" \ + % (c_key, c_value))) + else: + ly.warning \ + (_ ("deprecated ly-option used: %s" \ + % key)) + ly.warning \ + (_ ("compatibility mode translation: %s" \ + % c_key)) + + (key, value) = (c_key, c_value) + + if value: override[key] = value else: - key = i - if i not in override.keys (): - override[i] = None - - if key in ly_options[NOTES].keys (): - notes_options.append (ly_options[NOTES][key]) - elif key in ly_options[PREAMBLE].keys (): - preamble_options.append (ly_options[PREAMBLE][key]) - elif key in ly_options[PAPER].keys (): - paper_options.append (ly_options[PAPER][key]) - elif key not in (FRAGMENT, NOFRAGMENT, PRINTFILENAME, - RELATIVE, VERBATIM, TEXIDOC): - ly.warning (_("ignoring unknown ly option: %s") % i) - - #URGS + if not override.has_key (key): + override[key] = None + + found = 0 + for type in compose_types: + if ly_options[type].has_key (key): + compose_dict[type].append (ly_options[type][key]) + found = 1 + break + + if not found and key not in simple_options: + ly.warning (_ ("ignoring unknown ly option: %s") % i) + + # URGS if RELATIVE in override.keys () and override[RELATIVE]: relative = string.atoi (override[RELATIVE]) @@ -357,37 +723,33 @@ def compose_ly (code, options): # 1 = central C if relative < 0: - relative_quotes += ',' * (- relative - 1) + relative_quotes += ',' * (- relative) elif relative > 0: relative_quotes += "'" * relative - + program_name = __main__.program_name - paper_string = string.join (paper_options, '\n ') % override - preamble_string = string.join (preamble_options, '\n ') % override - notes_string = string.join (notes_options, '\n ') % vars () + + paper_string = \ + string.join (compose_dict[PAPER], '\n ') % override + layout_string = \ + string.join (compose_dict[LAYOUT], '\n ') % override + notes_string = \ + string.join (compose_dict[NOTES], '\n ') % vars () + preamble_string = \ + string.join (compose_dict[PREAMBLE], '\n ') % override + return (PREAMBLE_LY + body) % vars () -# BARF -# use lilypond-bin for latex (.lytex) books, -# and lilypond --preview for html, texinfo books? -def to_eps (file): - cmd = r'latex "\nonstopmode \input %s"' % file - # Ugh. (La)TeX writes progress and error messages on stdout - # Redirect to stderr - cmd = '(( %s >&2 ) >&- )' % cmd - ly.system (cmd) - ly.system ('dvips -Ppdf -u+lilypond.map -E -o %s.eps %s' \ - % (file, file)) def find_file (name): for i in include_path: full = os.path.join (i, name) if os.path.exists (full): return full - ly.error (_ ('file not found: %s\n' % name)) + ly.error (_ ("file not found: %s") % name + '\n') ly.exit (1) return '' - + def verbatim_html (s): return re.sub ('>', '>', re.sub ('<', '<', @@ -399,14 +761,19 @@ def verbatim_texinfo (s): re.sub ('@', '@@', s))) def split_options (option_string): - return re.split (format_res[format]['option-sep'], option_string) - + return re.split (format_res[format]['option_sep'], option_string) class Chunk: def replacement_text (self): return '' - def is_outdated (self): + def filter_text (self): + return self.replacement_text () + + def ly_is_outdated (self): + return 0 + + def png_is_outdated (self): return 0 class Substring (Chunk): @@ -416,8 +783,8 @@ class Substring (Chunk): self.end = end def replacement_text (self): - return self.source [self.start:self.end] - + return self.source[self.start:self.end] + class Snippet (Chunk): def __init__ (self, type, match, format): self.type = type @@ -427,16 +794,13 @@ class Snippet (Chunk): self.format = format def replacement_text (self): - return self.match.group (0) - + return self.match.group ('match') + def substring (self, s): return self.match.group (s) - def filter_code (self): - pass # todo - def __repr__ (self): - return `self.__class__` + " type = " + self.type + return `self.__class__` + ' type = ' + self.type class Include_snippet (Snippet): def processed_filename (self): @@ -444,9 +808,9 @@ class Include_snippet (Snippet): return os.path.splitext (f)[0] + format2ext[format] def replacement_text (self): - s = self.match.group (0) + s = self.match.group ('match') f = self.substring ('filename') - + return re.sub (f, self.processed_filename (), s) class Lilypond_snippet (Snippet): @@ -457,19 +821,15 @@ class Lilypond_snippet (Snippet): self.options = split_options (os) def ly (self): - if self.type == 'lilypond_file': - name = self.substring ('filename') - return '\\renameinput \"%s\"\n' % name\ - + open (find_file (name)).read () - else: - return self.substring ('code') - + return self.substring ('code') + def full_ly (self): s = self.ly () if s: - return compose_ly (s, self.options) + return compose_ly (s, self.options, self.type) return '' - + + # TODO: Use md5? def get_hash (self): if not self.hash: self.hash = abs (hash (self.full_ly ())) @@ -484,81 +844,116 @@ class Lilypond_snippet (Snippet): outf = open (self.basename () + '.ly', 'w') outf.write (self.full_ly ()) - def is_outdated (self): + open (self.basename () + '.txt', 'w').write ('image of music') + + def ly_is_outdated (self): base = self.basename () - ## FIXME: adding PNG to is_outdated test fixes - ## interrupted (web) builds. - ## should only do this if PNG is actually target - if os.path.exists (base + '.ly') \ - and os.path.exists (base + '.tex') \ - and os.path.exists (base + '.png') \ - and (use_hash_p \ - or self.ly () == open (base + '.ly').read ()): - # TODO: something smart with target formats - # (ps, png) and m/ctimes + + tex_file = '%s.tex' % base + eps_file = '%s.eps' % base + system_file = '%s-systems.tex' % base + ly_file = '%s.ly' % base + ok = os.path.exists (ly_file) \ + and os.path.exists (system_file)\ + and os.stat (system_file)[stat.ST_SIZE] \ + and re.match ('% eof', open (system_file).readlines ()[-1]) + + if ok and (use_hash_p \ + or self.ly () == open (ly_file).read ()): + # TODO: Do something smart with target formats + # (ps, png) and m/ctimes. return None return self - + + def png_is_outdated (self): + base = self.basename () + ok = self.ly_is_outdated () + if format == HTML or format == TEXINFO: + ok = ok and (os.path.exists (base + '.png') + or glob.glob (base + '-page*.png')) + return not ok + def texstr_is_outdated (self): + if backend == 'ps': + return 0 + + base = self.basename () + ok = self.ly_is_outdated () + ok = ok and (os.path.exists (base + '.texstr')) + return not ok + + def filter_text (self): + code = self.substring ('code') + s = run_filter (code) + d = { + 'code': s, + 'options': self.match.group ('options') + } + # TODO + return output[self.format][FILTER] % d + def replacement_text (self): - func = Lilypond_snippet.__dict__ ['output_' + self.format] + func = Lilypond_snippet.__dict__['output_' + self.format] return func (self) - + def get_images (self): base = self.basename () - # URGUGHUGHUGUGHU + # URGUGHUGHUGUGH single = '%(base)s.png' % vars () multiple = '%(base)s-page1.png' % vars () images = (single,) if os.path.exists (multiple) \ - and (not os.path.exists (single)\ + and (not os.path.exists (single) \ or (os.stat (multiple)[stat.ST_MTIME] \ > os.stat (single)[stat.ST_MTIME])): images = glob.glob ('%(base)s-page*.png' % vars ()) return images - + def output_html (self): - base = self.basename () str = '' + base = self.basename () if format == HTML: - str = self.output_print_filename (HTML) + str += self.output_print_filename (HTML) if VERBATIM in self.options: verb = verbatim_html (self.substring ('code')) str += write (output[HTML][VERBATIM] % vars ()) + if QUOTE in self.options: + str = output[HTML][QUOTE] % vars () str += output[HTML][BEFORE] % vars () for image in self.get_images (): - base, ext = os.path.splitext (image) + (base, ext) = os.path.splitext (image) str += output[HTML][OUTPUT] % vars () str += output[HTML][AFTER] % vars () return str def output_info (self): - str = output[TEXINFO][BEFORE] % vars () + str = self.output_print_filename (HTML) for image in self.get_images (): - base, ext = os.path.splitext (image) - # URG, makeinfo implicitely prepends dot to ext - # specifying no extension is most robust + (base, ext) = os.path.splitext (image) + + # URG, makeinfo implicitly prepends dot to extension. + # Specifying no extension is most robust. ext = '' str += output[TEXINFO][OUTPUT] % vars () - str += output[TEXINFO][AFTER] % vars () return str def output_latex (self): str = '' base = self.basename () if format == LATEX: - str = self.output_print_filename (LATEX) - if VERBATIM in self.options: + str += self.output_print_filename (LATEX) + if VERBATIM in self.options: verb = self.substring ('code') str += (output[LATEX][VERBATIM] % vars ()) - str += (output[LATEX][BEFORE] - + (output[LATEX][OUTPUT] % vars ()) - + output[LATEX][AFTER]) + if QUOTE in self.options: + str = output[LATEX][QUOTE] % vars () + + str += (output[LATEX][OUTPUT] % vars ()) return str - def output_print_filename (self,format): + def output_print_filename (self, format): str = '' - if PRINTFILENAME in self.options: + if PRINTFILENAME in self.options: base = self.basename () filename = self.substring ('filename') str = output[format][PRINTFILENAME] % vars () @@ -566,42 +961,64 @@ class Lilypond_snippet (Snippet): def output_texinfo (self): str = '' + if self.output_print_filename (TEXINFO): + str += ('@html\n' + + self.output_print_filename (HTML) + + '\n@end html\n') + str += ('@tex\n' + + self.output_print_filename (LATEX) + + '\n@end tex\n') base = self.basename () if TEXIDOC in self.options: texidoc = base + '.texidoc' if os.path.exists (texidoc): - str += '@include %(texidoc)s\n' % vars () + str += '@include %(texidoc)s\n\n' % vars () - if VERBATIM in self.options: + if VERBATIM in self.options: verb = verbatim_texinfo (self.substring ('code')) - str += (output[TEXINFO][VERBATIM] % vars ()) + str += (output[TEXINFO][VERBATIM] % vars ()) + if not QUOTE in self.options: + str = output[TEXINFO][NOQUOTE] % vars() + + str += self.output_info () +# str += ('@ifinfo\n' + self.output_info () + '\n@end ifinfo\n') +# str += ('@tex\n' + self.output_latex () + '\n@end tex\n') +# str += ('@html\n' + self.output_html () + '\n@end html\n') + + if QUOTE in self.options: + str = output[TEXINFO][QUOTE] % vars () - str += ('@ifinfo\n' + self.output_info () + '\n@end ifinfo\n') - str += ('@tex\n' + self.output_latex () + '\n@end tex\n') - str += ('@html\n' + self.output_html () + '\n@end html\n') # need par after image str += '\n' return str - + +class Lilypond_file_snippet (Lilypond_snippet): + def ly (self): + name = self.substring ('filename') + return '\\renameinput \"%s\"\n%s' \ + % (name, open (find_file (name)).read ()) + snippet_type_to_class = { - 'lilypond_file' : Lilypond_snippet, - 'lilypond_block' : Lilypond_snippet, - 'lilypond' : Lilypond_snippet, - 'include' : Include_snippet, - } + 'lilypond_file': Lilypond_file_snippet, + 'lilypond_block': Lilypond_snippet, + 'lilypond': Lilypond_snippet, + 'include': Include_snippet, +} def find_toplevel_snippets (s, types): - res = {} - for i in types: - res[i] = ly.re.compile (snippet_res[format][i]) - - snippets = [] - index = 0 - ## found = dict (map (lambda x: (x, None), types)) + res = {} + for i in types: + res[i] = ly.re.compile (snippet_res[format][i]) + + snippets = [] + index = 0 + ## found = dict (map (lambda x: (x, None), + ## types)) ## urg python2.1 - found = {} - map (lambda x, f=found: f.setdefault (x, None), types) + found = {} + map (lambda x, f = found: f.setdefault (x, None), + types) # We want to search for multiple regexes, without searching # the string multiple times for one regex. @@ -610,13 +1027,13 @@ def find_toplevel_snippets (s, types): # Since every part of the string is traversed at most once for # every type of snippet, this is linear. - while 1: - first = None - endex = 1 << 30 - for type in types: - if not found[type] or found[type][0] < index: - found[type] = None - m = res[type].search (s[index:endex]) + while 1: + first = None + endex = 1 << 30 + for type in types: + if not found[type] or found[type][0] < index: + found[type] = None + m = res[type].search (s[index:endex]) if not m: continue @@ -624,42 +1041,43 @@ def find_toplevel_snippets (s, types): if snippet_type_to_class.has_key (type): cl = snippet_type_to_class[type] snip = cl (type, m, format) - start = index + m.start (0) + start = index + m.start ('match') found[type] = (start, snip) - if found[type] \ - and (not first or found[type][0] < found[first][0]): - first = type + if found[type] \ + and (not first \ + or found[type][0] < found[first][0]): + first = type # FIXME. # Limiting the search space is a cute # idea, but this *requires* to search # for possible containing blocks - # first, at least long as we do not + # first, at least as long as we do not # search for the start of blocks, but # always/directly for the entire # @block ... @end block. - - endex = found[first][0] - if not first: + endex = found[first][0] + + if not first: snippets.append (Substring (s, index, len (s))) break - (start , snip) = found[first] + (start, snip) = found[first] snippets.append (Substring (s, index, start)) snippets.append (snip) found[first] = None - index = start + len (snip.match.group (0)) + index = start + len (snip.match.group ('match')) - return snippets + return snippets def filter_pipe (input, cmd): if verbose_p: - ly.progress (_ ("Opening filter `%s\'") % cmd) - - stdin, stdout, stderr = os.popen3 (cmd) + ly.progress (_ ("Opening filter `%s'") % cmd) + + (stdin, stdout, stderr) = os.popen3 (cmd) stdin.write (input) status = stdin.close () @@ -668,36 +1086,62 @@ def filter_pipe (input, cmd): output = stdout.read () status = stdout.close () error = stderr.read () - + if not status: status = 0 signal = 0x0f & status if status or (not output and error): exit_status = status >> 8 - ly.error (_ ("`%s\' failed (%d)") % (cmd, exit_status)) + ly.error (_ ("`%s' failed (%d)") % (cmd, exit_status)) ly.error (_ ("The error log is as follows:")) sys.stderr.write (error) sys.stderr.write (stderr.read ()) ly.exit (status) - + if verbose_p: ly.progress ('\n') return output - + def run_filter (s): return filter_pipe (s, filter_cmd) -def process_snippets (cmd, snippets): - names = filter (lambda x: x, map (Lilypond_snippet.basename, snippets)) - if names: - ly.system (string.join ([cmd] + names)) - - if format == HTML or format == TEXINFO: - for i in names: - if os.path.exists (i + '.tex'): - to_eps (i) - ly.make_ps_images (i + '.eps', resolution=110) +def is_derived_class (cl, baseclass): + if cl == baseclass: + return 1 + for b in cl.__bases__: + if is_derived_class (b, baseclass): + return 1 + return 0 + +def process_snippets (cmd, ly_snippets, texstr_snippets, png_snippets): + ly_names = filter (lambda x: x, + map (Lilypond_snippet.basename, ly_snippets)) + texstr_names = filter (lambda x: x, + map (Lilypond_snippet.basename, texstr_snippets)) + png_names = filter (lambda x: x, + map (Lilypond_snippet.basename, png_snippets)) + + status = 0 + def my_system (cmd): + status = ly.system (cmd, + ignore_error = 1, progress_p = 1) + + if status: + ly.error ('Process %s exited unsuccessfully.' % cmd) + raise Compile_error + + # UGH + # the --process=CMD switch is a bad idea + # it is too generic for lilypond-book. + if texstr_names and re.search ('^[0-9A-Za-z/]*lilypond', cmd): + + my_system (string.join ([cmd + ' --backend texstr ' ] + texstr_names)) + for l in texstr_names: + my_system ('latex %s.texstr' % l) + + if ly_names: + my_system (string.join ([cmd] + ly_names)) LATEX_DOCUMENT = r''' %(preamble)s @@ -707,12 +1151,13 @@ LATEX_DOCUMENT = r''' \makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother \end{document} ''' -#need anything else besides textwidth? + +# Do we need anything else besides `textwidth'? def get_latex_textwidth (source): m = re.search (r'''(?P\\begin\s*{document})''', source) preamble = source[:m.start (0)] latex_document = LATEX_DOCUMENT % vars () - parameter_string = filter_pipe (latex_document, latex_filter_cmd) + parameter_string = filter_pipe (latex_document, latex_filter_cmd) columns = 0 m = re.search ('columns=([0-9.]*)', parameter_string) @@ -725,7 +1170,7 @@ def get_latex_textwidth (source): columnsep = string.atof (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 = string.atof (m.group (1)) if columns: @@ -734,34 +1179,67 @@ def get_latex_textwidth (source): return textwidth ext2format = { - '.html' : HTML, - '.itely' : TEXINFO, - '.lytex' : LATEX, - '.tely' : TEXINFO, + '.html': HTML, + '.itely': TEXINFO, + '.latex': LATEX, + '.lytex': LATEX, + '.tely': TEXINFO, '.tex': LATEX, - '.texi' : TEXINFO, - '.texinfo' : TEXINFO, - '.xml' : HTML, - } - + '.texi': TEXINFO, + '.texinfo': TEXINFO, + '.xml': HTML, +} + format2ext = { HTML: '.html', - #TEXINFO: '.texinfo', + # TEXINFO: '.texinfo', TEXINFO: '.texi', LATEX: '.tex', - } - +} + +class Compile_error: + pass + +def do_process_cmd (chunks): + ly_outdated = \ + filter (lambda x: is_derived_class (x.__class__, + Lilypond_snippet) + and x.ly_is_outdated (), + chunks) + texstr_outdated = \ + filter (lambda x: is_derived_class (x.__class__, + Lilypond_snippet) + and x.texstr_is_outdated (), + chunks) + png_outdated = \ + filter (lambda x: is_derived_class (x.__class__, + Lilypond_snippet) + and x.png_is_outdated (), + chunks) + + ly.progress (_ ("Writing snippets...")) + map (Lilypond_snippet.write_ly, ly_outdated) + ly.progress ('\n') + + if ly_outdated: + ly.progress (_ ("Processing...\n")) + process_snippets (process_cmd, ly_outdated, texstr_outdated, png_outdated) + else: + ly.progress (_ ("All snippets are up to date...")) + ly.progress ('\n') + def do_file (input_filename): - #ugh + # Ugh. global format if not format: e = os.path.splitext (input_filename)[1] if e in ext2format.keys (): - #FIXME + # FIXME format = ext2format[e] else: ly.error (_ ("cannot determine format for: %s" \ % input_filename)) + ly.exit (1) if not input_filename or input_filename == '-': in_handle = sys.stdin @@ -776,14 +1254,14 @@ def do_file (input_filename): else: input_fullname = find_file (input_filename) in_handle = open (input_fullname) - + if input_filename == '-': input_base = 'stdin' else: input_base = os.path.basename \ (os.path.splitext (input_filename)[0]) - # only default to stdout when filtering + # Only default to stdout when filtering. if output_name == '-' or (not output_name and filter_cmd): output_filename = '-' output_file = sys.stdout @@ -797,93 +1275,98 @@ def do_file (input_filename): + '/' + input_base + format2ext[format]) - - if (os.path.exists (input_filename) and - os.path.exists (output_filename) and - os.path.samefile (output_filename, input_fullname)): - ly.error (_("Output would overwrite input file; use --output.")) - sys.exit (2) + if os.path.exists (input_filename) \ + and os.path.exists (output_filename) \ + and os.path.samefile (output_filename, input_fullname): + ly.error ( + _ ("Output would overwrite input file; use --output.")) + ly.exit (2) output_file = open (output_filename, 'w') if output_name: os.chdir (output_name) - - ly.progress (_ ("Reading %s...") % input_fullname) - source = in_handle.read () - ly.progress ('\n') - - # FIXME: containing blocks must be first, see find_toplevel_snippets - snippet_types = ( - 'multiline_comment', - 'verbatim', - 'lilypond_block', -# 'verb', - 'singleline_comment', - 'lilypond_file', - 'include', - 'lilypond', ) - ly.progress (_ ("Dissecting...")) - chunks = find_toplevel_snippets (source, snippet_types) - ly.progress ('\n') - - global default_ly_options - textwidth = 0 - if LINEWIDTH not in default_ly_options.keys (): - if format == LATEX: - textwidth = get_latex_textwidth (source) - default_ly_options[LINEWIDTH] = '''%.0f\\pt''' \ - % textwidth - elif format == TEXINFO: - for (k, v) in texinfo_linewidths.items (): - # FIXME: @paper is usually not in chunk #0: - # \input texinfo @c -*-texinfo-*- - # bluntly search first K of source - # s = chunks[0].replacement_text () - if re.search (k, source[:1024]): - default_ly_options[LINEWIDTH] = v - break - - if filter_cmd: - pass # todo - elif process_cmd: - outdated = filter (lambda x: x.__class__ == Lilypond_snippet \ - and x.is_outdated (), chunks) - ly.progress (_ ("Writing snippets...")) - map (Lilypond_snippet.write_ly, outdated) - ly.progress ('\n') - - if outdated: - ly.progress (_ ("Processing...")) - process_snippets (process_cmd, outdated) - else: - ly.progress (_ ("All snippets are up to date...")) + try: + ly.progress (_ ("Reading %s...") % input_fullname) + source = in_handle.read () ly.progress ('\n') - ly.progress (_ ("Compiling %s...") % output_filename) - output_file.writelines ([s.replacement_text () \ - for s in chunks]) + # FIXME: Containing blocks must be first, see + # find_toplevel_snippets. + snippet_types = ( + 'multiline_comment', + 'verbatim', + 'lilypond_block', + # 'verb', + 'singleline_comment', + 'lilypond_file', + 'include', + 'lilypond', + ) + ly.progress (_ ("Dissecting...")) + chunks = find_toplevel_snippets (source, snippet_types) ly.progress ('\n') - def process_include (snippet): + global default_ly_options + textwidth = 0 + if not default_ly_options.has_key (LINEWIDTH): + if format == LATEX: + textwidth = get_latex_textwidth (source) + default_ly_options[LINEWIDTH] = \ + '''%.0f\\pt''' % textwidth + elif format == TEXINFO: + for (k, v) in texinfo_linewidths.items (): + # FIXME: @layout is usually not in + # chunk #0: + # + # \input texinfo @c -*-texinfo-*- + # + # Bluntly search first K items of + # source. + # s = chunks[0].replacement_text () + if re.search (k, source[:1024]): + default_ly_options[LINEWIDTH] = v + break + + if filter_cmd: + output_file.writelines ([c.filter_text () \ + for c in chunks]) + + elif process_cmd: + do_process_cmd (chunks) + ly.progress (_ ("Compiling %s...") % output_filename) + output_file.writelines ([s.replacement_text () \ + for s in chunks]) + ly.progress ('\n') + + def process_include (snippet): + os.chdir (original_dir) + name = snippet.substring ('filename') + ly.progress (_ ("Processing include: %s") % name) + ly.progress ('\n') + do_file (name) + + map (process_include, + filter (lambda x: is_derived_class (x.__class__, + Include_snippet), + chunks)) + except Compile_error: os.chdir (original_dir) - name = snippet.substring ('filename') - ly.progress (_ ('Processing include: %s') % name) + ly.progress (_ ("Removing `%s'") % output_filename) ly.progress ('\n') - do_file (name) - - map (process_include, - filter (lambda x: x.__class__ == Include_snippet, chunks)) + + os.unlink (output_filename) + raise Compile_error def do_options (): global format, output_name global filter_cmd, process_cmd, verbose_p - + (sh, long) = ly.getopt_args (option_definitions) try: (options, files) = getopt.getopt (sys.argv[1:], sh, long) except getopt.error, s: sys.stderr.write ('\n') - ly.error (_ ("getopt says: `%s\'" % s)) + ly.error (_ ("getopt says: `%s'" % s)) sys.stderr.write ('\n') ly.help () ly.exit (2) @@ -901,6 +1384,8 @@ def do_options (): format = a if a == 'texi-html' or a == 'texi': format = TEXINFO + elif o == '--tex-backend ': + backend = 'tex' elif o == '--help' or o == '-h': ly.help () sys.exit (0) @@ -928,6 +1413,13 @@ def do_options (): def main (): files = do_options () global process_cmd + + formats = "ps" + if format == TEXINFO: + formats += ",png" + if process_cmd == '': + process_cmd = lilypond_binary + ' --formats=%s --backend ps ' % formats + if process_cmd: process_cmd += string.join ([(' -I %s' % p) for p in include_path]) @@ -935,7 +1427,10 @@ def main (): ly.identify (sys.stderr) ly.setup_environment () if files: - do_file (files[0]) + try: + do_file (files[0]) + except Compile_error: + ly.exit (1) if __name__ == '__main__': main ()