Refactor run_cargo to support better nested escaped parameters

Also fix a lot of arm64 support bugs.

Change-Id: I3b7a921160512e9ef32954e62ca790c718cefe1d
diff --git a/README.md b/README.md
index 3435bdb..081f019 100644
--- a/README.md
+++ b/README.md
@@ -13,19 +13,25 @@
         -v, --verbose     Print verbose output while performing commands
 
     OPTIONS:
-        -N, --device-name <device-name>    Name of device to target, needed if there are multiple devices visible on the network
+        -N, --device-name <device-name>
+                Name of device to target, needed if there are multiple devices visible on
+                the network
+        -T, --target-cpu <target-cpu>
+                Architecture of target device [default: x64]  [values: x64, arm64]
+
 
     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
-        cargo                Run a cargo command for Fuchsia. Use -- to indicate that all following arguments should be passed to
-                             cargo.
+        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
-        configure            Run a configure script for the cross compilation environment
-        create-facade        Create an in-tree facade crate for a FIDL interface.
+        configure            Run a configure script for the cross compilation
+                             environment
         enable-networking    Enable networking for a running emulator
-        help                 Prints this message or the help of the given subcommand(s)
+        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.
         pkg-config           Run pkg-config for the cross compilation environment
@@ -97,6 +103,14 @@
 Additionally, if you are using qemu you need to enable networking, otherwise fargo won't be able to
 copy the binary onto then fuchsia machine to run the tests.
 
+### Escaping parameters
+
+Sometimes you want to pass parameters through fargo and cargo and on to something like rustc. To make this easier fargo will convert a "++" parameter to "--" when invoking cargo. For example, the following command:
+
+    fargo cargo rustc -- ++ --emit=llvm-ir
+
+will get cargo to cause rustc to emil llvm ir files.
+
 ## Getting help
 
 For problems getting the Fuchsia build to complete, the #fuchsia IRC channel on
diff --git a/src/lib.rs b/src/lib.rs
index b52cf96..ca34272 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -149,7 +149,7 @@
     verbose: bool, release: bool, no_run: bool, target_options: &TargetOptions, test_target: &str,
     params: &[&str], target_params: Option<&str>,
 ) -> Result<(), Error> {
-    let mut args = vec!["test"];
+    let mut args = vec![];
 
     if !test_target.is_empty() {
         args.push("--test");
@@ -167,16 +167,30 @@
     if target_params.is_some() {
         let formatted_target_params = format!("--args={}", target_params.unwrap());
         run_cargo(
-            verbose,
-            release,
-            false,
+            RunCargoOptions {
+                verbose,
+                release,
+                set_root_view: false,
+            },
+            "test",
             &args,
             target_options,
             None,
             Some(&formatted_target_params),
         )?;
     } else {
-        run_cargo(verbose, release, false, &args, target_options, None, None)?;
+        run_cargo(
+            RunCargoOptions {
+                verbose,
+                release,
+                set_root_view: false,
+            },
+            "test",
+            &args,
+            target_options,
+            None,
+            None,
+        )?;
     }
 
     Ok(())
@@ -185,39 +199,49 @@
 fn build_binary(
     verbose: bool, release: bool, target_options: &TargetOptions, params: &[&str]
 ) -> Result<(), Error> {
-    let mut args = vec!["build"];
-    for param in params {
-        args.push(param);
-    }
-
-    run_cargo(verbose, release, false, &args, target_options, None, None)
+    run_cargo(
+        RunCargoOptions {
+            verbose,
+            release,
+            set_root_view: false,
+        },
+        "build",
+        params,
+        target_options,
+        None,
+        None,
+    )
 }
 
 fn check_binary(
     verbose: bool, release: bool, target_options: &TargetOptions, params: &[&str]
 ) -> Result<(), Error> {
-    let mut args = vec!["check"];
-    for param in params {
-        args.push(param);
-    }
-
-    run_cargo(verbose, release, false, &args, target_options, None, None)
+    run_cargo(
+        RunCargoOptions {
+            verbose,
+            release,
+            set_root_view: false,
+        },
+        "check",
+        params,
+        target_options,
+        None,
+        None,
+    )
 }
 
 fn run_binary(
     verbose: bool, release: bool, set_root_view: bool, target_options: &TargetOptions,
     params: &[&str],
 ) -> Result<(), Error> {
-    let mut args = vec!["run"];
-    for param in params {
-        args.push(param);
-    }
-
     run_cargo(
-        verbose,
-        release,
-        set_root_view,
-        &args,
+        RunCargoOptions {
+            verbose,
+            release,
+            set_root_view,
+        },
+        "run",
+        params,
         target_options,
         None,
         None,
@@ -226,8 +250,19 @@
 }
 
 fn load_driver(verbose: bool, release: bool, target_options: &TargetOptions) -> Result<(), Error> {
-    let args = vec!["build"];
-    run_cargo(verbose, release, false, &args, target_options, None, None)?;
+    let args = vec![];
+    run_cargo(
+        RunCargoOptions {
+            verbose,
+            release,
+            set_root_view: false,
+        },
+        "build",
+        &args,
+        target_options,
+        None,
+        None,
+    )?;
     let cwd = std::env::current_dir()?;
     let package = cwd.file_name()
         .ok_or(err_msg("No current directory"))?
@@ -243,6 +278,12 @@
     Ok(())
 }
 
+pub struct RunCargoOptions {
+    pub verbose: bool,
+    pub release: bool,
+    pub set_root_view: bool,
+}
+
 /// Runs the cargo tool configured to target Fuchsia. When used as a library,
 /// the runner options must contain the path to fargo or some other program
 /// that implements the `run-on-target` subcommand in a way compatible with
@@ -251,26 +292,56 @@
 /// # Examples
 ///
 /// ```
-/// use fargo::{run_cargo, TargetOptions};
+/// use fargo::{run_cargo, RunCargoOptions, TargetOptions};
 ///
-/// let target_options = TargetOptions::new(true, None);
-/// run_cargo(false, true, false, &["--help"], &target_options, None, None);
+/// let target_options = TargetOptions::new(true, "x64", None);
+/// run_cargo(
+///     RunCargoOptions {
+///         verbose: false,
+///         release: true,
+///         set_root_view: false,
+///     },
+///     "help",
+///     &[],
+///     &target_options,
+///     None,
+///     None,
+/// );
 /// ```
 pub fn run_cargo(
-    verbose: bool, release: bool, set_root_view: bool, args: &[&str],
-    target_options: &TargetOptions, runner: Option<PathBuf>, additional_target_args: Option<&str>,
+    options: RunCargoOptions, subcommand: &str, args: &[&str], target_options: &TargetOptions,
+    runner: Option<PathBuf>, additional_target_args: Option<&str>,
 ) -> Result<(), Error> {
-    let set_root_view_arg = format!("--{}", SET_ROOT_VIEW);
-    let mut target_args = vec!["--target", "x86_64-unknown-fuchsia"];
+    if options.verbose {
+        println!("target_options = {:?}", target_options);
+    }
 
-    if release {
+    let set_root_view_arg = format!("--{}", SET_ROOT_VIEW);
+
+    let triple_cpu = if target_options.target_cpu == X64 {
+        "x86_64"
+    } else {
+        "aarch64"
+    };
+    let target_triple = format!("{}-unknown-fuchsia", triple_cpu);
+    let mut target_args = vec!["--target", &target_triple];
+
+    if options.release {
         target_args.push("--release");
     }
 
-    if verbose {
+    if options.verbose {
+        println!(
+            "target_options.target_cpu = {:?}",
+            target_options.target_cpu
+        );
+        println!("triple_cpu = {:?}", triple_cpu);
+        println!("target_triple = {:?}", target_triple);
         println!("target_args = {:?}", target_args);
     }
 
+    let target_triple_uc = format!("{}_unknown_fuchsia", triple_cpu).to_uppercase();
+
     let fargo_path = if runner.is_some() {
         runner.unwrap()
     } else {
@@ -283,7 +354,7 @@
             .ok_or_else(|| err_msg("unable to convert path to utf8 encoding"))?,
     ];
 
-    if verbose {
+    if options.verbose {
         runner_args.push("-v");
     }
 
@@ -294,7 +365,7 @@
 
     runner_args.push("run-on-target");
 
-    if set_root_view {
+    if options.set_root_view {
         runner_args.push(&set_root_view_arg);
     }
 
@@ -304,7 +375,7 @@
 
     let fargo_command = runner_args.join(" ");
 
-    if verbose {
+    if options.verbose {
         println!("fargo_command: {:?}", fargo_command);
     }
 
@@ -313,21 +384,38 @@
     let sysroot_as_path = sysroot_path(target_options)?;
     let sysroot_as_str = sysroot_as_path.to_str().unwrap();
 
-    cmd.env("CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUNNER", fargo_command)
+    let args: Vec<&str> = args.iter()
+        .map(|a| if *a == "++" { "--" } else { *a })
+        .collect();
+
+    let runner_env_name = format!("CARGO_TARGET_{}_RUNNER", target_triple_uc);
+    let rustflags_env_name = format!("CARGO_TARGET_{}_RUSTFLAGS", target_triple_uc);
+    let linker_env_name = format!("CARGO_TARGET_{}_LINKER", target_triple_uc);
+    let rustc_env_name = format!("CARGO_TARGET_{}_RUSTC", target_triple_uc);
+
+    if options.verbose {
+        println!("runner_env_name: {:?}", runner_env_name);
+        println!("rustflags_env_name: {:?}", rustflags_env_name);
+        println!("linker_env_name: {:?}", linker_env_name);
+        println!("rustc_env_name: {:?}", rustc_env_name);
+    }
+
+    cmd.env(runner_env_name, fargo_command)
         .env(
-            "CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUSTFLAGS",
+            rustflags_env_name,
             format!(
-                "-C link-arg=--target=x86_64-unknown-fuchsia -C link-arg=--sysroot={} -Lnative={}",
+                "-C link-arg=--target={}-unknown-fuchsia -C link-arg=--sysroot={} -Lnative={}",
+                triple_cpu,
                 sysroot_as_str,
                 shared_libraries_path(target_options)?.to_str().unwrap(),
             ),
         )
         .env(
-            "CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_LINKER",
+            linker_env_name,
             clang_linker_path(target_options)?.to_str().unwrap(),
         )
         .env(
-            "CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUSTC",
+            rustc_env_name,
             rustc_path(target_options)?.to_str().unwrap(),
         )
         .env(
@@ -350,10 +438,11 @@
         .env("PKG_CONFIG_LIBDIR", pkg_path)
         .env("FUCHSIA_GEN_ROOT", target_gen_dir(target_options)?)
         .env("FIDL_GEN_ROOT", fidl2_target_gen_dir(target_options)?)
-        .args(args)
-        .args(target_args);
+        .arg(subcommand)
+        .args(target_args)
+        .args(args);
 
-    if verbose {
+    if options.verbose {
         println!("cargo cmd: {:?}", cmd);
     }
 
@@ -376,6 +465,8 @@
 static X64: &str = "x64";
 static ARM64: &str = "arm64";
 
+static SUBCOMMAND: &str = "subcommand";
+
 #[doc(hidden)]
 pub fn run() -> Result<(), Error> {
     let matches = App::new("fargo")
@@ -560,7 +651,8 @@
                     "Run a cargo command for Fuchsia. Use -- to indicate that all following \
                      arguments should be passed to cargo.",
                 )
-                .arg(Arg::with_name("cargo_params").index(1).multiple(true)),
+                .arg(Arg::with_name(SUBCOMMAND).required(true))
+                .arg(Arg::with_name("cargo_params").index(2).multiple(true)),
         )
         .subcommand(
             SubCommand::with_name("run-on-target")
@@ -759,14 +851,18 @@
     }
 
     if let Some(cargo_matches) = matches.subcommand_matches("cargo") {
+        let subcommand = cargo_matches.value_of(SUBCOMMAND).unwrap();
         let cargo_params = cargo_matches
             .values_of("cargo_params")
             .map(|x| x.collect())
             .unwrap_or_else(|| vec![]);
         return run_cargo(
-            verbose,
-            false,
-            false,
+            RunCargoOptions {
+                verbose,
+                release: false,
+                set_root_view: false,
+            },
+            subcommand,
             &cargo_params,
             &target_options,
             None,
diff --git a/src/sdk.rs b/src/sdk.rs
index 8938e6f..faa62cc 100644
--- a/src/sdk.rs
+++ b/src/sdk.rs
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+use X64;
 use failure::Error;
 use std::env;
 use std::fs::File;
@@ -29,7 +30,7 @@
     /// ```
     /// use fargo::TargetOptions;
     ///
-    /// let target_options = TargetOptions::new(true, Some("ivy-donut-grew-stoop"));
+    /// let target_options = TargetOptions::new(true, "x64", Some("ivy-donut-grew-stoop"));
     /// ```
 
     pub fn new(
@@ -147,7 +148,7 @@
 }
 
 pub fn shared_libraries_path(options: &TargetOptions) -> Result<PathBuf, Error> {
-    let shared_name = if options.target_cpu == "x64" {
+    let shared_name = if options.target_cpu == X64 {
         "x64-shared"
     } else {
         "arm64-shared"