summaryrefslogtreecommitdiffstats
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/lldb.xcodeproj/project.pbxproj10
-rw-r--r--lldb/scripts/Python/prepare_binding_Python.py435
-rwxr-xr-xlldb/scripts/prepare_bindings.py196
3 files changed, 636 insertions, 5 deletions
diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj
index 5b7cc9a1590..10d9c88a6e0 100644
--- a/lldb/lldb.xcodeproj/project.pbxproj
+++ b/lldb/lldb.xcodeproj/project.pbxproj
@@ -5984,7 +5984,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 2668020B115FD0EE008E1FE4 /* Build configuration list for PBXNativeTarget "LLDB" */;
buildPhases = (
- 26DC6A5813380D4300FF7998 /* Build swig wrapper classes */,
+ 26DC6A5813380D4300FF7998 /* Prepare Swig Bindings */,
26680202115FD0ED008E1FE4 /* Headers */,
26680203115FD0ED008E1FE4 /* Resources */,
26680204115FD0ED008E1FE4 /* Sources */,
@@ -6214,19 +6214,19 @@
shellPath = /bin/sh;
shellScript = "perl $SRCROOT/scripts/build-llvm.pl";
};
- 26DC6A5813380D4300FF7998 /* Build swig wrapper classes */ = {
+ 26DC6A5813380D4300FF7998 /* Prepare Swig Bindings */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
- name = "Build swig wrapper classes";
+ name = "Prepare Swig Bindings";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "$SRCROOT/scripts/build-swig-wrapper-classes.sh $SRCROOT $TARGET_BUILD_DIR $CONFIGURATION_BUILD_DIR \"\"\n";
+ shellPath = /bin/bash;
+ shellScript = "/usr/bin/python $SRCROOT/scripts/prepare_bindings.py --framework --src-root $SRCROOT --target-dir $TARGET_BUILD_DIR --config-build-dir $CONFIGURATION_BUILD_DIR --swig-executable `which swig`";
};
4959511A1A1ACE9500F6F8FC /* Install Clang compiler headers */ = {
isa = PBXShellScriptBuildPhase;
diff --git a/lldb/scripts/Python/prepare_binding_Python.py b/lldb/scripts/Python/prepare_binding_Python.py
new file mode 100644
index 00000000000..1996841baf1
--- /dev/null
+++ b/lldb/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)
diff --git a/lldb/scripts/prepare_bindings.py b/lldb/scripts/prepare_bindings.py
new file mode 100755
index 00000000000..58b33fd568a
--- /dev/null
+++ b/lldb/scripts/prepare_bindings.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+"""
+ The LLVM Compiler Infrastructure
+
+This file is distributed under the University of Illinois Open Source
+License. See LICENSE.TXT for details.
+
+Prepares language bindings for LLDB build process. Run with --help
+to see a description of the supported command line arguments.
+"""
+
+# Python modules:
+import argparse
+import logging
+import os
+import sys
+
+
+def prepare_binding_for_language(scripts_dir, script_lang, options):
+ """Prepares the binding for a specific language.
+
+ @param scripts_dir the full path to the scripts source directory.
+ @param script_lang the name of the script language. Should be a child
+ directory within the scripts dir, and should contain a
+ prepare_scripts_{script_lang}.py script file in it.
+ @param options the dictionary of parsed command line options.
+
+ There is no return value. If it returns, the process succeeded; otherwise,
+ the process will exit where it fails.
+ """
+ # Ensure the language-specific prepare module exists.
+ script_name = "prepare_binding_{}.py".format(script_lang)
+ lang_path = os.path.join(scripts_dir, script_lang)
+ script_path = os.path.join(lang_path, script_name)
+ if not os.path.exists(script_path):
+ logging.error(
+ "failed to find prepare script for language '%s' at '%s'",
+ script_lang,
+ script_path)
+ sys.exit(-9)
+
+ # Include this language-specific directory in the Python search
+ # path.
+ sys.path.append(os.path.normcase(lang_path))
+
+ # Execute the specific language script
+ module_name = os.path.splitext(script_name)[0]
+ module = __import__(module_name)
+ module.main(options)
+
+ # Remove the language-specific directory from the Python search path.
+ sys.path.remove(os.path.normcase(lang_path))
+
+
+def prepare_all_bindings(options):
+ """Prepares bindings for each of the languages supported.
+
+ @param options the parsed arguments from the command line
+
+ @return the exit value for the program. 0 is success, all othes
+ indicate some kind of failure.
+ """
+ # Check for the existence of the SWIG scripts folder
+ scripts_dir = os.path.join(options.src_root, "scripts")
+ if not os.path.exists(scripts_dir):
+ logging.error("failed to find scripts dir: '%s'", scripts_dir)
+ sys.exit(-8)
+
+ # Collect list of child directories. We expect there to be one
+ # for each supported script language.
+ child_dirs = [f for f in os.listdir(scripts_dir)
+ if os.path.isdir(os.path.join(scripts_dir, f))]
+
+ # Remove directories that do not represent script languages.
+ for removal_dir in [".svn", "interface", "__pycache__", "sphinx"]:
+ if removal_dir in child_dirs:
+ child_dirs.remove(removal_dir)
+
+ logging.info("found script directories: %s", child_dirs)
+
+ # Iterate script directory find any script language directories
+ for script_lang in child_dirs:
+ logging.info("executing language script for: '%s'", script_lang)
+ prepare_binding_for_language(scripts_dir, script_lang, options)
+
+
+def process_args(args):
+ """Returns options processed from the provided command line.
+
+ @param args the command line to process.
+ """
+
+ # Setup the parser arguments that are accepted.
+ parser = argparse.ArgumentParser(
+ description="Prepare language bindings for LLDB build.")
+
+ # Arguments to control logging verbosity.
+ parser.add_argument(
+ "--debug", "-d",
+ action="store_true",
+ help="Set program logging level to DEBUG.")
+ parser.add_argument(
+ "--verbose", "-v",
+ action="count",
+ default=0,
+ help=(
+ "Increase logging verbosity level. Default: only error and "
+ "higher are displayed. Each -v increases level of verbosity."))
+
+ # Arguments to control whether we're building an OS X-style
+ # framework. This is the opposite of the older "-m" (makefile)
+ # option.
+ parser.add_argument(
+ "--config-build-dir",
+ "--cfgBldDir",
+ help=(
+ "Configuration build dir, will use python module path "
+ "if unspecified."))
+ parser.add_argument(
+ "--framework",
+ action="store_true",
+ help="Prepare as OS X-style framework.")
+ parser.add_argument(
+ "--generate-dependency-file",
+ "-M",
+ action="store_true",
+ help="Make the dependency (.d) file for the wrappers.")
+ parser.add_argument(
+ "--prefix",
+ help="Override path where the LLDB module is placed.")
+ parser.add_argument(
+ "--src-root",
+ "--srcRoot",
+ "-s",
+ # Default to the parent directory of this script's directory.
+ default=os.path.abspath(
+ os.path.join(
+ os.path.dirname(os.path.realpath(__file__)),
+ os.path.pardir)),
+ help="Specifies the LLDB source root directory.")
+ parser.add_argument(
+ "--swig-executable",
+ "--swigExecutable",
+ help="Path to the swig executable.")
+ parser.add_argument(
+ "--target-dir",
+ "--targetDir",
+ required=True,
+ help=(
+ "Specifies the build dir where the language binding "
+ "should be placed"))
+
+ # Process args.
+ options = parser.parse_args(args)
+
+ # Set logging level based on verbosity count.
+ if options.debug:
+ log_level = logging.DEBUG
+ else:
+ # See logging documentation for error levels. We'll default
+ # to showing ERROR or higher error messages. For each -v
+ # specified, we'll shift to the next lower-priority log level.
+ log_level = logging.ERROR - 10 * options.verbose
+ if log_level < logging.NOTSET:
+ # Displays all logged messages.
+ log_level = logging.NOTSET
+ logging.basicConfig(level=log_level)
+ logging.info("logging is using level: %d", log_level)
+
+ return options
+
+
+def main(args):
+ """Drives the main script preparation steps.
+
+ @param args list of command line arguments.
+ """
+ # Process command line arguments.
+ options = process_args(args)
+ logging.debug("Processed args: options=%s", options)
+
+ # Check if the swig file exists.
+ swig_path = os.path.normcase(
+ os.path.join(options.src_root, "scripts", "lldb.swig"))
+ if not os.path.isfile(swig_path):
+ logging.error("swig file not found at '%s'", swig_path)
+ sys.exit(-3)
+
+ # Prepare bindings for each supported language binding.
+ # This will error out if it doesn't succeed.
+ prepare_all_bindings(options)
+ sys.exit(0)
+
+if __name__ == "__main__":
+ # Run the main driver loop.
+ main(sys.argv[1:])
OpenPOWER on IntegriCloud