blob: 2d5e25e0509fad5caa4eae6a8cf2fbb1c6b3933b [file] [log] [blame]
// Copyright 2019 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_triple_cpu,
sdk::{
amber_path, clang_base_path, cmc_path, fuchsia_dir, package_manager_path,
shared_libraries_path, zircon_build_path, FuchsiaConfig, TargetOptions,
},
utils::{strip_binary, target_crate_path},
RunCargoOptions,
};
use failure::{bail, format_err, Error, ResultExt};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::{
collections::HashMap,
fs::{create_dir_all, File},
io::Write,
path::{Path, PathBuf},
process::Command,
};
use tempfile::{tempdir, TempDir};
#[derive(Serialize, Deserialize)]
struct Sandbox {
services: Vec<String>,
}
#[derive(Serialize, Deserialize)]
struct SandboxFile {
program: HashMap<String, String>,
sandbox: Sandbox,
}
fn default_sandbox_file(temp_dir: &TempDir) -> Result<PathBuf, Error> {
let path = temp_dir.path().join("default.cmx");
let services = vec![];
let sandbox = Sandbox { services };
let mut program = HashMap::new();
program.insert("binary".to_string(), "bin/app".to_string());
let sandbox_file = SandboxFile { program, sandbox };
let serialized_sandbox_file = serde_json::to_string(&sandbox_file).expect("serialized");
let mut temp_sandbox_file = File::create(&path)?;
writeln!(temp_sandbox_file, "{}", serialized_sandbox_file)?;
Ok(path)
}
fn validate_cmx_file(
verbose: bool,
fuchsia_config: &FuchsiaConfig,
cmx_path: &Path,
) -> Result<(), Error> {
if verbose {
println!("validate_cmx_file: cmx_path = {:#?}", cmx_path);
}
let cmc = cmc_path(fuchsia_config)?;
let output = Command::new(cmc)
.arg("validate")
.arg(cmx_path)
.output()
.context("Running `cmc` to validate cmx file")?;
if !output.status.success() {
bail!("cmc returned error: {}", String::from_utf8_lossy(&output.stderr));
}
Ok(())
}
fn format_cmx_file(
fuchsia_config: &FuchsiaConfig,
cmx_path: &Path,
temp_dir: &TempDir,
) -> Result<PathBuf, Error> {
let temp_dir_str = temp_dir.path().to_string_lossy();
let destination_path = format!(
"{}/{}",
temp_dir_str,
cmx_path
.file_name()
.ok_or(format_err!("file_name failed on {:#?}", cmx_path))?
.to_string_lossy()
);
let cmc = cmc_path(fuchsia_config)?;
let output = Command::new(cmc)
.arg("format")
.arg(cmx_path)
.arg("-o")
.arg(&destination_path)
.output()
.context("Running `cmc` to format cmx file")?;
if !output.status.success() {
bail!("cmc returned error: {}", String::from_utf8_lossy(&output.stderr));
}
Ok(PathBuf::from(destination_path))
}
fn write_manifest_file(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
run_cargo_options: &RunCargoOptions,
target: &Path,
binary_path: &Path,
package_path: &Path,
cmx_path: &Path,
package_name: &str,
app_name: &str,
) -> Result<(), Error> {
if verbose {
println!("write_manifest_file: target = {:#?}", target);
}
let triple = get_triple_cpu(target_options);
let mut manifest = File::create(&target)?;
let zircon_build = zircon_build_path(&target_options.config)?;
let libc_path = format!(
"{}/user-{}-clang.shlib/obj/system/ulib/c/libc.so",
zircon_build.to_string_lossy(),
target_options.config.fuchsia_arch
);
let fdio_path = format!(
"{}/user-{}-clang.shlib/obj/system/ulib/fdio/libfdio.so",
zircon_build.to_string_lossy(),
target_options.config.fuchsia_arch
);
let libsyslog_path = format!(
"{}/user-{}-clang.shlib/obj/system/ulib/syslog/libsyslog.so",
zircon_build.to_string_lossy(),
target_options.config.fuchsia_arch
);
let libtraceengine_path = format!(
"{}/user-{}-clang.shlib/obj/system/ulib/trace-engine/libtrace-engine.so",
zircon_build.to_string_lossy(),
target_options.config.fuchsia_arch
);
let libasync_path = format!(
"{}/user-{}-clang.shlib/obj/system/ulib/async/libasync-default.so",
zircon_build.to_string_lossy(),
target_options.config.fuchsia_arch
);
let libcpp2_path = format!(
"{}/lib/{}-unknown-fuchsia/c++/libc++.so.2",
clang_base_path()?.to_string_lossy(),
triple,
);
let libcpp1abi_path = format!(
"{}/lib/{}-unknown-fuchsia/c++/libc++abi.so.1",
clang_base_path()?.to_string_lossy(),
triple,
);
let libunwind_path = format!(
"{}/lib/{}-unknown-fuchsia/c++/libunwind.so.1",
clang_base_path()?.to_string_lossy(),
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/{}={}
lib/ld.so.1={}
lib/libfdio.so={}
lib/libsyslog.so={}
lib/libtrace-engine.so={}
lib/libasync-default.so={}
lib/libc++.so.2={}
lib/libc++abi.so.1={}
lib/libunwind.so.1={}
meta/package={}
meta/{}.cmx={}
{}
{}
"#,
app_name,
binary_path.to_string_lossy(),
libc_path,
fdio_path,
libsyslog_path,
libtraceengine_path,
libasync_path,
libcpp2_path,
libcpp1abi_path,
libunwind_path,
package_path.to_string_lossy(),
package_name,
cmx_path.to_string_lossy(),
additional_lib_str,
data_files_str,
)?;
Ok(())
}
fn write_package_file(verbose: bool, target: &Path, package_name: &str) -> Result<(), Error> {
let mut package = File::create(&target)?;
let package_contents = json!({
"name": package_name,
"version": "0"
});
if verbose {
println!("write_package_file: target = {:#?}", target);
println!("write_package_file: package_contents = {:#?}", package_contents);
}
writeln!(package, "{}", package_contents.to_string())?;
Ok(())
}
fn pm_build(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
manifest_path: &Path,
output_path: &Path,
) -> Result<(), Error> {
let pm = package_manager_path(target_options.config)?;
let fuchsia_dir = fuchsia_dir()?;
let dev_key_path = fuchsia_dir.join("build/development.key");
if verbose {
println!("pm_build: package_manager_path = {:#?}", pm);
println!("pm_build: fuchsia_dir = {:#?}", fuchsia_dir);
println!("pm_build: dev_key_path = {:#?}", dev_key_path);
println!("pm_build: manifest_path = {:#?}", manifest_path);
println!("pm_build: output_path = {:#?}", output_path);
}
let output = Command::new(pm)
.arg("-k")
.arg(dev_key_path)
.arg("-o")
.arg(&output_path)
.arg("-m")
.arg(&manifest_path)
.arg("build")
.arg("-depfile")
.arg("-blobsfile")
.arg("-blobs-manifest")
.output()
.context("Running `cmc` to format cmx file")?;
if verbose {
println!("pm build stdout: {}", String::from_utf8_lossy(&output.stdout));
if output.status.success() {
println!("pm build stderr: {}", String::from_utf8_lossy(&output.stderr));
}
}
if !output.status.success() {
bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
}
Ok(())
}
fn pm_archive(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
output_path: &Path,
manifest_path: &Path,
) -> Result<(), Error> {
if verbose {
println!("pm_archive: output_path = {:#?}", output_path);
println!("pm_archive: manifest_path = {:#?}", manifest_path);
}
let pm = package_manager_path(target_options.config)?;
let output = Command::new(pm)
.arg("-o")
.arg(&output_path)
.arg("-m")
.arg(&manifest_path)
.arg("archive")
.output()
.context("Running `pm_archive` to build an archive")?;
if verbose {
println!("pm archive stdout: {}", String::from_utf8_lossy(&output.stdout));
if output.status.success() {
println!("pm archive stderr: {}", String::from_utf8_lossy(&output.stderr));
}
}
if !output.status.success() {
bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
}
Ok(())
}
fn pm_publish(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
output_path: &Path,
) -> Result<(), Error> {
let pm = package_manager_path(target_options.config)?;
let tuf_root = amber_path(target_options.config)?;
if verbose {
println!("pm_publish: output_path = {:#?}", output_path);
println!("pm_publish: tuf_root = {:#?}", tuf_root);
}
let output = Command::new(pm)
.arg("publish")
.arg("-a")
.arg("-f")
.arg(output_path)
.arg("-r")
.arg(tuf_root)
.arg("-vt")
.arg("-v")
.output()
.context("Running `publish` to publish package")?;
if verbose {
println!("pm publish stdout: {}", String::from_utf8_lossy(&output.stdout));
if output.status.success() {
println!("pm publish stderr: {}", String::from_utf8_lossy(&output.stderr));
}
}
if !output.status.success() {
bail!("pm returned error: {}", String::from_utf8_lossy(&output.stderr));
}
Ok(())
}
pub fn make_package(
verbose: bool,
target_options: &TargetOptions<'_, '_>,
run_cargo_options: &RunCargoOptions,
binary_path: &Path,
app_name: &str,
) -> Result<String, Error> {
let temp_dir = tempdir()?;
let dfs;
let cmx_path = if let Some(path) = run_cargo_options.cmx_path.as_ref() {
path
} else {
dfs = default_sandbox_file(&temp_dir)?;
&dfs
};
let binary_parent =
binary_path.parent().expect(&format!("Can't get parent of {:#?}", binary_path));
let mut package_name = binary_path
.file_name()
.expect("file_name failed on binary_path")
.to_string_lossy()
.to_string();
package_name.push_str("_fargo");
if verbose {
println!("make_package: package_name = {:#?}", package_name);
}
let output_path = binary_parent.join(&package_name);
if verbose {
println!("make_package: output_path = {:#?}", output_path);
}
create_dir_all(&output_path).context("create_dir_all failed")?;
let stripped_binary_path = strip_binary(binary_path)?;
validate_cmx_file(verbose, &target_options.config, &cmx_path)?;
let formatted_path = format_cmx_file(&target_options.config, &cmx_path, &temp_dir)?;
let package_path = temp_dir.path().join("package");
write_package_file(verbose, &package_path, &package_name)?;
let manifest_path = temp_dir.path().join("manifest");
write_manifest_file(
verbose,
&target_options,
&run_cargo_options,
&manifest_path,
&stripped_binary_path,
&package_path,
&formatted_path,
&package_name,
app_name,
)?;
pm_build(verbose, &target_options, &manifest_path, &output_path).context("pm_build failed")?;
pm_archive(verbose, &target_options, &output_path, &manifest_path)
.context("pm_archive failed")?;
pm_publish(verbose, &target_options, &output_path.join(format!("{}-0.far", package_name)))
.context("pm_publish failed")?;
Ok(format!("fuchsia-pkg://fuchsia.com/{}#meta/{}.cmx", package_name, package_name))
}