blob: 9297ba4856f00399d4c07e48c4698f9db240e92c [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
fn main() -> Result<()> {
if cfg!(feature = "tflm") {
build_tflm()?;
}
let soc_build_dir = Path::new("../../build/hps_platform");
// Tell linker to look in the directory that contains memory.x
println!("cargo:rustc-link-search={}", soc_build_dir.display());
let out_dir = std::path::PathBuf::from(std::env::var_os("OUT_DIR").expect("OUT_DIR not set"));
// Also tell linker to search in out_dir, so it finds the linker scripts we generate below.
println!("cargo:rustc-link-search={}", out_dir.display());
write_file(
&out_dir.join("linker_discard.x"),
"
SECTIONS {
/DISCARD/ :
{
*(.init_array)
*(.gcc_except_table.*)
*(.eh_frame*)
abort(*)
}
}",
);
if cfg!(feature = "tflm") {
// The symbol `end` is used by "sbrk" from libnosys. It's unlikely that sbrk
// is actually used from any reachable code, but linking fails if we don't
// define `end`.
//
// The arena is a 256KB section region containing the TfLM tensor arena.
// Since the standard link script does not know about the arena, We need to
// explicitly place arena sections in the arena region.
write_file(
&out_dir.join("tflm_inference.x"),
"
PROVIDE( end = . );
SECTIONS {
.arena (NOLOAD) :
{
_farena = .;
*(.arena)
_earena = .;
} > arena
}
",
);
println!("cargo:rustc-link-arg=-Ttflm_inference.x");
// For libc.a, libm.a and libstdc++.a.
println!(
"cargo:rustc-link-search={}",
find_builtin_lib("libc.a")?.display()
);
// For libgcc.a.
println!(
"cargo:rustc-link-search={}",
find_builtin_lib("libgcc.a")?.display()
);
// For libtensorflow-microlite.a.
println!("cargo:rustc-link-search=../../build/riscv-gcc");
}
Ok(())
}
fn find_builtin_lib(name: &str) -> Result<PathBuf> {
let mut gcc_output = Command::new("riscv64-unknown-elf-gcc")
.arg("-march=rv32im")
.arg("-mabi=ilp32")
.arg(format!("-print-file-name={}", name))
.output()
.with_context(|| format!("Failed to learn {} path from GCC", name))?
.stdout;
gcc_output.pop(); // discard trailing newline
let path = PathBuf::from(OsString::from_vec(gcc_output));
// If GCC doesn't find the library we are looking for, it just returns the (unqualified) name
// that we asked it for, instead of a full path.
if path == Path::new(name) {
bail!("GCC cannot find {}", name);
}
Ok(path.parent().unwrap().to_path_buf())
}
fn build_tflm() -> Result<()> {
const TFLM_DIR: &str = "../../../third_party/tflm";
// Regenerate ninja config
if !Command::new("gn")
.arg("gen")
.arg("../../../build")
.status()
.context("Failed to run gn gen build")?
.success()
{
bail!("Failed to run `gn gen build`");
}
// Run ninja
if !Command::new("ninja")
.arg("-C")
.arg("../../../build")
.arg("riscv-gcc/libtflite-micro.a")
.status()
.context("Failed to build libtflite-micro.a with ninja")?
.success()
{
bail!("Failed to build libtflite-micro.a with ninja");
}
println!("cargo:rerun-if-changed=../../../build/riscv-gcc/libtflite-micro.a");
// Make sure that build.rs gets rerun if any source files in TFLM get
// changed.
print_sources_files_in_dir(Path::new(TFLM_DIR))?;
Ok(())
}
fn print_sources_files_in_dir(tflm_gen_dir: &Path) -> Result<()> {
const SOURCE_EXTENSIONS: &[&str] = &["h", "cc", "c", "gn"];
for entry in tflm_gen_dir.read_dir()? {
let path = entry?.path();
if path
.extension()
.and_then(OsStr::to_str)
.map(|extension| SOURCE_EXTENSIONS.contains(&extension))
.unwrap_or(false)
{
println!("cargo:rerun-if-changed={}", path.display());
}
if path.is_dir() {
print_sources_files_in_dir(&path)?;
}
}
Ok(())
}
fn write_file(path: &std::path::Path, contents: &str) {
if std::fs::read_to_string(&path).unwrap_or_default() != contents {
if let Err(error) = std::fs::write(&path, contents) {
panic!("Failed to write {}: {}", path.display(), error);
}
}
}