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