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