vasabi: samba-build.py: python script to do out-of-tree samba build
authorMichael Adam <obnox@samba.org>
Tue, 14 Apr 2015 14:04:06 +0000 (16:04 +0200)
committerMichael Adam <obnox@samba.org>
Wed, 15 Apr 2015 07:21:58 +0000 (09:21 +0200)
Signed-off-by: Michael Adam <obnox@samba.org>
script/samba-build.py [new file with mode: 0755]

diff --git a/script/samba-build.py b/script/samba-build.py
new file mode 100755 (executable)
index 0000000..4d7c3b8
--- /dev/null
@@ -0,0 +1,368 @@
+#!/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()
+