Checking in aos/vision/download tool.

Change-Id: Ie1ced5ec11f07fbf3e18cb94a8f690bd25e6a185
diff --git a/aos/downloader/downloader.bzl b/aos/downloader/downloader.bzl
index 0219cf7..de1cf1c 100644
--- a/aos/downloader/downloader.bzl
+++ b/aos/downloader/downloader.bzl
@@ -6,7 +6,7 @@
     content = '\n'.join([
       '#!/bin/bash',
       'set -e',
-      'cd "${BASH_SOURCE[@]}.runfiles/%s"' % ctx.workspace_name,
+      'cd "${BASH_SOURCE[0]}.runfiles/%s"' % ctx.workspace_name,
     ] + ['%s %s --dirs %s -- %s "$@"' % (
        ctx.executable._downloader.short_path,
        ' '.join([src.short_path for src in d.downloader_srcs]),
diff --git a/aos/vision/download/BUILD b/aos/vision/download/BUILD
new file mode 100644
index 0000000..536a7ad
--- /dev/null
+++ b/aos/vision/download/BUILD
@@ -0,0 +1,7 @@
+py_binary(
+  name = 'downloader',
+  visibility = ['//visibility:public'],
+  srcs = [
+    'downloader.py',
+  ],
+)
diff --git a/aos/vision/download/downloader.bzl b/aos/vision/download/downloader.bzl
new file mode 100644
index 0000000..7100ab7
--- /dev/null
+++ b/aos/vision/download/downloader.bzl
@@ -0,0 +1,54 @@
+def _aos_vision_downloader_impl(ctx):
+  all_files = ctx.files.srcs
+  ctx.file_action(
+    output = ctx.outputs.executable,
+    executable = True,
+    content = '\n'.join([
+      '#!/bin/bash',
+      'set -e',
+      'cd "${BASH_SOURCE[0]}.runfiles/%s"' % ctx.workspace_name,
+    ] + [
+      'exec %s %s -- %s "$@"' % (ctx.executable._downloader.short_path,
+                                 ' '.join([src.short_path for src in all_files]),
+                                 ctx.attr.default_target),
+    ]),
+  )
+
+  to_download = all_files
+
+  return struct(
+    runfiles = ctx.runfiles(
+      files = to_download + ctx.files._downloader,
+      collect_data = True,
+      collect_default = True,
+    ),
+    files = set([ctx.outputs.executable]),
+  )
+
+'''Creates a binary which downloads code to a robot camera processing unit.
+
+Running this with `bazel run` will actually download everything.
+
+Attrs:
+  srcs: The files to download. They currently all get shoved into one folder.
+  default_target: The default host to download to. If not specified, defaults to
+                  root@10.9.71.179.
+'''
+aos_vision_downloader = rule(
+  implementation = _aos_vision_downloader_impl,
+  attrs = {
+    '_downloader': attr.label(
+      executable = True,
+      cfg = 'host',
+      default = Label('//aos/vision/download:downloader'),
+    ),
+    'srcs': attr.label_list(
+      mandatory = True,
+      allow_files = True,
+    ),
+    'default_target': attr.string(
+      default = 'root@10.9.71.179',
+    ),
+  },
+  executable = True,
+)
diff --git a/aos/vision/download/downloader.py b/aos/vision/download/downloader.py
new file mode 100644
index 0000000..3aea634
--- /dev/null
+++ b/aos/vision/download/downloader.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# This file is run by shell scripts generated by the aos_vision_downloader Skylark
+# macro. Everything before the first -- is a hard-coded list of files to
+# download.
+
+from __future__ import print_function
+
+import sys
+import subprocess
+import re
+import os
+import os.path
+
+def RunAndSplitShas(shcmd):
+  out = subprocess.check_output(shcmd)
+  return [line.split(' ')[0] for line in filter(lambda x: x, out.split('\n'))]
+
+def GetChecksums(fnames):
+  return RunAndSplitShas(["sha256sum"] + fnames)
+
+def GetJetsonChecksums(ssh_target, fnames):
+  target_files = ["/root/%s" % fname for fname in fnames]
+  subprocess.check_call(["ssh", ssh_target, "touch " + " ".join(target_files)])
+  cmds = ["ssh", ssh_target, "sha256sum " + " ".join(target_files)]
+  return RunAndSplitShas(cmds)
+
+def ToJetsonFname(fname):
+  if (fname[-9:] == ".stripped"):
+    fname = fname[:-9]
+  return os.path.basename(fname)
+
+def VerifyCheckSumsAndUpload(fnames, ssh_target):
+  jetson_fnames = [ToJetsonFname(fname) for fname in fnames]
+  checksums = GetChecksums(fnames)
+  jetson_checksums = GetJetsonChecksums(ssh_target, jetson_fnames)
+  for i in xrange(len(fnames)):
+    if (checksums[i] != jetson_checksums[i]):
+      # if empty, unlink
+      subprocess.check_call(["ssh", ssh_target, "unlink " + jetson_fnames[i]])
+      subprocess.check_call(["scp", fnames[i], ssh_target + ":" + jetson_fnames[i]])
+
+def main(argv):
+  args = argv[argv.index('--') + 1:]
+  files = argv[1:argv.index('--')]
+
+  VerifyCheckSumsAndUpload(files, args[-1])
+
+if __name__ == '__main__':
+  main(sys.argv)