| // Copyright 2017 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::utils::is_mac; |
| use crate::X64; |
| use failure::{bail, Error, ResultExt}; |
| use std::path::PathBuf; |
| use std::process::Command; |
| use std::{env, str}; |
| |
| /// The `TargetOptions` struct bundles together a number of parameters specific to |
| /// the Fuchsia target that need to be passed through various internal functions. For |
| /// the moment there is no way to set anything but the `release_os` field, but this |
| /// will change when fargo starts supporting ARM targets. |
| #[derive(Debug)] |
| pub struct TargetOptions<'a, 'b> { |
| pub device_name: Option<&'b str>, |
| pub config: &'a FuchsiaConfig, |
| } |
| |
| impl<'a, 'b> TargetOptions<'a, 'b> { |
| /// Constructs a new `TargetOptions`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use fargo::{FuchsiaConfig, TargetOptions}; |
| /// |
| /// let target_options = |
| /// TargetOptions::new(&FuchsiaConfig::default(), Some("ivy-donut-grew-stoop")); |
| /// ``` |
| |
| pub fn new(config: &'a FuchsiaConfig, device_name: Option<&'b str>) -> TargetOptions<'a, 'b> { |
| TargetOptions { device_name: device_name, config: config } |
| } |
| } |
| |
| fn get_path_from_env(env_name: &str, require_dir: bool) -> Result<Option<PathBuf>, Error> { |
| if let Ok(file_value) = env::var(env_name) { |
| let file_path = PathBuf::from(&file_value); |
| if !file_path.exists() { |
| bail!("{} is set to '{}' but nothing exists at that path.", env_name, &file_value); |
| } |
| if require_dir { |
| if !file_path.is_dir() { |
| bail!( |
| "{} is set to '{}' but that path does not point to a directory.", |
| env_name, |
| &file_value |
| ); |
| } |
| } |
| return Ok(Some(file_path)); |
| } |
| Ok(None) |
| } |
| |
| fn looks_like_fuchsia_dir(path: &PathBuf) -> bool { |
| for name in [".fx-build-dir", ".jiri_manifest"].iter() { |
| let config_path = path.join(name); |
| if !config_path.exists() { |
| return false; |
| } |
| } |
| true |
| } |
| |
| pub fn fuchsia_dir() -> Result<PathBuf, Error> { |
| let fuchsia_dir = if let Some(fuchsia_root) = get_path_from_env("FUCHSIA_ROOT", true)? { |
| fuchsia_root |
| } else if let Some(fuchsia_dir) = get_path_from_env("FUCHSIA_DIR", true)? { |
| fuchsia_dir |
| } else { |
| let mut path = env::current_dir()?; |
| loop { |
| if looks_like_fuchsia_dir(&path) { |
| return Ok(path); |
| } |
| path = if let Some(path) = path.parent() { |
| path.to_path_buf() |
| } else { |
| bail!( |
| "FUCHSIA_DIR not set and current directory is not in a Fuchsia tree with a \ |
| release-x64 build. You must set the environmental variable FUCHSIA_DIR to \ |
| point to a Fuchsia tree with .config and .jiri_manifest files." |
| ) |
| } |
| } |
| }; |
| |
| Ok(fuchsia_dir) |
| } |
| |
| pub fn target_out_dir(config: &FuchsiaConfig) -> Result<PathBuf, Error> { |
| let fuchsia_dir = fuchsia_dir()?; |
| Ok(fuchsia_dir.join(&config.fuchsia_build_dir)) |
| } |
| |
| 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")) |
| } |
| |
| pub fn sysroot_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> { |
| Ok(shared_libraries_path(&options)?.join("gen").join("build").join("config").join("fuchsia")) |
| } |
| |
| 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> { |
| let shared_name = |
| if options.config.fuchsia_arch == X64 { "x64-shared" } else { "arm64-shared" }; |
| Ok(target_out_dir(&options.config)?.join(shared_name)) |
| } |
| |
| fn platform_name() -> &'static str { |
| if is_mac() { |
| "mac-x64" |
| } else { |
| "linux-x64" |
| } |
| } |
| |
| fn prebuilt_path() -> Result<PathBuf, Error> { |
| Ok(fuchsia_dir()?.join("prebuilt")) |
| } |
| |
| fn rust_path() -> Result<PathBuf, Error> { |
| Ok(prebuilt_path()?.join("third_party/rust").join(platform_name())) |
| } |
| |
| pub fn cargo_path() -> Result<PathBuf, Error> { |
| if let Some(cargo_path) = get_path_from_env("FARGO_CARGO", false)? { |
| Ok(cargo_path) |
| } else { |
| Ok(rust_path()?.join("bin/cargo")) |
| } |
| } |
| |
| pub fn rustc_path() -> Result<PathBuf, Error> { |
| if let Some(rustc_path) = get_path_from_env("FARGO_RUSTC", false)? { |
| Ok(rustc_path) |
| } else { |
| Ok(rust_path()?.join("bin/rustc")) |
| } |
| } |
| |
| pub fn rustdoc_path() -> Result<PathBuf, Error> { |
| if let Some(rustdoc_path) = get_path_from_env("FARGO_RUSTDOC", false)? { |
| Ok(rustdoc_path) |
| } else { |
| Ok(rust_path()?.join("bin/rustdoc")) |
| } |
| } |
| |
| pub fn clang_base_path() -> Result<PathBuf, Error> { |
| Ok(prebuilt_path()?.join("third_party/clang").join(platform_name())) |
| } |
| |
| pub fn clang_c_compiler_path() -> Result<PathBuf, Error> { |
| Ok(clang_base_path()?.join("bin").join("clang")) |
| } |
| |
| pub fn clang_cpp_compiler_path() -> Result<PathBuf, Error> { |
| Ok(clang_base_path()?.join("bin").join("clang++")) |
| } |
| |
| pub fn clang_archiver_path() -> Result<PathBuf, Error> { |
| Ok(clang_base_path()?.join("bin").join("llvm-ar")) |
| } |
| |
| pub fn clang_ranlib_path() -> Result<PathBuf, Error> { |
| Ok(clang_base_path()?.join("bin").join("llvm-ranlib")) |
| } |
| |
| pub fn clang_resource_dir(target: &str) -> Result<PathBuf, Error> { |
| let clang = clang_c_compiler_path()?; |
| |
| let output = Command::new(clang) |
| .arg(format!("--target={}", target)) |
| .arg("-print-resource-dir") |
| .output() |
| .context("Running `clang` to get resource dir")?; |
| |
| if !output.status.success() { |
| bail!("Failed to get `clang` resource dir: {}", String::from_utf8_lossy(&output.stderr)); |
| } |
| |
| let path_string = |
| String::from_utf8(output.stdout).context("Invalid UTF8 in `clang` resource dir")?; |
| |
| Ok(PathBuf::from(path_string.trim())) |
| } |
| |
| pub fn fx_path() -> Result<PathBuf, Error> { |
| let fuchsia_dir = fuchsia_dir()?; |
| Ok(fuchsia_dir.join("scripts/fx")) |
| } |
| |
| #[derive(Debug, Default)] |
| pub struct FuchsiaConfig { |
| pub fuchsia_build_dir: String, |
| pub zircon_build_dir: String, |
| pub fuchsia_arch: String, |
| } |
| |
| impl FuchsiaConfig { |
| pub fn new_from_fx_exec() -> Result<FuchsiaConfig, Error> { |
| let fuchsia_dir = fuchsia_dir()?; |
| let fx_script = fx_path()?; |
| if !fx_script.exists() { |
| bail!("fx script not found at {:?}", fx_script); |
| } |
| let args = vec!["exec", "printenv"]; |
| 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(""), |
| 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 = '\''; |
| match parts[0] { |
| "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)), |
| _ => (), |
| } |
| } |
| Ok(config) |
| } |
| } |