[Lldb-commits] [lldb] r253317 - Add Pythonic language binding wrapper generation script.

Todd Fiala via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 17 08:18:33 PST 2015


Breaking out the binding generation into a separate step will also be
important for a couple reasons:

* (from before) I want to eliminate the requirement for the vast majority
of the builds to have a swig on their system, and

* (not stated before) we'd like to move away from swig for binding
generation at some point.

-Todd

On Tue, Nov 17, 2015 at 8:15 AM, Todd Fiala <todd.fiala at gmail.com> wrote:

> Yes, I am planning on separating out the usage of the swig output from the
> massaging of that into the build.
>
> Right now I'm just in phase 1, which is to clean up the existing process
> without modifying it, and adopting it on Xcode.
>
> Humorously, with our recent removal of the searching for swig in the
> python scripts (which our OS X CI depends on, but since we weren't using
> the Python ones, I didn't detect), I ended breaking our CI.  I'm adding the
> searching back in.
>
> On Tue, Nov 17, 2015 at 8:12 AM, Zachary Turner <zturner at google.com>
> wrote:
>
>> Is there any way to have the step that runs the swig executable not be
>> part of this script?  Seems like running swig should be a build step, and
>> running these scripts on the output of swig should be a step that follows
>>
>> On Mon, Nov 16, 2015 at 11:20 PM Todd Fiala via lldb-commits <
>> lldb-commits at lists.llvm.org> wrote:
>>
>>> Author: tfiala
>>> Date: Tue Nov 17 01:17:38 2015
>>> New Revision: 253317
>>>
>>> URL: http://llvm.org/viewvc/llvm-project?rev=253317&view=rev
>>> Log:
>>> Add Pythonic language binding wrapper generation script.
>>>
>>> This is only used by Xcode at the moment.  It replaces the
>>> buildSwigWrapperClasses.py and related per-script-language
>>> scripts.  It also fixes a couple bugs in those w/r/t Xcode
>>> usage:
>>>
>>> * the presence of the GCC_PREPROCESSOR_DEFINITIONS env var
>>>   should not be short-circuiting generation of the language
>>>   binding; rather, only if LLDB_DISABLE_PYTHON is present
>>>   within that environment variable.
>>>
>>> * some logic around what to do when building in "non-Makefile"
>>>   mode.  I've switched the handling of that to be on a
>>>   "--framework" flag - if specified, we build an OS X-style
>>>   framework; otherwise, we go with non.
>>>
>>> Putting this up now only attached to the Xcode build so
>>> others can look at it but not be affected by it yet.
>>> After this, I'll tackle the finalizer, along with trying
>>> it locally on Linux.
>>>
>>> Added:
>>>     lldb/trunk/scripts/Python/prepare_binding_Python.py
>>>     lldb/trunk/scripts/prepare_bindings.py   (with props)
>>> Modified:
>>>     lldb/trunk/lldb.xcodeproj/project.pbxproj
>>>
>>> Modified: lldb/trunk/lldb.xcodeproj/project.pbxproj
>>> URL:
>>> http://llvm.org/viewvc/llvm-project/lldb/trunk/lldb.xcodeproj/project.pbxproj?rev=253317&r1=253316&r2=253317&view=diff
>>>
>>> ==============================================================================
>>> --- lldb/trunk/lldb.xcodeproj/project.pbxproj (original)
>>> +++ lldb/trunk/lldb.xcodeproj/project.pbxproj Tue Nov 17 01:17:38 2015
>>> @@ -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;
>>>
>>> Added: lldb/trunk/scripts/Python/prepare_binding_Python.py
>>> URL:
>>> http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/Python/prepare_binding_Python.py?rev=253317&view=auto
>>>
>>> ==============================================================================
>>> --- lldb/trunk/scripts/Python/prepare_binding_Python.py (added)
>>> +++ lldb/trunk/scripts/Python/prepare_binding_Python.py Tue Nov 17
>>> 01:17:38 2015
>>> @@ -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)
>>>
>>> Added: lldb/trunk/scripts/prepare_bindings.py
>>> URL:
>>> http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/prepare_bindings.py?rev=253317&view=auto
>>>
>>> ==============================================================================
>>> --- lldb/trunk/scripts/prepare_bindings.py (added)
>>> +++ lldb/trunk/scripts/prepare_bindings.py Tue Nov 17 01:17:38 2015
>>> @@ -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:])
>>>
>>> Propchange: lldb/trunk/scripts/prepare_bindings.py
>>>
>>> ------------------------------------------------------------------------------
>>>     svn:executable = *
>>>
>>>
>>> _______________________________________________
>>> lldb-commits mailing list
>>> lldb-commits at lists.llvm.org
>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
>>>
>>
>
>
> --
> -Todd
>



-- 
-Todd
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20151117/9b3119f9/attachment-0001.html>


More information about the lldb-commits mailing list