Add support for more complex builds

Also up the amount of time waiting for tap0 to come up,
at 100ms it was timing out for femu all the time.

Change-Id: Ief73992f790427dbf63593d8f332201ee3601997
diff --git a/Cargo.lock b/Cargo.lock
index 4f11b53..d45d020 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -118,7 +118,7 @@
  "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
  "structopt 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -545,7 +545,7 @@
 
 [[package]]
 name = "toml"
-version = "0.4.10"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -702,7 +702,7 @@
 "checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203"
 "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
 "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
+"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
 "checksum uname 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
 "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
 "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
diff --git a/Cargo.toml b/Cargo.toml
index 6dd17c2..9fa889e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,7 @@
 serde_json = "1.0"
 structopt = "0.3"
 tempfile = "3.1"
-toml = "0.4"
+toml = "0.5"
 
 [[bin]]
 name = "fargo"
diff --git a/README.md b/README.md
index 06b592d..fd98ece 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@
 
 ## Getting started
 
-Since at the moment fargo requires the FUCHSIA\_DIR environmental variable be
+Since at the moment Fargo requires the FUCHSIA\_DIR environmental variable be
 set to the path to a Fuchsia source tree containing a Fuchsia build,
 the first step is to build Fuchsia.
 
@@ -54,7 +54,7 @@
 instruction are what you need. Make sure that `fx serve` is running pointed at
 this Fuchsia build.
 
-Once this build is complete, clone and build fargo. For this you will need
+Once this build is complete, clone and build Fargo. For this you will need
 a working host Rust installation, most easily installed with [rustup](https://rustup.rs).
 
     git clone https://fuchsia.googlesource.com/fargo
@@ -64,31 +64,31 @@
 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
-real device to copy build results and execute them. For Qemu there is a bit of
-[tricky set up](https://fuchsia.googlesource.com/fuchsia/+/master/zircon/docs/qemu.md#enabling-networking-under-qemu) to do.
+Fargo uses ssh to communicate between your host computer and either the emulator or a
+real device to copy build results and execute them. For the emulator there is a bit of
+[tricky set up](https://fuchsia.dev/fuchsia-src/getting_started#enabling_network) to do.
 
 ### Testing if Fargo is working
 
-Now to verify if fargo is working correctly, try starting a fuchsia machine and executing a test.
+Now to verify if Fargo is working correctly, try starting a fuchsia machine and executing a test.
 
     fargo start
     cd fargo/fargo-test
     fargo test
 
-Note that fargo start now depends on an environment using fx set. If that isn't the way you start
-Fuchsia emulators, use fargo enable-networking after you've started the emulator.
+Note that Fargo start now depends on an environment using fx set. If that isn't the way you start
+Fuchsia emulators, use `fargo enable-networking` after you've started the emulator.
 
 If all is well, you should see a successful test pass just as if you had ran cargo test on any other
 rust project.
 
-Additionally, if you are using qemu you need to enable networking, otherwise fargo won't be able to
-use `fx shell` to invoke the test binary.
+Additionally, if you are using the emulator you need to enable networking, otherwise
+Fargo won't be able to use `fx shell` to invoke the test binary.
 
 ### Escaping parameters
 
-Sometimes you want to pass parameters through fargo and cargo and on to
-somethinglike rustc. To make this easier fargo will convert a "++"
+Sometimes you want to pass parameters through Fargo and cargo and on to
+somethinglike rustc. To make this easier Fargo will convert a "++"
 parameter to "--" when invoking cargo. For example, the following
 command:
 
@@ -98,9 +98,10 @@
 
 ### Running view-producing Rust binaries
 
-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
+`fargo run` has the options, `--run-with-tiles` and `--run-with-sessionctl`, that will use
+`tiles_ctl` or `sessionctl`, respectively, to launch the Rust binary.
+Use this option when running if your binaries
+want 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
@@ -111,22 +112,74 @@
 for Fuchsia.
 
 The config file created will be for the architecture and debug/release options that are
-passed to fargo with the `write-config` command. If you wish to switch to a different
+passed to Fargo with the `write-config` command. If you wish to switch to a different
 architecture or build, re-run `write-config`.
 
+## The Fargo manifest file
+
+Some build configuration needed to create a functioning Fuchsia package are not contained
+inside the Cargo.toml files generated as part of `fx build`. The Fargo manifest file
+Fargo.toml exists to provide those configuration.
+
+There are three properties that one can set with this file, `library_search_paths`,
+`additional_shared_libraries` and `data_files` as shown in the example below.
+
+    library_search_paths = [
+        "obj/src/graphics/lib/compute/spinel",
+        "obj/src/graphics/lib/compute/common",
+        "obj/src/graphics/lib/compute/common/vk",
+        "obj/src/graphics/lib/compute/hotsort/platforms/vk",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/intel/gen8/hotsort",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/amd/gcn3/hotsort",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/nvidia/sm50/hotsort",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/intel/gen8",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/amd/gcn3",
+        "obj/src/graphics/lib/compute/spinel/platforms/vk/targets/vendors/nvidia/sm50",
+    ]
+
+    additional_shared_libraries = [
+        "libvulkan.so",
+        "librust-trace-provider.so",
+    ]
+
+    data_files = [
+        { src = "examples/shaders/copy.comp.spv", dst = "data/shaders/copy.comp.spv"},
+        { src = "examples/shaders/motioncopy.comp.spv", dst = "data/shaders/motioncopy.comp.spv"},
+    ]
+
+`library_search_paths` are additional paths to pass to `rustc` to resolve external
+references at compile time. The paths should be the partial path from the Fuchsia build
+directory.
+
+`additional_shared_libraries` are the names of shared libraries that should be included in
+the Fuchsia package that is created by Fargo to run Rust executables on Fuchsia. These
+are expected to be found in the shared libraries folder in the Fuchsia build
+directory
+
+`data_files` are partial paths to additional data files to be included in the Fuchsia
+package that is created by Fargo to run Rust executables on Fuchsia. These paths should be
+the partial path from the root of the crate being compiled.
+
+In the example above, `Fargo.toml` is used to resolve the static libraries used by Spinel,
+to include two shared libraries needed by some of the samples for Carnelian and to include
+two compiled shaders also needed by a particular sample.
+
+Currently there is no way to limit these configuration to one example or binary.
+
 ## Getting help
 
 For problems getting the Fuchsia build to complete, the [getting started](https://fuchsia.dev/fuchsia-src/getting_started.md)
 page on [fuchsia.dev](https://fuchsia.dev) is the best place to start looking for help.
 
-For fargo itself, the best place for help is the
+For Fargo itself, the best place for help is the
 [rust-fuchsia](https://groups.google.com/a/fuchsia.com/forum/#!aboutgroup/rust-fuchsia) Google group.
 
 ## Using different versions of cargo and rustc
 
-By default fargo will use the copies of cargo and rustc provided in `$FUCHSIA_DIR/buildtools`.
+By default Fargo will use the copies of cargo and rustc provided in `$FUCHSIA_DIR/buildtools`.
 To change this behavior, set the environmental variables `FARGO_CARGO` and `FARGO_RUSTC` before
-running fargo.
+running Fargo.
 
 If you need to be using a different version of nightly for some reason, you'll need the `x86_64-fuchsia` target.
 If you installed rust with [rustup](https://www.rustup.rs) you can install the target with:
@@ -135,9 +188,9 @@
     rustup target add x86_64-fuchsia
 
 
-## Environmental variables set by fargo
+## Environmental variables set by Fargo
 
-CARGO\_TARGET\_[X86\_64|AARCH64]\_UNKNOWN\_FUCHSIA\_RUNNER - set to the fargo binary to run remotely on simulator or device.
+CARGO\_TARGET\_[X86\_64|AARCH64]\_UNKNOWN\_FUCHSIA\_RUNNER - set to the Fargo binary to run remotely on simulator or device.
 
 CARGO\_TARGET\_[X86\_64|AARCH64]\_UNKNOWN\_FUCHSIA\_RUSTFLAGS - set to provide linker flags
 
@@ -178,7 +231,7 @@
 See `scripts/build_cairo_support.sh` for an example of how to use these
 functions to build native support.
 
-fargo sets the following environmental variables before invoking configure:
+Fargo sets the following environmental variables before invoking configure:
 
     CC, CXX, RANLIB, LD, AR, CFLAGS, CXXFLAGS, CPPFLAGS
     LDFLAGS, PKG_CONFIG_PATH, PKG_CONFIG_LIBDIR,
@@ -190,4 +243,4 @@
 
 ## Fargo roadmap
 
-The goal is to transition fargo to using something like an SDK instead.
+The goal is to transition Fargo to using something like an SDK instead.
diff --git a/src/command_line.rs b/src/command_line.rs
index dce3cc4..0873b21 100644
--- a/src/command_line.rs
+++ b/src/command_line.rs
@@ -5,7 +5,7 @@
     cross::run_pkg_config,
     device::{netls, ssh, start_emulator, stop_emulator, StartEmulatorOptions},
     enable_networking, format_project,
-    package::make_package,
+    manifest::load_manifest,
     random_story_name, run_binary, run_cargo, run_configure, run_program_on_target,
     run_switches_to_mode, run_tests,
     sdk::TargetOptions,
@@ -38,8 +38,6 @@
     Fmt,
     /// List visible Fuchsia devices
     ListDevices,
-    /// Make a Fuchsia package from an unstripped binary
-    MakePackage(MakePackage),
     /// Run pkg-config for the cross compilation environment
     PkgConfig(PkgConfig),
     /// Stop all Fuchsia emulators and start a new one
@@ -368,6 +366,7 @@
 #[doc(hidden)]
 pub fn run() -> Result<(), Error> {
     let opt = FargoOption::from_args();
+    let fargo_manifest = load_manifest(&opt.manifest_path)?;
     let verbose = opt.verbose;
     let fuchsia_config = FuchsiaConfig::new_from_fx_exec()?;
     if verbose {
@@ -376,7 +375,12 @@
 
     let target_options =
         TargetOptions::new(&fuchsia_config, opt.device_name.as_ref().map(String::as_str));
-    let run_cargo_options = RunCargoOptions { verbose, ..RunCargoOptions::default() };
+    let run_cargo_options = RunCargoOptions {
+        verbose,
+        fargo_manifest: fargo_manifest.clone(),
+        manifest_path: opt.manifest_path.clone(),
+        ..RunCargoOptions::default()
+    };
 
     match opt.command {
         FargoCommand::Autotest(autotest_opts) => {
@@ -409,6 +413,7 @@
                 cargo_opts.cargo_params.iter().map(String::as_str).collect();
             return run_cargo(
                 &RunCargoOptions {
+                    fargo_manifest,
                     verbose,
                     release: false,
                     nocapture: false,
@@ -467,20 +472,6 @@
             return netls(opt.verbose, &target_options);
         }
 
-        FargoCommand::MakePackage(make_package_opts) => {
-            println!(
-                "make package {}",
-                make_package(
-                    verbose,
-                    &target_options,
-                    &make_package_opts.binary_path,
-                    &make_package_opts.cmx_path,
-                    &make_package_opts.app_name
-                )?
-            );
-            return Ok(());
-        }
-
         FargoCommand::PkgConfig(pkg_config_opts) => {
             let pkg_params: Vec<&str> =
                 pkg_config_opts.pkg_config_params.iter().map(String::as_str).collect();
@@ -557,7 +548,7 @@
                 &fuchsia_config,
                 &target_options,
                 run_mode,
-                &run_cargo_options.cmx_path,
+                &run_cargo_options,
                 &run_on_target.story_name.unwrap_or(random_story_name()),
                 &run_on_target.mod_name.unwrap_or(DEFAULT_MOD_NAME.to_string()),
                 &run_on_target.app_name,
diff --git a/src/device.rs b/src/device.rs
index 18f458a..6d831b4 100644
--- a/src/device.rs
+++ b/src/device.rs
@@ -114,7 +114,7 @@
                 bail!("ifconfig failed: {}", ifconfig_status);
             }
             loop_count += 1;
-            thread::sleep(time::Duration::from_millis(100));
+            thread::sleep(time::Duration::from_millis(300));
         } else {
             break;
         }
diff --git a/src/lib.rs b/src/lib.rs
index 4ab7738..2699443 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,7 @@
 pub mod command_line;
 mod cross;
 mod device;
+mod manifest;
 mod package;
 mod sdk;
 mod utils;
@@ -25,8 +26,9 @@
 use crate::sdk::{
     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,
+    sysroot_path, target_out_dir, zircon_build_path,
 };
+use manifest::Manifest;
 
 use failure::{bail, err_msg, Error, ResultExt};
 use std::fs;
@@ -43,7 +45,7 @@
     config: &FuchsiaConfig,
     target_options: &TargetOptions<'_, '_>,
     run_mode: RunMode,
-    cmx_path: &Option<PathBuf>,
+    run_cargo_options: &RunCargoOptions,
     story_name: &str,
     mod_name: &str,
     app_name: &str,
@@ -52,7 +54,8 @@
 ) -> Result<(), Error> {
     let source_path = PathBuf::from(&filename);
 
-    let target_string = make_package(verbose, target_options, &source_path, cmx_path, app_name)?;
+    let target_string =
+        make_package(verbose, target_options, &run_cargo_options, &source_path, app_name)?;
 
     let mut command_string = match run_mode {
         RunMode::Tiles => "tiles_ctl add ".to_string(),
@@ -241,6 +244,7 @@
 
 #[derive(Debug, Clone, Default)]
 pub struct RunCargoOptions {
+    pub fargo_manifest: Manifest,
     pub verbose: bool,
     pub release: bool,
     pub run_mode: RunMode,
@@ -322,6 +326,7 @@
 }
 
 fn get_rustflags(
+    run_cargo_options: &RunCargoOptions,
     target_options: &TargetOptions<'_, '_>,
     sysroot_as_path: &PathBuf,
 ) -> Result<String, Error> {
@@ -352,6 +357,12 @@
         rust_flags.push("-Clink-arg=--fix-cortex-a53-843419".to_string());
     }
 
+    for search_path in &run_cargo_options.fargo_manifest.library_search_paths {
+        let full_search_path = target_out_dir(&target_options.config)?.join(search_path);
+        let arg = format!("-Lnative={}", full_search_path.to_string_lossy());
+        rust_flags.push(arg);
+    }
+
     Ok(rust_flags.join(" "))
 }
 
@@ -370,6 +381,7 @@
         options.get_story_name(),
         options.get_mod_name()
     );
+    let manifest_path_string;
     let cmx_arg = format!("--{}", CMX_PATH);
     let app_name_arg = format!("--{}", APP_NAME);
     let run_arg = format!("--{}", RUN_WITH_RUN);
@@ -389,6 +401,12 @@
         runner_args.push("-v");
     }
 
+    if let Some(manifest_path) = options.manifest_path.as_ref() {
+        manifest_path_string = manifest_path.to_string_lossy();
+        runner_args.push("--manifest-path");
+        runner_args.push(&manifest_path_string);
+    }
+
     if let Some(device_name) = target_options.device_name {
         runner_args.push("--device-name");
         runner_args.push(device_name);
@@ -531,7 +549,7 @@
     }
 
     cmd.env(runner_env_name, fargo_command)
-        .env(rustflags_env_name, get_rustflags(target_options, &sysroot_as_path)?)
+        .env(rustflags_env_name, get_rustflags(options, target_options, &sysroot_as_path)?)
         .env("RUSTC", rustc_path()?.to_string_lossy().as_ref())
         .env("RUSTDOC", rustdoc_path()?.to_string_lossy().as_ref())
         .env("RUSTDOCFLAGS", "--cap-lints allow -Z unstable-options")
@@ -597,7 +615,11 @@
 
     let sysroot_as_path = sysroot_path(target_options)?;
     writeln!(config, "[target.{}]", get_target_triple(target_options))?;
-    writeln!(config, "rustflags = \"{}\"", get_rustflags(target_options, &sysroot_as_path)?)?;
+    writeln!(
+        config,
+        "rustflags = \"{}\"",
+        get_rustflags(options, target_options, &sysroot_as_path)?
+    )?;
     writeln!(
         config,
         "runner = \"{}\"",
diff --git a/src/manifest.rs b/src/manifest.rs
new file mode 100644
index 0000000..efbe999
--- /dev/null
+++ b/src/manifest.rs
@@ -0,0 +1,39 @@
+// Copyright 2020 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 failure::{Error, ResultExt};
+use serde_derive::Deserialize;
+use std::{fs, path::PathBuf};
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct DataFile {
+    pub src: String,
+    pub dst: String,
+}
+
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct Manifest {
+    #[serde(default)]
+    pub library_search_paths: Vec<String>,
+    #[serde(default)]
+    pub additional_shared_libraries: Vec<String>,
+    #[serde(default)]
+    pub data_files: Vec<DataFile>,
+}
+
+pub fn load_manifest(manifest_path: &Option<PathBuf>) -> Result<Manifest, Error> {
+    let cwd: PathBuf = if let Some(actual_manifest_path) = manifest_path.as_ref() {
+        actual_manifest_path.parent().expect("manifest_path parent").to_path_buf()
+    } else {
+        std::fs::canonicalize(std::env::current_dir()?)
+            .context("autotest: canonicalize working directory")?
+    };
+    let manifest_path = cwd.join("Fargo.toml");
+    if !manifest_path.exists() {
+        return Ok(Manifest::default());
+    }
+    let manifest_contents = fs::read_to_string(manifest_path)?;
+    let manifest = toml::from_str(&manifest_contents)?;
+    Ok(manifest)
+}
diff --git a/src/package.rs b/src/package.rs
index 5d9ecf6..2d5e25e 100644
--- a/src/package.rs
+++ b/src/package.rs
@@ -6,9 +6,10 @@
     get_triple_cpu,
     sdk::{
         amber_path, clang_base_path, cmc_path, fuchsia_dir, package_manager_path,
-        zircon_build_path, FuchsiaConfig, TargetOptions,
+        shared_libraries_path, zircon_build_path, FuchsiaConfig, TargetOptions,
     },
-    utils::strip_binary,
+    utils::{strip_binary, target_crate_path},
+    RunCargoOptions,
 };
 use failure::{bail, format_err, Error, ResultExt};
 use serde::{Deserialize, Serialize};
@@ -105,6 +106,7 @@
 fn write_manifest_file(
     verbose: bool,
     target_options: &TargetOptions<'_, '_>,
+    run_cargo_options: &RunCargoOptions,
     target: &Path,
     binary_path: &Path,
     package_path: &Path,
@@ -159,6 +161,32 @@
         triple,
     );
 
+    let shared_lib_path = shared_libraries_path(target_options)?;
+    let additional_libs: Vec<String> = run_cargo_options
+        .fargo_manifest
+        .additional_shared_libraries
+        .iter()
+        .map(|lib_name| {
+            format!("lib/{}={}/{}", lib_name, shared_lib_path.to_string_lossy(), lib_name)
+        })
+        .collect();
+
+    let additional_lib_str = additional_libs.join("\n");
+
+    let target_crate_path = target_crate_path(&run_cargo_options.manifest_path)?;
+    let target_crate_path_string = target_crate_path.to_string_lossy();
+
+    let data_files: Vec<String> = run_cargo_options
+        .fargo_manifest
+        .data_files
+        .iter()
+        .map(|data_file| {
+            format!("{}={}/{}", data_file.dst, target_crate_path_string, data_file.src)
+        })
+        .collect();
+
+    let data_files_str = data_files.join("\n");
+
     writeln!(
         manifest,
         r#"bin/{}={}
@@ -172,6 +200,8 @@
 lib/libunwind.so.1={}
 meta/package={}
 meta/{}.cmx={}
+{}
+{}
 "#,
         app_name,
         binary_path.to_string_lossy(),
@@ -186,6 +216,8 @@
         package_path.to_string_lossy(),
         package_name,
         cmx_path.to_string_lossy(),
+        additional_lib_str,
+        data_files_str,
     )?;
     Ok(())
 }
@@ -324,13 +356,13 @@
 pub fn make_package(
     verbose: bool,
     target_options: &TargetOptions<'_, '_>,
+    run_cargo_options: &RunCargoOptions,
     binary_path: &Path,
-    optional_cmx_path: &Option<PathBuf>,
     app_name: &str,
 ) -> Result<String, Error> {
     let temp_dir = tempdir()?;
     let dfs;
-    let cmx_path = if let Some(path) = optional_cmx_path.as_ref() {
+    let cmx_path = if let Some(path) = run_cargo_options.cmx_path.as_ref() {
         path
     } else {
         dfs = default_sandbox_file(&temp_dir)?;
@@ -362,6 +394,7 @@
     write_manifest_file(
         verbose,
         &target_options,
+        &run_cargo_options,
         &manifest_path,
         &stripped_binary_path,
         &package_path,
diff --git a/src/utils.rs b/src/utils.rs
index 6b14d2e..a68094e 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -39,3 +39,14 @@
 
     Ok(target_path)
 }
+
+pub fn target_crate_path(manifest_path: &Option<PathBuf>) -> Result<PathBuf, Error> {
+    let cwd: PathBuf = if let Some(actual_manifest_path) = manifest_path.as_ref() {
+        actual_manifest_path.parent().expect("manifest_path parent").to_path_buf()
+    } else {
+        std::fs::canonicalize(std::env::current_dir()?)
+            .context("autotest: canonicalize working directory")?
+    };
+
+    Ok(cwd)
+}