build: Remove workaround for missing os.path.relpath in Python < 2.6
[samba.git] / buildtools / wafsamba / samba_utils.py
index 58a0b71796c0e89923c045d39fe9b82628827db8..4a5a34abae7a7aa561d6167e232bbb5697d5d961 100644 (file)
@@ -1,6 +1,7 @@
 # a waf tool to add autoconf-like macros to the configure section
 # and for SAMBA_ macros for building libraries, binaries etc
 
+import errno
 import os, sys, re, fnmatch, shlex, inspect
 from optparse import SUPPRESS_HELP
 from waflib import Build, Options, Utils, Task, Logs, Configure, Errors, Context
@@ -15,9 +16,43 @@ from waflib.Build import CACHE_SUFFIX
 LIB_PATH="shared"
 
 
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+
+    # helper function to get a string from a variable that maybe 'str' or
+    # 'bytes' if 'bytes' then it is decoded using 'utf8'. If 'str' is passed
+    # it is returned unchanged
+    # Using this function is PY2/PY3 code should ensure in most cases
+    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
+    # decodes the variable (see PY2 implementation of this function below)
+    def get_string(bytesorstring):
+        tmp = bytesorstring
+        if isinstance(bytesorstring, bytes):
+            tmp = bytesorstring.decode('utf8')
+        elif not isinstance(bytesorstring, str):
+            raise ValueError('Expected byte of string for %s:%s' % (type(bytesorstring), bytesorstring))
+        return tmp
+
+else:
+
+    # Helper function to return string.
+    # if 'str' or 'unicode' passed in they are returned unchanged
+    # otherwise an exception is generated
+    # Using this function is PY2/PY3 code should ensure in most cases
+    # the PY2 code runs unchanged in PY2 whereas the code in PY3 possibly
+    # decodes the variable (see PY3 implementation of this function above)
+    def get_string(bytesorstring):
+        tmp = bytesorstring
+        if not(isinstance(bytesorstring, str) or isinstance(bytesorstring, unicode)):
+            raise ValueError('Expected str or unicode for %s:%s' % (type(bytesorstring), bytesorstring))
+        return tmp
+
 # sigh, python octal constants are a mess
 MODE_644 = int('644', 8)
+MODE_744 = int('744', 8)
 MODE_755 = int('755', 8)
+MODE_777 = int('777', 8)
 
 def conf(f):
     # override in order to propagate the argument "mandatory"
@@ -170,24 +205,6 @@ def process_depends_on(self):
                   self.includes += " " + y.more_includes
 
 
-os_path_relpath = getattr(os.path, 'relpath', None)
-if os_path_relpath is None:
-    # Python < 2.6 does not have os.path.relpath, provide a replacement
-    # (imported from Python2.6.5~rc2)
-    def os_path_relpath(path, start):
-        """Return a relative version of a path"""
-        start_list = os.path.abspath(start).split("/")
-        path_list = os.path.abspath(path).split("/")
-
-        # Work out how much of the filepath is shared by start and path.
-        i = len(os.path.commonprefix([start_list, path_list]))
-
-        rel_list = ['..'] * (len(start_list)-i) + path_list[i:]
-        if not rel_list:
-            return start
-        return os.path.join(*rel_list)
-
-
 def unique_list(seq):
     '''return a uniquified list in the same order as the existing list'''
     seen = {}
@@ -251,10 +268,22 @@ def recursive_dirlist(dir, relbase, pattern=None):
         else:
             if pattern and not fnmatch.fnmatch(f, pattern):
                 continue
-            ret.append(os_path_relpath(f2, relbase))
+            ret.append(os.path.relpath(f2, relbase))
     return ret
 
 
+def symlink(src, dst, force=True):
+    """Can create symlink by force"""
+    try:
+        os.symlink(src, dst)
+    except OSError as exc:
+        if exc.errno == errno.EEXIST and force:
+            os.remove(dst)
+            os.symlink(src, dst)
+        else:
+            raise
+
+
 def mkdir_p(dir):
     '''like mkdir -p'''
     if not dir:
@@ -429,7 +458,7 @@ def RECURSE(ctx, directory):
         # already done it
         return
     visited_dirs.add(key)
-    relpath = os_path_relpath(abspath, ctx.path.abspath())
+    relpath = os.path.relpath(abspath, ctx.path.abspath())
     if ctxclass in ['tmp', 'OptionsContext', 'ConfigurationContext', 'BuildContext']:
         return ctx.recurse(relpath)
     if 'waflib.extras.compat15' in sys.modules:
@@ -440,14 +469,14 @@ Options.OptionsContext.RECURSE = RECURSE
 Build.BuildContext.RECURSE = RECURSE
 
 
-def CHECK_MAKEFLAGS(bld):
+def CHECK_MAKEFLAGS(options):
     '''check for MAKEFLAGS environment variable in case we are being
     called from a Makefile try to honor a few make command line flags'''
     if not 'WAF_MAKE' in os.environ:
         return
     makeflags = os.environ.get('MAKEFLAGS')
     if makeflags is None:
-        return
+        makeflags = ""
     jobs_set = False
     jobs = None
     # we need to use shlex.split to cope with the escaping of spaces
@@ -455,7 +484,7 @@ def CHECK_MAKEFLAGS(bld):
     for opt in shlex.split(makeflags):
         # options can come either as -x or as x
         if opt[0:2] == 'V=':
-            Options.options.verbose = Logs.verbose = int(opt[2:])
+            options.verbose = Logs.verbose = int(opt[2:])
             if Logs.verbose > 0:
                 Logs.zones = ['runner']
             if Logs.verbose > 2:
@@ -469,26 +498,53 @@ def CHECK_MAKEFLAGS(bld):
             # this is also how "make test TESTS=testpattern" works, and
             # "make VERBOSE=1" as well as things like "make SYMBOLCHECK=1"
             loc = opt.find('=')
-            setattr(Options.options, opt[0:loc], opt[loc+1:])
+            setattr(options, opt[0:loc], opt[loc+1:])
         elif opt[0] != '-':
             for v in opt:
                 if re.search(r'j[0-9]*$', v):
                     jobs_set = True
                     jobs = opt.strip('j')
                 elif v == 'k':
-                    Options.options.keep = True
+                    options.keep = True
         elif re.search(r'-j[0-9]*$', opt):
             jobs_set = True
             jobs = opt.strip('-j')
         elif opt == '-k':
-            Options.options.keep = True
+            options.keep = True
     if not jobs_set:
         # default to one job
-        Options.options.jobs = 1
+        options.jobs = 1
     elif jobs_set and jobs:
-        Options.options.jobs = int(jobs)
-
-Build.BuildContext.CHECK_MAKEFLAGS = CHECK_MAKEFLAGS
+        options.jobs = int(jobs)
+
+waflib_options_parse_cmd_args = Options.OptionsContext.parse_cmd_args
+def wafsamba_options_parse_cmd_args(self, _args=None, cwd=None, allow_unknown=False):
+    (options, commands, envvars) = \
+        waflib_options_parse_cmd_args(self,
+                                      _args=_args,
+                                      cwd=cwd,
+                                      allow_unknown=allow_unknown)
+    CHECK_MAKEFLAGS(options)
+    if options.jobs == 1:
+        #
+        # waflib.Runner.Parallel processes jobs inline if the possible number
+        # of jobs is just 1. But (at least in waf <= 2.0.12) it still calls
+        # create a waflib.Runner.Spawner() which creates a single
+        # waflib.Runner.Consumer() thread that tries to process jobs from the
+        # queue.
+        #
+        # This has strange effects, which are not noticed typically,
+        # but at least on AIX python has broken threading and fails
+        # in random ways.
+        #
+        # So we just add a dummy Spawner class.
+        class NoOpSpawner(object):
+            def __init__(self, master):
+                return
+        from waflib import Runner
+        Runner.Spawner = NoOpSpawner
+    return options, commands, envvars
+Options.OptionsContext.parse_cmd_args = wafsamba_options_parse_cmd_args
 
 option_groups = {}
 
@@ -529,7 +585,7 @@ def load_file(filename):
 
 def reconfigure(ctx):
     '''rerun configure if necessary'''
-    if not os.path.exists(".lock-wscript"):
+    if not os.path.exists(os.environ.get('WAFLOCK', '.lock-wscript')):
         raise Errors.WafError('configure has not been run')
     import samba_wildcard
     bld = samba_wildcard.fake_build_environment()