thirdparty:waf: New files for waf 1.9.10
[samba.git] / third_party / waf / waflib / Scripting.py
diff --git a/third_party/waf/waflib/Scripting.py b/third_party/waf/waflib/Scripting.py
new file mode 100644 (file)
index 0000000..9b27840
--- /dev/null
@@ -0,0 +1,627 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
+
+#!/usr/bin/env python
+# encoding: utf-8
+# Thomas Nagy, 2005-2016 (ita)
+
+"Module called for configuring, compiling and installing targets"
+
+import os, shlex, shutil, traceback, errno, sys, stat
+from waflib import Utils, Configure, Logs, Options, ConfigSet, Context, Errors, Build, Node
+
+build_dir_override = None
+
+no_climb_commands = ['configure']
+
+default_cmd = "build"
+
+def waf_entry_point(current_directory, version, wafdir):
+       """
+       This is the main entry point, all Waf execution starts here.
+
+       :param current_directory: absolute path representing the current directory
+       :type current_directory: string
+       :param version: version number
+       :type version: string
+       :param wafdir: absolute path representing the directory of the waf library
+       :type wafdir: string
+       """
+
+       Logs.init_log()
+
+       if Context.WAFVERSION != version:
+               Logs.error('Waf script %r and library %r do not match (directory %r)', version, Context.WAFVERSION, wafdir)
+               sys.exit(1)
+
+       if '--version' in sys.argv:
+               Context.run_dir = current_directory
+               ctx = Context.create_context('options')
+               ctx.curdir = current_directory
+               ctx.parse_args()
+               sys.exit(0)
+
+       if len(sys.argv) > 1:
+               # os.path.join handles absolute paths in sys.argv[1] accordingly (it discards the previous ones)
+               # if sys.argv[1] is not an absolute path, then it is relative to the current working directory
+               potential_wscript = os.path.join(current_directory, sys.argv[1])
+               # maybe check if the file is executable
+               # perhaps extract 'wscript' as a constant
+               if os.path.basename(potential_wscript) == 'wscript' and os.path.isfile(potential_wscript):
+                       # need to explicitly normalize the path, as it may contain extra '/.'
+                       # TODO abspath?
+                       current_directory = os.path.normpath(os.path.dirname(potential_wscript))
+                       sys.argv.pop(1)
+
+       Context.waf_dir = wafdir
+       Context.launch_dir = current_directory
+
+       # if 'configure' is in the commands, do not search any further
+       no_climb = os.environ.get('NOCLIMB')
+       if not no_climb:
+               for k in no_climb_commands:
+                       for y in sys.argv:
+                               if y.startswith(k):
+                                       no_climb = True
+                                       break
+
+       # if --top is provided assume the build started in the top directory
+       for i, x in enumerate(sys.argv):
+               # WARNING: this modifies sys.argv
+               if x.startswith('--top='):
+                       Context.run_dir = Context.top_dir = Utils.sane_path(x[6:])
+                       sys.argv[i] = '--top=' + Context.run_dir
+               if x.startswith('--out='):
+                       Context.out_dir = Utils.sane_path(x[6:])
+                       sys.argv[i] = '--out=' + Context.out_dir
+
+       # try to find a lock file (if the project was configured)
+       # at the same time, store the first wscript file seen
+       cur = current_directory
+       while cur and not Context.top_dir:
+               try:
+                       lst = os.listdir(cur)
+               except OSError:
+                       lst = []
+                       Logs.error('Directory %r is unreadable!', cur)
+               if Options.lockfile in lst:
+                       env = ConfigSet.ConfigSet()
+                       try:
+                               env.load(os.path.join(cur, Options.lockfile))
+                               ino = os.stat(cur)[stat.ST_INO]
+                       except EnvironmentError:
+                               pass
+                       else:
+                               # check if the folder was not moved
+                               for x in (env.run_dir, env.top_dir, env.out_dir):
+                                       if not x:
+                                               continue
+                                       if Utils.is_win32:
+                                               if cur == x:
+                                                       load = True
+                                                       break
+                                       else:
+                                               # if the filesystem features symlinks, compare the inode numbers
+                                               try:
+                                                       ino2 = os.stat(x)[stat.ST_INO]
+                                               except OSError:
+                                                       pass
+                                               else:
+                                                       if ino == ino2:
+                                                               load = True
+                                                               break
+                               else:
+                                       Logs.warn('invalid lock file in %s', cur)
+                                       load = False
+
+                               if load:
+                                       Context.run_dir = env.run_dir
+                                       Context.top_dir = env.top_dir
+                                       Context.out_dir = env.out_dir
+                                       break
+
+               if not Context.run_dir:
+                       if Context.WSCRIPT_FILE in lst:
+                               Context.run_dir = cur
+
+               next = os.path.dirname(cur)
+               if next == cur:
+                       break
+               cur = next
+
+               if no_climb:
+                       break
+
+       if not Context.run_dir:
+               if '-h' in sys.argv or '--help' in sys.argv:
+                       Logs.warn('No wscript file found: the help message may be incomplete')
+                       Context.run_dir = current_directory
+                       ctx = Context.create_context('options')
+                       ctx.curdir = current_directory
+                       ctx.parse_args()
+                       sys.exit(0)
+               Logs.error('Waf: Run from a directory containing a file named %r', Context.WSCRIPT_FILE)
+               sys.exit(1)
+
+       try:
+               os.chdir(Context.run_dir)
+       except OSError:
+               Logs.error('Waf: The folder %r is unreadable', Context.run_dir)
+               sys.exit(1)
+
+       try:
+               set_main_module(os.path.normpath(os.path.join(Context.run_dir, Context.WSCRIPT_FILE)))
+       except Errors.WafError ,e:
+               Logs.pprint('RED', e.verbose_msg)
+               Logs.error(str(e))
+               sys.exit(1)
+       except Exception ,e:
+               Logs.error('Waf: The wscript in %r is unreadable', Context.run_dir)
+               traceback.print_exc(file=sys.stdout)
+               sys.exit(2)
+
+       if '--profile' in sys.argv:
+               import cProfile, pstats
+               cProfile.runctx('from waflib import Scripting; Scripting.run_commands()', {}, {}, 'profi.txt')
+               p = pstats.Stats('profi.txt')
+               p.sort_stats('time').print_stats(75) # or 'cumulative'
+       else:
+               try:
+                       run_commands()
+               except Errors.WafError ,e:
+                       if Logs.verbose > 1:
+                               Logs.pprint('RED', e.verbose_msg)
+                       Logs.error(e.msg)
+                       sys.exit(1)
+               except SystemExit:
+                       raise
+               except Exception ,e:
+                       traceback.print_exc(file=sys.stdout)
+                       sys.exit(2)
+               except KeyboardInterrupt:
+                       Logs.pprint('RED', 'Interrupted')
+                       sys.exit(68)
+
+def set_main_module(file_path):
+       """
+       Read the main wscript file into :py:const:`waflib.Context.Context.g_module` and
+       bind default functions such as ``init``, ``dist``, ``distclean`` if not defined.
+       Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization.
+
+       :param file_path: absolute path representing the top-level wscript file
+       :type file_path: string
+       """
+       Context.g_module = Context.load_module(file_path)
+       Context.g_module.root_path = file_path
+
+       # note: to register the module globally, use the following:
+       # sys.modules['wscript_main'] = g_module
+
+       def set_def(obj):
+               name = obj.__name__
+               if not name in Context.g_module.__dict__:
+                       setattr(Context.g_module, name, obj)
+       for k in (dist, distclean, distcheck):
+               set_def(k)
+       # add dummy init and shutdown functions if they're not defined
+       if not 'init' in Context.g_module.__dict__:
+               Context.g_module.init = Utils.nada
+       if not 'shutdown' in Context.g_module.__dict__:
+               Context.g_module.shutdown = Utils.nada
+       if not 'options' in Context.g_module.__dict__:
+               Context.g_module.options = Utils.nada
+
+def parse_options():
+       """
+       Parses the command-line options and initialize the logging system.
+       Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization.
+       """
+       Context.create_context('options').execute()
+
+       for var in Options.envvars:
+               (name, value) = var.split('=', 1)
+               os.environ[name.strip()] = value
+
+       if not Options.commands:
+               Options.commands = [default_cmd]
+       Options.commands = [x for x in Options.commands if x != 'options'] # issue 1076
+
+       # process some internal Waf options
+       Logs.verbose = Options.options.verbose
+       #Logs.init_log()
+
+       if Options.options.zones:
+               Logs.zones = Options.options.zones.split(',')
+               if not Logs.verbose:
+                       Logs.verbose = 1
+       elif Logs.verbose > 0:
+               Logs.zones = ['runner']
+
+       if Logs.verbose > 2:
+               Logs.zones = ['*']
+
+def run_command(cmd_name):
+       """
+       Executes a single Waf command. Called by :py:func:`waflib.Scripting.run_commands`.
+
+       :param cmd_name: command to execute, like ``build``
+       :type cmd_name: string
+       """
+       ctx = Context.create_context(cmd_name)
+       ctx.log_timer = Utils.Timer()
+       ctx.options = Options.options # provided for convenience
+       ctx.cmd = cmd_name
+       try:
+               ctx.execute()
+       finally:
+               # Issue 1374
+               ctx.finalize()
+       return ctx
+
+def run_commands():
+       """
+       Execute the Waf commands that were given on the command-line, and the other options
+       Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization, and executed
+       after :py:func:`waflib.Scripting.parse_options`.
+       """
+       parse_options()
+       run_command('init')
+       while Options.commands:
+               cmd_name = Options.commands.pop(0)
+               ctx = run_command(cmd_name)
+               Logs.info('%r finished successfully (%s)', cmd_name, ctx.log_timer)
+       run_command('shutdown')
+
+###########################################################################################
+
+def distclean_dir(dirname):
+       """
+       Distclean function called in the particular case when::
+
+               top == out
+
+       :param dirname: absolute path of the folder to clean
+       :type dirname: string
+       """
+       for (root, dirs, files) in os.walk(dirname):
+               for f in files:
+                       if f.endswith(('.o', '.moc', '.exe')):
+                               fname = os.path.join(root, f)
+                               try:
+                                       os.remove(fname)
+                               except OSError:
+                                       Logs.warn('Could not remove %r', fname)
+
+       for x in (Context.DBFILE, 'config.log'):
+               try:
+                       os.remove(x)
+               except OSError:
+                       pass
+
+       try:
+               shutil.rmtree('c4che')
+       except OSError:
+               pass
+
+def distclean(ctx):
+       '''removes the build directory'''
+       lst = os.listdir('.')
+       for f in lst:
+               if f == Options.lockfile:
+                       try:
+                               proj = ConfigSet.ConfigSet(f)
+                       except IOError:
+                               Logs.warn('Could not read %r', f)
+                               continue
+
+                       if proj['out_dir'] != proj['top_dir']:
+                               try:
+                                       shutil.rmtree(proj['out_dir'])
+                               except EnvironmentError ,e:
+                                       if e.errno != errno.ENOENT:
+                                               Logs.warn('Could not remove %r', proj['out_dir'])
+                       else:
+                               distclean_dir(proj['out_dir'])
+
+                       for k in (proj['out_dir'], proj['top_dir'], proj['run_dir']):
+                               p = os.path.join(k, Options.lockfile)
+                               try:
+                                       os.remove(p)
+                               except OSError ,e:
+                                       if e.errno != errno.ENOENT:
+                                               Logs.warn('Could not remove %r', p)
+
+               # remove local waf cache folders
+               if not Options.commands:
+                       for x in '.waf-1. waf-1. .waf3-1. waf3-1.'.split():
+                               if f.startswith(x):
+                                       shutil.rmtree(f, ignore_errors=True)
+
+class Dist(Context.Context):
+       '''creates an archive containing the project source code'''
+       cmd = 'dist'
+       fun = 'dist'
+       algo = 'tar.bz2'
+       ext_algo = {}
+
+       def execute(self):
+               """
+               See :py:func:`waflib.Context.Context.execute`
+               """
+               self.recurse([os.path.dirname(Context.g_module.root_path)])
+               self.archive()
+
+       def archive(self):
+               """
+               Creates the source archive.
+               """
+               import tarfile
+
+               arch_name = self.get_arch_name()
+
+               try:
+                       self.base_path
+               except AttributeError:
+                       self.base_path = self.path
+
+               node = self.base_path.make_node(arch_name)
+               try:
+                       node.delete()
+               except OSError:
+                       pass
+
+               files = self.get_files()
+
+               if self.algo.startswith('tar.'):
+                       tar = tarfile.open(node.abspath(), 'w:' + self.algo.replace('tar.', ''))
+
+                       for x in files:
+                               self.add_tar_file(x, tar)
+                       tar.close()
+               elif self.algo == 'zip':
+                       import zipfile
+                       zip = zipfile.ZipFile(node.abspath(), 'w', compression=zipfile.ZIP_DEFLATED)
+
+                       for x in files:
+                               archive_name = self.get_base_name() + '/' + x.path_from(self.base_path)
+                               zip.write(x.abspath(), archive_name, zipfile.ZIP_DEFLATED)
+                       zip.close()
+               else:
+                       self.fatal('Valid algo types are tar.bz2, tar.gz, tar.xz or zip')
+
+               try:
+                       from hashlib import sha1
+               except ImportError:
+                       digest = ''
+               else:
+                       digest = ' (sha=%r)' % sha1(node.read(flags='rb')).hexdigest()
+
+               Logs.info('New archive created: %s%s', self.arch_name, digest)
+
+       def get_tar_path(self, node):
+               """
+               Return the path to use for a node in the tar archive, the purpose of this
+               is to let subclases resolve symbolic links or to change file names
+
+               :return: absolute path
+               :rtype: string
+               """
+               return node.abspath()
+
+       def add_tar_file(self, x, tar):
+               """
+               Adds a file to the tar archive. Symlinks are not verified.
+
+               :param x: file path
+               :param tar: tar file object
+               """
+               p = self.get_tar_path(x)
+               tinfo = tar.gettarinfo(name=p, arcname=self.get_tar_prefix() + '/' + x.path_from(self.base_path))
+               tinfo.uid   = 0
+               tinfo.gid   = 0
+               tinfo.uname = 'root'
+               tinfo.gname = 'root'
+
+               if os.path.isfile(p):
+                       fu = open(p, 'rb')
+                       try:
+                               tar.addfile(tinfo, fileobj=fu)
+                       finally:
+                               fu.close()
+               else:
+                       tar.addfile(tinfo)
+
+       def get_tar_prefix(self):
+               """
+               Returns the base path for files added into the archive tar file
+
+               :rtype: string
+               """
+               try:
+                       return self.tar_prefix
+               except AttributeError:
+                       return self.get_base_name()
+
+       def get_arch_name(self):
+               """
+               Returns the archive file name.
+               Set the attribute *arch_name* to change the default value::
+
+                       def dist(ctx):
+                               ctx.arch_name = 'ctx.tar.bz2'
+
+               :rtype: string
+               """
+               try:
+                       self.arch_name
+               except AttributeError:
+                       self.arch_name = self.get_base_name() + '.' + self.ext_algo.get(self.algo, self.algo)
+               return self.arch_name
+
+       def get_base_name(self):
+               """
+               Returns the default name of the main directory in the archive, which is set to *appname-version*.
+               Set the attribute *base_name* to change the default value::
+
+                       def dist(ctx):
+                               ctx.base_name = 'files'
+
+               :rtype: string
+               """
+               try:
+                       self.base_name
+               except AttributeError:
+                       appname = getattr(Context.g_module, Context.APPNAME, 'noname')
+                       version = getattr(Context.g_module, Context.VERSION, '1.0')
+                       self.base_name = appname + '-' + version
+               return self.base_name
+
+       def get_excl(self):
+               """
+               Returns the patterns to exclude for finding the files in the top-level directory.
+               Set the attribute *excl* to change the default value::
+
+                       def dist(ctx):
+                               ctx.excl = 'build **/*.o **/*.class'
+
+               :rtype: string
+               """
+               try:
+                       return self.excl
+               except AttributeError:
+                       self.excl = Node.exclude_regs + ' **/waf-1.8.* **/.waf-1.8* **/waf3-1.8.* **/.waf3-1.8* **/*~ **/*.rej **/*.orig **/*.pyc **/*.pyo **/*.bak **/*.swp **/.lock-w*'
+                       if Context.out_dir:
+                               nd = self.root.find_node(Context.out_dir)
+                               if nd:
+                                       self.excl += ' ' + nd.path_from(self.base_path)
+                       return self.excl
+
+       def get_files(self):
+               """
+               Files to package are searched automatically by :py:func:`waflib.Node.Node.ant_glob`.
+               Set *files* to prevent this behaviour::
+
+                       def dist(ctx):
+                               ctx.files = ctx.path.find_node('wscript')
+
+               Files are also searched from the directory 'base_path', to change it, set::
+
+                       def dist(ctx):
+                               ctx.base_path = path
+
+               :rtype: list of :py:class:`waflib.Node.Node`
+               """
+               try:
+                       files = self.files
+               except AttributeError:
+                       files = self.base_path.ant_glob('**/*', excl=self.get_excl())
+               return files
+
+def dist(ctx):
+       '''makes a tarball for redistributing the sources'''
+       pass
+
+class DistCheck(Dist):
+       """
+       Creates an archive of the project, then attempts to build the project in a temporary directory::
+
+               $ waf distcheck
+       """
+       fun = 'distcheck'
+       cmd = 'distcheck'
+
+       def execute(self):
+               """
+               See :py:func:`waflib.Context.Context.execute`
+               """
+               self.recurse([os.path.dirname(Context.g_module.root_path)])
+               self.archive()
+               self.check()
+
+       def make_distcheck_cmd(self, tmpdir):
+               cfg = []
+               if Options.options.distcheck_args:
+                       cfg = shlex.split(Options.options.distcheck_args)
+               else:
+                       cfg = [x for x in sys.argv if x.startswith('-')]
+               cmd = [sys.executable, sys.argv[0], 'configure', 'build', 'install', 'uninstall', '--destdir=' + tmpdir] + cfg
+               return cmd
+
+       def check(self):
+               """
+               Creates the archive, uncompresses it and tries to build the project
+               """
+               import tempfile, tarfile
+
+               try:
+                       t = tarfile.open(self.get_arch_name())
+                       for x in t:
+                               t.extract(x)
+               finally:
+                       t.close()
+
+               instdir = tempfile.mkdtemp('.inst', self.get_base_name())
+               cmd = self.make_distcheck_cmd(instdir)
+               ret = Utils.subprocess.Popen(cmd, cwd=self.get_base_name()).wait()
+               if ret:
+                       raise Errors.WafError('distcheck failed with code %r' % ret)
+
+               if os.path.exists(instdir):
+                       raise Errors.WafError('distcheck succeeded, but files were left in %s' % instdir)
+
+               shutil.rmtree(self.get_base_name())
+
+
+def distcheck(ctx):
+       '''checks if the project compiles (tarball from 'dist')'''
+       pass
+
+def autoconfigure(execute_method):
+       """
+       Decorator that enables context commands to run *configure* as needed.
+       """
+       def execute(self):
+               """
+               Wraps :py:func:`waflib.Context.Context.execute` on the context class
+               """
+               if not Configure.autoconfig:
+                       return execute_method(self)
+
+               env = ConfigSet.ConfigSet()
+               do_config = False
+               try:
+                       env.load(os.path.join(Context.top_dir, Options.lockfile))
+               except EnvironmentError:
+                       Logs.warn('Configuring the project')
+                       do_config = True
+               else:
+                       if env.run_dir != Context.run_dir:
+                               do_config = True
+                       else:
+                               h = 0
+                               for f in env.files:
+                                       try:
+                                               h = Utils.h_list((h, Utils.readf(f, 'rb')))
+                                       except EnvironmentError:
+                                               do_config = True
+                                               break
+                               else:
+                                       do_config = h != env.hash
+
+               if do_config:
+                       cmd = env.config_cmd or 'configure'
+                       if Configure.autoconfig == 'clobber':
+                               tmp = Options.options.__dict__
+                               Options.options.__dict__ = env.options
+                               try:
+                                       run_command(cmd)
+                               finally:
+                                       Options.options.__dict__ = tmp
+                       else:
+                               run_command(cmd)
+                       run_command(self.cmd)
+               else:
+                       return execute_method(self)
+       return execute
+Build.BuildContext.execute = autoconfigure(Build.BuildContext.execute)