Created
January 3, 2021 09:24
-
-
Save leostera/1f17db360c767f4122e41735b3b6098a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use anyhow::*; | |
use std::collections::HashMap; | |
use std::collections::HashSet; | |
use std::path::PathBuf; | |
use zap_core::{Action, Artifact, ComputedTarget, Label, Rule, Target, ToolchainManager}; | |
use zap_toolchains::ErlangToolchain; | |
#[derive(Debug, Clone)] | |
pub struct ErlangLibrary { | |
label: Label, | |
srcs: Vec<PathBuf>, | |
headers: Vec<PathBuf>, | |
behaviors: Vec<PathBuf>, | |
deps: Vec<Label>, | |
outputs: Vec<PathBuf>, | |
toolchains: Vec<Label>, | |
all_sources: Vec<PathBuf>, | |
} | |
impl ErlangLibrary { | |
pub fn new(label: Label) -> ErlangLibrary { | |
let toolchains = vec![Label::new("//_toolchains:erlang")]; | |
ErlangLibrary { | |
label, | |
srcs: vec![], | |
headers: vec![], | |
deps: vec![], | |
outputs: vec![], | |
behaviors: vec![], | |
all_sources: vec![], | |
toolchains, | |
} | |
} | |
pub fn set_behaviors(&self, behaviors: Vec<PathBuf>) -> ErlangLibrary { | |
ErlangLibrary { | |
behaviors, | |
..self.clone() | |
} | |
} | |
pub fn set_headers(&self, headers: Vec<PathBuf>) -> ErlangLibrary { | |
let mut lib = ErlangLibrary { | |
headers, | |
..self.clone() | |
}; | |
lib.update_all_sources(); | |
lib | |
} | |
fn update_all_sources(&mut self) { | |
self.all_sources = vec![self.headers.clone(), self.srcs.clone()] | |
.iter() | |
.flatten() | |
.cloned() | |
.collect(); | |
} | |
} | |
impl Rule for ErlangLibrary { | |
fn mnemonic(&self) -> &str { | |
"erlang_library" | |
} | |
fn toolchains(&self) -> &[Label] { | |
&self.toolchains | |
} | |
fn execute( | |
&self, | |
target: &ComputedTarget, | |
toolchains: &ToolchainManager, | |
) -> Result<Vec<Action>, anyhow::Error> { | |
// Collect all the transitive deps' outputs | |
let transitive_deps: Vec<PathBuf> = target | |
.deps() | |
.iter() | |
.flat_map(|node| node.outs.clone()) | |
.flat_map(|artifact| artifact.outputs().to_vec()) | |
.collect(); | |
// Collect all the headers | |
let transitive_headers: HashSet<&PathBuf> = transitive_deps | |
.iter() | |
.filter(|out| { | |
out.extension() | |
.and_then(std::ffi::OsStr::to_str) | |
.map(|str| str.eq_ignore_ascii_case("hrl")) | |
.unwrap_or(false) | |
}) | |
.collect(); | |
let headers: Vec<PathBuf> = self | |
.headers | |
.iter() | |
.chain(transitive_headers) | |
.cloned() | |
.collect(); | |
// Collect all the srcs | |
let transitive_srcs: HashSet<&PathBuf> = transitive_deps | |
.iter() | |
.filter(|out| { | |
out.extension() | |
.and_then(std::ffi::OsStr::to_str) | |
.map(|str| str.eq_ignore_ascii_case("erl")) | |
.unwrap_or(false) | |
}) | |
.collect(); | |
let srcs: Vec<PathBuf> = self | |
.behaviors | |
.iter() | |
.chain(&self.srcs) | |
.chain(transitive_srcs) | |
.cloned() | |
.collect(); | |
// Collect all the bea files | |
let transitive_beam_files: HashSet<&PathBuf> = transitive_deps | |
.iter() | |
.filter(|out| { | |
out.extension() | |
.and_then(std::ffi::OsStr::to_str) | |
.map(|str| str.eq_ignore_ascii_case("beam")) | |
.unwrap_or(false) | |
}) | |
.collect(); | |
let beam_files: Vec<PathBuf> = vec![] | |
.iter() | |
.chain(transitive_beam_files) | |
.cloned() | |
.collect(); | |
let include_paths: HashSet<&str> = headers | |
.iter() | |
.map(|hrl| { | |
let parent = hrl.parent().unwrap().to_str().unwrap(); | |
if parent.is_empty() { | |
"." | |
} else { | |
parent | |
} | |
}) | |
.collect(); | |
let includes: Vec<&str> = include_paths | |
.iter() | |
.cloned() | |
.flat_map(|dir| vec!["-I", dir]) | |
.collect(); | |
let extra_lib_paths: HashSet<&str> = beam_files | |
.iter() | |
.map(|beam| beam.parent().unwrap().to_str().unwrap()) | |
.collect(); | |
let extra_libs: Vec<&str> = extra_lib_paths | |
.iter() | |
.cloned() | |
.flat_map(|dir| vec!["-pa", dir]) | |
.collect(); | |
let mut paths: HashMap<String, Vec<String>> = HashMap::with_capacity(srcs.len()); | |
for src in srcs { | |
let parent = { | |
let parent = src.parent().unwrap().to_str().unwrap(); | |
if parent.is_empty() { | |
".".to_string() | |
} else { | |
parent.to_string() | |
} | |
}; | |
let src = src.to_str().unwrap().to_string(); | |
paths.entry(parent).or_insert(vec![]).push(src); | |
} | |
let erl_tools = toolchains.get(&ErlangToolchain::label()).tools(); | |
let erlc = erl_tools | |
.get("erlc") | |
.context("Could not find erlc in the ErlangToolchain")? | |
.to_path_buf(); | |
let actions = paths | |
.iter() | |
.map(|(out, srcs)| { | |
let erlc = erlc.clone(); | |
let srcs: Vec<&str> = srcs.iter().map(|s| s.as_str()).collect(); | |
let mut action = Action::exec(erlc); | |
action.args(&["-b", "beam", "-Wall"]); | |
action.args(&includes); | |
action.args(&extra_libs); | |
action.args(&["-o", out]); | |
action.args(&["--"]); | |
action.args(&srcs); | |
action.build() | |
}) | |
.collect(); | |
Ok(actions) | |
} | |
} | |
impl Target for ErlangLibrary { | |
fn rule(&self) -> &dyn Rule { | |
self | |
} | |
fn set_deps(&mut self, deps: &[Label]) { | |
self.deps = deps.to_vec(); | |
self.deps.extend(self.rule().toolchains().to_vec()); | |
} | |
fn label(&self) -> &Label { | |
&self.label | |
} | |
fn deps(&self) -> &[Label] { | |
&self.deps | |
} | |
fn srcs(&self, _deps: &[Artifact]) -> &[PathBuf] { | |
&self.all_sources | |
} | |
fn set_srcs(&mut self, srcs: &[PathBuf]) { | |
self.srcs = srcs.to_vec(); | |
self.update_all_sources(); | |
} | |
fn outputs(&self, deps: &[Artifact]) -> Vec<Artifact> { | |
let extra_srcs: Vec<PathBuf> = deps | |
.iter() | |
.flat_map(|a| a.outputs().clone()) | |
.filter(|path| { | |
path.extension() | |
.and_then(std::ffi::OsStr::to_str) | |
.map(|str| str.eq_ignore_ascii_case("erl")) | |
.unwrap_or(false) | |
}) | |
.cloned() | |
.collect(); | |
let outputs: Vec<PathBuf> = self | |
.srcs | |
.iter() | |
.cloned() | |
.chain(extra_srcs) | |
.map(|file| file.with_extension("beam")) | |
.chain(self.headers.clone()) | |
.collect(); | |
vec![Artifact::builder() | |
.set_inputs(self.srcs(&deps)) | |
.set_outputs(&outputs) | |
.build() | |
.unwrap()] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment