X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Flilypond-book.py;h=44c08b5af988ba35568d299176bb4bf39d4c2643;hb=7437506ec4b53da278664cfc97f77acb3099166d;hp=68ff22873f5e3424573affb665f68fe8f921347a;hpb=f8d1bb34b7c5f7deafa32be53f540f3102e82f3a;p=lilypond.git diff --git a/scripts/lilypond-book.py b/scripts/lilypond-book.py index 68ff22873f..44c08b5af9 100644 --- a/scripts/lilypond-book.py +++ b/scripts/lilypond-book.py @@ -1,27 +1,61 @@ #!@PYTHON@ # vim: set noexpandtab: -# TODO: -# * junk --outdir for --output -# * Figure out clean set of options. -# * -# * texinfo: add support for @pagesize -# todo: dimension handling (all the x2y) is clumsy. (tca: Thats -# because the values are taken directly from texinfo.tex, -# geometry.sty and article.cls. Give me a hint, and I'll -# fix it.) +""" -# -# TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips. -# + TODO: + * junk --outdir for--output + * Figure out clean set of options. + * + * texinfo: add support for @pagesize + + todo: dimension handling (all the x2y) is clumsy. (tca: Thats + because the values are taken directly from texinfo.tex, + geometry.sty and article.cls. Give me a hint, and I'll + fix it.) + + + TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips. + + + + This is a slightly hairy program. The general approach is as follows + The input string is chopped up in chunks, i.e. , a list of tuples + + with the format (TAG_STR, MAIN_STR, OPTIONS, TODO, BASE) + + This list is built step by step: first ignore and verbatim commands + are handled, delivering a list of chunks. + + then all chunks containing lilypond commands are chopped up + + when all chunks have their final form, all bodies from lilypond blocks are + extracted, and if applicable, written do disk and run through lilypond. + + +tags supported + + ignore + lilypond + input + verb + verbatim + multicols + numcols + -# This is was the idea for handling of comments: + + +""" + +# This is was the idea for handling of comments: # Multiline comments, @ignore .. @end ignore is scanned for # in read_doc_file, and the chunks are marked as 'ignore', so # lilypond-book will not touch them any more. The content of the # chunks are written to the output file. Also 'include' and 'input' # regex has to check if they are commented out. # + # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'. # These three regex's has to check if they are on a commented line, # % for latex, @c for texinfo. @@ -34,139 +68,110 @@ # The the rest of the rexeces are searched for. They don't have to test # if they are on a commented out line. - - -import os +import glob import stat import string -import getopt -import sys -import __main__ -# Handle bug in Python 1.6-2.1 -# -# there are recursion limits for some patterns in Python 1.6 til 2.1. -# fix this by importing the 1.5.2 implementation pre instead. Fix by Mats. +################################################################ +# Users of python modules should include this snippet +# and customize variables below. -## We would like to do this for python 2.2 as well, unfortunately -## python 2.2 has another bug, see Sf.net bugtracker -## -## https://sourceforge.net/tracker/?func=detail&aid=604803&group_id=5470&atid=105470 -## +# 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) +# 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') : + datadir = os.environ['LILYPONDPREFIX'] + while datadir[-1] == os.sep: + datadir= datadir[:-1] -## -## -## +sys.path.insert (0, os.path.join (datadir, 'python')) +# Customize these +#if __name__ == '__main__': +import lilylib as ly +global _;_=ly._ +global re;re = ly.re +# lilylib globals +program_version = '@TOPLEVEL_VERSION@' +program_name = 'lilypond-book' +verbose_p = 0 +pseudo_filter_p = 0 +original_dir = os.getcwd () -if float (sys.version[0:3]) <= 2.1: -## or sys.version[0:5] == '2.2.1': -## still broken on python 2.2.1 / RH8. - - try: - import pre - re = pre - del pre - except ImportError: - import re -else: - import re - -# Attempt to fix problems with limited stack size set by Python! -# Sets unlimited stack size. Note that the resource module only -# is available on UNIX. -try: - import resource - resource.setrlimit (resource.RLIMIT_STACK, (-1, -1)) -except: - pass -errorport = sys.stderr -verbose_p = 0 +preview_resolution = 90 +## FIXME +## ly2dvi: silly name? +## do -P or -p by default? +##help_summary = _ ("Run LilyPond using LaTeX for titling") +help_summary = _ ("Process LilyPond snippets in hybrid html, LaTeX or texinfo document") +copyright = ('Tom Cato Amundsen ', + 'Han-Wen Nienhuys ') +option_definitions = [ + (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")), + (_ ("DIM"), '', 'default-music-fontsize', _ ("default fontsize for music. DIM is assumed to be in points")), + (_ ("DIM"), '', 'default-lilypond-fontsize', _ ("deprecated, use --default-music-fontsize")), + (_ ("OPT"), '', 'extra-options', _ ("pass OPT quoted to the lilypond command line")), + (_ ("DIM"), '', 'force-music-fontsize', _ ("force fontsize for all inline lilypond. DIM is assumed to be in points")), + (_ ("DIM"), '', 'force-lilypond-fontsize', _ ("deprecated, use --force-music-fontsize")), + ('', 'h', 'help', _ ("print this help")), + (_ ("DIR"), 'I', 'include', _ ("include path")), + ('', 'M', 'dependencies', _ ("write dependencies")), + (_ ("PREF"), '', 'dep-prefix', _ ("prepend PREF before each -M dependency")), + ('', 'n', 'no-lily', _ ("don't run lilypond")), + ('', '', 'no-pictures', _ ("don't generate pictures")), + ('', '', 'no-music', _ ("strip all lilypond blocks from output")), + (_ ("FILE"), 'o', 'outname', _ ("filename main output file")), + (_ ("FILE"), '', 'outdir', _ ("where to place generated files")), + (_ ('RES'), '', 'preview-resolution', + _ ("set the resolution of the preview to RES")), + ('', 'V', 'verbose', _ ("be verbose")), + ('', 'v', 'version', _ ("print version information")), + ('', 'w', 'warranty', _ ("show warranty and copyright")), + ] -try: - import gettext - gettext.bindtextdomain ('lilypond', localedir) - gettext.textdomain ('lilypond') - _ = gettext.gettext -except: - def _ (s): - return s +# format specific strings, ie. regex-es for input, and % strings for output -def progress (s): - errorport.write (s + '\n') +# global variables +include_path = [os.getcwd ()] -program_version = '@TOPLEVEL_VERSION@' -if program_version == '@' + 'TOPLEVEL_VERSION' + '@': - program_version = '1.6.0' +#lilypond_binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond' -# if set, LILYPONDPREFIX must take prevalence -# if datadir is not set, we're doing a build and LILYPONDPREFIX -datadir = '@local_lilypond_datadir@' +lilypond_binary = os.path.join ('@bindir@', 'lilypond-bin') -if os.environ.has_key ('LILYPONDPREFIX') : - datadir = os.environ['LILYPONDPREFIX'] -else: - datadir = '@local_lilypond_datadir@' - -while datadir[-1] == os.sep: - datadir= datadir[:-1] - -kpse = os.popen ('kpsexpand \$TEXMF').read() -kpse = re.sub('[ \t\n]+$','', kpse) -type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read () - -binary = 'lilypond' -#binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond' -environment = { - # TODO: * prevent multiple addition. - # * clean TEXINPUTS, MFINPUTS, TFMFONTS, - # as these take prevalence over $TEXMF - # and thus may break tex run? - 'TEXMF' : "{%s,%s}" % (datadir, kpse) , - 'GS_FONTPATH' : type1_paths, - 'GS_LIB' : datadir + '/ps', -} +# only use installed binary when we're installed too. +if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary): + lilypond_binary = 'lilypond-bin' -# tex needs lots of memory, more than it gets by default on Debian -non_path_environment = { - 'extra_mem_top' : '1000000', - 'extra_mem_bottom' : '1000000', - 'pool_size' : '250000', -} -def setup_environment (): - # $TEXMF is special, previous value is already taken care of - if os.environ.has_key ('TEXMF'): - del os.environ['TEXMF'] - for key in environment.keys (): - val = environment[key] - if os.environ.has_key (key): - val = val + os.pathsep + os.environ[key] - os.environ[key] = val +ly2dvi_binary = os.path.join ('@bindir@', 'ly2dvi') - for key in non_path_environment.keys (): - val = non_path_environment[key] - os.environ[key] = val +# only use installed binary when we're installed too. +if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary): + ly2dvi_binary = 'ly2dvi' -include_path = [os.getcwd()] -# g_ is for global (?) g_extra_opts = '' g_here_dir = os.getcwd () g_dep_prefix = '' g_outdir = '' g_force_music_fontsize = 0 -g_read_lys = 0 g_do_pictures = 1 g_do_music = 1 g_make_html = 0 @@ -179,25 +184,28 @@ default_music_fontsize = 16 default_text_fontsize = 12 paperguru = None +################################################################ +# Dimension handling for LaTeX. +# class LatexPaper: - def __init__(self): + def __init__ (self): self.m_document_preamble = [] self.m_num_cols = 1 self.m_multicols = 1 - def find_latex_dims(self): + def find_latex_dims (self): if g_outdir: - fname = os.path.join(g_outdir, "lily-tmp.tex") + fname = os.path.join (g_outdir, "lily-tmp.tex") else: fname = "lily-tmp.tex" try: - f = open(fname, "w") + f = open (fname, "w") except IOError: error ("Error creating temporary file '%s'" % fname) for s in self.m_document_preamble: - f.write(s) - f.write(r""" + f.write (s) + f.write (r""" \begin{document} \typeout{---} \typeout{\columnsep \the\columnsep} @@ -205,24 +213,30 @@ class LatexPaper: \typeout{---} \end{document} """) - f.close() - re_dim = re.compile(r"\\(\w+)\s+(\d+\.\d+)") + f.close () + re_dim = re.compile (r"\\(\w+)\s+(\d+\.\d+)") cmd = "latex '\\nonstopmode \input %s'" % fname - if verbose_p: - sys.stderr.write ("Invoking `%s' as pipe" % cmd) - try: - status = quiet_system (cmd, "Latex for finding dimensions") - except: - sys.stderr.write (_("Invoking LaTeX failed.") + '\n' ) - sys.stderr.write (_("This is the error log:\n") + '\n') - - lns = open ('lily-tmp.log').readlines() - + # Ugh. (La)TeX writes progress and error messages on stdout + # Redirect to stderr + cmd = '(( %s >&2 ) >&- )' % cmd + status = ly.system (cmd, ignore_error = 1) + signal = 0xf & status + exit_status = status >> 8 + + if status: + ly.error (_ ("LaTeX failed.")) + ly.error (_ ("The error log is as follows:")) + + #URG see ly2dvi + try: + lns = open ('lily-tmp.log').readlines () + except: + lns = '' countdown = -3 for ln in lns: sys.stderr.write (ln) - if re.match('^!', ln): + if re.match ('^!', ln): countdown = 3 if countdown == 0: @@ -234,64 +248,64 @@ class LatexPaper: sys.stderr.write (" ... (further messages elided)...\n") sys.exit (1) - lns = open ('lily-tmp.log').readlines() + lns = open ('lily-tmp.log').readlines () for ln in lns: - ln = string.strip(ln) - m = re_dim.match(ln) + ln = string.strip (ln) + m = re_dim.match (ln) if m: - if m.groups()[0] in ('textwidth', 'columnsep'): - self.__dict__['m_%s' % m.groups()[0]] = float(m.groups()[1]) + if m.groups ()[0] in ('textwidth', 'columnsep'): + self.__dict__['m_%s' % m.groups ()[0]] = float (m.groups ()[1]) try: os.remove (fname) - os.remove (os.path.splitext(fname)[0]+".aux") - os.remove (os.path.splitext(fname)[0]+".log") + os.remove (os.path.splitext (fname)[0]+".aux") + os.remove (os.path.splitext (fname)[0]+".log") except: pass if not self.__dict__.has_key ('m_textwidth'): raise 'foo!' - def get_linewidth(self): + def get_linewidth (self): if self.m_num_cols == 1: w = self.m_textwidth else: w = (self.m_textwidth - self.m_columnsep)/2 if self.m_multicols > 1: - return (w - self.m_columnsep*(self.m_multicols-1)) \ + return (w - self.m_columnsep* (self.m_multicols-1)) \ / self.m_multicols return w class HtmlPaper: - def __init__(self): + def __init__ (self): self.m_papersize = 'letterpaper' self.m_fontsize = 12 - def get_linewidth(self): + def get_linewidth (self): return html_linewidths[self.m_papersize][self.m_fontsize] class TexiPaper: - def __init__(self): + def __init__ (self): self.m_papersize = 'letterpaper' self.m_fontsize = 12 - def get_linewidth(self): + def get_linewidth (self): return texi_linewidths[self.m_papersize][self.m_fontsize] -def mm2pt(x): +def mm2pt (x): return x * 2.8452756 -def in2pt(x): +def in2pt (x): return x * 72.26999 -def em2pt(x, fontsize = 10): +def em2pt (x, fontsize = 10): return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x -def ex2pt(x, fontsize = 10): +def ex2pt (x, fontsize = 10): return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x -def pt2pt(x): +def pt2pt (x): return x dimension_conversion_dict ={ 'mm': mm2pt, - 'cm': lambda x: mm2pt(10*x), + 'cm': lambda x: mm2pt (10*x), 'in': in2pt, 'em': em2pt, 'ex': ex2pt, @@ -300,65 +314,43 @@ dimension_conversion_dict ={ # Convert numeric values, with or without specific dimension, to floats. # Keep other strings -def conv_dimen_to_float(value): - if type(value) == type(""): +def conv_dimen_to_float (value): + if type (value) == type (""): m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value) if m: unit = m.group (2) - num = string.atof(m.group (1)) - conv = dimension_conversion_dict[m.group(2)] + num = string.atof (m.group (1)) + conv = dimension_conversion_dict[m.group (2)] - value = conv(num) + value = conv (num) elif re.match ("^[0-9.]+$",value): - value = float(value) + value = float (value) return value texi_linewidths = { - 'afourpaper': {12: mm2pt(160)}, - 'afourwide': {12: in2pt(6.5)}, - 'afourlatex': {12: mm2pt(150)}, - 'smallbook': {12: in2pt(5)}, - 'letterpaper': {12: in2pt(6)}} + 'afourpaper': {12: mm2pt (160)}, + 'afourwide': {12: in2pt (6.5)}, + 'afourlatex': {12: mm2pt (150)}, + 'smallbook': {12: in2pt (5)}, + 'letterpaper': {12: in2pt (6)}} html_linewidths = { - 'afourpaper': {12: mm2pt(160)}, - 'afourwide': {12: in2pt(6.5)}, - 'afourlatex': {12: mm2pt(150)}, - 'smallbook': {12: in2pt(5)}, - 'letterpaper': {12: in2pt(6)}} + 'afourpaper': {12: mm2pt (160)}, + 'afourwide': {12: in2pt (6.5)}, + 'afourlatex': {12: mm2pt (150)}, + 'smallbook': {12: in2pt (5)}, + 'letterpaper': {12: in2pt (6)}} -option_definitions = [ - ('EXT', 'f', 'format', 'use output format EXT (texi [default], texi-html, latex, html)'), - ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'), - ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'), - ('OPT', '', 'extra-options' , 'pass OPT quoted to the lilypond command line'), - ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'), - ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'), - ('', 'h', 'help', 'this help'), - ('DIR', 'I', 'include', 'include path'), - ('', 'M', 'dependencies', 'write dependencies'), - ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'), - ('', 'n', 'no-lily', 'don\'t run lilypond'), - ('', '', 'no-pictures', "don\'t generate pictures"), - ('', '', 'no-music', "strip all lilypond blocks from output"), - ('', '', 'read-lys', "don't write ly files."), - ('FILE', 'o', 'outname', 'filename main output file'), - ('FILE', '', 'outdir', "where to place generated files"), - ('', 'V', 'verbose', 'verbose' ), - ('', 'v', 'version', 'print version information' ), - ] -# format specific strings, ie. regex-es for input, and % strings for output +################################################################ +# How to output various structures. output_dict= { 'html' : { - 'output-lilypond': ''' -%s -''', 'output-filename' : r'''