]> git.donarmstrong.com Git - lilypond.git/blob - scripts/update-lily.py
patch::: 1.4.0.jcn1
[lilypond.git] / scripts / update-lily.py
1 #!@PYTHON@
2 #
3 # update-lily.py -- lilypond autobuilder
4
5 # source file of the GNU LilyPond music typesetter
6 #
7 # download and rebuild latest lilypond or from specified url
8
9
10 '''
11 TODO:
12
13     * use urllib iso ftplib
14
15     * more flexible build/ftp/patches/releases paths
16
17     
18     show only: --command='echo "latest is: %n-%v"'
19 '''
20
21 import ftplib
22 import fnmatch
23 import getopt
24 import re
25 import operator
26 import os
27 import tempfile
28 import stat
29 import string
30 import sys
31 import __main__
32
33 try:
34         import gettext
35         gettext.bindtextdomain ('lilypond', '@localedir@')
36         gettext.textdomain('lilypond')
37         _ = gettext.gettext
38 except:
39         def _ (s):
40                 return s
41
42 sys.path.append ('@datadir@/python')
43 import gettext
44 gettext.bindtextdomain ('lilypond', '@localedir@')
45 gettext.textdomain('lilypond')
46 _ = gettext.gettext
47
48
49 program_name = 'build-lily'
50 package_name = 'lilypond'
51 help_summary = _("Fetch and rebuild from latest source package")
52 build_root = os.path.join (os.environ ['HOME'], 'usr', 'src')
53 release_dir = build_root + '/releases'
54 patch_dir = build_root + '/patches'
55 notify = 0
56
57 build_command = '''
58 set -x
59 cd %b &&
60 [ -d %n-%v ] && exit 1 || true;
61 mkdir -p %n-%v
62 (
63 tar xzf %r/%t &&
64 rm -f building &&
65 ln -s %n-%v building &&
66 cd %n-%v &&
67 ./configure --prefix=$HOME/usr && make all web
68 ) >> %n-%v/log.txt 2>&1 &&
69 rm -f %n &&
70 ln -s %n-%v %n
71 '''
72
73
74 url = 'file:/home/ftp/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
75 url = 'ftp://appel.lilypond.org/pub/gnu/LilyPond/development/lilypond-*.tar.gz'
76 url = 'ftp://ftp.cs.uu.nl/pub/GNU/LilyPond/development/lilypond-*.tar.gz'
77
78 remove_previous_p = 0
79
80
81 # lily_py.py -- options and stuff
82
83 # source file of the GNU LilyPond music typesetter
84
85 # BEGIN Library for these?
86 # cut-n-paste from ly2dvi
87
88 program_version = '@TOPLEVEL_VERSION@'
89 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
90         program_version = '1.3.142'
91
92
93 original_dir = os.getcwd ()
94 temp_dir = '%s.dir' % program_name
95 keep_temp_dir_p = 0
96 verbose_p = 0
97
98 def identify ():
99         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
100
101 def warranty ():
102         identify ()
103         sys.stdout.write ('\n')
104         sys.stdout.write (_ ('Copyright (c) %s by' % ' 2001'))
105         sys.stdout.write ('\n')
106         sys.stdout.write ('  Han-Wen Nienhuys')
107         sys.stdout.write ('  Jan Nieuwenhuizen')
108         sys.stdout.write ('\n')
109         sys.stdout.write (_ (r'''
110 Distributed under terms of the GNU General Public License. It comes with
111 NO WARRANTY.'''))
112         sys.stdout.write ('\n')
113
114 def progress (s):
115         sys.stderr.write (s + '\n')
116
117 def warning (s):
118         sys.stderr.write (_ ("warning: ") + s)
119         sys.stderr.write ('\n')
120         
121                 
122 def error (s):
123         sys.stderr.write (_ ("error: ") + s)
124         sys.stderr.write ('\n')
125         raise _ ("Exiting ... ")
126
127 def getopt_args (opts):
128         '''Construct arguments (LONG, SHORT) for getopt from  list of options.'''
129         short = ''
130         long = []
131         for o in opts:
132                 if o[1]:
133                         short = short + o[1]
134                         if o[0]:
135                                 short = short + ':'
136                 if o[2]:
137                         l = o[2]
138                         if o[0]:
139                                 l = l + '='
140                         long.append (l)
141         return (short, long)
142
143 def option_help_str (o):
144         '''Transform one option description (4-tuple ) into neatly formatted string'''
145         sh = '  '       
146         if o[1]:
147                 sh = '-%s' % o[1]
148
149         sep = ' '
150         if o[1] and o[2]:
151                 sep = ','
152                 
153         long = ''
154         if o[2]:
155                 long= '--%s' % o[2]
156
157         arg = ''
158         if o[0]:
159                 if o[2]:
160                         arg = '='
161                 arg = arg + o[0]
162         return '  ' + sh + sep + long + arg
163
164
165 def options_help_str (opts):
166         '''Convert a list of options into a neatly formatted string'''
167         w = 0
168         strs =[]
169         helps = []
170
171         for o in opts:
172                 s = option_help_str (o)
173                 strs.append ((s, o[3]))
174                 if len (s) > w:
175                         w = len (s)
176
177         str = ''
178         for s in strs:
179                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
180         return str
181
182 def help ():
183         sys.stdout.write (_ ("Usage: %s [OPTION]... FILE") % program_name)
184         sys.stdout.write ('\n\n')
185         sys.stdout.write (help_summary)
186         sys.stdout.write ('\n\n')
187         sys.stdout.write (_ ("Options:"))
188         sys.stdout.write ('\n')
189         sys.stdout.write (options_help_str (option_definitions))
190         sys.stdout.write ('\n\n')
191         sys.stdout.write (_ ("Report bugs to %s") % 'bug-gnu-music@gnu.org')
192         sys.stdout.write ('\n')
193         sys.exit (0)
194
195
196 def setup_temp ():
197         global temp_dir
198         if not keep_temp_dir_p:
199                 temp_dir = tempfile.mktemp (program_name)
200         try:
201                 os.mkdir (temp_dir, 0777)
202         except OSError:
203                 pass
204                 
205         
206 def system (cmd, ignore_error = 0):
207         if verbose_p:
208                 progress (_ ("Invoking `%s\'") % cmd)
209         st = os.system (cmd)
210         if st:
211                 msg =  ( _ ("error: ") + _ ("command exited with value %d") % st)
212                 if ignore_error:
213                         sys.stderr.write (msg + ' ' + _ ("(ignored)") + ' ')
214                 else:
215                         error (msg)
216
217         return st
218
219
220 def cleanup_temp ():
221         if not keep_temp_dir_p:
222                 if verbose_p:
223                         progress (_ ("Cleaning `%s'...") % temp_dir)
224                 system ('rm -rf %s' % temp_dir)
225
226
227 def set_setting (dict, key, val):
228         try:
229                 val = string.atof (val)
230         except ValueError:
231                 #warning (_ ("invalid value: %s") % `val`)
232                 pass
233
234         try:
235                 dict[key].append (val)
236         except KeyError:
237                 warning (_ ("no such setting: %s") % `key`)
238                 dict[key] = [val]
239
240 # END Library
241
242 option_definitions = [
243         ('DIR', 'b', 'build-root', _ ("unpack and build in DIR [%s]") % build_root),
244         ('COMMAND', 'c', 'command', _ ("execute COMMAND, subtitute:") \
245          + '\n                            ' + _ ("%b: build root") \
246          + '\n                            ' + _ ("%n: package name") \
247          + '\n                            ' + _ ("%r: release directory") \
248          + '\n                            ' + _ ("%t: tarball") \
249          + '\n                            ' + _ ("%v: package version") \
250          ),
251         ('', 'h', 'help', _ ("this help")),
252         ('', 'k', 'keep', _ ("keep all output, and name the directory %s") % temp_dir),
253         ('EMAIL', 'n', 'notify', _ ("upon failure notify EMAIL[,EMAIL]")),
254         ('', 'r', 'remove-previous', _ ("remove previous build")),
255         ('', 'V', 'verbose', _ ("verbose")),
256         ('', 'v', 'version', _ ("print version number")),
257         ('URL', 'u', 'url', _ ("fetch and build URL [%s]") % url),
258         ('', 'w', 'warranty', _ ("show warranty and copyright")),
259         ]
260
261 def list_file (user, passwd, host, dir, file):
262         match = []
263         for i in os.listdir (dir):
264                 if fnmatch.fnmatch (i, file):
265                         match.append (i)
266         return match
267
268 list_ = list_file
269
270 def list_ftp (user, passwd, host, dir, file):
271         if user == 'None':
272                 user = 'anonymous'
273         if passwd == 'None':
274                 passwd = program_name
275
276         ftp = ftplib.FTP (host)
277         ftp.login (user, passwd)
278         ftp.set_pasv (1)
279         ftp.cwd (dir)
280         list = ftp.nlst (file)
281         try:
282                 ftp.quit ()
283         except:
284                 ftp.close ()
285         return list
286         
287 def split_url (url):
288         m = re.match ('([^:/]*)(:)?(/*([^:]*):)?(/*([^@]*)@)?(//([^/]*))?(.*)/(.*)',
289                       url)
290         if not m:
291                 error ("can't parse url: %s " % url)
292         return (m.group (1), m.group (4), m.group (6), m.group (8),
293                 m.group (9), m.group (10))
294         
295 def list_url (url):
296         s = "list_%s ('%s', '%s', '%s', '%s', '%s')" % split_url (url)
297         return eval (s)
298
299 def version_tuple_to_str (t):
300         if t[3]:
301                 my = '.%s%d' % (t[3], t[4])
302         else:
303                 my = ''
304         return ('%d.%d.%d' % t[0:3]) + my
305
306 def version_str_to_tuple (s):
307         t = string.split (s, '.')
308         if len (t) >= 4:
309                 my_name = t[3][:-1]
310                 my_number = string.atoi (t[3][-1])
311         else:
312                 my_name = None
313                 my_number = None
314         return (string.atoi (t[0]), string.atoi (t[1]), string.atoi (t[2]),
315                 my_name, my_number)
316
317 def split_package (p):
318         m = re.match ('(.*)-([0-9]*.*).tar.gz', p)
319         return (m.group (1), version_str_to_tuple (m.group (2)))
320
321 def join_package (t):
322         return t[0] + '-' + version_tuple_to_str (t[1])
323
324 def copy_file (user, passwd, host, dir, file):
325         os.system ('cp %s/%s .' % (dir, file))
326
327 copy_ = copy_file
328
329 def copy_ftp (user, passwd, host, dir, file):
330         if user == 'None':
331                 user = 'anonymous'
332         if passwd == 'None':
333                 passwd = program_name
334
335         ftp = ftplib.FTP (host)
336         ftp.login (user, passwd)
337         ftp.set_pasv (1)
338         t = tempfile.mktemp (program_name)
339         try:
340                 f = open (t, 'w')
341                 ftp.retrbinary ('RETR %s/%s' % (dir, file),
342                         lambda x, f=f: f.write (x))
343                 f.close ()
344                 # huh? Invalid cross-device link
345                 # os.rename (t, file)
346                 system ('mv %s %s' % (t, file))
347         except:
348                 os.remove (t)
349                 raise 'Foo'
350         try:
351                 ftp.quit ()
352         except:
353                 ftp.close ()
354         return list
355         
356
357
358 def copy_url (url, dir):
359         os.chdir (dir)
360         s = "copy_%s ('%s', '%s', '%s', '%s', '%s')" % split_url (url)
361         eval (s)
362
363
364 def find_latest (url):
365         progress (_ ("Listing `%s'...") % url)
366         list = map (split_package, list_url (url))
367         list.sort ()
368         return join_package (list[-1])
369
370 def build (p):
371         tar_ball = p + '.tar.gz'
372         (tar_name, tar_version) = split_package (tar_ball)
373         
374         expand = {
375                 '%b' : build_root,
376                 '%n' : tar_name,
377                 '%r' : release_dir,
378                 '%v' : version_tuple_to_str (tar_version),
379                 '%t' : tar_ball,
380                 }
381
382         c = build_command
383         for i in expand.keys ():
384                 c = re.sub (i, expand[i], c)
385         return system (c, 1)
386
387 (sh, long) = getopt_args (__main__.option_definitions)
388 try:
389         (options, files) = getopt.getopt (sys.argv[1:], sh, long)
390 except:
391         help ()
392         sys.exit (2)
393         
394 for opt in options:     
395         o = opt[0]
396         a = opt[1]
397
398         if 0:
399                 pass
400         elif o == '--help' or o == '-h':
401                 help ()
402         elif o == '--buid-root' or o == '-b':
403                 build_root = a
404         elif o == '--command' or o == '-c':
405                 build_command = a
406         elif o == '--notify' or o == '-n':
407                 notify = a
408         elif o == '--remove-previous' or o == '-r':
409                 remove_previous_p = 1
410         elif o == '--url' or o == '-u':
411                 url = a
412         elif o == '--verbose' or o == '-V':
413                 verbose_p = 1
414         elif o == '--version' or o == '-v':
415                 identify ()
416                 sys.exit (0)
417         elif o == '--warranty' or o == '-w':
418                 warranty ()
419                 sys.exit (0)
420                 
421 if 1:
422         latest = find_latest (url)
423
424         # if os.path.isdir ('%s/%s' % (build_root, latest)):
425         if os.path.exists ('%s/%s/index.html' % (build_root, latest)):
426                 progress (_ ("latest is: %s") % latest)
427                 progress (_ ("relax, %s is up to date" % package_name))
428                 sys.exit (0)
429
430         get_base = url[:string.rindex (url, '/')] + '/'
431         if os.path.isdir (patch_dir):
432                 os.chdir (patch_dir)
433                 if not os.path.isfile (latest + '.diff.gz'):
434                         get = get_base + latest + '.diff.gz'
435                         progress (_ ("Fetching `%s'...") % get)
436                         copy_url (get, '.')
437
438         if not os.path.isdir (build_root):
439                 build_root = temp_dir
440                 
441         if not os.path.isdir (release_dir):
442                 release_dir = temp_dir
443                 setup_temp ()
444                 
445         os.chdir (release_dir)
446         if not os.path.isfile (latest + '.tar.gz'):
447                 get = get_base + latest + '.tar.gz'
448                 progress (_ ("Fetching `%s'...") % get)
449                 copy_url (get, '.')
450
451         if os.path.isdir (os.path.join (build_root, package_name)):
452                 os.chdir (os.path.join (build_root, package_name))
453                 previous = os.getcwd ()
454         else:
455                 previous = 0
456
457         progress (_ ("Building `%s'...") % latest)
458         os.chdir (build_root)
459         if not build (latest):
460                 if previous and remove_previous_p:
461                         system ('rm -rf %s' % os.path.join (build_root, previous))
462         else:
463                 if notify:
464                         system ('(date; uname -a) | mail -s "%s failed" %s' % (program_name, notify))
465                 sys.exit (1)
466                 
467         os.chdir (original_dir)
468         if release_dir != temp_dir:
469                 cleanup_temp ()
470         sys.exit (0)
471