]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/update-lily.py
* GNUmakefile.in: config.h message fix.
[lilypond.git] / scripts / update-lily.py
index b758cccb28628e441b0051245fb9fc176e1fb8e2..224601e3edd779e513843fb280100146db7df806 100644 (file)
 #!@PYTHON@
-#
 # update-lily.py -- lilypond autobuilder
 # 
 # source file of the GNU LilyPond music typesetter
 #
 # download and rebuild latest lilypond or from specified url
-# 
+#
+# To show latest version do:
+#
+#     update-lily --command='echo "Latest is: %n-%v"'
+#
+
 
 '''
 TODO:
-    * more flexible build paths
-    * cleanup previous tree
-    * flexible build command
-    * show only?
+
+    * use urllib iso ftplib
+
+    * more flexible build/ftp/patches/releases paths
+    
 '''
 
-import os
+import ftplib
 import fnmatch
+import getopt
+import re
+import operator
+import os
+import tempfile
+import shutil
 import stat
 import string
-import re
-import getopt
 import sys
 import __main__
-import operator
-import tempfile
 
+package_name = 'lilypond'
+program_name = 'build-lily'
+program_version = '@TOPLEVEL_VERSION@'
+
+original_dir = os.getcwd ()
+temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
+errorport = sys.stderr
+keep_temp_dir_p = 0
+verbose_p = 0
+remove_previous_p = 0
 
-sys.path.append ('@datadir@/python')
-import gettext
-gettext.bindtextdomain ('lilypond', '@localedir@')
-gettext.textdomain('lilypond')
-_ = gettext.gettext
+url = 'file:/home/ftp/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
+url = 'ftp://appel.lilypond.org/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
+url = 'ftp://ftp.cs.uu.nl/pub/GNU/LilyPond/development/lilypond-*.tar.gz'
 
 
-program_name = 'build-lily'
-package_name = 'lilypond'
-help_summary = _("Fetch and rebuild from latest source package")
-build_root = os.environ ['HOME'] + '/usr/src'
-build_command = './configure; make web'
+build_root = os.path.join (os.environ ['HOME'], 'usr', 'src')
 release_dir = build_root + '/releases'
 patch_dir = build_root + '/patches'
+symlink_name = ''
 
-url = 'file:/home/ftp/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
-url = 'ftp://appel.lilypond.org/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
-url = 'ftp://ftp.cs.uu.nl/pub/GNU/LilyPond/development/lilypond-*.tar.gz'
+localedir = '@localedir@'
+try:
+       import gettext
+       gettext.bindtextdomain ('lilypond', localedir)
+       gettext.textdomain ('lilypond')
+       _ = gettext.gettext
+except:
+       def _ (s):
+               return s
+
+# 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
+
+
+help_summary = _ ("Fetch and rebuild from latest source package")
+
+option_definitions = [
+       ('DIR', 'b', 'build-root', _ ("unpack and build in DIR [%s]") % build_root),
+       ('COMMAND', 'c', 'command', _ ("execute COMMAND, subtitute:") \
+        + '\n                            ' + _ ("%b: build root") \
+        + '\n                            ' + _ ("%n: package name") \
+        + '\n                            ' + _ ("%r: release directory") \
+        + '\n                            ' + _ ("%t: tarball") \
+        + '\n                            ' + _ ("%v: package version") \
+        ),
+       ('', 'h', 'help', _ ("this help")),
+        ('', 'k', 'keep', _ ("keep all output, and name the directory %s") % temp_dir),
+        ('EMAIL', 'n', 'notify', _ ("upon failure notify EMAIL[,EMAIL]")),
+       ('', 'r', 'remove-previous', _ ("remove previous build")),
+       ('', 'V', 'verbose', _ ("verbose")),
+       ('', 'v', 'version', _ ("print version number")),
+       ('URL', 'u', 'url', _ ("fetch and build URL [%s]") % url),
+       ('', 'w', 'warranty', _ ("show warranty and copyright")),
+       ]
 
 
-# lily_py.py -- options and stuff
+################################################################
+# lilylib.py -- options and stuff
 # 
 # source file of the GNU LilyPond music typesetter
 
-# BEGIN Library for these?
-# cut-n-paste from ly2dvi
-
-program_version = '@TOPLEVEL_VERSION@'
-if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
-       program_version = '1.3.142'
+import os
 
+try:
+       import gettext
+       gettext.bindtextdomain ('lilypond', localedir)
+       gettext.textdomain ('lilypond')
+       _ = gettext.gettext
+except:
+       def _ (s):
+               return s
 
-original_dir = os.getcwd ()
-temp_dir = '%s.dir' % program_name
-keep_temp_dir_p = 0
-verbose_p = 0
+if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
+       program_version = '1.7.0'
 
 def identify ():
        sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
@@ -70,7 +121,7 @@ def identify ():
 def warranty ():
        identify ()
        sys.stdout.write ('\n')
-       sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001'))
+       sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
        sys.stdout.write ('\n')
        sys.stdout.write ('  Han-Wen Nienhuys')
        sys.stdout.write ('  Jan Nieuwenhuizen')
@@ -81,16 +132,25 @@ NO WARRANTY.'''))
        sys.stdout.write ('\n')
 
 def progress (s):
-       sys.stderr.write (s + '\n')
+       errorport.write (s + '\n')
 
 def warning (s):
-       sys.stderr.write (_ ("warning: ") + s)
-       sys.stderr.write ('\n')
-       
+       progress (_ ("warning: ") + s)
                
 def error (s):
-       sys.stderr.write (_ ("error: ") + s)
-       sys.stderr.write ('\n')
+
+
+       '''Report the error S.  Exit by raising an exception. Please
+       do not abuse by trying to catch this error. If you do not want
+       a stack trace, write to the output directly.
+
+       RETURN VALUE
+
+       None
+       
+       '''
+       
+       progress (_ ("error: ") + s)
        raise _ ("Exiting ... ")
 
 def getopt_args (opts):
@@ -149,20 +209,22 @@ def options_help_str (opts):
        return str
 
 def help ():
-       sys.stdout.write (_ ("Usage: %s [OPTION]... FILE") % program_name)
-       sys.stdout.write ('\n\n')
-       sys.stdout.write (help_summary)
-       sys.stdout.write ('\n\n')
-       sys.stdout.write (_ ("Options:"))
-       sys.stdout.write ('\n')
-       sys.stdout.write (options_help_str (option_definitions))
-       sys.stdout.write ('\n\n')
-       sys.stdout.write (_ ("Report bugs to %s") % 'bug-gnu-music@gnu.org')
-       sys.stdout.write ('\n')
-       sys.exit (0)
-
-
+       ls = [(_ ("Usage: %s [OPTION]... FILE") % program_name),
+               ('\n\n'),
+               (help_summary),
+               ('\n\n'),
+               (_ ("Options:")),
+               ('\n'),
+               (options_help_str (option_definitions)),
+               ('\n\n'),
+               (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
+               ('\n')]
+       map (sys.stdout.write, ls)
+       
 def setup_temp ():
+       """
+       Create a temporary directory, and return its name. 
+       """
        global temp_dir
        if not keep_temp_dir_p:
                temp_dir = tempfile.mktemp (program_name)
@@ -170,16 +232,26 @@ def setup_temp ():
                os.mkdir (temp_dir, 0777)
        except OSError:
                pass
-               
-       
+
+       return temp_dir
+
+
 def system (cmd, ignore_error = 0):
+       """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
+
+       RETURN VALUE
+
+       Exit status of CMD
+       """
+       
        if verbose_p:
                progress (_ ("Invoking `%s\'") % cmd)
        st = os.system (cmd)
        if st:
-               msg =  ( _ ("error: ") + _ ("command exited with value %d") % st)
+               name = re.match ('[ \t]*([^ \t]*)', cmd).group (1)
+               msg = name + ': ' + _ ("command exited with value %d") % st
                if ignore_error:
-                       sys.stderr.write (msg + ' ' + _ ("(ignored)") + ' ')
+                       warning (msg + ' ' + _ ("(ignored)") + ' ')
                else:
                        error (msg)
 
@@ -189,34 +261,37 @@ def system (cmd, ignore_error = 0):
 def cleanup_temp ():
        if not keep_temp_dir_p:
                if verbose_p:
-                       progress (_ ('Cleaning up `%s\'') % temp_dir)
-               system ('rm -rf %s' % temp_dir)
-
-
-def set_setting (dict, key, val):
-       try:
-               val = string.atof (val)
-       except ValueError:
-               #warning (_ ("invalid value: %s") % `val`)
-               pass
-
-       try:
-               dict[key].append (val)
-       except KeyError:
-               warning (_ ("no such setting: %s") % `key`)
-               dict[key] = [val]
-
-# END Library
+                       progress (_ ("Cleaning %s...") % temp_dir)
+               shutil.rmtree (temp_dir)
+
+
+def strip_extension (f, ext):
+       (p, e) = os.path.splitext (f)
+       if e == ext:
+               e = ''
+       return p + e
+
+
+
+notify = 0
+
+build_command = '''
+set -x
+cd %b &&
+[ -d %n-%v ] && exit 1 || true;
+mkdir -p %n-%v
+(
+tar xzf %r/%t &&
+rm -f building &&
+ln -s %n-%v building &&
+cd %n-%v &&
+./configure --prefix=$(pwd) && make all web
+) >> %n-%v/log.txt 2>&1 &&
+rm -f %s &&
+ln -s %n-%v %s
+'''
 
-option_definitions = [
-       ('DIR', 'b', 'build-root', _ ("unpack and build in DIR [%s]") % build_root),
-       ('', 'h', 'help', _ ("this help")),
-        ('', 'k', 'keep', _ ("keep all output, and name the directory %s") % temp_dir),
-       ('', 'V', 'verbose', _ ("verbose")),
-       ('', 'v', 'version', _ ("print version number")),
-       ('URL', 'u', 'url', _ ("fetch and build URL [%s]") % url),
-       ('', 'w', 'warranty', _ ("show warranty and copyright")),
-       ]
+### URL lib
 
 def list_file (user, passwd, host, dir, file):
        match = []
@@ -227,29 +302,22 @@ def list_file (user, passwd, host, dir, file):
 
 list_ = list_file
 
-#
-# ugh: use ftp module.
-#
 def list_ftp (user, passwd, host, dir, file):
        if user == 'None':
                user = 'anonymous'
        if passwd == 'None':
                passwd = program_name
 
-       command = '''
-open -u%s,%s -p21 %s
-set passive-mode off
-cd "%s"
-ls -1 "%s"
-''' % (user, passwd, host, dir, file)
-       temp = tempfile.mktemp (program_name)
-       f = open (temp, 'w')
-       f.write (command)
-       f.close ()
-       p = os.popen ('lftp -f %s' % temp, 'r')
-       s = p.read ()
-       status = p.close ()
-       return string.split (s[:-1], '\n')
+       ftp = ftplib.FTP (host)
+       ftp.login (user, passwd)
+       ftp.set_pasv (1)
+       ftp.cwd (dir)
+       list = ftp.nlst (file)
+       try:
+               ftp.quit ()
+       except:
+               ftp.close ()
+       return list
        
 def split_url (url):
        m = re.match ('([^:/]*)(:)?(/*([^:]*):)?(/*([^@]*)@)?(//([^/]*))?(.*)/(.*)',
@@ -263,6 +331,45 @@ def list_url (url):
        s = "list_%s ('%s', '%s', '%s', '%s', '%s')" % split_url (url)
        return eval (s)
 
+def copy_file (user, passwd, host, dir, file):
+       os.system ('cp %s/%s .' % (dir, file))
+
+copy_ = copy_file
+
+def copy_ftp (user, passwd, host, dir, file):
+       if user == 'None':
+               user = 'anonymous'
+       if passwd == 'None':
+               passwd = program_name
+
+       ftp = ftplib.FTP (host)
+       ftp.login (user, passwd)
+       ftp.set_pasv (1)
+       t = tempfile.mktemp (program_name)
+       try:
+               f = open (t, 'w')
+               ftp.retrbinary ('RETR %s/%s' % (dir, file),
+                       lambda x, f=f: f.write (x))
+               f.close ()
+               # huh? Invalid cross-device link
+               # os.rename (t, file)
+               system ('mv %s %s' % (t, file))
+       except:
+               os.remove (t)
+               raise 'Foo'
+       try:
+               ftp.quit ()
+       except:
+               ftp.close ()
+       return list
+       
+def copy_url (url, dir):
+       os.chdir (dir)
+       s = "copy_%s ('%s', '%s', '%s', '%s', '%s')" % split_url (url)
+       eval (s)
+
+### End URL lib
+
 def version_tuple_to_str (t):
        if t[3]:
                my = '.%s%d' % (t[3], t[4])
@@ -281,63 +388,99 @@ def version_str_to_tuple (s):
        return (string.atoi (t[0]), string.atoi (t[1]), string.atoi (t[2]),
                my_name, my_number)
 
+def next_version (t):
+       #print 'tup: %s' % `t`
+       l = list (t)
+       if len (l) >= 4:
+               # if l[3]:  # 1.0.0.my1 -> 1.0.0.my1
+               if l[4]:  # 1.0.0.my1 -> 1.0.1
+                       l[4] += 1
+               else:
+                       l[3] = l[4] = ''
+                       l[2] += 1
+       else:
+               l[2] += 1
+
+       return tuple (l)
+
+def prev_version (t):
+       #print 'tup: %s' % `t`
+       l = list (t)
+       if len (l) >= 4:
+               if l[4]: # 1.0.0.my1 -> 1.0.0
+                       if l[4] == 1:
+                               l[3] = l[4] = ''
+                       else:
+                               l[4] -= 1
+               # if l[3]: # 1.0.0.my1 -> 1.0.0.my0
+               #       l[4] -= 1
+               else:
+                       l[3] = l[4] = ''
+                       if l[2]:
+                               l[2] -= 1
+                       elif l[1]:
+                               l[1] -= 1
+                       else:
+                               l[0] -= 1
+       else:
+                       if l[2]:
+                               l[2] -= 1
+                       elif l[1]:
+                               l[1] -= 1
+                       else:
+                               l[0] -= 1
+               
+       return tuple (l)
+
 def split_package (p):
-       m = re.match ('(.*)-([0-9]*.*).tar.gz', p)
+       m = re.match ('(.*)-([0-9]*.*?)(.tar.gz)?$', p)
        return (m.group (1), version_str_to_tuple (m.group (2)))
 
 def join_package (t):
        return t[0] + '-' + version_tuple_to_str (t[1])
 
-def copy_file (user, passwd, host, dir, file):
-       os.system ('cp %s/%s .' % (dir, file))
-
-copy_ = copy_file
-
-def copy_ftp (user, passwd, host, dir, file):
-       if user == 'None':
-               user = 'anonymous'
-       if passwd == 'None':
-               passwd = program_name
-
-       command = '''
-open -u%s,%s -p21 %s
-set passive-mode off
-cd "%s"
-get "%s"
-''' % (user, passwd, host, dir, file)
-       temp = tempfile.mktemp (program_name)
-       f = open (temp, 'w')
-       f.write (command)
-       f.close ()
-       p = os.popen ('lftp -f %s' % temp, 'r')
-       s = p.read ()
-       status = p.close ()
+def diff_name (p):
+       t = split_package (p)
+       return '%s-%s-%s' % (t[0], version_tuple_to_str (prev_version (t[1])),
+                            version_tuple_to_str (t[1]))
        
-def copy_url (url, dir):
-       os.chdir (dir)
-       s = "copy_%s ('%s', '%s', '%s', '%s', '%s')" % split_url (url)
-       eval (s)
-
-
 def find_latest (url):
-       progress (_ ("listing %s...") % url)
+       progress (_ ("Listing `%s'...") % url)
        list = map (split_package, list_url (url))
        list.sort ()
        return join_package (list[-1])
 
 def build (p):
-       os.chdir (build_root)
-       system ('tar xzf %s/%s.tar.gz' % (release_dir, p))
-       os.chdir (p)
-       return system (build_command)
+       tar_ball = p + '.tar.gz'
+       (tar_name, tar_version) = split_package (tar_ball)
+       
+       expand = {
+               '%b' : build_root,
+               '%n' : tar_name,
+               '%r' : release_dir,
+               '%v' : version_tuple_to_str (tar_version),
+               '%s' : symlink_name,
+               '%t' : tar_ball,
+               }
+
+       c = build_command
+       for i in expand.keys ():
+               c = re.sub (i, expand[i], c)
+       return system (c, 1)
+
+
 
 (sh, long) = getopt_args (__main__.option_definitions)
 try:
-       (options, files) = getopt.getopt (sys.argv[1:], sh, long)
-except:
+       (options, files) = getopt.getopt(sys.argv[1:], sh, long)
+except getopt.error, s:
+       errorport.write ('\n')
+       errorport.write (_ ("error: ") + _ ("getopt says: `%s\'" % s))
+       errorport.write ('\n')
+       errorport.write ('\n')
        help ()
        sys.exit (2)
-       
+
 for opt in options:    
        o = opt[0]
        a = opt[1]
@@ -346,8 +489,15 @@ for opt in options:
                pass
        elif o == '--help' or o == '-h':
                help ()
-       elif o == '--buid-root' or o == '-b':
+               sys.exit (0)
+       elif o == '--build-root' or o == '-b':
                build_root = a
+       elif o == '--command' or o == '-c':
+               build_command = a
+       elif o == '--notify' or o == '-n':
+               notify = a
+       elif o == '--remove-previous' or o == '-r':
+               remove_previous_p = 1
        elif o == '--url' or o == '-u':
                url = a
        elif o == '--verbose' or o == '-V':
@@ -358,25 +508,36 @@ for opt in options:
        elif o == '--warranty' or o == '-w':
                warranty ()
                sys.exit (0)
+       else:
+               sys.exit (2)
                
 if 1:
        latest = find_latest (url)
 
-       if os.path.isdir ('%s/%s' % (build_root, latest)):
-               progress (_ ("latest is %s") % latest)
+       # if os.path.isdir ('%s/%s' % (build_root, latest)):
+       if os.path.exists ('%s/%s/index.html' % (build_root, latest)):
+               progress (_ ("latest is: %s") % latest)
                progress (_ ("relax, %s is up to date" % package_name))
                sys.exit (0)
 
+       if not symlink_name:
+               symlink_name = string.split (url, '/')[-2]
+               
        get_base = url[:string.rindex (url, '/')] + '/'
        if os.path.isdir (patch_dir):
                os.chdir (patch_dir)
-               if not os.path.isfile (latest + '.diff.gz'):
-                       get = get_base + latest + '.diff.gz'
-                       progress (_ ("fetching %s...") % get)
-                       copy_url (get, '.')
+               latest_diff = diff_name (latest)
+               if not os.path.isfile (latest_diff + '.diff.gz'):
+                       get = get_base + latest_diff + '.diff.gz'
+                       progress (_ ("Fetching `%s'...") % get)
+                       try:
+                               copy_url (get, '.')
+                       except:
+                               warning (_ ("can't open: %s") % get)
 
        if not os.path.isdir (build_root):
                build_root = temp_dir
+               
        if not os.path.isdir (release_dir):
                release_dir = temp_dir
                setup_temp ()
@@ -384,22 +545,27 @@ if 1:
        os.chdir (release_dir)
        if not os.path.isfile (latest + '.tar.gz'):
                get = get_base + latest + '.tar.gz'
-               progress (_ ("fetching %s...") % get)
+               progress (_ ("Fetching `%s'...") % get)
                copy_url (get, '.')
 
-       build_command = './configure --prefix=$HOME/usr && make web'
+       if os.path.isdir (os.path.join (build_root, package_name)):
+               os.chdir (os.path.join (build_root, package_name))
+               previous = os.getcwd ()
+       else:
+               previous = 0
+
+       progress (_ ("Building `%s'...") % latest)
+       os.chdir (build_root)
        if not build (latest):
-               if os.path.isdir ('%s/%s' % (build_root, package_name)):
-                       os.chdir ('%s/%s' % (build_root, package_name))
-                       previous = os.getcwd ()
-                       os.chdir (build_root)
-                       system ('rm -f %s' % package_name)
-                       system ('echo rm -rf %s/%s' % (build_root, previous))
-                       
-               os.symlink ('%s/%s' % (build_root, latest),  package_name)
+               if previous and remove_previous_p:
+                       system ('rm -rf %s' % os.path.join (build_root, previous))
+       else:
+               if notify:
+                       system ('(date; uname -a) | mail -s "%s failed" %s' % (program_name, notify))
+               sys.exit (1)
                
        os.chdir (original_dir)
-       if release_dir != temp_dir:
+       if release_dir != temp_dir and os.path.isdir (temp_dir):
                cleanup_temp ()
        sys.exit (0)