X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=cdist;h=cab4ddf25c2a29d46c8ca981bd6438f75854f283;hb=32fcfd575dde16a65f548990ae9ee59ab6a65052;hp=a5abbccc064f656471ee1b2054f3a64b1480c085;hpb=b7862ed487bd807a2ce1356cc4bde9558bc5ebbc;p=cdist.git diff --git a/cdist b/cdist index a5abbcc..cab4ddf 100755 --- a/cdist +++ b/cdist @@ -16,6 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +from __future__ import print_function import os import sys import shutil @@ -28,7 +29,7 @@ import re import copy import inspect -TEMPORARY_DIRECTORY = '/tmp' +TEMPORARY_DIRECTORY = '/var/tmp' class Error(Exception): def __init__(self, value): @@ -50,7 +51,7 @@ class Trees: def get(self, name, specifier, target): for t in self.trees: if t.name == name and t.specifier == specifier and t.target == target: - return p + return t elif t.name == name and t.specifier != specifier: raise Error('conflicting versions of %s requested (%s and %s)' % (name, specifier, t.specifier)) @@ -124,15 +125,19 @@ class Config: raise Error('Required setting %s not found' % k) + def set(self, k, v): + for o in self.options: + o.offer(k, v) + config = Config() # # Utility bits -# +# def log(m): if not globals.quiet: - print '\x1b[33m* %s\x1b[0m' % m + print('\x1b[33m* %s\x1b[0m' % m) def scp_escape(n): s = n.split(':') @@ -183,7 +188,7 @@ def read_wscript_variable(directory, variable): l = f.readline() if l == '': break - + s = l.split() if len(s) == 3 and s[0] == variable: f.close() @@ -202,10 +207,10 @@ def set_version_in_wscript(version): s = l.split() if len(s) == 3 and s[0] == "VERSION": - print "Writing %s" % version - print >>o,"VERSION = '%s'" % version + print("Writing %s" % version) + print("VERSION = '%s'" % version, file=o) else: - print >>o,l, + print(l, file=o, end="") f.close() o.close() @@ -259,7 +264,7 @@ class Version: s = s[1:] if s.endswith("'"): s = s[0:-1] - + if s.endswith('devel'): s = s[0:-5] self.devel = True @@ -327,10 +332,11 @@ class Target(object): tree.build(tree) return tree.call('package', tree.version), tree.git_commit - def test(self, tree): + def test(self, tree, test): + """test is the test case to run, or None""" tree.build_dependencies() tree.build() - return tree.call('test') + return tree.call('test', test) def set(self, a, b): self.variables[a] = b @@ -342,14 +348,18 @@ class Target(object): return self.variables[a] def append_with_space(self, k, v): - if not k in self.variables: - self.variables[k] = v + if (not k in self.variables) or len(self.variables[k]) == 0: + self.variables[k] = '"%s"' % v else: - self.variables[k] = '%s %s' % (self.variables[k], v) + e = self.variables[k] + if e[0] == '"' and e[-1] == '"': + self.variables[k] = '"%s %s"' % (e[1:-1], v) + else: + self.variables[k] = '"%s %s"' % (e, v) def variables_string(self, escaped_quotes=False): e = '' - for k, v in self.variables.iteritems(): + for k, v in self.variables.items(): if escaped_quotes: v = v.replace('"', '\\"') e += '%s=%s ' % (k, v) @@ -359,19 +369,20 @@ class Target(object): if self.rmdir: rmtree(self.directory) -# +# # Windows # class WindowsTarget(Target): - def __init__(self, bits, directory=None): + def __init__(self, version, bits, directory=None): super(WindowsTarget, self).__init__('windows', directory) + self.version = version self.bits = bits self.windows_prefix = '%s/%d' % (config.get('windows_environment_prefix'), self.bits) if not os.path.exists(self.windows_prefix): raise Error('windows prefix %s does not exist' % self.windows_prefix) - + if self.bits == 32: self.mingw_name = 'i686' else: @@ -396,35 +407,50 @@ class WindowsTarget(Target): self.set('CXXFLAGS', '"%s"' % cxx) self.set('CPPFLAGS', '') self.set('LINKFLAGS', '"%s"' % link) + self.set('LDFLAGS', '"%s"' % link) def command(self, c): log('host -> %s' % c) command('%s %s' % (self.variables_string(), c)) -# -# Linux -# - class LinuxTarget(Target): + """Parent for Linux targets""" def __init__(self, distro, version, bits, directory=None): super(LinuxTarget, self).__init__('linux', directory) self.distro = distro self.version = version self.bits = bits - # e.g. ubuntu-14.04-64 - self.chroot = '%s-%s-%d' % (self.distro, self.version, self.bits) - # e.g. /home/carl/Environments/ubuntu-14.04-64 - self.chroot_prefix = '%s/%s' % (config.get('linux_chroot_prefix'), self.chroot) self.set('CXXFLAGS', '-I%s/include' % self.directory) self.set('CPPFLAGS', '') self.set('LINKFLAGS', '-L%s/lib' % self.directory) - self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig:/usr/local/lib/pkgconfig' % self.directory) - self.set('PATH', '%s:/usr/local/bin' % (os.environ['PATH'])) + self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig:%s/lib64/pkgconfig:/usr/local/lib/pkgconfig' % (self.directory, self.directory)) + self.set('PATH', '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin') + +class ChrootTarget(LinuxTarget): + """Build in a chroot""" + def __init__(self, distro, version, bits, directory=None): + super(ChrootTarget, self).__init__(distro, version, bits, directory) + # e.g. ubuntu-14.04-64 + if self.version is not None and self.bits is not None: + self.chroot = '%s-%s-%d' % (self.distro, self.version, self.bits) + else: + self.chroot = self.distro + # e.g. /home/carl/Environments/ubuntu-14.04-64 + self.chroot_prefix = '%s/%s' % (config.get('linux_chroot_prefix'), self.chroot) def command(self, c): command('%s schroot -c %s -p -- %s' % (self.variables_string(), self.chroot, c)) + +class HostTarget(LinuxTarget): + """Build directly on the host""" + def __init__(self, distro, version, bits, directory=None): + super(HostTarget, self).__init__(distro, version, bits, directory) + + def command(self, c): + command('%s %s' % (self.variables_string(), c)) + # # OS X # @@ -432,6 +458,8 @@ class LinuxTarget(Target): class OSXTarget(Target): def __init__(self, directory=None): super(OSXTarget, self).__init__('osx', directory) + self.sdk = config.get('osx_sdk') + self.sdk_prefix = config.get('osx_sdk_prefix') def command(self, c): command('%s %s' % (self.variables_string(False), c)) @@ -447,7 +475,7 @@ class OSXSingleTarget(OSXTarget): else: arch = 'x86_64' - flags = '-isysroot %s/MacOSX%s.sdk -arch %s' % (config.get('osx_sdk_prefix'), config.get('osx_sdk'), arch) + flags = '-isysroot %s/MacOSX%s.sdk -arch %s' % (self.sdk_prefix, self.sdk, arch) enviro = '%s/%d' % (config.get('osx_environment_prefix'), bits) # Environment variables @@ -475,16 +503,13 @@ class OSXUniversalTarget(OSXTarget): tree = globals.trees.get(project, checkout, target) tree.build_dependencies() tree.build() - - tree = globals.trees.get(project, checkout, self) + + tree = globals.trees.get(project, checkout, self) with TreeDirectory(tree): return tree.call('package', tree.version), tree.git_commit -# -# Source -# - class SourceTarget(Target): + """Build a source .tar.bz2""" def __init__(self): super(SourceTarget, self).__init__('source') @@ -497,7 +522,7 @@ class SourceTarget(Target): def package(self, project, checkout): tree = globals.trees.get(project, checkout, self) - with TreeDirectory(self, tree): + with TreeDirectory(tree): name = read_wscript_variable(os.getcwd(), 'APPNAME') command('./waf dist') return os.path.abspath('%s-%s.tar.bz2' % (name, tree.version)), tree.git_commit @@ -509,18 +534,44 @@ class SourceTarget(Target): # or debian-version-{32,64} # or centos-version-{32,64} # or osx-{32,64} -# or source +# or source # @param debug True to build with debugging symbols (where possible) def target_factory(s, debug, work): target = None if s.startswith('windows-'): - target = WindowsTarget(int(s.split('-')[1]), work) + x = s.split('-') + if len(x) == 2: + target = WindowsTarget(None, int(x[1]), work) + elif len(x) == 3: + target = WindowsTarget(x[1], int(x[2]), work) + else: + raise Error("Bad Windows target name `%s'") elif s.startswith('ubuntu-') or s.startswith('debian-') or s.startswith('centos-'): p = s.split('-') if len(p) != 3: - print >>sys.stderr,"Bad Linux target name `%s'; must be something like ubuntu-12.04-32 (i.e. distro-version-bits)" % s - sys.exit(1) - target = LinuxTarget(p[0], p[1], int(p[2]), work) + raise Error("Bad Linux target name `%s'; must be something like ubuntu-12.04-32 (i.e. distro-version-bits)" % s) + target = ChrootTarget(p[0], p[1], int(p[2]), work) + elif s.startswith('arch-'): + p = s.split('-') + if len(p) != 2: + raise Error("Bad Arch target name `%s'; must be arch-32 or arch-64") + target = ChrootTarget(p[0], None, p[1], work) + elif s == 'raspbian': + target = ChrootTarget(s, None, None, work) + elif s == 'host': + if command_and_read('uname -m').read().strip() == 'x86_64': + bits = 64 + else: + bits = 32 + try: + f = open('/etc/fedora-release', 'r') + l = f.readline().strip().split() + target = HostTarget("fedora", l[2], bits, work) + except Exception as e: + if os.path.exists('/etc/arch-release'): + target = HostTarget("arch", None, bits, work) + else: + raise Error("could not identify distribution for `host' target (%s)" % e) elif s.startswith('osx-'): target = OSXSingleTarget(int(s.split('-')[1]), work) elif s == 'osx': @@ -531,16 +582,17 @@ def target_factory(s, debug, work): elif s == 'source': target = SourceTarget() - if target is not None: - target.debug = debug + if target is None: + raise Error("Bad target `%s'" % s) + target.debug = debug return target # # Tree # - + class Tree(object): """Description of a tree, which is a checkout of a project, possibly built. This class is never exposed to cscripts. @@ -583,7 +635,7 @@ class Tree(object): proj = '%s/src/%s' % (target.directory, self.name) self.cscript = {} - execfile('%s/cscript' % proj, self.cscript) + exec(open('%s/cscript' % proj).read(), self.cscript) if os.path.exists('%s/wscript' % proj): v = read_wscript_variable(proj, "VERSION"); @@ -609,7 +661,7 @@ class Tree(object): if 'option_defaults' in dep.cscript: options = dep.cscript['option_defaults']() if len(d) > 2: - for k, v in d[2].iteritems(): + for k, v in d[2].items(): options[k] = v dep.build(options) @@ -624,7 +676,7 @@ class Tree(object): self.call('build', options) else: self.call('build') - + self.target.variables = variables self.built = True @@ -645,12 +697,13 @@ def main(): "latest": "print out the latest version", "test": "run the project's unit tests", "shell": "build the project then start a shell in its chroot", + "checkout": "check out the project", "revision": "print the head git revision number" } one_of = "Command is one of:\n" summary = "" - for k, v in commands.iteritems(): + for k, v in commands.items(): one_of += "\t%s\t%s\n" % (k, v) summary += k + " " @@ -667,8 +720,14 @@ def main(): parser.add_argument('-k', '--keep', help='keep working tree', action='store_true') parser.add_argument('--debug', help='build with debugging symbols where possible', action='store_true') parser.add_argument('-w', '--work', help='override default work directory') + parser.add_argument('-g', '--git-prefix', help='override configured git prefix') + parser.add_argument('--test', help='name of test to run (with `test''), defaults to all') args = parser.parse_args() + # Override configured stuff + if args.git_prefix is not None: + config.set('git_prefix', args.git_prefix) + if args.output.find(':') == -1: # This isn't of the form host:path so make it absolute args.output = os.path.abspath(args.output) + '/' @@ -683,7 +742,7 @@ def main(): if args.project is None and args.command != 'shell': raise Error('you must specify -p or --project') - + globals.quiet = args.quiet globals.command = args.command @@ -793,11 +852,11 @@ def main(): v = Version(s[2]) if v.micro == 0: if last is not None and len(changes) > 0: - print >>html,"

Changes between version %s and %s

" % (s[2], last) - print >>html,"", file=html) last = s[2] changes = [] versions -= 1 @@ -837,7 +896,7 @@ def main(): dirs = [dirs] for d in dirs: - copytree(d, '%s%s' % (args.output, 'doc')) + copytree(d, args.output) target.cleanup() @@ -862,7 +921,7 @@ def main(): if args.major is None or v.major == args.major: latest = v - print latest + print(latest) target.cleanup() elif globals.command == 'test': @@ -874,7 +933,7 @@ def main(): target = target_factory(args.target, args.debug, args.work) tree = globals.trees.get(args.project, args.checkout, target) with TreeDirectory(tree): - target.test(tree) + target.test(tree, args.test) except Error as e: if target is not None: target.cleanup() @@ -895,7 +954,18 @@ def main(): target = SourceTarget() tree = globals.trees.get(args.project, args.checkout, target) with TreeDirectory(tree): - print command_and_read('git rev-parse HEAD').readline().strip()[:7] + print(command_and_read('git rev-parse HEAD').readline().strip()[:7]) + target.cleanup() + + elif globals.command == 'checkout': + + if args.output is None: + raise Error('you must specify -o or --output') + + target = SourceTarget() + tree = globals.trees.get(args.project, args.checkout, target) + with TreeDirectory(tree): + shutil.copytree('.', args.output) target.cleanup() else: @@ -904,5 +974,5 @@ def main(): try: main() except Error as e: - print >>sys.stderr,'cdist: %s' % str(e) + print('cdist: %s' % str(e), file=sys.stderr) sys.exit(1)