blob: 3b3d9c68142080a409ce70cda77189e39214e125 [file] [log] [blame]
// 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::get_target_triple;
use crate::utils::is_mac;
use crate::X64;
use failure::{bail, Error};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
/// 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 release_os: bool,
pub target_cpu: &'a str,
pub target_cpu_linker: &'a str,
pub device_name: Option<&'b str>,
}
impl<'a, 'b> TargetOptions<'a, 'b> {
/// Constructs a new `TargetOptions`.
///
/// # Examples
///
/// ```
/// use fargo::TargetOptions;
///
/// let target_options = TargetOptions::new(true, "x64", Some("ivy-donut-grew-stoop"));
/// ```
pub fn new(
release_os: bool, target_cpu: &'a str, device_name: Option<&'b str>,
) -> TargetOptions<'a, 'b> {
TargetOptions {
release_os: release_os,
target_cpu: target_cpu,
target_cpu_linker: target_cpu,
device_name: device_name,
}
}
}
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)
}
pub fn fuchsia_dir(options: &TargetOptions<'_, '_>) -> 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().unwrap();
loop {
if possible_target_out_dir(&path, options).is_ok() {
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 a release-x64 build."
)
}
}
};
Ok(fuchsia_dir)
}
pub fn possible_target_out_dir(
fuchsia_dir: &PathBuf, options: &TargetOptions<'_, '_>,
) -> Result<PathBuf, Error> {
let out_dir_name_prefix = if options.release_os {
"release"
} else {
"debug"
};
let out_dir_name = format!("{}-{}", out_dir_name_prefix, options.target_cpu);
let target_out_dir = fuchsia_dir.join("out").join(out_dir_name);
if !target_out_dir.exists() {
bail!("no target out directory found at {:?}", target_out_dir);
}
Ok(target_out_dir)
}
pub fn target_out_dir(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let fuchsia_dir = fuchsia_dir(options)?;
possible_target_out_dir(&fuchsia_dir, options)
}
pub fn cargo_out_dir(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let fuchsia_dir = fuchsia_dir(options)?;
let target_triple = get_target_triple(options);
Ok(fuchsia_dir
.join("garnet")
.join("target")
.join(target_triple)
.join("debug"))
}
pub fn strip_tool_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?.join("bin/llvm-objcopy"))
}
pub fn sysroot_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(target_out_dir(&options)?
.join("sdks")
.join("zircon_sysroot")
.join("arch")
.join(options.target_cpu)
.join("sysroot"))
}
pub fn zircon_build_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let fuchsia_dir = fuchsia_dir(options)?;
let build_name = if options.target_cpu == X64 {
"build-x64"
} else {
"build-arm64"
};
let zircon_build = fuchsia_dir
.join("out")
.join("build-zircon")
.join(build_name);
Ok(zircon_build)
}
pub fn shared_libraries_path(options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let shared_name = if options.target_cpu == X64 {
"x64-shared"
} else {
"arm64-shared"
};
Ok(target_out_dir(&options)?.join(shared_name))
}
fn buildtools_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let platform_name = if is_mac() { "mac-x64" } else { "linux-x64" };
Ok(fuchsia_dir(target_options)?
.join("buildtools")
.join(platform_name))
}
pub fn cargo_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
if let Some(cargo_path) = get_path_from_env("FARGO_CARGO", false)? {
Ok(cargo_path)
} else {
Ok(buildtools_path(target_options)?.join("rust/bin/cargo"))
}
}
pub fn rustc_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
if let Some(rustc_path) = get_path_from_env("FARGO_RUSTC", false)? {
Ok(rustc_path)
} else {
Ok(buildtools_path(target_options)?.join("rust/bin/rustc"))
}
}
pub fn rustdoc_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
if let Some(rustdoc_path) = get_path_from_env("FARGO_RUSTDOC", false)? {
Ok(rustdoc_path)
} else {
Ok(buildtools_path(target_options)?.join("rust/bin/rustdoc"))
}
}
pub fn toolchain_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(buildtools_path(target_options)?.join("clang"))
}
pub fn clang_linker_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?.join("bin").join("clang"))
}
pub fn clang_c_compiler_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?.join("bin").join("clang"))
}
pub fn clang_cpp_compiler_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?.join("bin").join("clang++"))
}
pub fn clang_archiver_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?.join("bin").join("llvm-ar"))
}
pub fn clang_ranlib_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
Ok(toolchain_path(target_options)?
.join("bin")
.join("llvm-ranlib"))
}
pub fn fx_path(target_options: &TargetOptions<'_, '_>) -> Result<PathBuf, Error> {
let fuchsia_dir = fuchsia_dir(target_options)?;
Ok(fuchsia_dir.join("scripts/fx"))
}
#[derive(Debug)]
pub struct FuchsiaConfig {
pub fuchsia_build_dir: String,
pub fuchsia_variant: String,
pub fuchsia_arch: String,
pub zircon_project: String,
}
impl FuchsiaConfig {
pub fn new(target_options: &TargetOptions<'_, '_>) -> Result<FuchsiaConfig, Error> {
let mut config = FuchsiaConfig {
fuchsia_build_dir: String::from(""),
fuchsia_variant: String::from(""),
fuchsia_arch: String::from(""),
zircon_project: String::from(""),
};
let fuchsia_dir = fuchsia_dir(target_options)?;
let config_path = fuchsia_dir.join(".config");
let mut config_file = File::open(&config_path)?;
let mut config_file_contents_str = String::new();
config_file.read_to_string(&mut config_file_contents_str)?;
for one_line in config_file_contents_str.lines() {
let parts: Vec<&str> = one_line.split("=").collect();
if parts.len() == 2 {
match parts[0] {
"FUCHSIA_BUILD_DIR" => {
config.fuchsia_build_dir = String::from(parts[1].trim_matches('"'))
}
"FUCHSIA_VARIANT" => {
config.fuchsia_variant = String::from(parts[1].trim_matches('"'))
}
"FUCHSIA_ARCH" => {
config.fuchsia_arch = String::from(parts[1].trim_matches('"'))
}
"ZIRCON_PROJECT" => {
config.zircon_project = String::from(parts[1].trim_matches('"'))
}
_ => (),
}
}
}
Ok(config)
}
pub fn is_release(&self) -> bool {
self.fuchsia_variant != "debug"
}
}