-#!/usr/bin/python
+#!/usr/bin/python3
# Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
#
def __init__(self):
self.options = [ Option('mxe_prefix'),
Option('git_prefix'),
+ Option('git_reference'),
Option('osx_environment_prefix'),
Option('osx_sdk_prefix'),
Option('osx_sdk'),
# Utility bits
#
-def log(m):
+def log_normal(m):
if not globals.quiet:
print('\x1b[33m* %s\x1b[0m' % m)
+def log_verbose(m):
+ if globals.verbose:
+ print('\x1b[35m* %s\x1b[0m' % m)
+
def escape_spaces(s):
return s.replace(' ', '\\ ')
return '\"%s\"' % n.substr(' ', '\\ ')
def copytree(a, b):
- log('copy %s -> %s' % (scp_escape(a), scp_escape(b)))
+ log_normal('copy %s -> %s' % (scp_escape(a), scp_escape(b)))
if b.startswith('s3://'):
command('s3cmd -P -r put "%s" "%s"' % (a, b))
else:
command('scp -r %s %s' % (scp_escape(a), scp_escape(b)))
def copyfile(a, b):
- log('copy %s -> %s' % (scp_escape(a), scp_escape(b)))
+ log_normal('copy %s -> %s' % (scp_escape(a), scp_escape(b)))
if b.startswith('s3://'):
command('s3cmd -P put "%s" "%s"' % (a, b))
else:
command('ssh %s -- mkdir -p %s' % (s[0], s[1]))
def rmdir(a):
- log('remove %s' % a)
+ log_normal('remove %s' % a)
os.rmdir(a)
def rmtree(a):
- log('remove %s' % a)
+ log_normal('remove %s' % a)
shutil.rmtree(a, ignore_errors=True)
def command(c):
- log(c)
+ log_normal(c)
r = os.system(c)
if (r >> 8):
raise Error('command %s failed' % c)
def command_and_read(c):
- log(c)
+ log_normal(c)
p = subprocess.Popen(c.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = p.communicate()
if p.returncode != 0:
raise Error('command %s failed (%s)' % (c, err))
- return out.splitlines()
+ return str(out, 'utf-8').splitlines()
def read_wscript_variable(directory, variable):
f = open('%s/wscript' % directory, 'r')
try:
f = open('ChangeLog', 'r')
except:
- log('Could not open ChangeLog')
+ log_normal('Could not open ChangeLog')
return
c = f.read()
def append_version_to_debian_changelog(version):
if not os.path.exists('debian'):
- log('Could not find debian directory')
+ log_normal('Could not find debian directory')
return
command('dch -b -v %s-1 "New upstream release."' % version)
filename = filename.replace('devel', '-%s' % git_commit)
return filename
-def argument_options(args):
- opts = dict()
+
+def get_command_line_options(args):
+ """Get the options specified by --option on the command line"""
+ options = dict()
if args.option is not None:
for o in args.option:
b = o.split(':')
if len(b) != 2:
raise Error("Bad option `%s'" % o)
if b[1] == 'False':
- opts[b[0]] = False
+ options[b[0]] = False
elif b[1] == 'True':
- opts[b[0]] = True
+ options[b[0]] = True
else:
- opts[b[0]] = b[1]
- return opts
+ options[b[0]] = b[1]
+ return options
class TreeDirectory:
self.set('CCACHE_BASEDIR', os.path.realpath(self.directory))
self.set('CCACHE_NOHASHDIR', '')
else:
- self.directory = directory
+ self.directory = os.path.realpath(directory)
self.rmdir = False
pass
def package(self, project, checkout, output_dir, options):
- tree = globals.trees.get(project, checkout, self)
- if self.build_dependencies:
- tree.build_dependencies(options)
- tree.build(options)
- if len(inspect.getargspec(tree.cscript['package']).args) == 3:
+ tree = self.build(project, checkout, options)
+ tree.add_defaults(options)
+ if len(inspect.getfullargspec(tree.cscript['package']).args) == 3:
packages = tree.call('package', tree.version, options)
else:
- log("Deprecated cscript package() method with no options parameter")
+ log_normal("Deprecated cscript package() method with no options parameter")
packages = tree.call('package', tree.version)
- if isinstance(packages, (str, unicode)):
- copyfile(packages, os.path.join(output_dir, os.path.basename(devel_to_git(tree.git_commit, packages))))
- else:
+ if isinstance(packages, list):
for p in packages:
copyfile(p, os.path.join(output_dir, os.path.basename(devel_to_git(tree.git_commit, p))))
+ else:
+ copyfile(packages, os.path.join(output_dir, os.path.basename(devel_to_git(tree.git_commit, packages))))
def build(self, project, checkout, options):
tree = globals.trees.get(project, checkout, self)
if self.build_dependencies:
tree.build_dependencies(options)
tree.build(options)
+ return tree
def test(self, tree, test, options):
"""test is the test case to run, or None"""
if self.build_dependencies:
tree.build_dependencies(options)
tree.build(options)
- return tree.call('test', test)
+
+ tree.add_defaults(options)
+ if len(inspect.getfullargspec(tree.cscript['test']).args) == 3:
+ return tree.call('test', options, test)
+ else:
+ log_normal('Deprecated cscript test() method with no options parameter')
+ return tree.call('test', test)
def set(self, a, b):
self.variables[a] = b
return ''
return '-u %s' % getpass.getuser()
+ def _mount_option(self, d):
+ return '-v %s:%s ' % (os.path.realpath(d), os.path.realpath(d))
+
def setup(self):
- opts = '-v %s:%s ' % (self.directory, self.directory)
+ opts = self._mount_option(self.directory)
for m in self.mounts:
- opts += '-v %s:%s ' % (m, m)
+ opts += self._mount_option(m)
+ if config.has('git_reference'):
+ opts += self._mount_option(config.get('git_reference'))
if self.privileged:
opts += '--privileged=true '
if self.ccache:
- opts += "-e CCACHE_DIR=/ccache --volumes-from ccache-%s" % self.image
+ opts += "-e CCACHE_DIR=/ccache/%s-%d --mount source=ccache,target=/ccache" % (self.image, os.getuid())
tag = self.image
if config.has('docker_hub_repository'):
self.set('PKG_CONFIG_LIBDIR', '%s/lib/pkgconfig' % self.environment_prefix)
self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig:%s/bin/pkgconfig' % (self.directory, self.directory))
self.set('PATH', '%s/bin:%s:%s' % (self.environment_prefix, self.tool_path, os.environ['PATH']))
- self.set('CC', '%s-gcc' % self.name)
- self.set('CXX', '%s-g++' % self.name)
self.set('LD', '%s-ld' % self.name)
self.set('RANLIB', '%s-ranlib' % self.name)
self.set('WINRC', '%s-windres' % self.name)
if environment_version is not None:
self.image += '_%s' % environment_version
+ def setup(self):
+ super().setup()
+ if self.ccache:
+ self.set('CC', '"ccache %s-gcc"' % self.name)
+ self.set('CXX', '"ccache %s-g++"' % self.name)
+ else:
+ self.set('CC', '%s-gcc' % self.name)
+ self.set('CXX', '%s-g++' % self.name)
+
@property
def library_prefix(self):
- log('Deprecated property library_prefix: use environment_prefix')
+ log_normal('Deprecated property library_prefix: use environment_prefix')
return self.environment_prefix
@property
def windows_prefix(self):
- log('Deprecated property windows_prefix: use environment_prefix')
+ log_normal('Deprecated property windows_prefix: use environment_prefix')
return self.environment_prefix
@property
def mingw_prefixes(self):
- log('Deprecated property mingw_prefixes: use environment_prefix')
+ log_normal('Deprecated property mingw_prefixes: use environment_prefix')
return [self.environment_prefix]
@property
def mingw_path(self):
- log('Deprecated property mingw_path: use tool_path')
+ log_normal('Deprecated property mingw_path: use tool_path')
return self.tool_path
@property
def mingw_name(self):
- log('Deprecated property mingw_name: use name')
+ log_normal('Deprecated property mingw_name: use name')
return self.name
class AppImageTarget(LinuxTarget):
def __init__(self, work):
- super(AppImageTarget, self).__init__('ubuntu', '16.04', 64, work)
+ super(AppImageTarget, self).__init__('ubuntu', '18.04', 64, work)
self.detail = 'appimage'
self.privileged = True
tree = globals.trees.get(project, checkout, self)
with TreeDirectory(tree):
- if len(inspect.getargspec(tree.cscript['package']).args) == 3:
+ if len(inspect.getfullargspec(tree.cscript['package']).args) == 3:
packages = tree.call('package', tree.version, options)
else:
- log("Deprecated cscript package() method with no options parameter")
+ log_normal("Deprecated cscript package() method with no options parameter")
packages = tree.call('package', tree.version)
for p in packages:
copyfile(p, os.path.join(output_dir, os.path.basename(devel_to_git(tree.git_commit, p))))
super(SourceTarget, self).__init__('source')
def command(self, c):
- log('host -> %s' % c)
+ log_normal('host -> %s' % c)
command('%s %s' % (self.variables_string(), c))
def cleanup(self):
if globals.quiet:
flags = '-q'
redirect = '>/dev/null'
- command('git clone %s %s/%s.git %s/src/%s' % (flags, config.get('git_prefix'), self.name, target.directory, self.name))
+ if config.has('git_reference'):
+ ref = '--reference-if-able %s/%s.git' % (config.get('git_reference'), self.name)
+ else:
+ ref = ''
+ command('git clone %s %s %s/%s.git %s/src/%s' % (flags, ref, config.get('git_prefix'), self.name, target.directory, self.name))
os.chdir('%s/src/%s' % (target.directory, self.name))
spec = self.specifier
exec(open('%s/cscript' % proj).read(), self.cscript)
# cscript can include submodules = False to stop submodules being fetched
- if not 'submodules' in self.cscript or self.cscript['submodules'] == True:
- command('git submodule init --quiet')
- command('git submodule update --quiet')
+ if (not 'submodules' in self.cscript or self.cscript['submodules'] == True) and os.path.exists('.gitmodules'):
+ command('git submodule --quiet init')
+ paths = command_and_read('git config --file .gitmodules --get-regexp path')
+ urls = command_and_read('git config --file .gitmodules --get-regexp url')
+ for path, url in zip(paths, urls):
+ path = path.split(' ')[1]
+ url = url.split(' ')[1]
+ ref = ''
+ if config.has('git_reference'):
+ ref_path = os.path.join(config.get('git_reference'), os.path.basename(url))
+ if os.path.exists(ref_path):
+ ref = '--reference %s' % ref_path
+ command('git submodule --quiet update %s %s' % (ref, path))
if os.path.exists('%s/wscript' % proj):
v = read_wscript_variable(proj, "VERSION");
try:
self.version = Version(v)
except:
- tag = subprocess.Popen(shlex.split('git -C %s describe --tags' % proj), stdout=subprocess.PIPE).communicate()[0][1:]
+ tag = command_and_read('git -C %s describe --tags' % proj)[0][1:]
self.version = Version.from_git_tag(tag)
os.chdir(cwd)
return self.cscript[function](self.target, *args)
def add_defaults(self, options):
- """Add the defaults from this into a dict options"""
+ """Add the defaults from self into a dict options"""
if 'option_defaults' in self.cscript:
from_cscript = self.cscript['option_defaults']
if isinstance(from_cscript, dict):
defaults_dict = from_cscript
else:
- log("Deprecated cscript option_defaults method; replace with a dict")
+ log_normal("Deprecated cscript option_defaults method; replace with a dict")
defaults_dict = from_cscript()
for k, v in defaults_dict.items():
if not k in options:
options[k] = v
def dependencies(self, options):
+ """
+ yield details of the dependencies of this tree. Each dependency is returned
+ as a tuple of (tree, options). The 'options' parameter are the options that
+ we want to force for 'self'.
+ """
if not 'dependencies' in self.cscript:
return
- if len(inspect.getargspec(self.cscript['dependencies']).args) == 2:
- deps = self.call('dependencies', options)
+ if len(inspect.getfullargspec(self.cscript['dependencies']).args) == 2:
+ self_options = copy.copy(options)
+ self.add_defaults(self_options)
+ deps = self.call('dependencies', self_options)
else:
- log("Deprecated cscript dependencies() method with no options parameter")
+ log_normal("Deprecated cscript dependencies() method with no options parameter")
deps = self.call('dependencies')
+ # Loop over our immediate dependencies
for d in deps:
dep = globals.trees.get(d[0], d[1], self.target, self.name)
- # Start with the options passed in
- dep_options = copy.copy(options)
- # Add things specified by the parent
- if len(d) > 2:
- for k, v in d[2].items():
- if not k in dep_options:
- dep_options[k] = v
- # Then fill in the dependency's defaults
- dep.add_defaults(dep_options)
-
+ # deps only get their options from the parent's cscript
+ dep_options = d[2] if len(d) > 2 else {}
for i in dep.dependencies(dep_options):
yield i
yield (dep, dep_options)
pass
def build_dependencies(self, options):
+ """
+ Called on the 'main' project tree (-p on the command line) to build all dependencies.
+ 'options' will be the ones from the command line.
+ """
for i in self.dependencies(options):
- global args
- if args.verbose:
- print('Building a dependency of %s %s %s with %s' % (self.name, self.specifier, self.version, options))
i[0].build(i[1])
def build(self, options):
if self.built:
return
- global args
- if args.verbose:
- print("* Building %s %s %s with %s" % (self.name, self.specifier, self.version, options))
+ log_verbose("Building %s %s %s with %s" % (self.name, self.specifier, self.version, options))
variables = copy.copy(self.target.variables)
- # Start with the options passed in
options = copy.copy(options)
- # Fill in the defaults
self.add_defaults(options)
if not globals.dry_run:
- if len(inspect.getargspec(self.cscript['build']).args) == 2:
+ if len(inspect.getfullargspec(self.cscript['build']).args) == 2:
self.call('build', options)
else:
self.call('build')
"package": "package and build project",
"release": "release a project using its next version number (changing wscript and tagging)",
"pot": "build the project's .pot files",
- "changelog": "generate a simple HTML changelog",
"manual": "build the project's manual",
"doxygen": "build the project's Doxygen documentation",
"latest": "print out the latest version",
if args.work is not None:
args.work = os.path.abspath(args.work)
+ if not os.path.exists(args.work):
+ os.makedirs(args.work)
if args.project is None and args.command != 'shell':
raise Error('you must specify -p or --project')
globals.quiet = args.quiet
+ globals.verbose = args.verbose
globals.command = args.command
globals.dry_run = args.dry_run
raise Error('you must specify -t or --target')
target = target_factory(args)
- target.build(args.project, args.checkout, argument_options(args))
+ target.build(args.project, args.checkout, get_command_line_options(args))
if not args.keep:
target.cleanup()
output_dir = args.output
makedirs(output_dir)
-
- # Start with the options passed on the command line
- options = copy.copy(argument_options(args))
- # Fill in the defaults
- tree = globals.trees.get(args.project, args.checkout, target)
- tree.add_defaults(options)
- target.package(args.project, args.checkout, output_dir, options)
+ target.package(args.project, args.checkout, output_dir, get_command_line_options(args))
except Error as e:
if target is not None and not args.keep:
target.cleanup()
target.cleanup()
- elif globals.command == 'changelog':
- target = SourceTarget()
- tree = globals.trees.get(args.project, args.checkout, target)
-
- with TreeDirectory(tree):
- text = open('ChangeLog', 'r')
-
- html = tempfile.NamedTemporaryFile()
- versions = 8
-
- last = None
- changes = []
-
- while True:
- l = text.readline()
- if l == '':
- break
-
- if len(l) > 0 and l[0] == "\t":
- s = l.split()
- if len(s) == 4 and s[1] == "Version" and s[3] == "released.":
- v = Version(s[2])
- if v.micro == 0:
- if last is not None and len(changes) > 0:
- print("<h2>Changes between version %s and %s</h2>" % (s[2], last), file=html)
- print("<ul>", file=html)
- for c in changes:
- print("<li>%s" % c, file=html)
- print("</ul>", file=html)
- last = s[2]
- changes = []
- versions -= 1
- if versions < 0:
- break
- else:
- c = l.strip()
- if len(c) > 0:
- if c[0] == '*':
- changes.append(c[2:])
- else:
- changes[-1] += " " + c
-
- copyfile(html.file, '%schangelog.html' % args.output)
- html.close()
- target.cleanup()
-
elif globals.command == 'manual':
target = SourceTarget()
tree = globals.trees.get(args.project, args.checkout, target)
target = target_factory(args)
tree = globals.trees.get(args.project, args.checkout, target)
with TreeDirectory(tree):
- target.test(tree, args.test, argument_options(args))
+ target.test(tree, args.test, get_command_line_options(args))
except Error as e:
if target is not None and not args.keep:
target.cleanup()