#!/usr/bin/env python """ SWIG generation client. Supports both local and remote generation of SWIG bindings for multiple languages. """ # Future imports from __future__ import absolute_import from __future__ import print_function # Python modules import argparse import io import logging import os import socket import struct import sys # LLDB modules import use_lldb_suite from lldbsuite.support import fs from lldbsuite.support import sockutil # package imports from . import local from . import remote default_ip = "127.0.0.1" default_port = 8537 def add_subparser_args(parser): """Returns options processed from the provided command line. @param args the command line to process. """ # A custom action used by the --local command line option. It can be # used with either 0 or 1 argument. If used with 0 arguments, it # searches for a copy of swig located on the physical machine. If # used with 1 argument, the argument is the path to a swig executable. class FindLocalSwigAction(argparse.Action): def __init__(self, option_strings, dest, **kwargs): super(FindLocalSwigAction, self).__init__( option_strings, dest, nargs='?', **kwargs) def __call__(self, parser, namespace, values, option_string=None): swig_exe = None if values is None: swig_exe = fs.find_executable('swig') else: swig_exe = values setattr(namespace, self.dest, os.path.normpath(swig_exe)) # A custom action used by the --remote command line option. It can be # used with either 0 or 1 arguments. If used with 0 arguments it chooses # a default connection string. If used with one argument it is a string # of the form `ip_address[:port]`. If the port is unspecified, the # default port is used. class RemoteIpAction(argparse.Action): def __init__(self, option_strings, dest, **kwargs): super(RemoteIpAction, self).__init__( option_strings, dest, nargs='?', **kwargs) def __call__(self, parser, namespace, values, option_string=None): ip_port = None if values is None: ip_port = (default_ip, default_port) else: result = values.split(':') if len(result) == 1: ip_port = (result[0], default_port) elif len(result) == 2: ip_port = (result[0], int(result[1])) else: raise ValueError("Invalid connection string") setattr(namespace, self.dest, ip_port) parser.add_argument( "--local", action=FindLocalSwigAction, dest="swig_executable", help=( "Run the copy of swig at the specified location, or search PATH" "if the location is omitted")) parser.add_argument( "--remote", action=RemoteIpAction, help=( "Use the given connection string to connect to a remote " "generation service")) parser.add_argument( "--src-root", required=True, help="The root folder of the LLDB source tree.") parser.add_argument( "--target-dir", default=os.getcwd(), help=( "Specifies the build dir where the language binding " "should be placed")) parser.add_argument( "--language", dest="languages", action="append", help="Specifies the language to generate bindings for") def finalize_subparser_options(options): if options.languages is None: options.languages = ['python'] if options.remote is None and options.swig_executable is None: logging.error("Must specify either --local or --remote") sys.exit(-3) return options def establish_remote_connection(ip_port): logging.debug("Creating socket...") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) logging.info("Connecting to server {} on port {}" .format(ip_port[0], ip_port[1])) s.connect(ip_port) logging.info("Connection established...") return s def transmit_request(connection, packed_input): logging.info("Sending {} bytes of compressed data." .format(len(packed_input))) connection.sendall(struct.pack("!I", len(packed_input))) connection.sendall(packed_input) logging.info("Awaiting response.") response_len = struct.unpack("!I", sockutil.recvall(connection, 4))[0] logging.debug("Expecting {} byte response".format(response_len)) response = sockutil.recvall(connection, response_len) return response def handle_response(options, connection, response): logging.debug("Received {} byte response.".format(len(response))) logging.debug("Creating output directory {}" .format(options.target_dir)) os.makedirs(options.target_dir, exist_ok=True) logging.info("Unpacking response archive into {}" .format(options.target_dir)) local.unpack_archive(options.target_dir, response) response_file_path = os.path.normpath( os.path.join(options.target_dir, "swig_output.json")) if not os.path.isfile(response_file_path): logging.error("Response file '{}' does not exist." .format(response_file_path)) return try: response = remote.deserialize_response_status( io.open(response_file_path)) if response[0] != 0: logging.error("An error occurred during generation. Status={}" .format(response[0])) logging.error(response[1]) else: logging.info("SWIG generation successful.") if len(response[1]) > 0: logging.info(response[1]) finally: os.unlink(response_file_path) def run(options): if options.remote is None: logging.info("swig bot client using local swig installation at '{}'" .format(options.swig_executable)) if not os.path.isfile(options.swig_executable): logging.error("Swig executable '{}' does not exist." .format(options.swig_executable)) config = local.LocalConfig() config.languages = options.languages config.src_root = options.src_root config.target_dir = options.target_dir config.swig_executable = options.swig_executable local.generate(config) else: logging.info("swig bot client using remote generation with server '{}'" .format(options.remote)) connection = None try: config = remote.generate_config(options.languages) logging.debug("Generated config json {}".format(config)) inputs = [("include/lldb", ".h"), ("include/lldb/API", ".h"), ("scripts", ".swig"), ("scripts/Python", ".swig"), ("scripts/interface", ".i")] zip_data = io.BytesIO() packed_input = local.pack_archive( zip_data, options.src_root, inputs) logging.info("(null) -> config.json") packed_input.writestr("config.json", config) packed_input.close() connection = establish_remote_connection(options.remote) response = transmit_request(connection, zip_data.getvalue()) handle_response(options, connection, response) finally: if connection is not None: connection.close()