From 78b9749c0a4ea980a8b934645da6ae98fcc665e8 Mon Sep 17 00:00:00 2001
From: dim <dim@FreeBSD.org>
Date: Wed, 6 Jan 2016 20:12:03 +0000
Subject: Vendor import of lldb trunk r256945:
 https://llvm.org/svn/llvm-project/lldb/trunk@256945

---
 scripts/Python/prepare_binding_Python.py | 435 +++++++++++++++++++++++++++++++
 1 file changed, 435 insertions(+)
 create mode 100644 scripts/Python/prepare_binding_Python.py

(limited to 'scripts/Python/prepare_binding_Python.py')

diff --git a/scripts/Python/prepare_binding_Python.py b/scripts/Python/prepare_binding_Python.py
new file mode 100644
index 0000000..1996841
--- /dev/null
+++ b/scripts/Python/prepare_binding_Python.py
@@ -0,0 +1,435 @@
+"""
+                     The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+
+Python binding preparation script.
+"""
+
+# Python modules:
+from __future__ import print_function
+
+import logging
+import os
+import re
+import shutil
+import subprocess
+import sys
+
+
+class SwigSettings(object):
+    """Provides a single object to represent swig files and settings."""
+    def __init__(self):
+        self.extensions_file = None
+        self.header_files = None
+        self.input_file = None
+        self.interface_files = None
+        self.output_file = None
+        self.safecast_file = None
+        self.typemaps_file = None
+        self.wrapper_file = None
+
+    @classmethod
+    def _any_files_newer(cls, files, check_mtime):
+        """Returns if any of the given files has a newer modified time.
+
+        @param cls the class
+        @param files a list of zero or more file paths to check
+        @param check_mtime the modification time to use as a reference.
+
+        @return True if any file's modified time is newer than check_mtime.
+        """
+        for path in files:
+            path_mtime = os.path.getmtime(path)
+            if path_mtime > check_mtime:
+                # This path was modified more recently than the
+                # check_mtime.
+                return True
+        # If we made it here, nothing was newer than the check_mtime
+        return False
+
+    @classmethod
+    def _file_newer(cls, path, check_mtime):
+        """Tests how recently a file has been modified.
+
+        @param cls the class
+        @param path a file path to check
+        @param check_mtime the modification time to use as a reference.
+
+        @return True if the file's modified time is newer than check_mtime.
+        """
+        path_mtime = os.path.getmtime(path)
+        return path_mtime > check_mtime
+
+    def output_out_of_date(self):
+        """Returns whether the output file is out of date.
+
+        Compares output file time to all the input files.
+
+        @return True if any of the input files are newer than
+        the output file, or if the output file doesn't exist;
+        False otherwise.
+        """
+        if not os.path.exists(self.output_file):
+            logging.info("will generate, missing binding output file")
+            return True
+        output_mtime = os.path.getmtime(self.output_file)
+        if self._any_files_newer(self.header_files, output_mtime):
+            logging.info("will generate, header files newer")
+            return True
+        if self._any_files_newer(self.interface_files, output_mtime):
+            logging.info("will generate, interface files newer")
+            return True
+        if self._file_newer(self.input_file, output_mtime):
+            logging.info("will generate, swig input file newer")
+            return True
+        if self._file_newer(self.extensions_file, output_mtime):
+            logging.info("will generate, swig extensions file newer")
+            return True
+        if self._file_newer(self.wrapper_file, output_mtime):
+            logging.info("will generate, swig wrapper file newer")
+            return True
+        if self._file_newer(self.typemaps_file, output_mtime):
+            logging.info("will generate, swig typemaps file newer")
+            return True
+        if self._file_newer(self.safecast_file, output_mtime):
+            logging.info("will generate, swig safecast file newer")
+            return True
+
+        # If we made it here, nothing is newer than the output file.
+        # Thus, the output file is not out of date.
+        return False
+
+
+def get_header_files(options):
+    """Returns a list of paths to C++ header files for the LLDB API.
+
+    These are the files that define the C++ API that will be wrapped by Python.
+
+    @param options the dictionary of options parsed from the command line.
+
+    @return a list of full paths to the include files used to define the public
+    LLDB C++ API.
+    """
+
+    header_file_paths = []
+    header_base_dir = os.path.join(options.src_root, "include", "lldb")
+
+    # Specify the include files in include/lldb that are not easy to
+    # grab programatically.
+    for header in [
+            "lldb-defines.h",
+            "lldb-enumerations.h",
+            "lldb-forward.h",
+            "lldb-types.h"]:
+        header_file_paths.append(os.path.normcase(
+            os.path.join(header_base_dir, header)))
+
+    # Include the main LLDB.h file.
+    api_dir = os.path.join(header_base_dir, "API")
+    header_file_paths.append(os.path.normcase(
+        os.path.join(api_dir, "LLDB.h")))
+
+    filename_regex = re.compile(r"^SB.+\.h$")
+
+    # Include all the SB*.h files in the API dir.
+    for filename in os.listdir(api_dir):
+        if filename_regex.match(filename):
+            header_file_paths.append(
+                os.path.normcase(os.path.join(api_dir, filename)))
+
+    logging.debug("found public API header file paths: %s", header_file_paths)
+    return header_file_paths
+
+
+def get_interface_files(options):
+    """Returns a list of interface files used as input to swig.
+
+    @param options the options dictionary parsed from the command line args.
+
+    @return a list of full paths to the interface (.i) files used to describe
+    the public API language binding.
+    """
+    interface_file_paths = []
+    interface_dir = os.path.join(options.src_root, "scripts", "interface")
+
+    for filepath in [f for f in os.listdir(interface_dir)
+                     if os.path.splitext(f)[1] == ".i"]:
+        interface_file_paths.append(
+            os.path.normcase(os.path.join(interface_dir, filepath)))
+
+    logging.debug("found swig interface files: %s", interface_file_paths)
+    return interface_file_paths
+
+
+def remove_ignore_enoent(filename):
+    """Removes given file, ignoring error if it doesn't exist.
+
+    @param filename the path of the file to remove.
+    """
+    try:
+        os.remove(filename)
+    except OSError as error:
+        import errno
+        if error.errno != errno.ENOENT:
+            raise
+
+
+def do_swig_rebuild(options, dependency_file, config_build_dir, settings):
+    """Generates Python bindings file from swig.
+
+    This method will do a sys.exit() if something fails.  If it returns to
+    the caller, it succeeded.
+
+    @param options the parsed command line options structure.
+    @param dependency_file path to the bindings dependency file
+    to be generated; otherwise, None if a dependency file is not
+    to be generated.
+    @param config_build_dir used as the output directory used by swig
+    @param settings the SwigSettings that specify a number of aspects used
+    to configure building the Python binding with swig (mostly paths)
+    """
+    if options.generate_dependency_file:
+        temp_dep_file_path = dependency_file + ".tmp"
+
+    # Build the SWIG args list
+    command = [
+        options.swig_executable,
+        "-c++",
+        "-shadow",
+        "-python",
+        "-threads",
+        "-I\"%s\"" % os.path.normcase(
+            os.path.join(options.src_root, "include")),
+        "-I\"%s\"" % os.path.normcase("./."),
+        "-D__STDC_LIMIT_MACROS",
+        "-D__STDC_CONSTANT_MACROS"]
+    if options.generate_dependency_file:
+        command.append("-MMD -MF \"%s\"" % temp_dep_file_path)
+    command.extend([
+        "-outdir", "\"%s\"" % config_build_dir,
+        "-o", "\"%s\"" % settings.output_file,
+        "\"%s\"" % settings.input_file
+        ])
+    logging.info("running swig with: %s", command)
+
+    # Execute swig
+    process = subprocess.Popen(
+        ' '.join(command),
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        shell=True)
+    # Wait for SWIG process to terminate
+    swig_stdout, swig_stderr = process.communicate()
+    return_code = process.returncode
+    if return_code != 0:
+        logging.error(
+            "swig failed with error code %d: stdout=%s, stderr=%s",
+            return_code,
+            swig_stdout,
+            swig_stderr)
+        logging.error(
+            "command line:\n%s", ' '.join(command))
+        sys.exit(return_code)
+
+    logging.info("swig generation succeeded")
+    if swig_stdout is not None and len(swig_stdout) > 0:
+        logging.info("swig output: %s", swig_stdout)
+
+    # Move the depedency file we just generated to the proper location.
+    if options.generate_dependency_file:
+        if os.path.exists(temp_dep_file_path):
+            shutil.move(temp_dep_file_path, dependency_file)
+        else:
+            logging.error(
+                "failed to generate Python binding depedency file '%s'",
+                temp_dep_file_path)
+            if os.path.exists(dependency_file):
+                # Delete the old one.
+                os.remove(dependency_file)
+            sys.exit(-10)
+
+
+def run_python_script(script_and_args):
+    """Runs a python script, logging appropriately.
+
+    If the command returns anything non-zero, it is registered as
+    an error and exits the program.
+
+    @param script_and_args the python script to execute, along with
+    the command line arguments to pass to it.
+    """
+    command_line = "%s %s" % (sys.executable, script_and_args)
+    process = subprocess.Popen(command_line, shell=True)
+    script_stdout, script_stderr = process.communicate()
+    return_code = process.returncode
+    if return_code != 0:
+        logging.error("failed to run '%s': %s", command_line, script_stderr)
+        sys.exit(return_code)
+    else:
+        logging.info("ran script '%s'", command_line)
+        if script_stdout is not None:
+            logging.info("output: %s", script_stdout)
+
+
+def do_modify_python_lldb(options, config_build_dir):
+    """Executes the modify-python-lldb.py script.
+
+    @param options the parsed command line arguments
+    @param config_build_dir the directory where the Python output was created.
+    """
+    script_path = os.path.normcase(
+        os.path.join(
+            options.src_root,
+            "scripts",
+            "Python",
+            "modify-python-lldb.py"))
+
+    if not os.path.exists(script_path):
+        logging.error("failed to find python script: '%s'", script_path)
+        sys.exit(-11)
+
+    script_invocation = "%s %s" % (script_path, config_build_dir)
+    run_python_script(script_invocation)
+
+
+def get_python_module_path(options):
+    """Returns the location where the lldb Python module should be placed.
+
+    @param options dictionary of options parsed from the command line.
+
+    @return the directory where the lldb module should be placed.
+    """
+    if options.framework:
+        # Caller wants to use the OS X framework packaging.
+
+        # We are packaging in an OS X-style framework bundle. The
+        # module dir will be within the
+        # LLDB.framework/Resources/Python subdirectory.
+        return os.path.join(
+            options.target_dir,
+            "LLDB.framework",
+            "Resources",
+            "Python",
+            "lldb")
+    else:
+        from distutils.sysconfig import get_python_lib
+
+        if options.prefix is not None:
+            module_path = get_python_lib(True, False, options.prefix)
+        else:
+            module_path = get_python_lib(True, False)
+        return os.path.normcase(
+            os.path.join(module_path, "lldb"))
+
+
+def main(options):
+    """Pepares the Python language binding to LLDB.
+
+    @param options the parsed command line argument dictionary
+    """
+    # Setup generated dependency file options.
+    if options.generate_dependency_file:
+        dependency_file = os.path.normcase(os.path.join(
+            options.target_dir, "LLDBWrapPython.cpp.d"))
+    else:
+        dependency_file = None
+
+    # Keep track of all the swig-related settings.
+    settings = SwigSettings()
+
+    # Determine the final binding file path.
+    settings.output_file = os.path.normcase(
+        os.path.join(options.target_dir, "LLDBWrapPython.cpp"))
+
+    # Touch the output file (but don't really generate it) if python
+    # is disabled.
+    disable_python = os.getenv("LLDB_DISABLE_PYTHON", None)
+    if disable_python is not None and disable_python == "1":
+        remove_ignore_enoent(settings.output_file)
+        # Touch the file.
+        open(settings.output_file, 'w').close()
+        logging.info(
+            "Created empty python binding file due to LLDB_DISABLE_PYTHON "
+            "being set")
+        return
+
+    # We also check the GCC_PREPROCESSOR_DEFINITIONS to see if it
+    # contains LLDB_DISABLE_PYTHON.  If so, we skip generating
+    # the binding.
+    gcc_preprocessor_defs = os.getenv("GCC_PREPROCESSOR_DEFINITIONS", None)
+    if gcc_preprocessor_defs is not None:
+        if re.search(r"LLDB_DISABLE_PYTHON", gcc_preprocessor_defs):
+            remove_ignore_enoent(settings.output_file)
+            # Touch the file
+            open(settings.output_file, 'w').close()
+            logging.info(
+                "Created empty python binding file due to "
+                "finding LLDB_DISABLE_PYTHON in GCC_PREPROCESSOR_DEFINITIONS")
+            return
+
+    # Setup paths used during swig invocation.
+    settings.input_file = os.path.normcase(
+        os.path.join(options.src_root, "scripts", "lldb.swig"))
+    scripts_python_dir = os.path.dirname(os.path.realpath(__file__))
+    settings.extensions_file = os.path.normcase(
+        os.path.join(scripts_python_dir, "python-extensions.swig"))
+    settings.wrapper_file = os.path.normcase(
+        os.path.join(scripts_python_dir, "python-wrapper.swig"))
+    settings.typemaps_file = os.path.normcase(
+        os.path.join(scripts_python_dir, "python-typemaps.swig"))
+    settings.safecast_file = os.path.normcase(
+        os.path.join(scripts_python_dir, "python-swigsafecast.swig"))
+
+    settings.header_files = get_header_files(options)
+    settings.interface_files = get_interface_files(options)
+
+    generate_output = settings.output_out_of_date()
+
+    # Determine where to put the module.
+    python_module_path = get_python_module_path(options)
+    logging.info("python module path: %s", python_module_path)
+
+    # Handle the configuration build dir.
+    if options.config_build_dir is not None:
+        config_build_dir = options.config_build_dir
+    else:
+        config_build_dir = python_module_path
+
+    # Allow missing/non-link _lldb.so to force regeneration.
+    if not generate_output:
+        # Ensure the _lldb.so file exists.
+        so_path = os.path.join(python_module_path, "_lldb.so")
+        if not os.path.exists(so_path) or not os.path.islink(so_path):
+            logging.info("_lldb.so doesn't exist or isn't a symlink")
+            generate_output = True
+
+    # Allow missing __init__.py to force regeneration.
+    if not generate_output:
+        # Ensure the __init__.py for the lldb module can be found.
+        init_path = os.path.join(python_module_path, "__init__.py")
+        if not os.path.exists(init_path):
+            logging.info("__init__.py doesn't exist")
+            generate_output = True
+
+    if not generate_output:
+        logging.info(
+            "Skipping Python binding generation: everything is up to date")
+        return
+
+    # Generate the Python binding with swig.
+    logging.info("Python binding is out of date, regenerating")
+    do_swig_rebuild(options, dependency_file, config_build_dir, settings)
+    if options.generate_dependency_file:
+        return
+
+    # Post process the swig-generated file.
+    do_modify_python_lldb(options, config_build_dir)
+
+
+# This script can be called by another Python script by calling the main()
+# function directly
+if __name__ == "__main__":
+    print("Script cannot be called directly.")
+    sys.exit(-1)
-- 
cgit v1.1