Create and install Fuchsia packages when running

For all run modes except normal, create and install a Fuchsia
package rather than using scp.

Also expose this as fargo make-package, as that might be useful.

Also fix adapt to the recent zircon build changes.

Change-Id: I3490fd356afdd08453ebd93ce3fbeaea9c4b6271
diff --git a/Cargo.lock b/Cargo.lock
index 47cc2ea..143f8e1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -114,8 +114,9 @@
  "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "notify 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -186,6 +187,11 @@
 ]
 
 [[package]]
+name = "itoa"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "kernel32-sys"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -285,8 +291,13 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "ryu"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "serde"
-version = "1.0.11"
+version = "1.0.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -309,6 +320,16 @@
 ]
 
 [[package]]
+name = "serde_json"
+version = "1.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "slab"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -380,7 +401,7 @@
 version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -480,6 +501,7 @@
 "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
 "checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c"
 "checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
+"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum libc 0.2.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb7b49972ee23d8aa1026c365a5b440ba08e35075f18c459980c7395c221ec48"
 "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
@@ -491,9 +513,11 @@
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
 "checksum redox_syscall 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ddab7acd8e7bf3e49dfdf78ac1209b992329eb2f66e0bf672ab49c70a76d1d68"
 "checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
-"checksum serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f7726f29ddf9731b17ff113c461e362c381d9d69433f79de4f3dd572488823e9"
+"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
+"checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
 "checksum serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cf823e706be268e73e7747b147aa31c8f633ab4ba31f115efb57e5047c3a76dd"
 "checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
+"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
 "checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
 "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
 "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
diff --git a/Cargo.toml b/Cargo.toml
index f64047f..6c5f426 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@
 notify = "4.0.0"
 serde = "1.0"
 serde_derive = "1.0"
+serde_json = "1.0"
 toml = "0.4"
 
 [[bin]]
diff --git a/README.md b/README.md
index ca5cef8..4b2f658 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@
         -v, --verbose              Print verbose output while performing commands
 
     OPTIONS:
+            --cmx-path <cmx-path>              Path to sandbox file to use when running
         -N, --device-name <device-name>
                 Name of device to target, needed if there are multiple devices visible on the network
 
@@ -21,7 +22,7 @@
     SUBCOMMANDS:
         autotest             Auto build and test in Fuchsia device or emulator
         build                Build binary targeting Fuchsia device or emulator
-        build-tests          Build tests for Fuchsia device or emulator
+        build-rustc          Build rustc targeting Fuchsia
         cargo                Run a cargo command for Fuchsia. Use -- to indicate that all following
                              arguments should be passed to cargo.
         check                Check binary targeting Fuchsia device or emulator
@@ -31,6 +32,7 @@
         help                 Prints this message or the help of the given subcommand(s)
         list-devices         List visible Fuchsia devices
         load-driver          Build driver and load it on Fuchsia device or emulator.
+        make-package         Make a Fuchsia package from an unstripped binary
         pkg-config           Run pkg-config for the cross compilation environment
         restart              Stop all Fuchsia emulators and start a new one
         run                  Run binary on Fuchsia device or emulator
@@ -68,7 +70,7 @@
     cd fargo
     cargo install --force --path .
 
-Fargo uses the $FUCHSIA\_DIR/.config file written by ./scripts/fx set to know what
+Fargo uses the values set by `./scripts/fx set` to know what
 build directory to use.
 
 Fargo uses ssh to communicate between your host computer and either Qemu or a
@@ -102,9 +104,10 @@
 
 ### Running view-producing Rust binaries
 
-fargo run has an option, `--run-with-tiles`, that will use `tiles_ctl add` to launch the Rust
-binary. Use this option when running if your binaries wants to provide a
-[view provider service](https://fuchsia.googlesource.com/garnet/+/master/public/fidl/fuchsia.ui.views_v1/view_provider.fidl)
+fargo run has the options, `--run-with-tiles` and `--run-with-sessionctl`, that will use
+`tiles_ctl add` to launch the Rust binary. Use this option when running if your binaries
+wants to provide a
+[view provider service](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/sdk/fidl/fuchsia.ui.app/view_provider.fidl)
 
 ## Creating a .cargo/config
 
diff --git a/src/device.rs b/src/device.rs
index 302fed6..98ca071 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use crate::sdk::{fuchsia_dir, fx_path, target_out_dir, FuchsiaConfig, TargetOptions};
+use crate::sdk::{
+    fuchsia_dir, fx_path, target_out_dir, zircon_build_path, FuchsiaConfig, TargetOptions,
+};
 use crate::utils::is_mac;
 use failure::{bail, err_msg, Error, ResultExt};
 use std::env;
@@ -11,8 +13,8 @@
 use std::{str, thread, time};
 
 pub fn netaddr(verbose: bool, target_options: &TargetOptions<'_, '_>) -> Result<String, Error> {
-    let fuchsia_dir = fuchsia_dir()?;
-    let netaddr_binary = fuchsia_dir.join("out/build-zircon/tools/netaddr");
+    let zircon_build_dir = zircon_build_path(&target_options.config)?;
+    let netaddr_binary = zircon_build_dir.join("tools/netaddr");
     let mut args = vec!["--fuchsia", "--timeout=500", "--nowait"];
     if let Some(device_name) = target_options.device_name {
         args.push(device_name);
@@ -29,9 +31,9 @@
     Ok(result)
 }
 
-pub fn netls(verbose: bool) -> Result<(), Error> {
-    let fuchsia_dir = fuchsia_dir()?;
-    let netls_binary = fuchsia_dir.join("out/build-zircon/tools/netls");
+pub fn netls(verbose: bool, target_options: &TargetOptions<'_, '_>) -> Result<(), Error> {
+    let zircon_build_dir = zircon_build_path(&target_options.config)?;
+    let netls_binary = zircon_build_dir.join("tools/netls");
     let mut netls_command = Command::new(netls_binary);
     netls_command.arg("--nowait").arg("--timeout=500");
     if verbose {
@@ -100,7 +102,7 @@
     target_options: &TargetOptions<'_, '_>,
     command: &str,
 ) -> Result<(), Error> {
-    let netaddr = netaddr(verbose, target_options)?;
+    let netaddr = netaddr(verbose, target_options).context("netaddr failed")?;
     let ssh_config = target_out_dir(config)?.join("ssh-keys/ssh_config");
     if !ssh_config.exists() {
         bail!("ssh config not found at {:?}", ssh_config);
diff --git a/src/lib.rs b/src/lib.rs
index 3a94f26..d7b3601 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,6 +11,7 @@
 mod build_rustc;
 mod cross;
 mod device;
+mod package;
 mod sdk;
 mod utils;
 
@@ -22,12 +23,15 @@
     enable_networking, netaddr, netls, scp_to_device, ssh, start_emulator, stop_emulator,
     StartEmulatorOptions,
 };
-use crate::sdk::{
-    cargo_out_dir, cargo_path, clang_archiver_path, clang_c_compiler_path, clang_cpp_compiler_path,
-    clang_ranlib_path, clang_resource_dir, rustc_path, rustdoc_path, shared_libraries_path,
-    sysroot_path, zircon_build_path,
+use crate::package::make_package;
+use crate::{
+    sdk::{
+        cargo_out_dir, cargo_path, clang_archiver_path, clang_c_compiler_path,
+        clang_cpp_compiler_path, clang_ranlib_path, clang_resource_dir, rustc_path, rustdoc_path,
+        shared_libraries_path, sysroot_path, zircon_build_path,
+    },
+    utils::strip_binary,
 };
-use crate::utils::strip_binary;
 
 use clap::{App, AppSettings, Arg, SubCommand};
 use failure::{bail, err_msg, format_err, Error, ResultExt};
@@ -67,14 +71,26 @@
     config: &FuchsiaConfig,
     target_options: &TargetOptions<'_, '_>,
     run_mode: RunMode,
+    cmx_path: &Option<PathBuf>,
     story_name: &str,
     mod_name: &str,
     params: &[&str],
     test_args: Option<&str>,
 ) -> Result<(), Error> {
     let source_path = PathBuf::from(&filename);
-    let stripped_source_path = strip_binary(&source_path)?;
-    let destination_path = copy_to_target(&stripped_source_path, verbose, config, target_options)?;
+    let target_string = match run_mode {
+        RunMode::Normal => {
+            let stripped_source_path = strip_binary(&source_path)?;
+            copy_to_target(&stripped_source_path, verbose, config, target_options)?
+        }
+        _ => {
+            if cmx_path.is_none() {
+                bail!("Run modes other than normal require a path to a cmx file");
+            }
+            make_package(target_options, &source_path, cmx_path.as_ref().unwrap())?
+        }
+    };
+
     let mut command_string = match run_mode {
         RunMode::Tiles => "tiles_ctl add ".to_string(),
         RunMode::Ermine => {
@@ -83,7 +99,7 @@
         RunMode::Run => "run ".to_string(),
         RunMode::Normal => "".to_string(),
     };
-    command_string.push_str(&destination_path);
+    command_string.push_str(&target_string);
 
     match run_mode {
         RunMode::Ermine => {
@@ -111,7 +127,7 @@
         println!("running {}", command_string);
     }
 
-    ssh(verbose, config, target_options, &command_string)?;
+    ssh(verbose, config, target_options, &command_string).context("ssh failed")?;
     Ok(())
 }
 
@@ -293,6 +309,7 @@
     pub disable_cross: bool,
     pub nocapture: bool,
     pub manifest_path: Option<PathBuf>,
+    pub cmx_path: Option<PathBuf>,
 }
 
 impl RunCargoOptions {
@@ -306,6 +323,7 @@
             mod_name: None,
             disable_cross: false,
             manifest_path: None,
+            cmx_path: None,
         }
     }
 
@@ -319,6 +337,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -332,6 +351,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross: self.disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -345,6 +365,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross: self.disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -358,6 +379,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross: self.disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -371,6 +393,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross: self.disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -384,6 +407,7 @@
             mod_name: mod_name.map(|name| name.to_string()),
             disable_cross: self.disable_cross,
             manifest_path: self.manifest_path.clone(),
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -397,6 +421,7 @@
             mod_name: self.mod_name.clone(),
             disable_cross: self.disable_cross,
             manifest_path: manifest_path,
+            cmx_path: self.cmx_path.clone(),
         }
     }
 
@@ -417,11 +442,11 @@
     }
 }
 
-fn get_triple_cpu(target_options: &TargetOptions<'_, '_>) -> String {
+pub fn get_triple_cpu(target_options: &TargetOptions<'_, '_>) -> String {
     if (target_options.config.fuchsia_arch) == X64 { "x86_64" } else { "aarch64" }.to_string()
 }
 
-fn get_target_triple(target_options: &TargetOptions<'_, '_>) -> String {
+pub fn get_target_triple(target_options: &TargetOptions<'_, '_>) -> String {
     let triple_cpu = get_triple_cpu(target_options);
 
     format!("{}-fuchsia", triple_cpu)
@@ -432,6 +457,7 @@
     sysroot_as_path: &PathBuf,
 ) -> Result<String, Error> {
     let target_triple = get_target_triple(target_options);
+    let zircon_build_dir = zircon_build_path(&target_options.config)?;
     let sysroot_lib_pathbuf = sysroot_as_path.join("lib");
     let sysroot_lib = sysroot_lib_pathbuf.to_string_lossy();
     let clang_resource_lib = clang_resource_dir(&target_triple)?.join(&target_triple).join("lib");
@@ -442,6 +468,16 @@
         "-Clink-arg=--pack-dyn-relocs=relr".to_string(),
         "-Clink-arg=--threads".to_string(),
         format!("-Clink-arg=-L{}", sysroot_lib),
+        format!(
+            "-Clink-arg=-L{}/user-{}-gcc.shlib/obj/system/ulib/fdio",
+            zircon_build_dir.to_string_lossy(),
+            target_options.config.fuchsia_arch
+        ),
+        format!(
+            "-Clink-arg=-L{}/user-{}-gcc.shlib/obj/system/ulib/syslog",
+            zircon_build_dir.to_string_lossy(),
+            target_options.config.fuchsia_arch
+        ),
         format!("-Clink-arg=-L{}", clang_resource_lib.to_string_lossy()),
         format!("-Clink-arg=--sysroot={}", sysroot_as_path.to_string_lossy()),
         format!("-Lnative={}", shared_libraries_path(target_options)?.to_string_lossy()),
@@ -461,6 +497,7 @@
     target_options: &TargetOptions<'_, '_>,
     additional_target_args: Option<&str>,
 ) -> Result<String, Error> {
+    let mut cmx_path;
     let tiles_arg = format!("--{}", RUN_WITH_TILES);
     let ermine_arg = format!(
         "--{} --story-name={} --mod-name={}",
@@ -468,6 +505,7 @@
         options.get_story_name(),
         options.get_mod_name()
     );
+    let cmx_arg = format!("--{}", CMX_PATH);
     let run_arg = format!("--{}", RUN_WITH_RUN);
     let nocapture_arg = format!("--{}", NOCAPTURE);
 
@@ -477,9 +515,9 @@
         fs::canonicalize(std::env::current_exe()?)?
     };
 
-    let mut runner_args = vec![
-        fargo_path.to_str().ok_or_else(|| err_msg("unable to convert path to utf8 encoding"))?,
-    ];
+    let mut runner_args = vec![fargo_path
+        .to_str()
+        .ok_or_else(|| err_msg("unable to convert path to utf8 encoding"))?];
 
     if options.verbose {
         runner_args.push("-v");
@@ -490,6 +528,19 @@
         runner_args.push(device_name);
     }
 
+    if let Some(ref passed_path) = options.cmx_path {
+        cmx_path = passed_path.to_string_lossy().to_string();
+        runner_args.push(&cmx_arg);
+        runner_args.push(&cmx_path);
+    } else {
+        match options.run_mode {
+            RunMode::Normal => (),
+            _ => {
+                bail!("Run modes other than normal require a path to a cmx file");
+            }
+        }
+    }
+
     runner_args.push(RUN_ON_TARGET);
 
     if nocapture {
@@ -540,6 +591,7 @@
 ///         mod_name: None,
 ///         disable_cross: false,
 ///         manifest_path: None,
+///         cmx_path: None,
 ///     },
 ///     "help",
 ///     &[],
@@ -613,7 +665,7 @@
         .env("RUSTDOC", rustdoc_path()?.to_string_lossy().as_ref())
         .env("RUSTDOCFLAGS", "--cap-lints allow -Z unstable-options")
         .env("FUCHSIA_SHARED_ROOT", shared_libraries_path(target_options)?)
-        .env("ZIRCON_BUILD_ROOT", zircon_build_path(target_options)?)
+        .env("ZIRCON_BUILD_ROOT", zircon_build_path(&target_options.config)?)
         .arg(subcommand)
         .args(target_args)
         .args(args);
@@ -718,6 +770,7 @@
 static NO_NET: &str = "no-net";
 static FX_RUN_PARAMS: &str = "fx-run-params";
 static MANIFEST_PATH: &str = "manifest-path";
+static CMX_PATH: &str = "cmx-path";
 
 static RELEASE_HELP: &str = "Build artifacts in release mode, with optimizations";
 
@@ -733,6 +786,9 @@
 
 static RUN_ON_TARGET: &str = "run-on-target";
 
+static MAKE_PACKAGE: &str = "make-package";
+static BINARY_PATH: &str = "binary-path";
+
 #[doc(hidden)]
 pub fn run() -> Result<(), Error> {
     let matches = App::new("fargo")
@@ -768,6 +824,12 @@
                 .global(true)
                 .help("Path to Cargo.toml"),
         )
+        .arg(
+            Arg::with_name(CMX_PATH)
+                .long(CMX_PATH)
+                .value_name(CMX_PATH)
+                .help("Path to sandbox file to use when running"),
+        )
         .subcommand(
             SubCommand::with_name("autotest")
                 .about("Auto build and test in Fuchsia device or emulator")
@@ -1078,6 +1140,17 @@
                 )
                 .about("Build rustc targeting Fuchsia"),
         )
+        .subcommand(
+            SubCommand::with_name(MAKE_PACKAGE)
+                .arg(
+                    Arg::with_name(BINARY_PATH)
+                        .long(BINARY_PATH)
+                        .value_name(BINARY_PATH)
+                        .required(true)
+                        .help("Path to the binary to package"),
+                )
+                .about("Make a Fuchsia package from an unstripped binary"),
+        )
         .get_matches();
 
     let verbose = matches.is_present("verbose");
@@ -1099,6 +1172,7 @@
         mod_name: None,
         disable_cross,
         manifest_path: None,
+        cmx_path: matches.value_of(CMX_PATH).map(|s| PathBuf::from(s)),
     };
 
     if verbose {
@@ -1274,7 +1348,7 @@
     }
 
     if matches.subcommand_matches("list-devices").is_some() {
-        return netls(verbose);
+        return netls(verbose, &target_options);
     }
 
     if let Some(start_matches) = matches.subcommand_matches(START) {
@@ -1336,6 +1410,7 @@
                 mod_name: None,
                 disable_cross: disable_cross,
                 manifest_path: None,
+                cmx_path: None,
             },
             subcommand,
             &cargo_params,
@@ -1365,6 +1440,7 @@
             &fuchsia_config,
             &target_options,
             run_mode,
+            &run_cargo_options.cmx_path,
             run_on_target_matches.value_of(STORY_NAME).unwrap_or(&random_story_name()),
             run_on_target_matches.value_of(MOD_NAME).unwrap_or(DEFAULT_MOD_NAME),
             args,
@@ -1409,5 +1485,20 @@
         return build_rustc(&rust_root, &target_options);
     }
 
+    if let Some(make_package_matches) = matches.subcommand_matches(MAKE_PACKAGE) {
+        let binary_path = PathBuf::from(
+            make_package_matches
+                .value_of(BINARY_PATH)
+                .expect(&format!("{} is a required parameter of {}", BINARY_PATH, MAKE_PACKAGE)),
+        );
+        let cmx_path = PathBuf::from(
+            make_package_matches
+                .value_of(CMX_PATH)
+                .expect(&format!("{} is a required parameter of {}", CMX_PATH, MAKE_PACKAGE)),
+        );
+        println!("make package {}", make_package(&target_options, &binary_path, &cmx_path)?);
+        return Ok(());
+    }
+
     Ok(())
 }
diff --git a/src/package.rs b/src/package.rs
new file mode 100644
index 0000000..810ca4c
--- /dev/null
+++ b/src/package.rs
@@ -0,0 +1,232 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use crate::{
+    get_target_triple,
+    sdk::{
+        amber_path, clang_resource_dir, cmc_path, fuchsia_dir, package_manager_path,
+        zircon_build_path, FuchsiaConfig, TargetOptions,
+    },
+    utils::strip_binary,
+};
+use failure::{bail, format_err, Error, ResultExt};
+use serde_json::json;
+use std::{
+    fs::{create_dir_all, File},
+    io::Write,
+    path::{Path, PathBuf},
+    process::Command,
+};
+
+fn validate_cmx_file(fuchsia_config: &FuchsiaConfig, cmx_path: &Path) -> Result<(), Error> {
+    let cmc = cmc_path(fuchsia_config)?;
+
+    let output = Command::new(cmc)
+        .arg("validate")
+        .arg(cmx_path)
+        .output()
+        .context("Running `cmc` to validate cmx file")?;
+
+    if !output.status.success() {
+        bail!("cmc returned error: {}", String::from_utf8_lossy(&output.stderr));
+    }
+
+    Ok(())
+}
+
+fn format_cmx_file(fuchsia_config: &FuchsiaConfig, cmx_path: &Path) -> Result<PathBuf, Error> {
+    let destination_path = format!(
+        "/tmp/{}",
+        cmx_path
+            .file_name()
+            .ok_or(format_err!("file_name failed on {:#?}", cmx_path))?
+            .to_string_lossy()
+    );
+
+    let cmc = cmc_path(fuchsia_config)?;
+
+    let output = Command::new(cmc)
+        .arg("format")
+        .arg(cmx_path)
+        .arg("-o")
+        .arg(&destination_path)
+        .output()
+        .context("Running `cmc` to format cmx file")?;
+
+    if !output.status.success() {
+        bail!("cmc returned error: {}", String::from_utf8_lossy(&output.stderr));
+    }
+
+    Ok(PathBuf::from(destination_path))
+}
+
+fn write_manifest_file(
+    target_options: &TargetOptions<'_, '_>,
+    target: &Path,
+    binary_path: &Path,
+    package_path: &Path,
+    cmx_path: &Path,
+    package_name: &str,
+) -> Result<(), Error> {
+    let mut manifest = File::create(&target)?;
+    let zircon_build = zircon_build_path(&target_options.config)?;
+    let libc_path = format!(
+        "{}/user-{}-gcc.shlib/obj/system/ulib/c/libc.so",
+        zircon_build.to_string_lossy(),
+        target_options.config.fuchsia_arch
+    );
+    let fdio_path = format!(
+        "{}/user-{}-gcc.shlib/obj/system/ulib/fdio/libfdio.so",
+        zircon_build.to_string_lossy(),
+        target_options.config.fuchsia_arch
+    );
+    let target_triple = get_target_triple(target_options);
+    let clang_resource_lib = clang_resource_dir(&target_triple)?.join(&target_triple).join("lib");
+    let libsyslog_path = format!(
+        "{}/user-{}-gcc.shlib/obj/system/ulib/syslog/libsyslog.so",
+        zircon_build.to_string_lossy(),
+        target_options.config.fuchsia_arch
+    );
+    writeln!(
+        manifest,
+        r#"bin/app={}
+lib/ld.so.1={}
+lib/libfdio.so={}
+lib/libunwind.so.1={}/libunwind.so.1
+lib/libsyslog.so={}
+meta/package={}
+meta/{}.cmx={}
+"#,
+        binary_path.to_string_lossy(),
+        libc_path,
+        fdio_path,
+        clang_resource_lib.to_string_lossy(),
+        libsyslog_path,
+        package_path.to_string_lossy(),
+        package_name,
+        cmx_path.to_string_lossy(),
+    )?;
+    Ok(())
+}
+
+fn write_package_file(target: &Path, package_name: &str) -> Result<(), Error> {
+    let mut package = File::create(&target)?;
+    let package_contents = json!({
+        "name": package_name,
+        "version": "0"
+    });
+    writeln!(package, "{}", package_contents.to_string())?;
+    Ok(())
+}
+
+fn pm_build(
+    target_options: &TargetOptions<'_, '_>,
+    manifest_path: &Path,
+    output_path: &Path,
+) -> Result<(), Error> {
+    let pm = package_manager_path(target_options.config)?;
+
+    let fuchsia_dir = fuchsia_dir()?;
+    let dev_key_path = fuchsia_dir.join("build/development.key");
+    let output = Command::new(pm)
+        .arg("-k")
+        .arg(dev_key_path)
+        .arg("-o")
+        .arg(&output_path)
+        .arg("-m")
+        .arg(&manifest_path)
+        .arg("build")
+        .arg("-depfile")
+        .arg("-blobsfile")
+        .arg("-blobs-manifest")
+        .output()
+        .context("Running `cmc` to format cmx file")?;
+
+    if !output.status.success() {
+        bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
+    }
+
+    Ok(())
+}
+
+fn pm_archive(
+    target_options: &TargetOptions<'_, '_>,
+    output_path: &Path,
+    manifest_path: &Path,
+) -> Result<(), Error> {
+    let pm = package_manager_path(target_options.config)?;
+
+    let output = Command::new(pm)
+        .arg("-o")
+        .arg(&output_path)
+        .arg("-m")
+        .arg(&manifest_path)
+        .arg("archive")
+        .output()
+        .context("Running `cmc` to format cmx file")?;
+
+    if !output.status.success() {
+        bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
+    }
+
+    Ok(())
+}
+
+fn pm_publish(target_options: &TargetOptions<'_, '_>, output_path: &Path) -> Result<(), Error> {
+    let pm = package_manager_path(target_options.config)?;
+    let tuf_root = amber_path(target_options.config)?;
+    let output = Command::new(pm)
+        .arg("publish")
+        .arg("-a")
+        .arg("-f")
+        .arg(output_path)
+        .arg("-r")
+        .arg(tuf_root)
+        .arg("-vt")
+        .arg("-v")
+        .output()
+        .context("Running `cmc` to format cmx file")?;
+
+    if !output.status.success() {
+        bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
+    }
+
+    Ok(())
+}
+
+pub fn make_package(
+    target_options: &TargetOptions<'_, '_>,
+    binary_path: &Path,
+    cmx_path: &Path,
+) -> Result<String, Error> {
+    let binary_parent =
+        binary_path.parent().expect(&format!("Can't get parent of {:#?}", binary_path));
+    let mut package_name = binary_path
+        .file_name()
+        .expect("file_name failed on binary_path")
+        .to_string_lossy()
+        .to_string();
+    package_name.push_str("_fargo");
+    let output_path = binary_parent.join(&package_name);
+    create_dir_all(&output_path).context("create_dir_all failed")?;
+    let stripped_binary_path = strip_binary(binary_path)?;
+    validate_cmx_file(&target_options.config, cmx_path)?;
+    let formatted_path = format_cmx_file(&target_options.config, cmx_path)?;
+    let package_path = PathBuf::from("/tmp/package");
+    write_package_file(&package_path, &package_name)?;
+    let manifest_path = PathBuf::from("/tmp/manifest");
+    write_manifest_file(
+        &target_options,
+        &manifest_path,
+        &stripped_binary_path,
+        &package_path,
+        &formatted_path,
+        &package_name,
+    )?;
+    pm_build(&target_options, &manifest_path, &output_path).context("pm_build failed")?;
+    pm_archive(&target_options, &output_path, &manifest_path).context("pm_archive failed")?;
+    pm_publish(&target_options, &output_path.join(format!("{}-0.far", package_name)))
+        .context("pm_publish failed")?;
+    Ok(format!("fuchsia-pkg://fuchsia.com/{}#meta/{}.cmx", package_name, package_name))
+}
diff --git a/src/sdk.rs b/src/sdk.rs
index c5b1c31..9dfcbaa 100644
--- a/src/sdk.rs
+++ b/src/sdk.rs
@@ -104,6 +104,22 @@
     Ok(fuchsia_dir.join("garnet").join("target").join(target_triple).join("debug"))
 }
 
+pub fn host_out_dir(config: &FuchsiaConfig) -> Result<PathBuf, Error> {
+    Ok(target_out_dir(config)?.join("host_x64"))
+}
+
+pub fn package_manager_path(config: &FuchsiaConfig) -> Result<PathBuf, Error> {
+    Ok(host_out_dir(config)?.join("pm"))
+}
+
+pub fn cmc_path(config: &FuchsiaConfig) -> Result<PathBuf, Error> {
+    Ok(host_out_dir(config)?.join("cmc"))
+}
+
+pub fn amber_path(config: &FuchsiaConfig) -> Result<PathBuf, Error> {
+    Ok(target_out_dir(config)?.join("amber-files"))
+}
+
 pub fn strip_tool_path() -> Result<PathBuf, Error> {
     Ok(clang_base_path()?.join("bin/llvm-objcopy"))
 }
@@ -118,11 +134,8 @@
         .join("sysroot"))
 }
 
-pub fn zircon_build_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
-    let fuchsia_dir = fuchsia_dir()?;
-    let build_name = if options.config.fuchsia_arch == X64 { "build-x64" } else { "build-arm64" };
-    let zircon_build = fuchsia_dir.join("out").join("build-zircon").join(build_name);
-    Ok(zircon_build)
+pub fn zircon_build_path(config: &FuchsiaConfig) -> Result<PathBuf, Error> {
+    Ok(PathBuf::from(&config.zircon_build_dir))
 }
 
 pub fn shared_libraries_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
@@ -207,6 +220,7 @@
 #[derive(Debug, Default)]
 pub struct FuchsiaConfig {
     pub fuchsia_build_dir: String,
+    pub zircon_build_dir: String,
     pub fuchsia_arch: String,
 }
 
@@ -221,8 +235,11 @@
         let fx_exec_result =
             Command::new(fx_script).args(args).current_dir(&fuchsia_dir).output()?;
         let result = str::from_utf8(&fx_exec_result.stdout)?.trim().to_string();
-        let mut config =
-            FuchsiaConfig { fuchsia_build_dir: String::from(""), fuchsia_arch: String::from("") };
+        let mut config = FuchsiaConfig {
+            fuchsia_build_dir: String::from(""),
+            zircon_build_dir: String::from(""),
+            fuchsia_arch: String::from(""),
+        };
         for one_line in result.lines() {
             let parts: Vec<&str> = one_line.split("=").collect();
             const QUOTE: char = '\'';
@@ -230,6 +247,9 @@
                 "FUCHSIA_BUILD_DIR" => {
                     config.fuchsia_build_dir = String::from(parts[1].trim_matches(QUOTE))
                 }
+                "ZIRCON_BUILDROOT" => {
+                    config.zircon_build_dir = String::from(parts[1].trim_matches(QUOTE))
+                }
                 "FUCHSIA_ARCH" => config.fuchsia_arch = String::from(parts[1].trim_matches(QUOTE)),
                 _ => (),
             }
diff --git a/src/utils.rs b/src/utils.rs
index bf942d9..bd7c0c1 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -4,7 +4,7 @@
 
 use crate::sdk::strip_tool_path;
 use failure::{bail, format_err, Error, ResultExt};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::time::Duration;
 use uname::uname;
@@ -19,7 +19,7 @@
     uname().expect("uname failed").sysname == "Darwin"
 }
 
-pub fn strip_binary(binary: &PathBuf) -> Result<PathBuf, Error> {
+pub fn strip_binary(binary: &Path) -> Result<PathBuf, Error> {
     let file_name = binary.file_name().ok_or(format_err!("file_name failed on {:#?}", binary))?;
     let new_file_name = file_name.to_string_lossy().into_owned() + "_stripped";
     let target_path = binary