Skip to content

Instantly share code, notes, and snippets.

@leostera
Created January 3, 2021 09:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leostera/1f17db360c767f4122e41735b3b6098a to your computer and use it in GitHub Desktop.
Save leostera/1f17db360c767f4122e41735b3b6098a to your computer and use it in GitHub Desktop.
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