19 print '\x1b[33m* %s\x1b[0m' % m
22 print '\x1b[31mError: %s\x1b[0m' % e
26 log('copy %s -> %s' % (a, b))
30 log('copy %s -> %s' % (a, b))
35 shutil.rmtree(a, ignore_errors=True)
37 def command(c, can_fail=False):
40 if (r >> 8) and not can_fail:
41 error('command failed')
43 def command_and_read(c):
45 p = subprocess.Popen(c.split(), stdout=subprocess.PIPE)
46 f = os.fdopen(os.dup(p.stdout.fileno()))
55 def __init__(self, s):
70 self.beta = int(s[b+4:])
74 self.major = int(p[0])
75 self.minor = int(p[1])
86 def bump_and_to_pre(self):
99 elif self.beta is not None:
101 elif self.beta is None:
105 s = '%d.%02d' % (self.major, self.minor)
106 if self.beta is not None:
107 s += 'beta%d' % self.beta
118 class Environment(object):
123 self.variables[a] = b
126 return self.variables[a]
128 def variables_string(self):
130 for k, v in self.variables.iteritems():
131 e += '%s="%s" ' % (k, v)
134 def work_dir_cdist(self, sub):
137 def work_dir_cscript(self):
140 def build_dependencies(self, target, project):
142 if 'dependencies' in project.cscript:
143 for d in project.cscript['dependencies'](target):
144 dep = Project(d[0], '.', d[1])
146 self.build(target, dep)
149 def build(self, target, project):
150 project.cscript['build'](self, target)
152 def package(self, target, project):
153 project.checkout(self)
154 if target.platform != 'source':
155 self.build_dependencies(target, project)
156 if target.platform == 'source':
157 command('./waf dist')
158 return os.path.abspath('%s-%s.tar.bz2' % (project.name, project.version))
160 project.cscript['build'](self, target)
161 return project.cscript['package'](self, target, project.version)
170 class ChrootEnvironment(Environment):
171 def __init__(self, chroot):
172 super(ChrootEnvironment, self).__init__()
174 self.dir_in_chroot = '/home/carl'
175 self.chroot_dir = '/home/carl/Environments'
177 # ChrootEnvironments work in dir_in_chroot, and clear
179 for g in glob.glob('%s/*' % self.work_dir_cdist()):
182 # Environment variables
183 self.set('CXXFLAGS', '-I%s/include' % self.work_dir_cscript())
184 self.set('LINKFLAGS', '-L%s/lib' % self.work_dir_cscript())
185 self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig' % self.work_dir_cscript())
187 def work_dir_cdist(self):
188 return '%s/%s%s' % (self.chroot_dir, self.chroot, self.dir_in_chroot)
190 def work_dir_cscript(self):
191 return self.dir_in_chroot
193 def command(self, c):
194 # Work out the cwd for the chrooted command
196 prefix = '%s/%s' % (self.chroot_dir, self.chroot)
197 assert(cwd.startswith(prefix))
198 cwd = cwd[len(prefix):]
200 log('schroot [%s] -> %s' % (cwd, c))
201 command('%s schroot -c %s -d %s -p -- %s' % (self.variables_string(), self.chroot, cwd, c))
207 class HostEnvironment(Environment):
208 def __init__(self, directory=None):
209 super(HostEnvironment, self).__init__()
210 if directory is None:
211 self.directory = tempfile.mkdtemp()
214 self.directory = directory
217 def work_dir_cdist(self):
218 return self.directory
220 def work_dir_cscript(self):
221 return self.directory
223 def command(self, c):
224 log('host -> %s' % c)
225 command('%s %s' % (self.variables_string(), c))
229 rmtree(self.directory)
232 def prepare_for_windows(env, bits):
233 env.windows_prefix = '/home/carl/Environments/windows/%d' % bits
234 if not os.path.exists(env.windows_prefix):
235 error('windows prefix %s does not exist' % env.windows_prefix)
240 mingw_name = 'x86_64'
242 mingw_path = '/mingw/bin'
243 mingw_prefixes = ['/mingw', '/mingw/%s-w64-mingw32' % mingw_name]
245 env.set('PKG_CONFIG_LIBDIR', '%s/lib/pkgconfig' % env.windows_prefix)
246 env.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig' % env.work_dir_cscript())
247 env.set('PATH', '%s/bin:%s:%s' % (env.windows_prefix, mingw_path, os.environ['PATH']))
248 env.set('CC', '%s-w64-mingw32-gcc' % mingw_name)
249 env.set('CXX', '%s-w64-mingw32-g++' % mingw_name)
250 env.set('LD', '%s-w64-mingw32-ld' % mingw_name)
251 env.set('RANLIB', '%s-w64-mingw32-ranlib' % mingw_name)
252 env.set('WINRC', '%s-w64-mingw32-windres' % mingw_name)
253 cxx = '-I%s/include -I%s/include' % (env.windows_prefix, env.work_dir_cscript())
254 link = '-L%s/lib -L%s/lib' % (env.windows_prefix, env.work_dir_cscript())
255 for p in mingw_prefixes:
256 cxx += ' -I%s/include' % p
257 link += ' -L%s/lib' % p
258 env.set('CXXFLAGS', cxx)
259 env.set('LINKFLAGS', link)
267 def __init__(self, name):
269 if name.startswith('ubuntu-') or name.startswith('debian-'):
270 self.platform = 'linux'
271 self.version = name.split('-')[1]
272 self.bits = int(name.split('-')[2])
273 elif name.startswith('windows-'):
274 self.platform = 'windows'
275 self.bits = int(name.split('-')[1])
276 elif name == 'source':
277 self.platform = 'source'
279 def environment_for_target(target, directory):
280 if target.platform == 'linux':
281 return ChrootEnvironment(target.name)
282 elif target.platform == 'windows':
283 env = HostEnvironment(directory)
284 prepare_for_windows(env, target.bits)
286 elif target.platform == 'source':
287 return HostEnvironment()
295 class Project(object):
296 def __init__(self, name, directory, specifier=None):
298 self.directory = directory
299 self.git_dir = 'ssh://houllier/home/carl/git'
301 self.specifier = specifier
302 if self.specifier is None:
303 self.specifier = 'master'
305 def checkout(self, env):
310 redirect = '>/dev/null'
311 command('git clone --depth 0 %s %s/%s.git %s/src/%s' % (flags, self.git_dir, self.name, env.work_dir_cdist(), self.name))
312 os.chdir('%s/src/%s' % (env.work_dir_cdist(), self.name))
313 command('git checkout %s %s %s' % (flags, self.specifier, redirect))
314 command('git submodule init')
315 command('git submodule update')
316 os.chdir(self.directory)
318 proj = '%s/src/%s/%s' % (env.work_dir_cdist(), self.name, self.directory)
320 self.read_cscript('%s/cscript' % proj)
322 if os.path.exists('%s/wscript' % proj):
323 f = open('%s/wscript' % proj, 'r')
331 if len(s) == 3 and s[0] == "VERSION":
332 self.version = Version(s[2])
336 def read_cscript(self, s):
338 execfile(s, self.cscript)
340 def set_version_in_wscript(version):
341 f = open('wscript', 'rw')
342 o = open('wscript.tmp', 'w')
349 if len(s) == 3 and s[0] == "VERSION":
350 print "Writing %s" % version
351 print >>o,"VERSION = '%s'" % version
357 os.rename('wscript.tmp', 'wscript')
359 def append_version_to_changelog(version):
361 f = open('ChangeLog', 'r')
363 log('Could not open ChangeLog')
369 f = open('ChangeLog', 'w')
370 now = datetime.datetime.now()
371 f.write('%d-%02d-%02d Carl Hetherington <cth@carlh.net>\n\n\t* Version %s released.\n\n' % (now.year, now.month, now.day, version))
374 def append_version_to_debian_changelog(version):
375 if not os.path.exists('debian'):
376 log('Could not find debian directory')
379 command('dch -b -v %s-1 "New upstream release."' % version)
382 # Command-line parser
385 parser = argparse.ArgumentParser()
386 parser.add_argument('command')
387 parser.add_argument('-p', '--project', help='project name', required=True)
388 parser.add_argument('-d', '--directory', help='directory within project repo', default='.')
389 parser.add_argument('--beta', help='beta release', action='store_true')
390 parser.add_argument('--full', help='full release', action='store_true')
391 parser.add_argument('-c', '--checkout', help='string to pass to git for checkout')
392 parser.add_argument('-o', '--output', help='output directory', default='.')
393 parser.add_argument('-q', '--quiet', help='be quiet', action='store_true')
394 parser.add_argument('-t', '--target', help='target')
395 parser.add_argument('-k', '--keep', help='keep working tree', action='store_true')
396 args = parser.parse_args()
398 args.output = os.path.abspath(args.output)
400 if args.project is None:
401 error('you must specify -p or --project')
403 project = Project(args.project, args.directory, args.checkout)
405 if args.command == 'build':
406 if args.target is None:
407 error('you must specify -t or --target')
409 target = Target(args.target)
410 env = environment_for_target(target)
411 project.checkout(env)
412 env.build_dependencies(target, project)
413 env.build(target, project)
417 elif args.command == 'package':
418 if args.target is None:
419 error('you must specify -t or --target')
421 target = Target(args.target)
422 env = environment_for_target(target, None)
424 packages = env.package(target, project)
425 if hasattr(packages, 'strip') or (not hasattr(packages, '__getitem__') and not hasattr(packages, '__iter__')):
426 packages = [packages]
428 if target.platform == 'linux':
429 out = '%s/%s-%d' % (args.output, target.version, target.bits)
435 copyfile(p, '%s/%s' % (out, os.path.basename(p)))
438 copyfile(p, '%s/%s' % (args.output, os.path.basename(p)))
442 elif args.command == 'release':
443 if args.full is False and args.beta is False:
444 error('you must specify --full or --beta')
446 env = HostEnvironment()
447 project.checkout(env)
449 version = project.version
455 set_version_in_wscript(version)
456 append_version_to_changelog(version)
457 append_version_to_debian_changelog(version)
459 command('git commit -a -m "Bump version"')
460 command('git tag -m "v%s" v%s' % (version, version))
463 version.bump_and_to_pre()
464 set_version_in_wscript(version)
465 command('git commit -a -m "Bump version"')
468 command('git push --tags')
472 elif args.command == 'pot':
473 env = HostEnvironment()
474 project.checkout(env)
476 pots = project.cscript['make_pot'](env)
478 copyfile(p, '%s/%s' % (args.output, os.path.basename(p)))
482 elif args.command == 'changelog':
483 env = HostEnvironment()
484 project.checkout(env)
486 text = open('ChangeLog', 'r')
487 html = open('%s/changelog.html' % args.output, 'w')
498 if len(l) > 0 and l[0] == "\t":
500 if len(s) == 4 and s[1] == "Version" and s[3] == "released.":
501 if not "beta" in s[2]:
502 if last is not None and len(changes) > 0:
503 print >>html,"<h2>Changes between version %s and %s</h2>" % (s[2], last)
506 print >>html,"<li>%s" % c
517 changes.append(c[2:])
519 changes[-1] += " " + c
523 elif args.command == 'manual':
524 env = HostEnvironment()
525 project.checkout(env)
527 dirs = project.cscript['make_manual'](env)
529 copytree(d, '%s/%s' % (args.output, os.path.basename(d)))
533 elif args.command == 'doxygen':
534 env = HostEnvironment()
535 project.checkout(env)
537 dirs = project.cscript['make_doxygen'](env)
538 if hasattr(dirs, 'strip') or (not hasattr(dirs, '__getitem__') and not hasattr(dirs, '__iter__')):
542 copytree(d, '%s/%s' % (args.output, 'doc'))
546 elif args.command == 'latest':
547 env = HostEnvironment()
548 project.checkout(env)
550 f = command_and_read('git log --tags --simplify-by-decoration --pretty="%d"')
552 m = re.compile(".*\((.*)\).*").match(t)
555 tags = m.group(1).split(', ')
557 if len(t) > 0 and t[0] == 'v':
563 elif args.command == 'test':
564 if args.target is None:
565 error('you must specify -t or --target')
567 target = Target(args.target)
568 env = environment_for_target(target, '.')
569 project.read_cscript('cscript')
570 env.build(target, project)
573 error('invalid command %s' % args.command)