Have downloader rsync everything at once for speed
This speeds up the copy *significantly*. The extra SSH calls were
adding up to a lot of extra cost. 3 seconds for a NOP download!
Do this by building up a folder that looks like what the target should
look like, and then rsync that all in 1 go.
While we are here, rename .stripped to not have the extension. It makes
the commands harder to remember.
Change-Id: I50e50c90421e049f48af453c8113d7c2204c7774
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/aos/starter/starter.sh b/aos/starter/starter.sh
index 68d567a..2c8494e 100755
--- a/aos/starter/starter.sh
+++ b/aos/starter/starter.sh
@@ -17,5 +17,5 @@
cd "${ROBOT_CODE}"
export PATH="${PATH}:${ROBOT_CODE}"
while true; do
- starterd.stripped 2>&1
+ starterd 2>&1
done
diff --git a/frc971/downloader/downloader.bzl b/frc971/downloader/downloader.bzl
index e1b535e..9eb7e32 100644
--- a/frc971/downloader/downloader.bzl
+++ b/frc971/downloader/downloader.bzl
@@ -1,5 +1,12 @@
def _aos_downloader_impl(ctx):
all_files = ctx.files.srcs + ctx.files.start_srcs + [ctx.outputs._startlist]
+ target_files = []
+
+ # downloader looks for : in the inputs and uses the part after the : as
+ # the directory to copy to.
+ for d in ctx.attr.dirs:
+ target_files += [src.short_path + ":" + d.downloader_dir for src in d.downloader_srcs]
+
ctx.actions.write(
output = ctx.outputs.executable,
is_executable = True,
@@ -7,16 +14,12 @@
"#!/bin/bash",
"set -e",
'cd "${BASH_SOURCE[0]}.runfiles/%s"' % ctx.workspace_name,
- ] + ['%s --dir %s --target "$@" --type %s %s' % (
- ctx.executable._downloader.short_path,
- d.downloader_dir,
- ctx.attr.target_type,
- " ".join([src.short_path for src in d.downloader_srcs]),
- ) for d in ctx.attr.dirs] + [
- 'exec %s --target "$@" --type %s %s' % (
+ ] + [
+ 'exec %s --target "$@" --type %s %s %s' % (
ctx.executable._downloader.short_path,
ctx.attr.target_type,
" ".join([src.short_path for src in all_files]),
+ " ".join(target_files),
),
]),
)
diff --git a/frc971/downloader/downloader.py b/frc971/downloader/downloader.py
index 31155dd..dc14df1 100644
--- a/frc971/downloader/downloader.py
+++ b/frc971/downloader/downloader.py
@@ -6,9 +6,12 @@
import argparse
import sys
+from tempfile import TemporaryDirectory
import subprocess
import re
+import stat
import os
+import shutil
def install(ssh_target, pkg, ssh_path, scp_path):
@@ -40,20 +43,10 @@
required=True,
help="Target type for deployment")
parser.add_argument(
- "--dir",
- type=str,
- help="Directory within robot_code to copy the files to.")
- parser.add_argument(
"srcs", type=str, nargs='+', help="List of files to copy over")
args = parser.parse_args(argv[1:])
- relative_dir = ""
- recursive = False
-
srcs = args.srcs
- if args.dir is not None:
- relative_dir = args.dir
- recursive = True
destination = args.target
@@ -83,46 +76,74 @@
ssh_path = "external/ssh/ssh"
scp_path = "external/ssh/scp"
- rsync_cmd = ([
- "external/rsync/usr/bin/rsync", "-e", ssh_path, "-c", "-v", "-z",
- "--perms", "--copy-links"
- ] + srcs)
+ # Since rsync is pretty fixed in what it can do, build up a temporary
+ # directory with the exact contents we want the target to have. This
+ # is faster than multiple SSH connections.
+ with TemporaryDirectory() as temp_dir:
+ pwd = os.getcwd()
+ # Bazel gives us the same file twice, so dedup here rather than
+ # in starlark
+ copied = set()
+ for s in srcs:
+ if ":" in s:
+ folder = os.path.join(temp_dir, s[s.find(":") + 1:])
+ os.makedirs(folder, exist_ok=True)
+ s = os.path.join(pwd, s[:s.find(":")])
+ destination = os.path.join(folder, os.path.basename(s))
+ else:
+ s = os.path.join(pwd, s)
+ destination = os.path.join(temp_dir, os.path.basename(s))
- # If there is only 1 file to transfer, we would overwrite the destination
- # folder. In that case, specify the full path to the target.
- if len(srcs) == 1:
- rsync_cmd += [
- "%s:%s/%s/%s" % (ssh_target, target_dir, relative_dir, srcs[0])
- ]
- else:
- rsync_cmd += ["%s:%s/%s" % (ssh_target, target_dir, relative_dir)]
+ if s in copied:
+ continue
+ copied.add(s)
+ if s.endswith(".stripped"):
+ destination = destination[:destination.find(".stripped")]
+ shutil.copy2(s, destination)
+ # Make sure the folder that gets created on the roboRIO has open
+ # permissions or the executables won't be visible to init.
+ os.chmod(temp_dir, 0o775)
+ # Starter needs to be SUID so we transition from lvuser to admin.
+ os.chmod(os.path.join(temp_dir, "starterd"), 0o775 | stat.S_ISUID)
- try:
- subprocess.check_call(rsync_cmd)
- except subprocess.CalledProcessError as e:
- if e.returncode == 127 or e.returncode == 12:
- print("Unconfigured roboRIO, installing rsync.")
- install(ssh_target, "libattr1_2.4.47-r0.36_cortexa9-vfpv3.ipk",
- ssh_path, scp_path)
- install(ssh_target, "libacl1_2.2.52-r0.36_cortexa9-vfpv3.ipk",
- ssh_path, scp_path)
- install(ssh_target, "rsync_3.1.0-r0.7_cortexa9-vfpv3.ipk",
- ssh_path, scp_path)
- subprocess.check_call(rsync_cmd)
- elif e.returncode == 11:
- # Directory wasn't created, make it and try again. This keeps the happy path fast.
- subprocess.check_call(
- [ssh_path, ssh_target, "mkdir", "-p", target_dir])
- subprocess.check_call(rsync_cmd)
+ rsync_cmd = ([
+ "external/rsync/usr/bin/rsync",
+ "-e",
+ ssh_path,
+ "-c",
+ "-r",
+ "-v",
+ "--perms",
+ "-l",
+ temp_dir + "/",
+ ])
+
+ # If there is only 1 file to transfer, we would overwrite the destination
+ # folder. In that case, specify the full path to the target.
+ if len(srcs) == 1:
+ rsync_cmd += ["%s:%s/%s" % (ssh_target, target_dir, srcs[0])]
else:
- raise e
+ rsync_cmd += ["%s:%s" % (ssh_target, target_dir)]
- if not recursive:
- subprocess.check_call((ssh_path, ssh_target, "&&".join([
- "chmod u+s %s/starterd.stripped" % target_dir,
- "echo \'Done moving new executables into place\'",
- "bash -c \'sync\'",
- ])))
+ try:
+ subprocess.check_call(rsync_cmd)
+ except subprocess.CalledProcessError as e:
+ if e.returncode == 127 or e.returncode == 12:
+ print("Unconfigured roboRIO, installing rsync.")
+ install(ssh_target, "libattr1_2.4.47-r0.36_cortexa9-vfpv3.ipk",
+ ssh_path, scp_path)
+ install(ssh_target, "libacl1_2.2.52-r0.36_cortexa9-vfpv3.ipk",
+ ssh_path, scp_path)
+ install(ssh_target, "rsync_3.1.0-r0.7_cortexa9-vfpv3.ipk",
+ ssh_path, scp_path)
+ subprocess.check_call(rsync_cmd)
+ elif e.returncode == 11:
+ # Directory wasn't created, make it and try again. This keeps the happy path fast.
+ subprocess.check_call(
+ [ssh_path, ssh_target, "mkdir", "-p", target_dir])
+ subprocess.check_call(rsync_cmd)
+ else:
+ raise e
if __name__ == "__main__":
diff --git a/y2019/y2019.json b/y2019/y2019.json
index 719925c..5b34951 100644
--- a/y2019/y2019.json
+++ b/y2019/y2019.json
@@ -44,35 +44,35 @@
"applications": [
{
"name": "drivetrain",
- "executable_name": "drivetrain.stripped"
+ "executable_name": "drivetrain"
},
{
"name": "trajectory_generator",
- "executable_name": "trajectory_generator.stripped"
+ "executable_name": "trajectory_generator"
},
{
"name": "superstructure",
- "executable_name": "superstructure.stripped"
+ "executable_name": "superstructure"
},
{
"name": "server",
- "executable_name": "server.stripped"
+ "executable_name": "server"
},
{
"name": "logger_main",
- "executable_name": "logger_main.stripped"
+ "executable_name": "logger_main"
},
{
"name": "joystick_reader",
- "executable_name": "joystick_reader.stripped"
+ "executable_name": "joystick_reader"
},
{
"name": "autonomous_action",
- "executable_name": "autonomous_action.stripped"
+ "executable_name": "autonomous_action"
},
{
"name": "wpilib_interface",
- "executable_name": "wpilib_interface.stripped"
+ "executable_name": "wpilib_interface"
}
],
"imports": [
diff --git a/y2020/y2020_pi_template.json b/y2020/y2020_pi_template.json
index 8b14bef..759cfee 100644
--- a/y2020/y2020_pi_template.json
+++ b/y2020/y2020_pi_template.json
@@ -126,28 +126,28 @@
"applications": [
{
"name": "message_bridge_client",
- "executable_name": "message_bridge_client.stripped",
+ "executable_name": "message_bridge_client",
"nodes": [
"pi{{ NUM }}"
]
},
{
"name": "message_bridge_server",
- "executable_name": "message_bridge_server.stripped",
+ "executable_name": "message_bridge_server",
"nodes": [
"pi{{ NUM }}"
]
},
{
"name": "web_proxy",
- "executable_name": "web_proxy_main.stripped",
+ "executable_name": "web_proxy_main",
"nodes": [
"pi{{ NUM }}"
]
},
{
"name": "camera_reader",
- "executable_name": "camera_reader.stripped",
+ "executable_name": "camera_reader",
"nodes": [
"pi{{ NUM }}"
]
diff --git a/y2020/y2020_roborio.json b/y2020/y2020_roborio.json
index 67916b6..4cdd035 100644
--- a/y2020/y2020_roborio.json
+++ b/y2020/y2020_roborio.json
@@ -332,70 +332,70 @@
"applications": [
{
"name": "drivetrain",
- "executable_name": "drivetrain.stripped",
+ "executable_name": "drivetrain",
"nodes": [
"roborio"
]
},
{
"name": "trajectory_generator",
- "executable_name": "trajectory_generator.stripped",
+ "executable_name": "trajectory_generator",
"nodes": [
"roborio"
]
},
{
"name": "superstructure",
- "executable_name": "superstructure.stripped",
+ "executable_name": "superstructure",
"nodes": [
"roborio"
]
},
{
"name": "joystick_reader",
- "executable_name": "joystick_reader.stripped",
+ "executable_name": "joystick_reader",
"nodes": [
"roborio"
]
},
{
"name": "wpilib_interface",
- "executable_name": "wpilib_interface.stripped",
+ "executable_name": "wpilib_interface",
"nodes": [
"roborio"
]
},
{
"name": "autonomous_action",
- "executable_name": "autonomous_action.stripped",
+ "executable_name": "autonomous_action",
"nodes": [
"roborio"
]
},
{
"name": "web_proxy",
- "executable_name": "web_proxy_main.stripped",
+ "executable_name": "web_proxy_main",
"nodes": [
"roborio"
]
},
{
"name": "message_bridge_client",
- "executable_name": "message_bridge_client.stripped",
+ "executable_name": "message_bridge_client",
"nodes": [
"roborio"
]
},
{
"name": "message_bridge_server",
- "executable_name": "message_bridge_server.stripped",
+ "executable_name": "message_bridge_server",
"nodes": [
"roborio"
]
},
{
"name": "logger",
- "executable_name": "logger_main.stripped",
+ "executable_name": "logger_main",
"nodes": [
"roborio"
]