Skip to content
This repository was archived by the owner on May 21, 2019. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6777e06

Browse files
author
Zachary Turner
committedNov 24, 2015
swig_bot remote path connection / preliminary implementation.
With this patch, the client will package up all the required inputs into a compressed zip file, establish a connection to the server, send the input to the server, and wait for the server to send a response (in this case the response is just echoed back to the client). This gets the network communication in place, and in a subsequent patch I will follow up with the code that actually runs swig on the server and sends back the output instead of echoing back the input. git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@254023 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent df2a249 commit 6777e06

File tree

5 files changed

+276
-24
lines changed

5 files changed

+276
-24
lines changed
 
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
The LLVM Compiler Infrastructure
3+
4+
This file is distributed under the University of Illinois Open Source
5+
License. See LICENSE.TXT for details.
6+
7+
Helper functions for working with sockets.
8+
"""
9+
10+
# Python modules:
11+
import io
12+
import socket
13+
14+
# LLDB modules
15+
import use_lldb_suite
16+
17+
def recvall(sock, size):
18+
bytes = io.BytesIO()
19+
while size > 0:
20+
this_result = sock.recv(size)
21+
bytes.write(this_result)
22+
size -= len(this_result)
23+
return bytes.getvalue()

‎scripts/swig_bot.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,63 @@
11
#!/usr/bin/env python
22

3+
"""
4+
SWIG generation top-level script. Supports both local and remote generation
5+
of SWIG bindings for multiple languages.
6+
"""
7+
38
# Python modules
9+
import argparse
10+
import logging
411
import sys
12+
import traceback
513

614
# LLDB modules
715
import use_lldb_suite
816

17+
def process_args(args):
18+
parser = argparse.ArgumentParser(
19+
description='Run swig-bot client or server.')
20+
21+
# Arguments to control whether swig-bot runs as a client or server.
22+
parser.add_argument(
23+
"--mode",
24+
required=True,
25+
choices=["client", "server"],
26+
help="Run swig_bot in either client or server mode.")
27+
28+
# Arguments to control logging verbosity.
29+
parser.add_argument(
30+
"--verbose", "-v",
31+
action="store_true",
32+
default=False,
33+
help="Increase logging verbosity level.")
34+
35+
(options, remaining) = parser.parse_known_args(args)
36+
# Set logging level.
37+
if options.verbose:
38+
log_level = logging.DEBUG
39+
else:
40+
log_level = logging.NOTSET
41+
logging.basicConfig(level=log_level)
42+
logging.info("logging is using level: %d", log_level)
43+
44+
return (options, remaining)
45+
946
if __name__ == "__main__":
10-
from swig_bot_lib import client
11-
client.run(sys.argv[1:])
47+
(options, remaining) = process_args(sys.argv[1:])
48+
try:
49+
if options.mode == "client":
50+
logging.info("Running swig_bot in client mode")
51+
from swig_bot_lib import client
52+
client.run(remaining)
53+
elif options.mode == "server":
54+
logging.info("Running swig_bot in server mode")
55+
from swig_bot_lib import server
56+
server.run(remaining)
57+
else:
58+
logging.error("Unknown mode specified. Expected client or server.")
59+
sys.exit(-1)
60+
except Exception as e:
61+
error = traceback.format_exc()
62+
logging.error("An error occurred running swig-bot.")
63+
logging.error(error)

‎scripts/swig_bot_lib/client.py

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
#!/usr/bin/env python
22

3+
"""
4+
SWIG generation client. Supports both local and remote generation of SWIG
5+
bindings for multiple languages.
6+
"""
7+
38
# Future imports
49
from __future__ import absolute_import
510
from __future__ import print_function
@@ -8,21 +13,35 @@
813
import argparse
914
import logging
1015
import os
16+
import socket
17+
import struct
1118
import sys
1219

1320
# LLDB modules
1421
import use_lldb_suite
1522
from lldbsuite.support import fs
23+
from lldbsuite.support import sockutil
24+
25+
# package imports
26+
from . import local
27+
28+
default_ip = "127.0.0.1"
29+
default_port = 8537
1630

1731
def process_args(args):
1832
"""Returns options processed from the provided command line.
1933
2034
@param args the command line to process.
2135
"""
2236

37+
# A custom action used by the --local command line option. It can be
38+
# used with either 0 or 1 argument. If used with 0 arguments, it
39+
# searches for a copy of swig located on the physical machine. If
40+
# used with 1 argument, the argument is the path to a swig executable.
2341
class FindLocalSwigAction(argparse.Action):
2442
def __init__(self, option_strings, dest, **kwargs):
25-
super(FindLocalSwigAction, self).__init__(option_strings, dest, nargs='?', **kwargs)
43+
super(FindLocalSwigAction, self).__init__(
44+
option_strings, dest, nargs='?', **kwargs)
2645
def __call__(self, parser, namespace, values, option_string=None):
2746
swig_exe = None
2847
if values is None:
@@ -31,17 +50,33 @@ def __call__(self, parser, namespace, values, option_string=None):
3150
swig_exe = values
3251
setattr(namespace, self.dest, os.path.normpath(swig_exe))
3352

53+
# A custom action used by the --remote command line option. It can be
54+
# used with either 0 or 1 arguments. If used with 0 arguments it chooses
55+
# a default connection string. If used with one argument it is a string
56+
# of the form `ip_address[:port]`. If the port is unspecified, the
57+
# default port is used.
58+
class RemoteIpAction(argparse.Action):
59+
def __init__(self, option_strings, dest, **kwargs):
60+
super(RemoteIpAction, self).__init__(
61+
option_strings, dest, nargs='?', **kwargs)
62+
def __call__(self, parser, namespace, values, option_string=None):
63+
ip_port = None
64+
if values is None:
65+
ip_port = (default_ip, default_port)
66+
else:
67+
result = values.split(':')
68+
if len(result)==1:
69+
ip_port = (result[0], default_port)
70+
elif len(result)==2:
71+
ip_port = (result[0], int(result[1]))
72+
else:
73+
raise ValueError("Invalid connection string")
74+
setattr(namespace, self.dest, ip_port)
75+
3476
# Setup the parser arguments that are accepted.
3577
parser = argparse.ArgumentParser(
3678
description='Generate SWIG bindings.')
3779

38-
# Arguments to control logging verbosity.
39-
parser.add_argument(
40-
"--verbose", "-v",
41-
action="store_true",
42-
default=False,
43-
help="Increase logging verbosity level.")
44-
4580
parser.add_argument(
4681
"--local",
4782
action=FindLocalSwigAction,
@@ -52,7 +87,7 @@ def __call__(self, parser, namespace, values, option_string=None):
5287

5388
parser.add_argument(
5489
"--remote",
55-
action="store",
90+
action=RemoteIpAction,
5691
help=(
5792
"Use the given connection string to connect to a remote "
5893
"generation service"))
@@ -85,24 +120,42 @@ def __call__(self, parser, namespace, values, option_string=None):
85120
logging.error("Must specify either --local or --remote")
86121
sys.exit(-3)
87122

88-
# Set logging level based on verbosity count.
89-
if options.verbose:
90-
log_level = logging.DEBUG
91-
else:
92-
log_level = logging.NOTSET
93-
logging.basicConfig(level=log_level)
94-
logging.info("logging is using level: %d", log_level)
95-
96123
return options
97124

125+
def establish_remote_connection(ip_port):
126+
logging.debug("Creating socket...")
127+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
128+
logging.info("Connecting to server {} on port {}"
129+
.format(ip_port[0], ip_port[1]))
130+
s.connect(ip_port)
131+
logging.info("Connection established...")
132+
return s
133+
134+
def transmit_data(connection, packed_input):
135+
logging.info("Sending {} bytes of compressed data."
136+
.format(len(packed_input)))
137+
connection.sendall(struct.pack("!I", len(packed_input)))
138+
connection.sendall(packed_input)
139+
logging.info("Awaiting response.")
140+
response_len = struct.unpack("!I", sockutil.recvall(connection, 4))[0]
141+
logging.debug("Expecting {} byte response".format(response_len))
142+
response = sockutil.recvall(connection, response_len)
143+
return response
98144

99145
def run(args):
100146
options = process_args(args)
101147

102148
if options.remote is None:
149+
logging.info("swig bot client using local swig installation at '{}'"
150+
.format(options.swig_executable))
103151
if not os.path.isfile(options.swig_executable):
104-
logging.error("Swig executable '%s' does not exist." % options.swig_executable)
105-
from . import local
152+
logging.error("Swig executable '{}' does not exist."
153+
.format(options.swig_executable))
106154
local.generate(options)
107155
else:
108-
logging.error("Remote path is not yet implemented!")
156+
logging.info("swig bot client using remote generation with server '{}'"
157+
.format(options.remote))
158+
packed_input = local.pack_input(options)
159+
connection = establish_remote_connection(options.remote)
160+
response = transmit_data(connection, packed_input)
161+
logging.debug("Received {} byte response.".format(len(response)))

‎scripts/swig_bot_lib/local.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,68 @@
11
#!/usr/bin/env python
22

3+
"""
4+
Shared functionality used by `client` and `server` when generating or preparing
5+
to generate SWIG on the local machine.
6+
"""
7+
38
# Future imports
49
from __future__ import absolute_import
510
from __future__ import print_function
611

712
# Python modules
813
import argparse
914
import imp
15+
import io
1016
import logging
1117
import os
1218
import subprocess
1319
import sys
20+
import zipfile
1421

1522
# LLDB modules
1623
import use_lldb_suite
1724

25+
def pack_input(options):
26+
logging.info("Creating input file package...")
27+
zip_data = io.BytesIO()
28+
zip_file = None
29+
try:
30+
# It's possible that a custom-built interpreter will not have the
31+
# standard zlib module. If so, we can only store, not compress. By
32+
# try to compress since we usually have a standard Python distribution.
33+
zip_file = zipfile.ZipFile(zip_data, mode='w',
34+
compression=zipfile.ZIP_DEFLATED)
35+
except RuntimeError:
36+
zip_file = zipfile.ZipFile(zip_data, mode='w',
37+
compression=zipfile.ZIP_STORED)
38+
39+
filters = [("include/lldb", ".h"),
40+
("scripts", ".swig"),
41+
("scripts/Python", ".swig"),
42+
("scripts/interface", ".i")]
43+
def filter_func(t):
44+
subfolder = t[0]
45+
ext = t[1]
46+
full_path = os.path.normpath(os.path.join(options.src_root, subfolder))
47+
candidates = [os.path.normpath(os.path.join(full_path, f))
48+
for f in os.listdir(full_path)]
49+
actual = filter(
50+
lambda f : os.path.isfile(f) and os.path.splitext(f)[1] == ext,
51+
candidates)
52+
return (subfolder, map(lambda f : os.path.basename(f), actual))
53+
archive_entries = map(filter_func, filters)
54+
55+
for entry in archive_entries:
56+
subfolder = entry[0]
57+
files = entry[1]
58+
for file in files:
59+
relative_path = os.path.normpath(os.path.join(subfolder, file))
60+
full_path = os.path.normpath(
61+
os.path.join(options.src_root, relative_path))
62+
logging.info("{} -> {}".format(full_path, relative_path))
63+
zip_file.write(full_path, relative_path)
64+
return zip_data.getvalue()
65+
1866
def generate(options):
1967
include_folder = os.path.join(options.src_root, "include")
2068
in_file = os.path.join(options.src_root, "scripts", "lldb.swig")
@@ -43,7 +91,8 @@ def generate(options):
4391
in_file
4492
])
4593

46-
logging.info("generating swig {} bindings into {}".format(lang, out_dir))
94+
logging.info("generating swig {} bindings into {}"
95+
.format(lang, out_dir))
4796
logging.debug("swig command line: {}".format(swig_command))
4897
try:
4998
# Execute swig
@@ -54,5 +103,6 @@ def generate(options):
54103
if swig_output is not None and len(swig_output) > 0:
55104
logging.info("swig output: %s", swig_output)
56105
except subprocess.CalledProcessError as e:
57-
logging.error("An error occurred executing swig. returncode={}".format(e.returncode))
106+
logging.error("An error occurred executing swig. returncode={}"
107+
.format(e.returncode))
58108
logging.error(e.output)

‎scripts/swig_bot_lib/server.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,80 @@
11
#!/usr/bin/env python
22

3+
"""
4+
SWIG generation server. Listens for connections from swig generation clients
5+
and runs swig in the requested fashion, sending back the results.
6+
"""
7+
38
# Future imports
49
from __future__ import absolute_import
510
from __future__ import print_function
611

12+
# Python modules
13+
import argparse
14+
import logging
15+
import os
16+
import socket
17+
import struct
18+
import sys
19+
import traceback
20+
21+
# LLDB modules
22+
import use_lldb_suite
23+
from lldbsuite.support import sockutil
24+
25+
# package imports
26+
from . import local
27+
28+
default_port = 8537
29+
30+
def process_args(args):
31+
# Setup the parser arguments that are accepted.
32+
parser = argparse.ArgumentParser(description='SWIG generation server.')
33+
34+
parser.add_argument(
35+
"--port",
36+
action="store",
37+
default=default_port,
38+
help=("The local port to bind to"))
39+
40+
# Process args.
41+
return parser.parse_args(args)
42+
43+
def initialize_listening_socket(options):
44+
logging.debug("Creating socket...")
45+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
46+
47+
logging.info("Binding to ip address '', port {}".format(options.port))
48+
s.bind(('', options.port))
49+
50+
logging.debug("Putting socket in listen mode...")
51+
s.listen()
52+
return s
53+
54+
def accept_once(sock, options):
55+
logging.debug("Waiting for connection...")
56+
client, addr = sock.accept()
57+
logging.info("Received connection from {}".format(addr))
58+
data_size = struct.unpack("!I", sockutil.recvall(client, 4))[0]
59+
logging.debug("Expecting {} bytes of data from client".format(data_size))
60+
data = sockutil.recvall(client, data_size)
61+
logging.info("Received {} bytes of data from client".format(len(data)))
62+
63+
logging.info("Sending {} byte response".format(len(data)))
64+
client.sendall(struct.pack("!I", len(data)))
65+
client.sendall(data)
66+
67+
def accept_loop(sock, options):
68+
while True:
69+
try:
70+
accept_once(sock, options)
71+
except Exception as e:
72+
error = traceback.format_exc()
73+
logging.error("An error occurred while processing the connection.")
74+
logging.error(error)
75+
76+
def run(args):
77+
options = process_args(args)
78+
sock = initialize_listening_socket(options)
79+
accept_loop(sock, options)
80+
return options

0 commit comments

Comments
 (0)
This repository has been archived.