[sdk][bazel] Functional component runner.

Test: was able to start a component.
Bug: DX-750
Change-Id: Id242c4a10fff321131ec02e0fb7feb9ecafaec81
diff --git a/sdk/bazel/base/common/build_defs/internal/component_runner/component_runner.py b/sdk/bazel/base/common/build_defs/internal/component_runner/component_runner.py
index 9060ef0..5ce58da 100644
--- a/sdk/bazel/base/common/build_defs/internal/component_runner/component_runner.py
+++ b/sdk/bazel/base/common/build_defs/internal/component_runner/component_runner.py
@@ -3,7 +3,47 @@
 # found in the LICENSE file.
 
 import argparse
+import re
+import shutil
+from subprocess import Popen, PIPE
 import sys
+import tempfile
+
+
+def run_command(*args):
+    process = Popen(args, stdout=PIPE, stderr=PIPE)
+    stdout, stderr = process.communicate()
+    if process.returncode:
+        raise Exception('Command %s failed: %s' % (args, stdout + stderr))
+    return stdout
+
+
+def get_device_addresses(dev_finder):
+    # Find a target device.
+    stdout = run_command(dev_finder, 'list', '-device_limit', '1', '-full')
+    match = re.match('^([^\s]+)\s+([^\s]+)$', stdout.strip())
+    if not match:
+        raise Exception('Could not parse target parameters in %s' % stdout)
+    target_address = match.group(1)
+    target_name = match.group(2)
+    # TODO(DX-844): use full name.
+    target_name = target_name[1:-1]
+
+    # Get the matching host address for that device.
+    stdout = run_command(dev_finder, 'resolve', '-local', target_name)
+    host_address = stdout.strip()
+    return target_address, host_address
+
+
+def serve_package(pm, package, directory):
+    # Set up the package repository.
+    run_command(pm, 'newrepo', '-repo', directory)
+    run_command(pm, 'publish', '-a', '-r', directory, '-f', package)
+
+    # Start the server.
+    server = Popen([pm, 'serve', '-repo', directory+'/repository'], stdout=PIPE,
+                   stderr=PIPE)
+    return lambda: server.kill()
 
 
 def main():
@@ -11,12 +51,24 @@
     parser.add_argument('--config',
                         help='The path to the list of components in the package',
                         required=True)
+    parser.add_argument('--package-name',
+                        help='The name of the Fuchsia package',
+                        required=True)
     parser.add_argument('--package',
                         help='The path to the Fuchsia package',
                         required=True)
+    parser.add_argument('--dev-finder',
+                        help='The path to the dev_finder tool',
+                        required=True)
+    parser.add_argument('--pm',
+                        help='The path to the pm tool',
+                        required=True)
     subparse = parser.add_subparsers().add_parser('run')
     subparse.add_argument('component',
                           nargs=1)
+    subparse.add_argument('--ssh-key',
+                          help='The path to private key for SSH communications',
+                          required=True)
     args = parser.parse_args()
 
     with open(args.config, 'r') as config_file:
@@ -27,6 +79,28 @@
         print('Error: "%s" not in %s' % (component, components))
         return 1
 
+    staging_dir = tempfile.mkdtemp(prefix='fuchsia-run')
+
+    try:
+        target_address, host_address = get_device_addresses(args.dev_finder)
+        stop_server = serve_package(args.pm, args.package, staging_dir)
+        try:
+            def run_ssh_command(*params):
+                base = ['ssh', '-i', args.ssh_key, 'fuchsia@'+target_address]
+                run_command(*(base + list(params)))
+            server_address = 'http://%s:8083/config.json' % host_address
+            run_ssh_command('amber_ctl', 'add_src', '-x', '-f', server_address)
+            component_uri = "fuchsia-pkg://fuchsia.com/%s#meta/%s.cmx" % (
+                    args.package_name, component)
+            run_ssh_command('run', component_uri)
+        finally:
+            stop_server()
+    except Exception as e:
+        print(e)
+        return 1
+    finally:
+        shutil.rmtree(staging_dir, ignore_errors=True)
+
     print('One day I will run %s from %s' % (component, args.package))
 
     return 0
diff --git a/sdk/bazel/base/common/build_defs/package.bzl b/sdk/bazel/base/common/build_defs/package.bzl
index 1a314bd..11144c2 100644
--- a/sdk/bazel/base/common/build_defs/package.bzl
+++ b/sdk/bazel/base/common/build_defs/package.bzl
@@ -201,11 +201,22 @@
     )
 
     executable_file = context.actions.declare_file(context.attr.name + "_run.sh")
-    executable_contents = "#!/bin/sh\n"
-    executable_contents += "%s --config %s --package %s run \"$@\"" % (
+    executable_contents = """#!/bin/sh\n
+%s \\
+    --config %s \\
+    --package-name %s \\
+    --package %s \\
+    --dev-finder %s \\
+    --pm %s \\
+    run \\
+    \"$@\"
+""" % (
         context.executable._runner.short_path,
         components_file.short_path,
+        context.attr.name,
         package_archive.short_path,
+        context.executable._dev_finder.short_path,
+        context.executable._pm.short_path,
     )
     context.actions.write(
         output = executable_file,
@@ -215,6 +226,8 @@
 
     runfiles = context.runfiles(files = [
         components_file,
+        context.executable._dev_finder,
+        context.executable._pm,
         context.executable._runner,
         executable_file,
         package_archive,
@@ -248,8 +261,15 @@
             executable = True,
             cfg = "host",
         ),
+        "_dev_finder": attr.label(
+            default = Label("//tools:dev_finder"),
+            allow_single_file = True,
+            executable = True,
+            cfg = "host",
+        ),
         "_runner": attr.label(
             default = Label("//build_defs/internal/component_runner:component_runner.par"),
+            allow_single_file = True,
             executable = True,
             cfg = "host",
         ),
diff --git a/sdk/bazel/tests/cc/cc/manifest.cmx b/sdk/bazel/tests/cc/cc/manifest.cmx
index 0967ef4..100886a 100644
--- a/sdk/bazel/tests/cc/cc/manifest.cmx
+++ b/sdk/bazel/tests/cc/cc/manifest.cmx
@@ -1 +1,11 @@
-{}
+{
+    "program": {
+        "binary": "bin/pkg_dep"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.sys.Environment",
+            "fuchsia.sys.Loader"
+        ]
+    }
+}