--- /dev/null
+#!/usr/bin/env python
+
+# Program to do an out-of-tree build and test run for Samba.
+#
+# Copyright (C) Michael Adam 2015
+#
+# License GPLv3+ (see COPYING)
+
+import optparse
+import subprocess
+import os
+import time
+import errno
+import shutil
+import inspect
+import distutils
+
+scriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+
+options = None
+
+uname = subprocess.Popen(['uname', '-n'],
+ stdout=subprocess.PIPE).communicate()[0].rstrip()
+if not uname:
+ uname = "samba-build-oot"
+
+default_bindir = "bin.%s" % uname
+
+default_srcdir = "."
+homedir = os.path.expanduser("~")
+default_blddir = "%s/samba-build-oot" % homedir
+
+default_buildnice = "%s/buildnice" % scriptdir
+
+default_configure_options = ""
+
+timestr = time.strftime("%Y%m%d-%H%M%S")
+backup_suffix = "orig.%s" % timestr
+
+src_dir_bin = None
+bld_dir_bin = None
+rsync_verbose_opt = ""
+
+do_out_of_tree = True
+
+def which(program, fallback=None):
+ cmd = [ "which", program ]
+ p = subprocess.Popen(cmd,
+ stdin = subprocess.PIPE,
+ stdout = subprocess.PIPE)
+ p.wait()
+
+ if p.returncode != 0:
+ return fallback
+
+ return p.stdout.readlines()[0].rstrip()
+
+make = which("gmake", fallback="make")
+
+# check whether make undstands -j without argument
+cmd = [ make, "-j", "/dev/null" ]
+ret = subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+if ret == 0:
+ print "make (%s) understands '-j'" % make
+ make = "%s -j" % make
+
+def process_args():
+ global options
+ global src_dir_bin
+ global bld_dir_bin
+ global rsync_verbose_opt
+
+ parser = optparse.OptionParser()
+
+ parser.add_option("-v", "--verbose",
+ help = "be more verbose",
+ dest = "verbose",
+ default = False,
+ action = "store_true")
+
+ parser.add_option("", "--buildnice",
+ help = "path of a buildnice utility",
+ type = "str",
+ default = default_buildnice,
+ dest = "buildnice",
+ action = "store")
+
+ # execution options
+
+ parser.add_option("-c", "--no-configure",
+ help = "do not run configure",
+ dest = "do_configure",
+ default = True,
+ action = "store_false")
+
+ parser.add_option("-C", "--configure-options",
+ help = "options to pass to configure",
+ type = "str",
+ default = default_configure_options,
+ dest = "configure_options",
+ action = "store")
+
+ #
+ # by default, do not make clean
+ #
+ parser.add_option("-l", "--make-clean",
+ help = "run make clean",
+ dest = "do_make_clean",
+ default = False,
+ action = "store_true")
+
+ parser.add_option("-m", "--no-make",
+ help = "run make",
+ dest = "do_make",
+ default = True,
+ action = "store_false")
+
+ #
+ # By default, do not run make test.
+ #
+ parser.add_option("-t", "--test",
+ help = "run make test",
+ dest = "do_test",
+ default = False,
+ action = "store_true")
+
+ #
+ # If --test is specified, then --tests can be used
+ # to select which tests should be run. If --tests
+ # is not specified, then all tests are run.
+ #
+ parser.add_option("", "--tests",
+ help = "the list of tests (or patterns for tests) "
+ "to pass to make test",
+ type = "str",
+ dest = "tests",
+ action = "store")
+
+
+ #
+ # Options regarding setup of the out of tree build
+ #
+
+ parser.add_option("-s", "--src-dir",
+ help = "the directory containing the sources to build",
+ type = "str",
+ dest = "srcdir",
+ default = default_srcdir,
+ action = "store")
+
+ parser.add_option("-b", "--bld-dir",
+ help = "the directory where the build is performed "
+ "(currently needed as Samba does not support "
+ "proper out of tree builds...)",
+ type = "str",
+ dest = "blddir",
+ default = default_blddir,
+ action = "store")
+
+ parser.add_option("-B", "--bin-dir",
+ help = "the directory for storing the build results, "
+ "relative to src/bld dir "
+ "(linked to ./bin for the build)",
+ type = "str",
+ dest = "bindir",
+ default = default_bindir,
+ action = "store")
+
+ #
+ # We currently have problems symlinking the bindir
+ # instead of copying it. Not sure why, though:
+ # I thought I had tested this... Keep the possibility
+ # for now.
+ #
+ parser.add_option("", "--symlink-bindir",
+ help = "symlink the bindir instead of copying it",
+ dest = "do_copy_bindir",
+ default = True,
+ action = "store_false")
+
+ parser.add_option("-r", "--no-reuse-bindir",
+ help = "do not re-use the bindir, but create a new one",
+ dest = "do_reuse_bindir",
+ default = True,
+ action = "store_false")
+
+
+ (options, args) = parser.parse_args()
+
+ #
+ # We should find a way to only interpret and read args
+ # that lead to actions in this external program and
+ # simply pass extra args on to the program executed
+ # in the guest.
+ #
+ if len(args) != 0:
+ parser.error("too many arguments")
+
+ if do_out_of_tree and options.srcdir == options.blddir:
+ parser.error("srcdir may not be equal to blddir")
+
+ src_dir_bin = "%s/%s" % ( options.srcdir, options.bindir )
+ bld_dir_bin = "%s/%s" % ( options.blddir, options.bindir )
+
+ if options.verbose:
+ rsync_verbose_opt = "-i"
+
+ print ""
+ print "verbose: %s" % options.verbose
+ print "buildnice: '%s'" % options.buildnice
+ print ""
+ print "do_configure: %s" % options.do_configure
+ print "configure_options: '%s'" % options.configure_options
+ print "do_make: %s" % options.do_make
+ print "do_make_clean: %s" % options.do_make_clean
+ print "do_test: %s" % options.do_test
+ print "tests: '%s'" % options.tests
+ print ""
+ print "src-dir: '%s'" % options.srcdir
+ print "bld-dir: '%s'" % options.blddir
+ print "bin-dir: '%s'" % options.bindir
+ print "do_copy_bindir: %s" % options.do_copy_bindir
+ print "do_reuse_bindir: %s" % options.do_reuse_bindir
+ print ""
+
+
+def mkdir_p(path):
+ try:
+ os.makedirs(path)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else:
+ raise
+
+def rm_rf(path):
+ try:
+ # not sure how to handle this with exceptions or onerror...
+ if os.path.isdir(path) and not os.path.islink(path):
+ shutil.rmtree(path)
+ else:
+ os.remove(path)
+ except OSError as exc:
+ if exc.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+
+def setup_env():
+ """ setup the build environment for out of tree build """
+
+ # implement backup_first ?
+ #rm_rf("%s/bin" % options.srcdir)
+
+ if os.path.exists(src_dir_bin):
+ if not options.do_reuse_bindir or not os.path.isdir(src_dir_bin):
+ backup_dir = "%s.%s" % (src_dir_bin, backup_suffix)
+ os.rename(src_dir_bin, backup_dir)
+
+ mkdir_p(src_dir_bin)
+
+ # prepare for out of tree
+ # (Q: make this configurable?)
+
+ if os.path.exists(options.blddir):
+ if not os.path.isdir(options.blddir):
+ backup_dir = "%s.%s" % (options.blddir, backup_suffix)
+ os.rename(options.blddir, backup_dir)
+
+ mkdir_p(options.blddir)
+
+ rm_rf("%s/bin" % options.blddir)
+
+ # Only keep the old bindir copy in the bld dir if we
+ # are to copy (not symlink) the contents and reuse it.
+ if not options.do_reuse_bindir or not options.do_copy_bindir:
+ rm_rf(bld_dir_bin)
+
+ print "Symlinking contents of src dir '%s' into bld dir '%s'." % (options.srcdir, options.blddir)
+
+ cmd = [ "find",
+ options.srcdir,
+ "-maxdepth", "1",
+ "-mindepth", "1",
+ "-not", "-name", ".git",
+ "-not", "-name", ".vagrant",
+ "-not", "-name", "st",
+ "-not", "-name", "st*",
+ "-not", "-name", "bin",
+ "-not", "-name", "bin*",
+ "-exec", "ln", "-sf", "{}", options.blddir, ";" ]
+
+ subprocess.check_call(cmd)
+
+ if options.do_copy_bindir:
+ print "Copying '%s' into '%s'." % (src_dir_bin, options.blddir)
+ cmd = [ "rsync",
+ "-aSH",
+ rsync_verbose_opt,
+ "--delete",
+ src_dir_bin, options.blddir ]
+ if options.verbose:
+ print "cmd: '%s'" % ' '.join(cmd)
+
+ else:
+ print "Symlinking '%s' into '%s'." % (src_dir_bin, options.blddir)
+ cmd = [ "ln", "-sf", src_dir_bin, options.blddir ]
+
+ subprocess.check_call(cmd)
+
+ cmd = [ "ln", "-sf", options.bindir, "%s/bin" % options.blddir ]
+ subprocess.check_call(cmd)
+
+
+def do_build():
+ """ run the actual build and test commands """
+
+ curdir = os.getcwd()
+ os.chdir(options.blddir)
+
+ ret = 0
+
+ if ret == 0 and options.do_configure:
+ cmd = [ options.buildnice,
+ "./configure.developer" ] + options.configure_options.split()
+ ret = subprocess.call(cmd)
+
+ if ret == 0 and options.do_make_clean:
+ cmd = [ options.buildnice ] + make.split() + [ "clean" ]
+ ret = subprocess.call(cmd)
+
+ if ret == 0 and options.do_make:
+ cmd = [ options.buildnice ] + make.split()
+ ret = subprocess.call(cmd)
+
+ if ret == 0 and options.do_test:
+ tests = "TESTS='%s'" % options.tests
+ cmd = [ options.buildnice ] + make.split() + [ "test" ] + tests.split()
+ ret = subprocess.call(cmd)
+
+ os.chdir(curdir)
+
+ return ret
+
+def save_results():
+ """ save the results """
+ if do_out_of_tree and options.do_copy_bindir:
+ print "Copying back results from '%s' to '%s'." % (bld_dir_bin,
+ options.srcdir)
+ cmd = [ "rsync",
+ "-aSH",
+ rsync_verbose_opt,
+ "--delete",
+ bld_dir_bin, options.srcdir ]
+ if options.verbose:
+ print "cmd: '%s'" % ' '.join(cmd)
+ subprocess.check_call(cmd)
+
+def main():
+ process_args()
+ setup_env()
+ do_build()
+ save_results()
+
+
+main()
+