Allow option to specify app name in manifest

Some apps, like Voila, use something other than “app” for their app
name in their manifest.

Also rearrange options so the sandbox options only appear on the
commands that need them.

Also took the first step towards making fargo test able to use “run” to
run the tests but that will take more investigation.

Change-Id: I9cd9622d8549feee36424cb46b249ef62ea962bb
diff --git a/src/lib.rs b/src/lib.rs
index cbee4f6..4e59180 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -74,6 +74,7 @@
     cmx_path: &Option<PathBuf>,
     story_name: &str,
     mod_name: &str,
+    app_name: &str,
     params: &[&str],
     test_args: Option<&str>,
 ) -> Result<(), Error> {
@@ -87,7 +88,7 @@
             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())?
+            make_package(target_options, &source_path, cmx_path.as_ref().unwrap(), app_name)?
         }
     };
 
@@ -315,6 +316,7 @@
     pub nocapture: bool,
     pub manifest_path: Option<PathBuf>,
     pub cmx_path: Option<PathBuf>,
+    pub app_name: Option<String>,
 }
 
 impl RunCargoOptions {
@@ -350,6 +352,14 @@
         Self { manifest_path, ..self.clone() }
     }
 
+    pub fn cmx_path(&self, cmx_path: Option<PathBuf>) -> RunCargoOptions {
+        Self { cmx_path, ..self.clone() }
+    }
+
+    pub fn app_name(&self, app_name: &Option<&str>) -> RunCargoOptions {
+        Self { app_name: app_name.map(|name| name.to_string()), ..self.clone() }
+    }
+
     pub fn get_story_name(&self) -> String {
         if let Some(ref name) = self.story_name {
             name.clone()
@@ -431,6 +441,7 @@
         options.get_mod_name()
     );
     let cmx_arg = format!("--{}", CMX_PATH);
+    let app_name_arg = format!("--{}", APP_NAME);
     let run_arg = format!("--{}", RUN_WITH_RUN);
     let nocapture_arg = format!("--{}", NOCAPTURE);
 
@@ -453,6 +464,8 @@
         runner_args.push(device_name);
     }
 
+    runner_args.push(RUN_ON_TARGET);
+
     if let Some(ref passed_path) = options.cmx_path {
         cmx_path = passed_path.to_string_lossy().to_string();
         runner_args.push(&cmx_arg);
@@ -466,8 +479,6 @@
         }
     }
 
-    runner_args.push(RUN_ON_TARGET);
-
     if nocapture {
         runner_args.push(&nocapture_arg);
     }
@@ -483,6 +494,11 @@
         runner_args.push(&args_for_target);
     }
 
+    if let Some(app_name) = options.app_name.as_ref() {
+        runner_args.push(&app_name_arg);
+        runner_args.push(&app_name);
+    }
+
     Ok(runner_args.join(" "))
 }
 
@@ -672,6 +688,8 @@
 static STORY_NAME: &str = "story-name";
 static MOD_NAME: &str = "mod-name";
 static DEFAULT_MOD_NAME: &str = "fargo";
+static APP_NAME: &str = "app-name";
+static DEFAULT_APP_NAME: &str = "app";
 
 static CHECK: &str = "check";
 static RELEASE: &str = "release";
@@ -716,7 +734,7 @@
 
 #[doc(hidden)]
 pub fn run() -> Result<(), Error> {
-    let matches = App::new("fargo")
+    let global_matches = App::new("fargo")
         .version("v0.2.0")
         .setting(AppSettings::GlobalVersion)
         .setting(AppSettings::ArgRequiredElseHelp)
@@ -749,15 +767,15 @@
                 .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")
+                .arg(
+                    Arg::with_name(CMX_PATH)
+                        .long(CMX_PATH)
+                        .value_name(CMX_PATH)
+                        .help("Path to sandbox file to use when running"),
+                )
                 .arg(Arg::with_name(RELEASE).long(RELEASE).help("Build release"))
                 .arg(
                     Arg::with_name(NOCAPTURE)
@@ -799,6 +817,13 @@
                         .value_name("args")
                         .help("arguments to pass to the test runner"),
                 )
+                .arg(
+                    Arg::with_name(CMX_PATH)
+                        .long(CMX_PATH)
+                        .value_name(CMX_PATH)
+                        .help("Path to sandbox file to use when running"),
+                )
+                .arg(Arg::with_name(RUN_WITH_RUN).long(RUN_WITH_RUN).help("Use run to run tests."))
                 .arg(Arg::with_name("test_params").index(1).multiple(true)),
         )
         .subcommand(
@@ -907,6 +932,19 @@
                         .help("Name of mod to pass to sessionctl"),
                 )
                 .arg(
+                    Arg::with_name(APP_NAME)
+                        .long(APP_NAME)
+                        .value_name(APP_NAME)
+                        .default_value(DEFAULT_APP_NAME)
+                        .help("Name of app as it appears in the manifest file."),
+                )
+                .arg(
+                    Arg::with_name(CMX_PATH)
+                        .long(CMX_PATH)
+                        .value_name(CMX_PATH)
+                        .help("Path to sandbox file to use when running"),
+                )
+                .arg(
                     Arg::with_name(RUN_WITH_TILES)
                         .long(RUN_WITH_TILES)
                         .help("Use tiles_ctl add to run binary."),
@@ -1002,6 +1040,12 @@
                         .help("arguments to pass to the test runner"),
                 )
                 .arg(
+                    Arg::with_name(CMX_PATH)
+                        .long(CMX_PATH)
+                        .value_name(CMX_PATH)
+                        .help("Path to sandbox file to use when running"),
+                )
+                .arg(
                     Arg::with_name(NOCAPTURE)
                         .long(NOCAPTURE)
                         .help("Display all output when running tests."),
@@ -1031,6 +1075,13 @@
                         .value_name(MOD_NAME)
                         .help("Name of mod to pass to sessionctl"),
                 )
+                .arg(
+                    Arg::with_name(APP_NAME)
+                        .long(APP_NAME)
+                        .value_name(APP_NAME)
+                        .default_value(DEFAULT_APP_NAME)
+                        .help("Name of app"),
+                )
                 .arg(Arg::with_name("run_on_target_params").index(1).multiple(true))
                 .setting(AppSettings::Hidden),
         )
@@ -1074,42 +1125,53 @@
                         .required(true)
                         .help("Path to the binary to package"),
                 )
+                .arg(
+                    Arg::with_name(CMX_PATH)
+                        .long(CMX_PATH)
+                        .value_name(CMX_PATH)
+                        .help("Path to sandbox file to use when running"),
+                )
+                .arg(
+                    Arg::with_name(APP_NAME)
+                        .long(APP_NAME)
+                        .value_name(APP_NAME)
+                        .default_value(DEFAULT_APP_NAME)
+                        .help("Name of app"),
+                )
                 .about("Make a Fuchsia package from an unstripped binary"),
         )
         .get_matches();
 
-    let verbose = matches.is_present("verbose");
-    let disable_cross = matches.is_present(DISABLE_CROSS_ENV);
+    let verbose = global_matches.is_present("verbose");
+    let disable_cross = global_matches.is_present(DISABLE_CROSS_ENV);
 
     let fuchsia_config = FuchsiaConfig::new_from_fx_exec()?;
     if verbose {
         println!("fuchsia_config = {:#?}", fuchsia_config);
     }
 
-    let target_options = TargetOptions::new(&fuchsia_config, matches.value_of("device-name"));
+    let target_options =
+        TargetOptions::new(&fuchsia_config, global_matches.value_of("device-name"));
 
-    let run_cargo_options = RunCargoOptions {
-        verbose,
-        cmx_path: matches.value_of(CMX_PATH).map(|s| PathBuf::from(s)),
-        ..RunCargoOptions::default()
-    };
+    let run_cargo_options = RunCargoOptions { verbose, ..RunCargoOptions::default() };
 
     if verbose {
         println!("target_options = {:#?}", target_options);
     }
 
-    if let Some(autotest_matches) = matches.subcommand_matches("autotest") {
+    if let Some(autotest_matches) = global_matches.subcommand_matches("autotest") {
         let manifest_path = convert_manifest_path(&autotest_matches.value_of(MANIFEST_PATH));
         return autotest(
             &run_cargo_options
                 .release(autotest_matches.is_present(RELEASE))
                 .manifest_path(manifest_path)
+                .cmx_path(autotest_matches.value_of(CMX_PATH).map(|s| PathBuf::from(s)))
                 .nocapture(autotest_matches.is_present(NOCAPTURE)),
             &target_options,
         );
     }
 
-    if let Some(test_matches) = matches.subcommand_matches(TEST) {
+    if let Some(test_matches) = global_matches.subcommand_matches(TEST) {
         let mut params = vec![];
         if let Some(package) = test_matches.value_of("package") {
             params.push("--package");
@@ -1129,8 +1191,15 @@
 
         let test_args = test_matches.value_of("test_args");
         let manifest_path = convert_manifest_path(&test_matches.value_of(MANIFEST_PATH));
+        let run_mode = run_switches_to_mode(
+            test_matches.is_present(RUN_WITH_TILES),
+            test_matches.is_present(RUN_WITH_RUN),
+            test_matches.is_present(RUN_WITH_SESSIONCTL),
+        );
         return run_tests(
             &run_cargo_options
+                .cmx_path(test_matches.value_of(CMX_PATH).map(|s| PathBuf::from(s)))
+                .run_mode(run_mode)
                 .release(test_matches.is_present(RELEASE))
                 .manifest_path(manifest_path)
                 .nocapture(test_matches.is_present(NOCAPTURE)),
@@ -1141,7 +1210,7 @@
         );
     }
 
-    if let Some(build_matches) = matches.subcommand_matches("build") {
+    if let Some(build_matches) = global_matches.subcommand_matches("build") {
         let mut params = vec![];
         if let Some(package) = build_matches.value_of("package") {
             params.push("--package");
@@ -1177,7 +1246,7 @@
         return Ok(());
     }
 
-    if let Some(check_matches) = matches.subcommand_matches(CHECK) {
+    if let Some(check_matches) = global_matches.subcommand_matches(CHECK) {
         let mut params = vec![];
         if let Some(example) = check_matches.value_of(EXAMPLE) {
             params.push("--example");
@@ -1213,7 +1282,7 @@
         return Ok(());
     }
 
-    if let Some(run_matches) = matches.subcommand_matches(RUN) {
+    if let Some(run_matches) = global_matches.subcommand_matches(RUN) {
         let mut params = vec![];
         if let Some(package) = run_matches.value_of("package") {
             params.push("--package");
@@ -1240,13 +1309,15 @@
                 .run_mode(run_mode)
                 .story_name(&run_matches.value_of(STORY_NAME))
                 .mod_name(&run_matches.value_of(MOD_NAME))
-                .manifest_path(manifest_path),
+                .app_name(&run_matches.value_of(APP_NAME))
+                .manifest_path(manifest_path)
+                .cmx_path(run_matches.value_of(CMX_PATH).map(|s| PathBuf::from(s))),
             &target_options,
             &params,
         );
     }
 
-    if let Some(load_driver_matches) = matches.subcommand_matches("load-driver") {
+    if let Some(load_driver_matches) = global_matches.subcommand_matches("load-driver") {
         return load_driver(
             &run_cargo_options.release(load_driver_matches.is_present(RELEASE)),
             &fuchsia_config,
@@ -1254,7 +1325,7 @@
         );
     }
 
-    if let Some(doc_matches) = matches.subcommand_matches(DOC) {
+    if let Some(doc_matches) = global_matches.subcommand_matches(DOC) {
         let manifest_path = convert_manifest_path(&doc_matches.value_of(MANIFEST_PATH));
         return build_doc(
             &run_cargo_options
@@ -1266,11 +1337,11 @@
         );
     }
 
-    if matches.subcommand_matches("list-devices").is_some() {
+    if global_matches.subcommand_matches("list-devices").is_some() {
         return netls(verbose, &target_options);
     }
 
-    if let Some(start_matches) = matches.subcommand_matches(START) {
+    if let Some(start_matches) = global_matches.subcommand_matches(START) {
         let fx_run_params =
             start_matches.values_of(FX_RUN_PARAMS).map(|x| x.collect()).unwrap_or_else(|| vec![]);
 
@@ -1285,15 +1356,15 @@
         );
     }
 
-    if matches.subcommand_matches("stop").is_some() {
+    if global_matches.subcommand_matches("stop").is_some() {
         return stop_emulator();
     }
 
-    if matches.subcommand_matches("enable-networking").is_some() {
+    if global_matches.subcommand_matches("enable-networking").is_some() {
         return enable_networking();
     }
 
-    if let Some(restart_matches) = matches.subcommand_matches(RESTART) {
+    if let Some(restart_matches) = global_matches.subcommand_matches(RESTART) {
         stop_emulator()?;
 
         let fx_run_params =
@@ -1310,11 +1381,11 @@
         );
     }
 
-    if matches.subcommand_matches("ssh").is_some() {
+    if global_matches.subcommand_matches("ssh").is_some() {
         return ssh(verbose, &fuchsia_config, &target_options, "");
     }
 
-    if let Some(cargo_matches) = matches.subcommand_matches("cargo") {
+    if let Some(cargo_matches) = global_matches.subcommand_matches("cargo") {
         let subcommand =
             cargo_matches.value_of(SUBCOMMAND).expect("The cargo command requires a subcommand");
         let cargo_params =
@@ -1330,6 +1401,7 @@
                 disable_cross: disable_cross,
                 manifest_path: None,
                 cmx_path: None,
+                app_name: None,
             },
             subcommand,
             &cargo_params,
@@ -1339,7 +1411,10 @@
         );
     }
 
-    if let Some(run_on_target_matches) = matches.subcommand_matches(RUN_ON_TARGET) {
+    if let Some(run_on_target_matches) = global_matches.subcommand_matches(RUN_ON_TARGET) {
+        let run_cargo_options = run_cargo_options
+            .cmx_path(run_on_target_matches.value_of(CMX_PATH).map(|s| PathBuf::from(s)));
+
         let run_params = run_on_target_matches
             .values_of("run_on_target_params")
             .map(|x| x.collect())
@@ -1352,6 +1427,7 @@
             run_on_target_matches.is_present(RUN_WITH_RUN),
             run_on_target_matches.is_present(RUN_WITH_SESSIONCTL),
         );
+
         return run_program_on_target(
             program,
             verbose,
@@ -1362,12 +1438,13 @@
             &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),
+            run_on_target_matches.value_of(APP_NAME).unwrap_or(DEFAULT_APP_NAME),
             args,
             test_args,
         );
     }
 
-    if let Some(pkg_matches) = matches.subcommand_matches("pkg-config") {
+    if let Some(pkg_matches) = global_matches.subcommand_matches("pkg-config") {
         let pkg_params =
             pkg_matches.values_of("pkgconfig_param").map(|x| x.collect()).unwrap_or_else(|| vec![]);
         let exit_code = run_pkg_config(verbose, &pkg_params, &target_options)?;
@@ -1377,7 +1454,7 @@
         return Ok(());
     }
 
-    if let Some(configure_matches) = matches.subcommand_matches("configure") {
+    if let Some(configure_matches) = global_matches.subcommand_matches("configure") {
         let configure_params = configure_matches
             .values_of("configure_param")
             .map(|x| x.collect())
@@ -1391,11 +1468,11 @@
         return Ok(());
     }
 
-    if let Some(_write_config_matches) = matches.subcommand_matches(WRITE_CONFIG) {
+    if let Some(_write_config_matches) = global_matches.subcommand_matches(WRITE_CONFIG) {
         return write_config(&run_cargo_options, &target_options);
     }
 
-    if let Some(build_rustc_matches) = matches.subcommand_matches(BUILD_RUSTC) {
+    if let Some(build_rustc_matches) = global_matches.subcommand_matches(BUILD_RUSTC) {
         let rust_root = PathBuf::from(
             build_rustc_matches
                 .value_of(RUST_ROOT)
@@ -1404,7 +1481,7 @@
         return build_rustc(&rust_root, &target_options);
     }
 
-    if let Some(make_package_matches) = matches.subcommand_matches(MAKE_PACKAGE) {
+    if let Some(make_package_matches) = global_matches.subcommand_matches(MAKE_PACKAGE) {
         let binary_path = PathBuf::from(
             make_package_matches
                 .value_of(BINARY_PATH)
@@ -1415,7 +1492,7 @@
                 .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)?);
+        println!("make package {}", make_package(&target_options, &binary_path, &cmx_path, "app")?);
         return Ok(());
     }
 
diff --git a/src/package.rs b/src/package.rs
index 810ca4c..cc06d35 100644
--- a/src/package.rs
+++ b/src/package.rs
@@ -68,6 +68,7 @@
     package_path: &Path,
     cmx_path: &Path,
     package_name: &str,
+    app_name: &str,
 ) -> Result<(), Error> {
     let mut manifest = File::create(&target)?;
     let zircon_build = zircon_build_path(&target_options.config)?;
@@ -90,7 +91,7 @@
     );
     writeln!(
         manifest,
-        r#"bin/app={}
+        r#"bin/{}={}
 lib/ld.so.1={}
 lib/libfdio.so={}
 lib/libunwind.so.1={}/libunwind.so.1
@@ -98,6 +99,7 @@
 meta/package={}
 meta/{}.cmx={}
 "#,
+        app_name,
         binary_path.to_string_lossy(),
         libc_path,
         fdio_path,
@@ -199,6 +201,7 @@
     target_options: &TargetOptions<'_, '_>,
     binary_path: &Path,
     cmx_path: &Path,
+    app_name: &str,
 ) -> Result<String, Error> {
     let binary_parent =
         binary_path.parent().expect(&format!("Can't get parent of {:#?}", binary_path));
@@ -223,6 +226,7 @@
         &package_path,
         &formatted_path,
         &package_name,
+        app_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")?;