diff --git a/lldb/scripts/CMakeLists.txt b/lldb/scripts/CMakeLists.txt --- a/lldb/scripts/CMakeLists.txt +++ b/lldb/scripts/CMakeLists.txt @@ -1,12 +1,8 @@ file(GLOB SWIG_INTERFACES interface/*.i) file(GLOB_RECURSE SWIG_SOURCES *.swig) -set(SWIG_HEADERS - ${LLDB_SOURCE_DIR}/include/lldb/API/SBDefines.h - ${LLDB_SOURCE_DIR}/include/lldb/lldb-defines.h - ${LLDB_SOURCE_DIR}/include/lldb/lldb-enumerations.h - ${LLDB_SOURCE_DIR}/include/lldb/lldb-forward.h - ${LLDB_SOURCE_DIR}/include/lldb/lldb-types.h - ${LLDB_SOURCE_DIR}/include/lldb/lldb-versioning.h +file(GLOB SWIG_HEADERS + ${LLDB_SOURCE_DIR}/include/lldb/API/*.h + ${LLDB_SOURCE_DIR}/include/lldb/*.h ) if(LLDB_BUILD_FRAMEWORK) @@ -19,22 +15,35 @@ message(FATAL_ERROR "LLDB requires swig ${SWIG_MIN_VERSION}, your version is ${SWIG_VERSION}.") endif() +if(APPLE) + set(DARWIN_EXTRAS "-D__APPLE__") +else() + set(DARWIN_EXTRAS "") +endif() + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldb.py DEPENDS ${SWIG_SOURCES} DEPENDS ${SWIG_INTERFACES} DEPENDS ${SWIG_HEADERS} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Python/prepare_binding_Python.py - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/prepare_bindings.py - ${framework_arg} - --srcRoot=${LLDB_SOURCE_DIR} - --targetDir=${CMAKE_CURRENT_BINARY_DIR} - --cfgBldDir=${CMAKE_CURRENT_BINARY_DIR} - --prefix=${CMAKE_BINARY_DIR} - --swigExecutable=${SWIG_EXECUTABLE} + COMMAND ${SWIG_EXECUTABLE} + -c++ + -shadow + -python + -features + autodoc + -threads + -I${LLDB_SOURCE_DIR}/include + -I${CMAKE_CURRENT_SOURCE_DIR} + -D__STDC_LIMIT_MACROS + -D__STDC_CONSTANT_MACROS + ${DARWIN_EXTRAS} + -outdir ${CMAKE_CURRENT_BINARY_DIR} + -o ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp + ${LLDB_SOURCE_DIR}/scripts/lldb.swig VERBATIM - COMMENT "Python script building LLDB Python wrapper") + COMMENT "Builds LLDB Python wrapper") add_custom_target(swig_wrapper ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp diff --git a/lldb/scripts/Python/prepare_binding_Python.py b/lldb/scripts/Python/prepare_binding_Python.py deleted file mode 100644 --- a/lldb/scripts/Python/prepare_binding_Python.py +++ /dev/null @@ -1,396 +0,0 @@ -""" -Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -See https://llvm.org/LICENSE.txt for license information. -SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -Python binding preparation script. -""" - -# Python modules: -from __future__ import print_function - -import logging -import os -import re -import shutil -import subprocess -import sys -import platform - - -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 - is_darwin = options.target_platform == "Darwin" - gen_deps = options.generate_dependency_file - darwin_extras = ["-D__APPLE__"] if is_darwin else [] - deps_args = ["-MMD", "-MF", temp_dep_file_path] if gen_deps else [] - command = ([ - options.swig_executable, - "-c++", - "-shadow", - "-python", - "-features", "autodoc", - "-threads", - "-I" + os.path.normpath(os.path.join(options.src_root, "include")), - "-I" + os.path.curdir, - "-D__STDC_LIMIT_MACROS", - "-D__STDC_CONSTANT_MACROS" - ] - + darwin_extras - + deps_args - + [ - "-outdir", config_build_dir, - "-o", settings.output_file, - settings.input_file - ] - ) - logging.info("running swig with: %r", command) - - # Execute swig - process = subprocess.Popen( - command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - # Wait for SWIG process to terminate - swig_stdout, swig_stderr = process.communicate() - return_code = process.returncode - if return_code != 0: - swig_stdout = swig_stdout.decode('utf8', errors='replace').rstrip() - swig_stderr = swig_stderr.decode('utf8', errors='replace').rstrip() - swig_stdout = re.sub(r'^(?=.)', 'stdout: ', swig_stdout, flags=re.MULTILINE) - swig_stderr = re.sub(r'^(?=.)', 'stderr: ', swig_stderr, flags=re.MULTILINE) - logging.error( - "swig failed with error code %d\n%s%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 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) - - -# 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 deleted file mode 100755 --- a/lldb/scripts/prepare_bindings.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python -""" -Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -See https://llvm.org/LICENSE.txt for license information. -SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -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 platform -import sys - -# LLDB modules: -import use_lldb_suite -from lldbsuite.support import fs - - -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) - - child_dirs = ["Python"] - - # 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( - "--find-swig", - action="store_true", - help=( - "Indicates the swig executable should be searched for " - "if not eplicitly provided. Either this or the explicit " - "swig executable option must be provided.")) - 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")) - - parser.add_argument( - "--target-platform", - help=( - "Specifies the platform we are building for." - "Should be the same as what platform.system() returns.")) - # 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) - - # Ensure we have a swig executable. - if not options.swig_executable or len(options.swig_executable) == 0: - if options.find_swig: - try: - options.swig_executable = fs.find_executable("swig") - except Exception as e: - logging.error("Unable to find swig executable: %s" % e.message) - sys.exit(-6) - else: - logging.error( - "The --find-swig option must be specified " - "when the swig executable location is not " - "explicitly provided.") - sys.exit(-12) - - # 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:])