You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
12 KiB
468 lines
12 KiB
#!/usr/bin/env python
|
|
|
|
#
|
|
# Program Teensy, wait for it to come back online and start GDB.
|
|
# This program will:
|
|
# 1. Run teensy_post_compile to upload program.
|
|
# 2. Run teensy_ports to figure out what ports to use.
|
|
# 3. Run GDB in new window pointing to right port.
|
|
#
|
|
# Use with "-i" to install the program as the default uploader.
|
|
# This will:
|
|
# 1. copy this script to the "tools" directory.
|
|
# 2. create a boards.local.txt and platform.local.txt file.
|
|
# These files will redirect uploads to this script.
|
|
#
|
|
|
|
from __future__ import print_function
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
import os.path
|
|
import time
|
|
import sys
|
|
import re
|
|
import tempfile
|
|
import stat
|
|
import shutil
|
|
from os.path import expanduser
|
|
import glob
|
|
|
|
try:
|
|
import customize
|
|
customRun = customize.customRun
|
|
except:
|
|
customRun = None
|
|
|
|
#####################################
|
|
#
|
|
# Process args in style of teensy_post_compile
|
|
#
|
|
#####################################
|
|
|
|
class args:
|
|
def set(self, k, v):
|
|
self.__dict__[k] = v
|
|
def has(self, k):
|
|
return k in self.__dict__
|
|
|
|
def parseCommandLine(x=None):
|
|
ret = args()
|
|
if x is None:
|
|
x = sys.argv[1:]
|
|
|
|
for arg in x:
|
|
a = arg.split('=')
|
|
name = a[0]
|
|
if name[0] == '-':
|
|
name = name[1:]
|
|
else:
|
|
print("Invalid parameter", name)
|
|
continue
|
|
if len(a) > 1:
|
|
ret.set(name, a[1])
|
|
else:
|
|
ret.set(name, 1)
|
|
return ret
|
|
|
|
#####################################
|
|
#
|
|
# Installation code
|
|
#
|
|
#####################################
|
|
|
|
def askUser(question):
|
|
global args
|
|
if args.has("y"):
|
|
return True
|
|
print(question)
|
|
# print("[y/N]? ", end='')
|
|
sys.stdout.write("[y/N]? ")
|
|
sys.stdout.flush()
|
|
ask = sys.stdin.readline()
|
|
if ask[0] == 'y' or ask[0] == 'Y':
|
|
return True
|
|
return False
|
|
|
|
def pauseUser():
|
|
global args
|
|
if args.has("y"):
|
|
return
|
|
print("Press Enter to close")
|
|
sys.stdin.readline()
|
|
|
|
def installGDB():
|
|
global args
|
|
|
|
print("Install GDB in Teensyduino")
|
|
|
|
if os.name == 'nt':
|
|
EXT = ".exe"
|
|
elif sys.platform == 'darwin':
|
|
EXT = ""
|
|
else:
|
|
EXT = ""
|
|
|
|
# find the Arduino directory
|
|
if args.has("i") and args.i != 1:
|
|
DIR = args.i
|
|
else:
|
|
if os.name == 'nt':
|
|
DIR = "C:/Program Files (x86)/Arduino/"
|
|
elif sys.platform == 'darwin':
|
|
APPDIR = "/Applications/"
|
|
if os.path.exists(APPDIR + "Teensyduino.app"):
|
|
DIR = APPDIR + "Teensyduino.app/"
|
|
# elif os.path.exists(DIR + "Arduino.app"):
|
|
else:
|
|
DIR = APPDIR + "Arduino.app/"
|
|
else:
|
|
try:
|
|
DIR = os.readlink("/usr/local/bin/arduino")
|
|
DIR = os.path.dirname(DIR) + "/"
|
|
except:
|
|
DIR = ''
|
|
|
|
while(True):
|
|
if os.path.exists(DIR + "hardware/teensy"):
|
|
TOOLS = DIR + "hardware/tools/"
|
|
AVR = DIR + "hardware/teensy/avr/"
|
|
break
|
|
elif os.path.exists(DIR + "Contents/Java/hardware/teensy"):
|
|
TOOLS = DIR + "Contents/Java/hardware/tools/"
|
|
AVR = DIR + "Contents/Java/hardware/teensy/avr/"
|
|
break
|
|
else:
|
|
if os.path.exists(DIR + "hardware"):
|
|
print("Arduino found in %s" % DIR)
|
|
print("Teensyduino not found inside %s" % DIR)
|
|
print("Where is Arduino installed? ")
|
|
print("? ", end='')
|
|
DIR = input()
|
|
DIR = DIR.strip() + "/"
|
|
|
|
# find the custom library folder
|
|
HOME = expanduser("~")
|
|
DEST = HOME + "/Arduino/libraries/"
|
|
if not os.path.exists(DEST):
|
|
DEST = HOME + "/Documents/Arduino/libraries/"
|
|
|
|
while(not os.path.exists(DEST)):
|
|
print("Custom library folder not found in %s" % DEST)
|
|
print("Where are custom libraries installed? ")
|
|
print("? ", end='')
|
|
DEST = input()
|
|
DEST = DEST.strip() + "/"
|
|
|
|
DEST += "TeensyDebug/"
|
|
|
|
# confirm locations
|
|
print("Teensyduino found in %s" % DIR)
|
|
print("Teensyduino tools in %s" % TOOLS)
|
|
print("Teensyduino custom library in %s" % DEST)
|
|
|
|
ask = askUser("Is this OK?")
|
|
if not ask:
|
|
return 0
|
|
|
|
if os.path.exists(DEST):
|
|
print("Already installed. Updating.")
|
|
|
|
createFiles(AVR)
|
|
|
|
# shutil.copy("run.command", TOOLS)
|
|
if EXT == ".exe":
|
|
print("Copy teensy_debug.exe to %s" % TOOLS)
|
|
shutil.copy("teensy_debug.exe", TOOLS)
|
|
elif EXT == "command":
|
|
print("Copy teensy_debug to %s" % TOOLS)
|
|
shutil.copy("teensy_debug", TOOLS + "teensy_debug")
|
|
else:
|
|
print("Copy teensy_debug to %s" % TOOLS)
|
|
shutil.copy("teensy_debug", TOOLS + "teensy_debug")
|
|
|
|
if not os.path.exists(DEST):
|
|
os.makedirs(DEST)
|
|
|
|
for i in ("README.md", "library.properties", "keywords.txt", "license.txt", "TeensyDebug.h", "TeensyDebug.cpp", "gdbstub.cpp"):
|
|
print("Copy %s to %s" % (i, DEST))
|
|
shutil.copy(i, DEST)
|
|
|
|
print("Copy examples to %s" % (DEST))
|
|
try:
|
|
shutil.rmtree(DEST + '/examples')
|
|
except:
|
|
pass
|
|
shutil.copytree('examples', DEST + '/examples')
|
|
|
|
print("\nInstallation complete\n")
|
|
|
|
def createFiles(AVR):
|
|
mode = "w"
|
|
board = AVR + "boards.local.txt"
|
|
if os.path.exists(board):
|
|
ask = askUser("File %s already exists. Would you like to overwrite it? " % board)
|
|
if not ask:
|
|
ask = askUser("Would you like to append to it? ")
|
|
if not ask:
|
|
return
|
|
mode = "a"
|
|
|
|
print("Create %s" % (board))
|
|
with open(board, mode) as f:
|
|
f.write("menu.gdb=GDB\n")
|
|
for ver in ('41','40'):
|
|
f.write("""
|
|
teensy%s.menu.gdb.serial=Take over Serial
|
|
teensy%s.menu.gdb.serial.build.gdb=2
|
|
teensy%s.menu.gdb.serial.build.flags.optimize=-Og -g -DGDB_TAKE_OVER_SERIAL
|
|
teensy%s.menu.gdb.serial.upload.tool=gdbtool
|
|
teensy%s.menu.gdb.dual=Use Dual Serial
|
|
teensy%s.menu.gdb.dual.build.gdb=1
|
|
teensy%s.menu.gdb.dual.build.flags.optimize=-Og -g -DGDB_DUAL_SERIAL
|
|
teensy%s.menu.gdb.dual.upload.tool=gdbtool
|
|
teensy%s.menu.gdb.manual=Manual device selection
|
|
teensy%s.menu.gdb.manual.build.gdb=3
|
|
teensy%s.menu.gdb.manual.build.flags.optimize=-Og -g -DGDB_MANUAL_SELECTION
|
|
teensy%s.menu.gdb.manual.upload.tool=gdbtool
|
|
teensy%s.menu.gdb.compile=Just compile
|
|
teensy%s.menu.gdb.compile.build.gdb=0
|
|
teensy%s.menu.gdb.compile.build.flags.optimize=-Og -g -DGDB_MANUAL_SELECTION
|
|
teensy%s.menu.gdb.compile.upload.tool=gdbtool
|
|
teensy%s.menu.gdb.off=Off
|
|
teensy%s.menu.gdb.off.build.gdb=0
|
|
""".replace("%s", ver))
|
|
|
|
print("Create %s/platform.local.txt" % (AVR))
|
|
with open(AVR + "platform.local.txt", mode) as f:
|
|
f.write("""
|
|
tools.gdbtool.cmd.path={runtime.hardware.path}/../tools
|
|
tools.gdbtool.upload.params.quiet=
|
|
tools.gdbtool.upload.params.verbose=-verbose
|
|
tools.gdbtool.upload.pattern="{cmd.path}/teensy_debug" "-gdb={build.gdb}" "-file={build.project_name}" "-path={build.path}" "-tools={cmd.path}" "-board={build.board}" -reboot "-port={serial.port}" "-portlabel={serial.port.label}" "-portprotocol={serial.port.protocol}"
|
|
""")
|
|
|
|
|
|
#####################################
|
|
#
|
|
# Execution code
|
|
#
|
|
#####################################
|
|
|
|
def getPort():
|
|
global args
|
|
|
|
usedev = None
|
|
|
|
# wait for Teensy to come online
|
|
for i in range(10):
|
|
out = subprocess.Popen([args.tools + '/teensy_ports', '-L'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
stdout, stderr = out.communicate()
|
|
if len(stdout) > 10:
|
|
info = stdout.decode().split(' ')
|
|
# print("dev", info)
|
|
dev = info[1]
|
|
if dev != '[no_device]':
|
|
break
|
|
time.sleep(0.5)
|
|
|
|
# counldn't find it within timeout?
|
|
if len(stdout) < 10:
|
|
print("Could not find Teensy")
|
|
return None
|
|
|
|
if args.port.startswith("/dev/tty"):
|
|
dev = args.port
|
|
if args.port.upper().startswith("COM"):
|
|
dev = args.port
|
|
|
|
# For Windows, com port determination is complex so I'll just guess
|
|
if os.name == 'nt':
|
|
p = re.match(r'COM(\d+)', dev.upper())
|
|
comport = int(p.group(1))
|
|
# If taking over serial, return the passed in port
|
|
if args.gdb == "2":
|
|
usedev = "COM%d" % (comport)
|
|
# it's the one
|
|
# after the programming port in Dual Serial mode
|
|
elif args.gdb == "1":
|
|
usedev = "COM%d" % (comport+1)
|
|
else:
|
|
usedev = "COM%d" % (comport)
|
|
return "\\\\.\\"+usedev
|
|
|
|
# If we're taking over the serial port, return what was passed in
|
|
if args.gdb == "2":
|
|
return dev
|
|
|
|
p = re.search(r'^([^\d]+)(\d+)$', dev)
|
|
if p is None:
|
|
print("Could not find serial port id in ", dev)
|
|
return None
|
|
|
|
# For Mac/Linux, find the file pattern for serial devices, list them all
|
|
# and pick the one after our programming port
|
|
prefix = p.group(1)
|
|
found = False
|
|
for n in sorted(glob.glob(prefix + "*")):
|
|
if n == dev:
|
|
found = True
|
|
elif found:
|
|
return n
|
|
|
|
return None
|
|
|
|
# Get the port status and find the right port to connect with. The right port
|
|
# is any port that we are not using for programming and serial monitor. Probably
|
|
# should be smarter
|
|
out = subprocess.Popen([args.tools + '/teensy_ports'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
# wait for results
|
|
time.sleep(2)
|
|
# program stays on indefinitely, must kill it
|
|
out.kill()
|
|
# get output to parse
|
|
stdout,stderr = out.communicate()
|
|
|
|
devs = []
|
|
# parse the output to get the ports
|
|
for line in stdout.decode().splitlines():
|
|
if not re.search('label', line): continue
|
|
if re.search('no_device', line): continue
|
|
if not re.search('Serial', line): continue
|
|
a = line.split(': ')
|
|
dev = a[1][1:].split(' ')[0]
|
|
# if using dual serials and it's the programming port, ignore it
|
|
if args.gdb == "1" and dev == args.port: continue
|
|
# print(dev)
|
|
devs.append(dev)
|
|
|
|
if len(devs) <= 0:
|
|
print("Could not find an extra Serial device. Did you compile one?")
|
|
return None
|
|
|
|
# get the last port found
|
|
usedev = sorted(devs)[-1]
|
|
if len(devs)==1:
|
|
usedev = devs[0]
|
|
print("Using device", usedev)
|
|
else:
|
|
usedev = sorted(devs)[-1]
|
|
print("Using device", usedev, "from available", devs)
|
|
|
|
return usedev
|
|
|
|
def runGDB(arguments):
|
|
global args
|
|
|
|
# just install
|
|
if args.has("i"):
|
|
installGDB()
|
|
exit(0)
|
|
|
|
# create boards.local.txt, etc.
|
|
if args.has("c"):
|
|
createFiles("./")
|
|
exit(0)
|
|
|
|
elf = convertPathSlashes("%s/%s.elf" % (args.path, args.file))
|
|
|
|
# run command for programming Teensy
|
|
post = convertPathSlashes('%s/teensy_post_compile' % args.tools)
|
|
if os.name == 'nt':
|
|
x = subprocess.run([post] + arguments)
|
|
x = x.returncode
|
|
else:
|
|
argline = '" "'.join(arguments)
|
|
cmd1='"%s" "%s"' % (post, argline)
|
|
x = os.system(cmd1)
|
|
|
|
# something went wrong with programming
|
|
if x != 0:
|
|
exit(x)
|
|
|
|
# if gdb option is off or missing, don't run gdb so just end here
|
|
if args.has("gdb"):
|
|
if args.gdb == "0":
|
|
return
|
|
else:
|
|
return
|
|
|
|
usedev = getPort()
|
|
if usedev is None:
|
|
return
|
|
|
|
gpath = args.tools + "/arm/bin/"
|
|
GDB = convertPathSlashes("%s/arm-none-eabi-gdb" % gpath)
|
|
|
|
if not customRun is None:
|
|
customRun(GDB, usedev, elf)
|
|
return
|
|
|
|
if args.gdb == "3":
|
|
gdbcommand = '"%s" "%s"' % (GDB, elf)
|
|
else:
|
|
gdbcommand = '"%s" -ex "target extended-remote %s" "%s"' % (GDB, usedev, elf)
|
|
|
|
print("RUN:", gdbcommand)
|
|
runCommand(gdbcommand)
|
|
|
|
def convertPathSlashes(f):
|
|
if os.name == 'nt':
|
|
return f.replace("/", "\\")
|
|
return f
|
|
|
|
def runOnMac(command):
|
|
f = tempfile.NamedTemporaryFile("w", suffix=".command", delete=False)
|
|
f.write(command)
|
|
f.write("\n")
|
|
f.close()
|
|
# make sure it's executable for Unix-like systems
|
|
os.chmod(f.name, stat.S_IREAD | stat.S_IEXEC)
|
|
# run in separate terminal
|
|
os.system("/usr/bin/open -a Terminal %s &" % f.name)
|
|
|
|
def runOnLinux(command):
|
|
f = tempfile.NamedTemporaryFile("w", suffix=".sh", delete=False)
|
|
f.write("#!/bin/sh\n")
|
|
f.write(command)
|
|
f.write("\n")
|
|
f.close()
|
|
# make sure it's executable for Unix-like systems
|
|
os.chmod(f.name, stat.S_IREAD | stat.S_IEXEC)
|
|
# run in separate terminal
|
|
os.system("/usr/bin/xterm -e %s &" % f.name)
|
|
|
|
def runOnWindows(command):
|
|
f = tempfile.NamedTemporaryFile("w", suffix=".bat", delete=False)
|
|
f.write(command)
|
|
f.write("\n")
|
|
f.close()
|
|
# run in separate terminal
|
|
# print("batch file is %s" % f.name)
|
|
os.system("start %s" % f.name)
|
|
# os.system(command)
|
|
# subprocess.run(["cmd.exe", "/k", f.name])
|
|
|
|
def runCommand(command):
|
|
if os.name == 'nt':
|
|
runOnWindows(command)
|
|
elif sys.platform == 'darwin':
|
|
runOnMac(command)
|
|
else:
|
|
runOnLinux(command)
|
|
|
|
#
|
|
# Main code
|
|
#
|
|
|
|
args = parseCommandLine(sys.argv[1:])
|
|
|
|
if len(sys.argv)==1:
|
|
try:
|
|
installGDB()
|
|
except Exception as ex:
|
|
print(ex)
|
|
pauseUser()
|
|
else:
|
|
runGDB(sys.argv[1:])
|
|
|