Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python
"""
Convert the raw message sources from git patch emails to git-am friendly files.
Usage:
1. Mail.app -> Save As -> api.eml (Raw Message Source)
2. .../convert.py api.eml
3. git am [--signoff] < api.eml
4. git svn dcommit [--commit-url https://id@llvm.org/svn/llvm-project/lldb/trunk]
"""
import os
import re
import sys
import StringIO
def usage(problem_file=None):
if problem_file:
print "%s is not a file" % problem_file
print "Usage: convert.py raw-message-source [raw-message-source2 ...]"
sys.exit(0)
def do_convert(file):
"""Skip all preceding mail message headers until 'From: ' is encountered.
Then for each line ('From: ' header included), replace the dos style CRLF
end-of-line with unix style LF end-of-line.
"""
print "converting %s ..." % file
with open(file, 'r') as f_in:
content = f_in.read()
# The new content to be written back to the same file.
new_content = StringIO.StringIO()
# Boolean flag controls whether to start printing lines.
from_header_seen = False
# By default, splitlines() don't include line breaks. CRLF should be gone.
for line in content.splitlines():
# Wait till we scan the 'From: ' header before start printing the
# lines.
if not from_header_seen:
if not line.startswith('From: '):
continue
else:
from_header_seen = True
print >> new_content, line
with open(file, 'w') as f_out:
f_out.write(new_content.getvalue())
print "done"
def main():
if len(sys.argv) == 1:
usage()
# Convert the raw message source one by one.
for file in sys.argv[1:]:
if not os.path.isfile(file):
usage(file)
do_convert(file)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,36 @@
LLDB (Terminal) User Interface
------------------------------
This directory contains the curses user interface for LLDB. To use it, ensure Python can find your lldb module. You may have to modify PYTHONPATH for that purpose:
$ export PYTHONPATH=/path/to/lldb/module
Then, run the lui.py. To load a core file:
$ ./lui.py --core core
To create a target from an executable:
$ ./lui.py /bin/echo "hello world"
To attach to a running process:
$ ./lui.py --attach <pid>
Known Issues
------------
1. Resizing the terminal will most likely cause lui to crash.
2. Missing paging in command-window
3. Only minimal testing (on Ubuntu Linux x86_64)
Missing Features
----------------
- stdin/stdout/stderr windows
- memory window
- backtrace window
- threads window
- tab-completion
- syntax-highlighting (via pygments library)
- (local) variables window
- registers window
- disassembly window
- custom layout

View File

@@ -0,0 +1,94 @@
##===-- breakwin.py ------------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import cui
import curses
import lldb
import lldbutil
import re
class BreakWin(cui.ListWin):
def __init__(self, driver, x, y, w, h):
super(BreakWin, self).__init__(x, y, w, h)
self.driver = driver
self.update()
self.showDetails = {}
def handleEvent(self, event):
if isinstance(event, lldb.SBEvent):
if lldb.SBBreakpoint.EventIsBreakpointEvent(event):
self.update()
if isinstance(event, int):
if event == ord('d'):
self.deleteSelected()
if event == curses.ascii.NL or event == curses.ascii.SP:
self.toggleSelected()
elif event == curses.ascii.TAB:
if self.getSelected() != -1:
target = self.driver.getTarget()
if not target.IsValid():
return
i = target.GetBreakpointAtIndex(self.getSelected()).id
self.showDetails[i] = not self.showDetails[i]
self.update()
super(BreakWin, self).handleEvent(event)
def toggleSelected(self):
if self.getSelected() == -1:
return
target = self.driver.getTarget()
if not target.IsValid():
return
bp = target.GetBreakpointAtIndex(self.getSelected())
bp.SetEnabled(not bp.IsEnabled())
def deleteSelected(self):
if self.getSelected() == -1:
return
target = self.driver.getTarget()
if not target.IsValid():
return
bp = target.GetBreakpointAtIndex(self.getSelected())
target.BreakpointDelete(bp.id)
def update(self):
target = self.driver.getTarget()
if not target.IsValid():
self.win.erase()
self.win.noutrefresh()
return
selected = self.getSelected()
self.clearItems()
for i in range(0, target.GetNumBreakpoints()):
bp = target.GetBreakpointAtIndex(i)
if bp.IsInternal():
continue
text = lldbutil.get_description(bp)
# FIXME: Use an API for this, not parsing the description.
match = re.search('SBBreakpoint: id = ([^,]+), (.*)', text)
try:
id = match.group(1)
desc = match.group(2).strip()
if bp.IsEnabled():
text = '%s: %s' % (id, desc)
else:
text = '%s: (disabled) %s' % (id, desc)
except ValueError as e:
# bp unparsable
pass
if self.showDetails.setdefault(bp.id, False):
for location in bp:
desc = lldbutil.get_description(
location, lldb.eDescriptionLevelFull)
text += '\n ' + desc
self.addItem(text)
self.setSelected(selected)

View File

@@ -0,0 +1,131 @@
##===-- commandwin.py ----------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import cui
import curses
import lldb
from itertools import islice
class History(object):
def __init__(self):
self.data = {}
self.pos = 0
self.tempEntry = ''
def previous(self, curr):
if self.pos == len(self.data):
self.tempEntry = curr
if self.pos < 0:
return ''
if self.pos == 0:
self.pos -= 1
return ''
if self.pos > 0:
self.pos -= 1
return self.data[self.pos]
def next(self):
if self.pos < len(self.data):
self.pos += 1
if self.pos < len(self.data):
return self.data[self.pos]
elif self.tempEntry != '':
return self.tempEntry
else:
return ''
def add(self, c):
self.tempEntry = ''
self.pos = len(self.data)
if self.pos == 0 or self.data[self.pos - 1] != c:
self.data[self.pos] = c
self.pos += 1
class CommandWin(cui.TitledWin):
def __init__(self, driver, x, y, w, h):
super(CommandWin, self).__init__(x, y, w, h, "Commands")
self.command = ""
self.data = ""
driver.setSize(w, h)
self.win.scrollok(1)
self.driver = driver
self.history = History()
def enterCallback(content):
self.handleCommand(content)
def tabCompleteCallback(content):
self.data = content
matches = lldb.SBStringList()
commandinterpreter = self.getCommandInterpreter()
commandinterpreter.HandleCompletion(
self.data, self.el.index, 0, -1, matches)
if matches.GetSize() == 2:
self.el.content += matches.GetStringAtIndex(0)
self.el.index = len(self.el.content)
self.el.draw()
else:
self.win.move(self.el.starty, self.el.startx)
self.win.scroll(1)
self.win.addstr("Available Completions:")
self.win.scroll(1)
for m in islice(matches, 1, None):
self.win.addstr(self.win.getyx()[0], 0, m)
self.win.scroll(1)
self.el.draw()
self.startline = self.win.getmaxyx()[0] - 2
self.el = cui.CursesEditLine(
self.win,
self.history,
enterCallback,
tabCompleteCallback)
self.el.prompt = self.driver.getPrompt()
self.el.showPrompt(self.startline, 0)
def handleCommand(self, cmd):
# enter!
self.win.scroll(1) # TODO: scroll more for longer commands
if cmd == '':
cmd = self.history.previous('')
elif cmd in ('q', 'quit'):
self.driver.terminate()
return
self.history.add(cmd)
ret = self.driver.handleCommand(cmd)
if ret.Succeeded():
out = ret.GetOutput()
attr = curses.A_NORMAL
else:
out = ret.GetError()
attr = curses.color_pair(3) # red on black
self.win.addstr(self.startline, 0, out + '\n', attr)
self.win.scroll(1)
self.el.showPrompt(self.startline, 0)
def handleEvent(self, event):
if isinstance(event, int):
if event == curses.ascii.EOT and self.el.content == '':
# When the command is empty, treat CTRL-D as EOF.
self.driver.terminate()
return
self.el.handleEvent(event)
def getCommandInterpreter(self):
return self.driver.getCommandInterpreter()

339
external/llvm-project/lldb/utils/lui/cui.py vendored Executable file
View File

@@ -0,0 +1,339 @@
##===-- cui.py -----------------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import curses
import curses.ascii
import threading
class CursesWin(object):
def __init__(self, x, y, w, h):
self.win = curses.newwin(h, w, y, x)
self.focus = False
def setFocus(self, focus):
self.focus = focus
def getFocus(self):
return self.focus
def canFocus(self):
return True
def handleEvent(self, event):
return
def draw(self):
return
class TextWin(CursesWin):
def __init__(self, x, y, w):
super(TextWin, self).__init__(x, y, w, 1)
self.win.bkgd(curses.color_pair(1))
self.text = ''
self.reverse = False
def canFocus(self):
return False
def draw(self):
w = self.win.getmaxyx()[1]
text = self.text
if len(text) > w:
#trunc_length = len(text) - w
text = text[-w + 1:]
if self.reverse:
self.win.addstr(0, 0, text, curses.A_REVERSE)
else:
self.win.addstr(0, 0, text)
self.win.noutrefresh()
def setReverse(self, reverse):
self.reverse = reverse
def setText(self, text):
self.text = text
class TitledWin(CursesWin):
def __init__(self, x, y, w, h, title):
super(TitledWin, self).__init__(x, y + 1, w, h - 1)
self.title = title
self.title_win = TextWin(x, y, w)
self.title_win.setText(title)
self.draw()
def setTitle(self, title):
self.title_win.setText(title)
def draw(self):
self.title_win.setReverse(self.getFocus())
self.title_win.draw()
self.win.noutrefresh()
class ListWin(CursesWin):
def __init__(self, x, y, w, h):
super(ListWin, self).__init__(x, y, w, h)
self.items = []
self.selected = 0
self.first_drawn = 0
self.win.leaveok(True)
def draw(self):
if len(self.items) == 0:
self.win.erase()
return
h, w = self.win.getmaxyx()
allLines = []
firstSelected = -1
lastSelected = -1
for i, item in enumerate(self.items):
lines = self.items[i].split('\n')
lines = lines if lines[len(lines) - 1] != '' else lines[:-1]
if len(lines) == 0:
lines = ['']
if i == self.getSelected():
firstSelected = len(allLines)
allLines.extend(lines)
if i == self.selected:
lastSelected = len(allLines) - 1
if firstSelected < self.first_drawn:
self.first_drawn = firstSelected
elif lastSelected >= self.first_drawn + h:
self.first_drawn = lastSelected - h + 1
self.win.erase()
begin = self.first_drawn
end = begin + h
y = 0
for i, line in list(enumerate(allLines))[begin:end]:
attr = curses.A_NORMAL
if i >= firstSelected and i <= lastSelected:
attr = curses.A_REVERSE
line = '{0:{width}}'.format(line, width=w - 1)
# Ignore the error we get from drawing over the bottom-right char.
try:
self.win.addstr(y, 0, line[:w], attr)
except curses.error:
pass
y += 1
self.win.noutrefresh()
def getSelected(self):
if self.items:
return self.selected
return -1
def setSelected(self, selected):
self.selected = selected
if self.selected < 0:
self.selected = 0
elif self.selected >= len(self.items):
self.selected = len(self.items) - 1
def handleEvent(self, event):
if isinstance(event, int):
if len(self.items) > 0:
if event == curses.KEY_UP:
self.setSelected(self.selected - 1)
if event == curses.KEY_DOWN:
self.setSelected(self.selected + 1)
if event == curses.ascii.NL:
self.handleSelect(self.selected)
def addItem(self, item):
self.items.append(item)
def clearItems(self):
self.items = []
def handleSelect(self, index):
return
class InputHandler(threading.Thread):
def __init__(self, screen, queue):
super(InputHandler, self).__init__()
self.screen = screen
self.queue = queue
def run(self):
while True:
c = self.screen.getch()
self.queue.put(c)
class CursesUI(object):
""" Responsible for updating the console UI with curses. """
def __init__(self, screen, event_queue):
self.screen = screen
self.event_queue = event_queue
curses.start_color()
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
self.screen.bkgd(curses.color_pair(1))
self.screen.clear()
self.input_handler = InputHandler(self.screen, self.event_queue)
self.input_handler.daemon = True
self.focus = 0
self.screen.refresh()
def focusNext(self):
self.wins[self.focus].setFocus(False)
old = self.focus
while True:
self.focus += 1
if self.focus >= len(self.wins):
self.focus = 0
if self.wins[self.focus].canFocus():
break
self.wins[self.focus].setFocus(True)
def handleEvent(self, event):
if isinstance(event, int):
if event == curses.KEY_F3:
self.focusNext()
def eventLoop(self):
self.input_handler.start()
self.wins[self.focus].setFocus(True)
while True:
self.screen.noutrefresh()
for i, win in enumerate(self.wins):
if i != self.focus:
win.draw()
# Draw the focused window last so that the cursor shows up.
if self.wins:
self.wins[self.focus].draw()
curses.doupdate() # redraw the physical screen
event = self.event_queue.get()
for win in self.wins:
if isinstance(event, int):
if win.getFocus() or not win.canFocus():
win.handleEvent(event)
else:
win.handleEvent(event)
self.handleEvent(event)
class CursesEditLine(object):
""" Embed an 'editline'-compatible prompt inside a CursesWin. """
def __init__(self, win, history, enterCallback, tabCompleteCallback):
self.win = win
self.history = history
self.enterCallback = enterCallback
self.tabCompleteCallback = tabCompleteCallback
self.prompt = ''
self.content = ''
self.index = 0
self.startx = -1
self.starty = -1
def draw(self, prompt=None):
if not prompt:
prompt = self.prompt
(h, w) = self.win.getmaxyx()
if (len(prompt) + len(self.content)) / w + self.starty >= h - 1:
self.win.scroll(1)
self.starty -= 1
if self.starty < 0:
raise RuntimeError('Input too long; aborting')
(y, x) = (self.starty, self.startx)
self.win.move(y, x)
self.win.clrtobot()
self.win.addstr(y, x, prompt)
remain = self.content
self.win.addstr(remain[:w - len(prompt)])
remain = remain[w - len(prompt):]
while remain != '':
y += 1
self.win.addstr(y, 0, remain[:w])
remain = remain[w:]
length = self.index + len(prompt)
self.win.move(self.starty + length / w, length % w)
def showPrompt(self, y, x, prompt=None):
self.content = ''
self.index = 0
self.startx = x
self.starty = y
self.draw(prompt)
def handleEvent(self, event):
if not isinstance(event, int):
return # not handled
key = event
if self.startx == -1:
raise RuntimeError('Trying to handle input without prompt')
if key == curses.ascii.NL:
self.enterCallback(self.content)
elif key == curses.ascii.TAB:
self.tabCompleteCallback(self.content)
elif curses.ascii.isprint(key):
self.content = self.content[:self.index] + \
chr(key) + self.content[self.index:]
self.index += 1
elif key == curses.KEY_BACKSPACE or key == curses.ascii.BS:
if self.index > 0:
self.index -= 1
self.content = self.content[
:self.index] + self.content[self.index + 1:]
elif key == curses.KEY_DC or key == curses.ascii.DEL or key == curses.ascii.EOT:
self.content = self.content[
:self.index] + self.content[self.index + 1:]
elif key == curses.ascii.VT: # CTRL-K
self.content = self.content[:self.index]
elif key == curses.KEY_LEFT or key == curses.ascii.STX: # left or CTRL-B
if self.index > 0:
self.index -= 1
elif key == curses.KEY_RIGHT or key == curses.ascii.ACK: # right or CTRL-F
if self.index < len(self.content):
self.index += 1
elif key == curses.ascii.SOH: # CTRL-A
self.index = 0
elif key == curses.ascii.ENQ: # CTRL-E
self.index = len(self.content)
elif key == curses.KEY_UP or key == curses.ascii.DLE: # up or CTRL-P
self.content = self.history.previous(self.content)
self.index = len(self.content)
elif key == curses.KEY_DOWN or key == curses.ascii.SO: # down or CTRL-N
self.content = self.history.next()
self.index = len(self.content)
self.draw()

View File

@@ -0,0 +1,143 @@
##===-- debuggerdriver.py ------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import lldb
import lldbutil
import sys
from threading import Thread
class DebuggerDriver(Thread):
""" Drives the debugger and responds to events. """
def __init__(self, debugger, event_queue):
Thread.__init__(self)
self.event_queue = event_queue
# This is probably not great because it does not give liblldb a chance
# to clean up
self.daemon = True
self.initialize(debugger)
def initialize(self, debugger):
self.done = False
self.debugger = debugger
self.listener = debugger.GetListener()
if not self.listener.IsValid():
raise "Invalid listener"
self.listener.StartListeningForEventClass(self.debugger,
lldb.SBTarget.GetBroadcasterClassName(),
lldb.SBTarget.eBroadcastBitBreakpointChanged
#| lldb.SBTarget.eBroadcastBitModuleLoaded
#| lldb.SBTarget.eBroadcastBitModuleUnloaded
| lldb.SBTarget.eBroadcastBitWatchpointChanged
#| lldb.SBTarget.eBroadcastBitSymbolLoaded
)
self.listener.StartListeningForEventClass(self.debugger,
lldb.SBThread.GetBroadcasterClassName(),
lldb.SBThread.eBroadcastBitStackChanged
# lldb.SBThread.eBroadcastBitBreakpointChanged
| lldb.SBThread.eBroadcastBitThreadSuspended
| lldb.SBThread.eBroadcastBitThreadResumed
| lldb.SBThread.eBroadcastBitSelectedFrameChanged
| lldb.SBThread.eBroadcastBitThreadSelected
)
self.listener.StartListeningForEventClass(self.debugger,
lldb.SBProcess.GetBroadcasterClassName(),
lldb.SBProcess.eBroadcastBitStateChanged
| lldb.SBProcess.eBroadcastBitInterrupt
| lldb.SBProcess.eBroadcastBitSTDOUT
| lldb.SBProcess.eBroadcastBitSTDERR
| lldb.SBProcess.eBroadcastBitProfileData
)
self.listener.StartListeningForEventClass(self.debugger,
lldb.SBCommandInterpreter.GetBroadcasterClass(),
lldb.SBCommandInterpreter.eBroadcastBitThreadShouldExit
| lldb.SBCommandInterpreter.eBroadcastBitResetPrompt
| lldb.SBCommandInterpreter.eBroadcastBitQuitCommandReceived
| lldb.SBCommandInterpreter.eBroadcastBitAsynchronousOutputData
| lldb.SBCommandInterpreter.eBroadcastBitAsynchronousErrorData
)
def createTarget(self, target_image, args=None):
self.handleCommand("target create %s" % target_image)
if args is not None:
self.handleCommand("settings set target.run-args %s" % args)
def attachProcess(self, pid):
self.handleCommand("process attach -p %d" % pid)
pass
def loadCore(self, corefile):
self.handleCommand("target create -c %s" % corefile)
pass
def setDone(self):
self.done = True
def isDone(self):
return self.done
def getPrompt(self):
return self.debugger.GetPrompt()
def getCommandInterpreter(self):
return self.debugger.GetCommandInterpreter()
def getSourceManager(self):
return self.debugger.GetSourceManager()
def setSize(self, width, height):
# FIXME: respect height
self.debugger.SetTerminalWidth(width)
def getTarget(self):
return self.debugger.GetTargetAtIndex(0)
def handleCommand(self, cmd):
ret = lldb.SBCommandReturnObject()
self.getCommandInterpreter().HandleCommand(cmd, ret)
return ret
def eventLoop(self):
while not self.isDone():
event = lldb.SBEvent()
got_event = self.listener.WaitForEvent(lldb.UINT32_MAX, event)
if got_event and not event.IsValid():
self.winAddStr("Warning: Invalid or no event...")
continue
elif not event.GetBroadcaster().IsValid():
continue
self.event_queue.put(event)
def run(self):
self.eventLoop()
def terminate(self):
lldb.SBDebugger.Terminate()
sys.exit(0)
def createDriver(debugger, event_queue):
driver = DebuggerDriver(debugger, event_queue)
# driver.start()
# if pid specified:
# - attach to pid
# else if core file specified
# - create target from corefile
# else
# - create target from file
# - settings append target.run-args <args-from-cmdline>
# source .lldbinit file
return driver

View File

@@ -0,0 +1,27 @@
##===-- eventwin.py ------------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import cui
import lldb
import lldbutil
class EventWin(cui.TitledWin):
def __init__(self, x, y, w, h):
super(EventWin, self).__init__(x, y, w, h, 'LLDB Event Log')
self.win.scrollok(1)
super(EventWin, self).draw()
def handleEvent(self, event):
if isinstance(event, lldb.SBEvent):
self.win.scroll()
h = self.win.getmaxyx()[0]
self.win.addstr(h - 1, 0, lldbutil.get_description(event))
return

File diff suppressed because it is too large Load Diff

150
external/llvm-project/lldb/utils/lui/lui.py vendored Executable file
View File

@@ -0,0 +1,150 @@
#!/usr/bin/env python
##===-- lui.py -----------------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import curses
import lldb
import lldbutil
from optparse import OptionParser
import os
import signal
import sys
import Queue
import debuggerdriver
import cui
import breakwin
import commandwin
import eventwin
import sourcewin
import statuswin
event_queue = None
def handle_args(driver, argv):
parser = OptionParser()
parser.add_option(
"-p",
"--attach",
dest="pid",
help="Attach to specified Process ID",
type="int")
parser.add_option(
"-c",
"--core",
dest="core",
help="Load specified core file",
type="string")
(options, args) = parser.parse_args(argv)
if options.pid is not None:
try:
pid = int(options.pid)
driver.attachProcess(ui, pid)
except ValueError:
print "Error: expecting integer PID, got '%s'" % options.pid
elif options.core is not None:
if not os.path.exists(options.core):
raise Exception(
"Specified core file '%s' does not exist." %
options.core)
driver.loadCore(options.core)
elif len(args) == 2:
if not os.path.isfile(args[1]):
raise Exception("Specified target '%s' does not exist" % args[1])
driver.createTarget(args[1])
elif len(args) > 2:
if not os.path.isfile(args[1]):
raise Exception("Specified target '%s' does not exist" % args[1])
driver.createTarget(args[1], args[2:])
def sigint_handler(signal, frame):
global debugger
debugger.terminate()
class LLDBUI(cui.CursesUI):
def __init__(self, screen, event_queue, driver):
super(LLDBUI, self).__init__(screen, event_queue)
self.driver = driver
h, w = self.screen.getmaxyx()
command_win_height = 20
break_win_width = 60
self.status_win = statuswin.StatusWin(0, h - 1, w, 1)
h -= 1
self.command_win = commandwin.CommandWin(
driver, 0, h - command_win_height, w, command_win_height)
h -= command_win_height
self.source_win = sourcewin.SourceWin(driver, 0, 0,
w - break_win_width - 1, h)
self.break_win = breakwin.BreakWin(driver, w - break_win_width, 0,
break_win_width, h)
self.wins = [self.status_win,
# self.event_win,
self.source_win,
self.break_win,
self.command_win,
]
self.focus = len(self.wins) - 1 # index of command window;
def handleEvent(self, event):
# hack
if isinstance(event, int):
if event == curses.KEY_F10:
self.driver.terminate()
if event == 20: # ctrl-T
def foo(cmd):
ret = lldb.SBCommandReturnObject()
self.driver.getCommandInterpreter().HandleCommand(cmd, ret)
foo('target create a.out')
foo('b main')
foo('run')
super(LLDBUI, self).handleEvent(event)
def main(screen):
signal.signal(signal.SIGINT, sigint_handler)
global event_queue
event_queue = Queue.Queue()
global debugger
debugger = lldb.SBDebugger.Create()
driver = debuggerdriver.createDriver(debugger, event_queue)
view = LLDBUI(screen, event_queue, driver)
driver.start()
# hack to avoid hanging waiting for prompts!
driver.handleCommand("settings set auto-confirm true")
handle_args(driver, sys.argv)
view.eventLoop()
if __name__ == "__main__":
try:
curses.wrapper(main)
except KeyboardInterrupt:
exit()

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
##===-- sandbox.py -------------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import curses
import os
import signal
import sys
import Queue
import cui
event_queue = None
class SandboxUI(cui.CursesUI):
def __init__(self, screen, event_queue):
super(SandboxUI, self).__init__(screen, event_queue)
height, width = self.screen.getmaxyx()
w2 = width / 2
h2 = height / 2
self.wins = []
#self.wins.append(cui.TitledWin(w2, h2, w2, h2, "Test Window 4"))
list_win = cui.ListWin(w2, h2, w2, h2)
for i in range(0, 40):
list_win.addItem('Item %s' % i)
self.wins.append(list_win)
self.wins.append(cui.TitledWin(0, 0, w2, h2, "Test Window 1"))
self.wins.append(cui.TitledWin(w2, 0, w2, h2, "Test Window 2"))
self.wins.append(cui.TitledWin(0, h2, w2, h2, "Test Window 3"))
# def callback(s, content):
# self.wins[0].win.scroll(1)
# self.wins[0].win.addstr(10, 0, '%s: %s' % (s, content))
# self.wins[0].win.scroll(1)
# self.el.showPrompt(10, 0)
# self.wins[0].win.scrollok(1)
# self.el = cui.CursesEditLine(self.wins[0].win, None,
# lambda c: callback('got', c), lambda c: callback('tab', c))
#self.el.prompt = '>>> '
#self.el.showPrompt(10, 0)
def handleEvent(self, event):
if isinstance(event, int):
if event == ord('q'):
sys.exit(0)
# self.el.handleEvent(event)
super(SandboxUI, self).handleEvent(event)
def main(screen):
global event_queue
event_queue = Queue.Queue()
sandbox = SandboxUI(screen, event_queue)
sandbox.eventLoop()
if __name__ == "__main__":
try:
curses.wrapper(main)
except KeyboardInterrupt:
exit()

View File

@@ -0,0 +1,239 @@
##===-- sourcewin.py -----------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import cui
import curses
import lldb
import lldbutil
import re
import os
class SourceWin(cui.TitledWin):
def __init__(self, driver, x, y, w, h):
super(SourceWin, self).__init__(x, y, w, h, "Source")
self.sourceman = driver.getSourceManager()
self.sources = {}
self.filename = None
self.pc_line = None
self.viewline = 0
self.breakpoints = {}
self.win.scrollok(1)
self.markerPC = ":) "
self.markerBP = "B> "
self.markerNone = " "
try:
from pygments.formatters import TerminalFormatter
self.formatter = TerminalFormatter()
except ImportError:
#self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.")
self.lexer = None
self.formatter = None
pass
# FIXME: syntax highlight broken
self.formatter = None
self.lexer = None
def handleEvent(self, event):
if isinstance(event, int):
self.handleKey(event)
return
if isinstance(event, lldb.SBEvent):
if lldb.SBBreakpoint.EventIsBreakpointEvent(event):
self.handleBPEvent(event)
if lldb.SBProcess.EventIsProcessEvent(event) and \
not lldb.SBProcess.GetRestartedFromEvent(event):
process = lldb.SBProcess.GetProcessFromEvent(event)
if not process.IsValid():
return
if process.GetState() == lldb.eStateStopped:
self.refreshSource(process)
elif process.GetState() == lldb.eStateExited:
self.notifyExited(process)
def notifyExited(self, process):
self.win.erase()
target = lldbutil.get_description(process.GetTarget())
pid = process.GetProcessID()
ec = process.GetExitStatus()
self.win.addstr(
"\nProcess %s [%d] has exited with exit-code %d" %
(target, pid, ec))
def pageUp(self):
if self.viewline > 0:
self.viewline = self.viewline - 1
self.refreshSource()
def pageDown(self):
if self.viewline < len(self.content) - self.height + 1:
self.viewline = self.viewline + 1
self.refreshSource()
pass
def handleKey(self, key):
if key == curses.KEY_DOWN:
self.pageDown()
elif key == curses.KEY_UP:
self.pageUp()
def updateViewline(self):
half = self.height / 2
if self.pc_line < half:
self.viewline = 0
else:
self.viewline = self.pc_line - half + 1
if self.viewline < 0:
raise Exception(
"negative viewline: pc=%d viewline=%d" %
(self.pc_line, self.viewline))
def refreshSource(self, process=None):
(self.height, self.width) = self.win.getmaxyx()
if process is not None:
loc = process.GetSelectedThread().GetSelectedFrame().GetLineEntry()
f = loc.GetFileSpec()
self.pc_line = loc.GetLine()
if not f.IsValid():
self.win.addstr(0, 0, "Invalid source file")
return
self.filename = f.GetFilename()
path = os.path.join(f.GetDirectory(), self.filename)
self.setTitle(path)
self.content = self.getContent(path)
self.updateViewline()
if self.filename is None:
return
if self.formatter is not None:
from pygments.lexers import get_lexer_for_filename
self.lexer = get_lexer_for_filename(self.filename)
bps = [] if not self.filename in self.breakpoints else self.breakpoints[self.filename]
self.win.erase()
if self.content:
self.formatContent(self.content, self.pc_line, bps)
def getContent(self, path):
content = []
if path in self.sources:
content = self.sources[path]
else:
if os.path.exists(path):
with open(path) as x:
content = x.readlines()
self.sources[path] = content
return content
def formatContent(self, content, pc_line, breakpoints):
source = ""
count = 1
self.win.erase()
end = min(len(content), self.viewline + self.height)
for i in range(self.viewline, end):
line_num = i + 1
marker = self.markerNone
attr = curses.A_NORMAL
if line_num == pc_line:
attr = curses.A_REVERSE
if line_num in breakpoints:
marker = self.markerBP
line = "%s%3d %s" % (marker, line_num, self.highlight(content[i]))
if len(line) >= self.width:
line = line[0:self.width - 1] + "\n"
self.win.addstr(line, attr)
source += line
count = count + 1
return source
def highlight(self, source):
if self.lexer and self.formatter:
from pygments import highlight
return highlight(source, self.lexer, self.formatter)
else:
return source
def addBPLocations(self, locations):
for path in locations:
lines = locations[path]
if path in self.breakpoints:
self.breakpoints[path].update(lines)
else:
self.breakpoints[path] = lines
def removeBPLocations(self, locations):
for path in locations:
lines = locations[path]
if path in self.breakpoints:
self.breakpoints[path].difference_update(lines)
else:
raise "Removing locations that were never added...no good"
def handleBPEvent(self, event):
def getLocations(event):
locs = {}
bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
if bp.IsInternal():
# don't show anything for internal breakpoints
return
for location in bp:
# hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for
# inlined frames, so we get the description (which does take
# into account inlined functions) and parse it.
desc = lldbutil.get_description(
location, lldb.eDescriptionLevelFull)
match = re.search('at\ ([^:]+):([\d]+)', desc)
try:
path = match.group(1)
line = int(match.group(2).strip())
except ValueError as e:
# bp loc unparsable
continue
if path in locs:
locs[path].add(line)
else:
locs[path] = set([line])
return locs
event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event)
if event_type == lldb.eBreakpointEventTypeEnabled \
or event_type == lldb.eBreakpointEventTypeAdded \
or event_type == lldb.eBreakpointEventTypeLocationsResolved \
or event_type == lldb.eBreakpointEventTypeLocationsAdded:
self.addBPLocations(getLocations(event))
elif event_type == lldb.eBreakpointEventTypeRemoved \
or event_type == lldb.eBreakpointEventTypeLocationsRemoved \
or event_type == lldb.eBreakpointEventTypeDisabled:
self.removeBPLocations(getLocations(event))
elif event_type == lldb.eBreakpointEventTypeCommandChanged \
or event_type == lldb.eBreakpointEventTypeConditionChanged \
or event_type == lldb.eBreakpointEventTypeIgnoreChanged \
or event_type == lldb.eBreakpointEventTypeThreadChanged \
or event_type == lldb.eBreakpointEventTypeInvalidType:
# no-op
pass
self.refreshSource()

View File

@@ -0,0 +1,42 @@
##===-- statuswin.py -----------------------------------------*- Python -*-===##
##
# The LLVM Compiler Infrastructure
##
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
##
##===----------------------------------------------------------------------===##
import lldb
import lldbutil
import cui
import curses
class StatusWin(cui.TextWin):
def __init__(self, x, y, w, h):
super(StatusWin, self).__init__(x, y, w)
self.keys = [ # ('F1', 'Help', curses.KEY_F1),
('F3', 'Cycle-focus', curses.KEY_F3),
('F10', 'Quit', curses.KEY_F10)]
def draw(self):
self.win.addstr(0, 0, '')
for key in self.keys:
self.win.addstr('{0}'.format(key[0]), curses.A_REVERSE)
self.win.addstr(' {0} '.format(key[1]), curses.A_NORMAL)
super(StatusWin, self).draw()
def handleEvent(self, event):
if isinstance(event, int):
pass
elif isinstance(event, lldb.SBEvent):
if lldb.SBProcess.EventIsProcessEvent(event):
state = lldb.SBProcess.GetStateFromEvent(event)
status = lldbutil.state_type_to_str(state)
self.win.erase()
x = self.win.getmaxyx()[1] - len(status) - 1
self.win.addstr(0, x, status)
return

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python
"""
Greps and returns the first svn log entry containing a line matching the regular
expression pattern passed as the only arg.
Example:
svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h$'
"""
import fileinput
import re
import sys
import StringIO
# Separator string for "svn log -v" output.
separator = '-' * 72
usage = """Usage: grep-svn-log.py line-pattern
Example:
svn log -v | grep-svn-log.py '^ D.+why_are_you_missing.h'"""
class Log(StringIO.StringIO):
"""Simple facade to keep track of the log content."""
def __init__(self):
self.reset()
def add_line(self, a_line):
"""Add a line to the content, if there is a previous line, commit it."""
global separator
if self.prev_line is not None:
print >> self, self.prev_line
self.prev_line = a_line
self.separator_added = (a_line == separator)
def del_line(self):
"""Forget about the previous line, do not commit it."""
self.prev_line = None
def reset(self):
"""Forget about the previous lines entered."""
StringIO.StringIO.__init__(self)
self.prev_line = None
def finish(self):
"""Call this when you're finished with populating content."""
if self.prev_line is not None:
print >> self, self.prev_line
self.prev_line = None
def grep(regexp):
# The log content to be written out once a match is found.
log = Log()
LOOKING_FOR_MATCH = 0
FOUND_LINE_MATCH = 1
state = LOOKING_FOR_MATCH
while True:
line = sys.stdin.readline()
if not line:
return
line = line.splitlines()[0]
if state == FOUND_LINE_MATCH:
# At this state, we keep on accumulating lines until the separator
# is encountered. At which point, we can return the log content.
if line == separator:
log.finish()
print log.getvalue()
return
log.add_line(line)
elif state == LOOKING_FOR_MATCH:
if line == separator:
log.reset()
log.add_line(line)
# Update next state if necessary.
if regexp.search(line):
state = FOUND_LINE_MATCH
def main():
if len(sys.argv) != 2:
print usage
sys.exit(0)
regexp = re.compile(sys.argv[1])
grep(regexp)
sys.stdin.close()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,293 @@
syncsource.py
OVERVIEW
The syncsource.py utility transfers groups of files between
computers. The primary use case is to enable developing LLVM project
software on one machine, transfer it efficiently to other machines ---
possibly of other architectures --- and test it there. syncsource.py
supports configurable, named source-to-destination mappings and has a
transfer agent plug-in architecture. The current distribution provides
an rsync-over-ssh transfer agent.
The primary benefits of using syncsource.py are:
* Provides a simple, reliable way to get a mirror copy of primary-
machine files onto several different destinations without concern
of compromising the patch during testing on different machines.
* Handles directory-mapping differences between two machines. For
LLDB, this is helpful when going between OS X and any other non-OS X
target system.
EXAMPLE WORKFLOW
This utility was developed in the context of working on the LLDB
project. Below we show the transfers we'd like to have happen,
and the configuration that supports it.
Workflow Example:
* Develop on OS X (primary machine)
* Test candidate changes on OS X.
* Test candidate changes on a Linux machine (machine-name: lldb-linux).
* Test candidate changes on a FreeBSD machine (machine-name: lldb-freebsd).
* Do check-ins from OS X machine.
Requirements:
* OS X machine requires the lldb source layout: lldb, lldb/llvm,
lldb/llvm/tools/clang. Note this is different than the canonical
llvm, llvm/tools/clang, llvm/tools/lldb layout that we'll want on
the Linux and FreeBSD test machines.
* Linux machine requires the llvm, llvm/tools/clang and
llvm/tools/lldb layout.
* FreeBSD machine requires the same layout as the llvm machine.
syncsource.py configuration in ~/.syncsourcerc:
# This is my configuration with a comment. Configuration
# files are JSON-based.
{ "configurations": [
# Here we have a collection of named configuration blocks.
# Configuration blocks can chain back to a parent by name.
{
# Every block has a name so it can be referenced from
# the command line or chained back to by a child block
# for sharing.
"name": "base_tot_settings",
# This directive lists the "directory ids" that we'll care
# about. If your local repository has additional directories
# for other projects that need to ride along, add them here.
# For defaulting purposes, it makes sense to name the
# directory IDs as the most likely name for the directory
# itself. For stock LLDB from top of tree, we generally only
# care about lldb, llvm and clang.
"dir_names": [ "llvm", "clang", "lldb" ],
# This section describes where the source files live on
# the primary machine. There should always be a base_dir
# entry, which indicates where in the local filesystem the
# projects are rooted. For each dir in dir_names, there
# should be either:
# 1. an entry named {dir-id}_dir (e.g. llvm_dir), which
# specifies the source directory for the given dir id
# relative to the base_dir entry, OR
# 2. no entry, in which case the directory is assumed to
# be the same as {dir-id}. In the example below, the
# base_dir-relative directory for the "lldb" dir-id is
# defaulted to being "lldb". That's exactly what
# we need in an OS X-style lldb dir layout.
"source": {
"base_dir": "~/work/lldb-tot",
"llvm_dir": "lldb/llvm",
"clang_dir": "lldb/llvm/tools/clang"
},
# source_excludes covers any exclusions that:
# * should be applied when copying files from the source
# * should be excluded from deletion on the destination
#
# By default, ".git", ".svn" and ".pyc" are added to
# all dir-id exclusions. The default excludes can be
# controlled by the syncsource.py --default-excludes
# option.
#
# Below, I have transfer of the lldb dir skip everything
# rooted at "/llvm" below the the lldb dir. This is
# because we want the source OS X lldb to move to
# a destination of {some-dest-root}/llvm/tools/lldb, and
# not have the OS-X-inverted llvm copy over with the lldb
# transfer portion. We'll see the complete picture of
# how this works when we get to specifying destinations
# later on in the config.
#
# We also exclude the "/build" and "/llvm-build" dir rooted in
# the OS X-side sources. The Xcode configuration on this
# OS X machine will dump lldb builds in the /build directory
# relative to the lldb dir, and it will build llvm+clang in
# the /llvm-build dir relative to the lldb dir.
#
# Note the first forward slash in "/build" indicates to the
# transfer agent that we only want to exclude the
# ~/work/lldb-tot/lldb/build dir, not just any file or
# directory named "build" somewhere underneath the lldb
# directory. Without the leading forward slash, any file
# or directory called build anywhere underneath the lldb dir
# will be excluded, which is definitely not what we want here.
#
# For the llvm dir, we do a source-side exclude for
# "/tools/clang". We manage the clang transfer as a separate
# entity, so we don't want the movement of llvm to also move
# clang.
#
# The llvm_dir exclusion of "/tools/lldb" is the first example
# of an exclude targeting a requirement of the destination
# side. Normally the transfer agent will delete anything on
# the destination that is not present on the source. It is
# trying to mirror, and ensure both sides have the same
# content. The source side of llvm on OS X does not have a
# "/tools/lldb", so at first this exclude looks non-sensical.
# But on the canonical destination layout, lldb lives in
# {some-dest-root}/llvm/tools/lldb. Without this exclude,
# the transfer agent would blow away the tools/lldb directory
# on the destination every time we transfer, and then have to
# copy the lldb dir all over again. For rsync+ssh, that
# totally would defeat the huge transfer efficiencies gained
# by using rsync in the first place.
#
# Note the overloading of both source and dest style excludes
# ultimately comes from the rsync-style exclude mechanism.
# If it wasn't for that, I would have separated source and
# dest excludes out better.
"source_excludes": {
"lldb_dir": ["/llvm", "/build", "/llvm-build"],
"llvm_dir": ["/tools/lldb", "/tools/clang"]
}
},
# Top of tree public, common settings for all destinations.
{
# The name for this config block.
"name": "common_tot",
# Here is our first chaining back to a parent config block.
# Any settings in "common_tot" not specified here are going
# to be retrieved from the parent.
"parent": "base_tot_settings",
# The transfer agent class to use. Right now, the only one
# available is this one here that uses rsync over ssh.
# If some other mechanism is needed to reach this destination,
# it can be specified here in full [[package.]module.]class form.
"transfer_class": "transfer.rsync.RsyncOverSsh",
# Specifies the destination-root-relative directories.
# Here our desination is rooted at:
# {some-yet-to-be-specified-destination-root} + "base_dir".
# In other words, each destination will have some kind of root
# for all relative file placement. We'll see those defined
# later, as they can change per destination machine.
# The block below describes the settings relative to that
# destination root.
#
# As before, each dir-id used in this configuration is
# expected to have either:
# 1. an entry named {dir-id}_dir (e.g. llvm_dir), which
# specifies the destination directory for the given dir id
# relative to the dest_root+base_dir entries, OR
# 2. no entry, in which case the directory is assumed to
# be the same as {dir-id}. In the example below, the
# dest_root+base_dir-relative directory for the "llvm" dir-id is
# defaulted to being "llvm". That's exactly what
# we need in a canonical llvm/clang/lldb setup on
# Linux/FreeBSD.
#
# Note we see the untangling of the OS X lldb-centric
# directory structure to the canonical llvm,
# llvm/tools/clang, llvm/tools/lldb structure below.
# We are mapping lldb into a subdirectory of the llvm
# directory.
#
# The transfer logic figures out which directories to copy
# first by finding the shortest destination absolute path
# and doing them in that order. For our case, this ensures
# llvm is copied over before lldb or clang.
"dest": {
"base_dir": "work/mirror/git",
"lldb_dir": "llvm/tools/lldb",
"clang_dir": "llvm/tools/clang"
}
},
# Describe the lldb-linux destination. With this,
# we're done with the mapping for transfer setup
# for the lldb-linux box. This configuration can
# be used either by:
# 1. having a parent "default" blockthat points to this one,
# which then gets used by default, or
# 2. using the --configuration/-c CONFIG option to
# specify using this name on the syncsource.py command line.
{
"name": "lldb-linux"
"parent": "common_tot",
# The ssh block is understood by the rsync+ssh transfer
# agent. Other agents would probably require different
# agent-specific details that they could read from
# other blocks.
"ssh": {
# This specifies the host name (or IP address) as would
# be used as the target for an ssh command.
"dest_host": "lldb-linux.example.com",
# root_dir specifies the global root directory for
# this destination. All destinations on this target
# will be in a directory that is built from
# root_dir + base_dir + {dir_id}_dir.
"root_dir" : "/home/tfiala",
# The ssh user is specified here.
"user": "tfiala",
# The ssh port is specified here.
"port": 22
}
},
# Describe the lldb-freebsd destination.
# Very similar to the lldb-linux one.
{
"name": "lldb-freebsd"
"parent": "common_tot",
"ssh": {
"dest_host": "lldb-freebsd.example.com",
# Specify a different destination-specific root dir here.
"root_dir" : "/mnt/ssd02/fialato",
"user": "fialato",
# The ssh port is specified here.
"port": 2022
}
},
# If a block named "default" exists, and if no configuration
# is specified on the command line, then the default block
# will be used. Use this block to point to the most common
# transfer destination you would use.
{
"name": "default",
"parent": "lldb-linux"
}
]
}
Using it
Now that we have a .syncsourcerc file set up, we can do a transfer.
The .syncsourcerc file will be searched for as follows, using the
first one that is found:
* First check the --rc-file RCFILE option. If this is specified
and doesn't exist, it will raise an error and quit.
* Check if the current directory has a .syncsourcerc file. If so,
use that.
* Use the .syncsourcerc file from the user's home directory.
Run the command:
python /path/to/syncsource.rc -c {configuration-name}
The -c {configuration-name} can be left off, in which case a
configuration with the name 'default' will be used.
After that, the transfer will occur. With the rsync-over-ssh
transfer agent, one rsync per dir-id will be used. rsync output
is redirected to the console.
FEEDBACK
Feel free to pass feedback along to Todd Fiala (todd.fiala@gmail.com).

View File

@@ -0,0 +1,8 @@
class Protocol(object):
def __init__(self, options, config):
self.options = options
self.config = config
def transfer(transfer_specs, dry_run):
raise "transfer must be overridden by transfer implementation"

View File

@@ -0,0 +1,61 @@
import os.path
import pprint
import subprocess
import sys
import transfer.protocol
class RsyncOverSsh(transfer.protocol.Protocol):
def __init__(self, options, config):
super(RsyncOverSsh, self).__init__(options, config)
self.ssh_config = config.get_value("ssh")
def build_rsync_command(self, transfer_spec, dry_run):
dest_path = os.path.join(
self.ssh_config["root_dir"],
transfer_spec.dest_path)
flags = "-avz"
if dry_run:
flags += "n"
cmd = [
"rsync",
flags,
"-e",
"ssh -p {}".format(self.ssh_config["port"]),
"--rsync-path",
# The following command needs to know the right way to do
# this on the dest platform - ensures the target dir exists.
"mkdir -p {} && rsync".format(dest_path)
]
# Add source dir exclusions
if transfer_spec.exclude_paths:
for exclude_path in transfer_spec.exclude_paths:
cmd.append("--exclude")
cmd.append(exclude_path)
cmd.extend([
"--delete",
transfer_spec.source_path + "/",
"{}@{}:{}".format(
self.ssh_config["user"],
self.ssh_config["dest_host"],
dest_path)])
return cmd
def transfer(self, transfer_specs, dry_run):
if self.options.verbose:
printer = pprint.PrettyPrinter()
for spec in transfer_specs:
printer.pprint(spec)
for spec in transfer_specs:
cmd = self.build_rsync_command(spec, dry_run)
if self.options.verbose:
print "executing the following command:\n{}".format(cmd)
result = subprocess.call(
cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)
if result != 0:
return result

View File

@@ -0,0 +1,12 @@
class TransferSpec(object):
def __init__(self, source_path, exclude_paths, dest_path):
self.source_path = source_path
self.exclude_paths = exclude_paths
self.dest_path = dest_path
def __repr__(self):
fmt = (
"TransferSpec(source_path='{}', exclude_paths='{}', "
"dest_path='{}')")
return fmt.format(self.source_path, self.exclude_paths, self.dest_path)

View File

@@ -0,0 +1,2 @@
[Master]
init-hook='import os; import sys; sys.path.append(os.path.join(os.getcwd(), "lib")); print("hello from {}".format(os.getcwd()))'

View File

@@ -0,0 +1,271 @@
#!/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.
Sync lldb and related source from a local machine to a remote machine.
This facilitates working on the lldb sourcecode on multiple machines
and multiple OS types, verifying changes across all.
"""
import argparse
import cStringIO
import importlib
import json
import os.path
import re
import sys
# Add the local lib directory to the python path.
LOCAL_LIB_PATH = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"lib")
sys.path.append(LOCAL_LIB_PATH)
import transfer.transfer_spec
DOTRC_BASE_FILENAME = ".syncsourcerc"
class Configuration(object):
"""Provides chaining configuration lookup."""
def __init__(self, rcdata_configs):
self.__rcdata_configs = rcdata_configs
def get_value(self, key):
"""
Return the first value in the parent chain that has the key.
The traversal starts from the most derived configuration (i.e.
child) and works all the way up the parent chain.
@return the value of the first key in the parent chain that
contains a value for the given key.
"""
for config in self.__rcdata_configs:
if key in config:
return config[key]
return None
def __getitem__(self, key):
value = self.get_value(key)
if value:
return value
else:
raise KeyError(key)
def parse_args():
"""@return options parsed from the command line."""
parser = argparse.ArgumentParser()
parser.add_argument(
"--config-name", "-c", action="store", default="default",
help="specify configuration name to use")
parser.add_argument(
"--default-excludes", action="store", default="*.git,*.svn,*.pyc",
help=("comma-separated list of default file patterns to exclude "
"from each source directory and to protect from deletion "
"on each destination directory; if starting with forward "
"slash, it only matches at the top of the base directory"))
parser.add_argument(
"--dry-run", "-n", action="store_true",
help="do a dry run of the transfer operation, don't really transfer")
parser.add_argument(
"--rc-file", "-r", action="store",
help="specify the sync-source rc file to use for configurations")
parser.add_argument(
"--verbose", "-v", action="store_true", help="turn on verbose output")
return parser.parse_args()
def read_rcfile(filename):
"""Returns the json-parsed contents of the input file."""
# First parse file contents, removing all comments but
# preserving the line count.
regex = re.compile(r"#.*$")
comment_stripped_file = cStringIO.StringIO()
with open(filename, "r") as json_file:
for line in json_file:
comment_stripped_file.write(regex.sub("", line))
return json.load(cStringIO.StringIO(comment_stripped_file.getvalue()))
def find_appropriate_rcfile(options):
# Use an options-specified rcfile if specified.
if options.rc_file and len(options.rc_file) > 0:
if not os.path.isfile(options.rc_file):
# If it doesn't exist, error out here.
raise "rcfile '{}' specified but doesn't exist".format(
options.rc_file)
return options.rc_file
# Check if current directory .sync-sourcerc exists. If so, use it.
local_rc_filename = os.path.abspath(DOTRC_BASE_FILENAME)
if os.path.isfile(local_rc_filename):
return local_rc_filename
# Check if home directory .sync-sourcerc exists. If so, use it.
homedir_rc_filename = os.path.abspath(
os.path.join(os.path.expanduser("~"), DOTRC_BASE_FILENAME))
if os.path.isfile(homedir_rc_filename):
return homedir_rc_filename
# Nothing matched. We don't have an rc filename candidate.
return None
def get_configuration(options, rcdata, config_name):
rcdata_configs = []
next_config_name = config_name
while next_config_name:
# Find the next rcdata configuration for the given name.
rcdata_config = next(
config for config in rcdata["configurations"]
if config["name"] == next_config_name)
# See if we found it.
if rcdata_config:
# This is our next configuration to use in the chain.
rcdata_configs.append(rcdata_config)
# If we have a parent, check that next.
if "parent" in rcdata_config:
next_config_name = rcdata_config["parent"]
else:
next_config_name = None
else:
raise "failed to find specified parent config '{}'".format(
next_config_name)
return Configuration(rcdata_configs)
def create_transfer_agent(options, configuration):
transfer_class_spec = configuration.get_value("transfer_class")
if options.verbose:
print "specified transfer class: '{}'".format(transfer_class_spec)
# Load the module (possibly package-qualified).
components = transfer_class_spec.split(".")
module = importlib.import_module(".".join(components[:-1]))
# Create the class name we need to load.
clazz = getattr(module, components[-1])
return clazz(options, configuration)
def sync_configured_sources(options, configuration, default_excludes):
# Look up the transfer method.
transfer_agent = create_transfer_agent(options, configuration)
# For each configured dir_names source, do the following transfer:
# 1. Start with base_dir + {source-dir-name}_dir
# 2. Copy all files recursively, but exclude
# all dirs specified by source_excludes:
# skip all base_dir + {source-dir-name}_dir +
# {source-dir-name}_dir excludes.
source_dirs = configuration.get_value("source")
source_excludes = configuration.get_value("source_excludes")
dest_dirs = configuration.get_value("dest")
source_base_dir = source_dirs["base_dir"]
dest_base_dir = dest_dirs["base_dir"]
dir_ids = configuration.get_value("dir_names")
transfer_specs = []
for dir_id in dir_ids:
dir_key = "{}_dir".format(dir_id)
# Build the source dir (absolute) that we're copying from.
# Defaults the base-relative source dir to the source id (e.g. lldb)
rel_source_dir = source_dirs.get(dir_key, dir_id)
transfer_source_dir = os.path.expanduser(
os.path.join(source_base_dir, rel_source_dir))
# Exclude dirs do two things:
# 1) stop items from being copied on the source side, and
# 2) protect things from being deleted on the dest side.
#
# In both cases, they are specified relative to the base
# directory on either the source or dest side.
#
# Specifying a leading '/' in the directory will limit it to
# be rooted in the base directory. i.e. "/.git" will only
# match {base-dir}/.git, not {base-dir}/subdir/.git, but
# ".svn" will match {base-dir}/.svn and
# {base-dir}/subdir/.svn.
#
# If excludes are specified for this dir_id, then pass along
# the excludes. These are relative to the dir_id directory
# source, and get passed along that way as well.
transfer_source_excludes = []
# Add the source excludes for this dir.
skip_defaults = False
if source_excludes and dir_key in source_excludes:
transfer_source_excludes.extend(source_excludes[dir_key])
if "<no-defaults>" in source_excludes[dir_key]:
skip_defaults = True
transfer_source_excludes.remove("<no-defaults>")
if not skip_defaults and default_excludes is not None:
transfer_source_excludes.extend(list(default_excludes))
# Build the destination-base-relative dest dir into which
# we'll be syncing. Relative directory defaults to the
# dir id
rel_dest_dir = dest_dirs.get(dir_key, dir_id)
transfer_dest_dir = os.path.join(dest_base_dir, rel_dest_dir)
# Add the exploded paths to the list that we'll ask the
# transfer agent to transfer for us.
transfer_specs.append(
transfer.transfer_spec.TransferSpec(
transfer_source_dir,
transfer_source_excludes,
transfer_dest_dir))
# Do the transfer.
if len(transfer_specs) > 0:
transfer_agent.transfer(transfer_specs, options.dry_run)
else:
raise Exception("nothing to transfer, bad configuration?")
def main():
"""Drives the main program."""
options = parse_args()
if options.default_excludes and len(options.default_excludes) > 0:
default_excludes = options.default_excludes.split(",")
else:
default_excludes = []
# Locate the rc filename to load, then load it.
rc_filename = find_appropriate_rcfile(options)
if rc_filename:
if options.verbose:
print "reading rc data from file '{}'".format(rc_filename)
rcdata = read_rcfile(rc_filename)
else:
sys.stderr.write("no rcfile specified, cannot guess configuration")
exit(1)
# Find configuration.
configuration = get_configuration(options, rcdata, options.config_name)
if not configuration:
sys.stderr.write("failed to find configuration for {}".format(
options.config_data))
exit(2)
# Kick off the transfer.
sync_configured_sources(options, configuration, default_excludes)
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More