2015-11-25 05:35:17 +08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
2015-11-25 05:35:32 +08:00
|
|
|
"""
|
|
|
|
SWIG generation client. Supports both local and remote generation of SWIG
|
|
|
|
bindings for multiple languages.
|
|
|
|
"""
|
|
|
|
|
2015-11-25 05:35:17 +08:00
|
|
|
# Future imports
|
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
# Python modules
|
|
|
|
import argparse
|
2015-12-01 06:31:13 +08:00
|
|
|
import io
|
2015-11-25 05:35:17 +08:00
|
|
|
import logging
|
|
|
|
import os
|
2015-11-25 05:35:32 +08:00
|
|
|
import socket
|
|
|
|
import struct
|
2015-11-25 05:35:17 +08:00
|
|
|
import sys
|
|
|
|
|
|
|
|
# LLDB modules
|
|
|
|
import use_lldb_suite
|
|
|
|
from lldbsuite.support import fs
|
2015-11-25 05:35:32 +08:00
|
|
|
from lldbsuite.support import sockutil
|
|
|
|
|
|
|
|
# package imports
|
|
|
|
from . import local
|
2015-12-01 06:31:13 +08:00
|
|
|
from . import remote
|
2015-11-25 05:35:32 +08:00
|
|
|
|
|
|
|
default_ip = "127.0.0.1"
|
|
|
|
default_port = 8537
|
2015-11-25 05:35:17 +08:00
|
|
|
|
2015-12-03 03:00:52 +08:00
|
|
|
def add_subparser_args(parser):
|
2015-11-25 05:35:17 +08:00
|
|
|
"""Returns options processed from the provided command line.
|
|
|
|
|
|
|
|
@param args the command line to process.
|
|
|
|
"""
|
|
|
|
|
2015-11-25 05:35:32 +08:00
|
|
|
# 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.
|
2015-11-25 05:35:17 +08:00
|
|
|
class FindLocalSwigAction(argparse.Action):
|
|
|
|
def __init__(self, option_strings, dest, **kwargs):
|
2015-11-25 05:35:32 +08:00
|
|
|
super(FindLocalSwigAction, self).__init__(
|
|
|
|
option_strings, dest, nargs='?', **kwargs)
|
2015-11-25 05:35:17 +08:00
|
|
|
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))
|
|
|
|
|
2015-11-25 05:35:32 +08:00
|
|
|
# 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)
|
|
|
|
|
2015-11-25 05:35:17 +08:00
|
|
|
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",
|
2015-11-25 05:35:32 +08:00
|
|
|
action=RemoteIpAction,
|
2015-11-25 05:35:17 +08:00
|
|
|
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")
|
|
|
|
|
2015-12-03 03:00:52 +08:00
|
|
|
def finalize_subparser_options(options):
|
2015-11-25 05:35:17 +08:00
|
|
|
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
|
|
|
|
|
2015-11-25 05:35:32 +08:00
|
|
|
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
|
|
|
|
|
2015-12-01 06:31:24 +08:00
|
|
|
def transmit_request(connection, packed_input):
|
2015-11-25 05:35:32 +08:00
|
|
|
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
|
2015-11-25 05:35:17 +08:00
|
|
|
|
2015-12-01 06:31:24 +08:00
|
|
|
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)
|
|
|
|
|
2015-12-03 03:00:52 +08:00
|
|
|
def run(options):
|
2015-11-25 05:35:17 +08:00
|
|
|
if options.remote is None:
|
2015-11-25 05:35:32 +08:00
|
|
|
logging.info("swig bot client using local swig installation at '{}'"
|
|
|
|
.format(options.swig_executable))
|
2015-11-25 05:35:17 +08:00
|
|
|
if not os.path.isfile(options.swig_executable):
|
2015-11-25 05:35:32 +08:00
|
|
|
logging.error("Swig executable '{}' does not exist."
|
|
|
|
.format(options.swig_executable))
|
2015-12-01 06:31:13 +08:00
|
|
|
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)
|
2015-11-25 05:35:17 +08:00
|
|
|
else:
|
2015-11-25 05:35:32 +08:00
|
|
|
logging.info("swig bot client using remote generation with server '{}'"
|
|
|
|
.format(options.remote))
|
2015-11-25 05:35:58 +08:00
|
|
|
connection = None
|
|
|
|
try:
|
2015-12-01 06:31:13 +08:00
|
|
|
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()
|
2015-11-25 05:35:58 +08:00
|
|
|
connection = establish_remote_connection(options.remote)
|
2015-12-01 06:31:24 +08:00
|
|
|
response = transmit_request(connection, zip_data.getvalue())
|
|
|
|
handle_response(options, connection, response)
|
2015-11-25 05:35:58 +08:00
|
|
|
finally:
|
|
|
|
if connection is not None:
|
|
|
|
connection.close()
|