use crate::BuildStep;
use crate::util::execute_build_command;
use std::process::Command;
use crate::build::{BuildResult, Build, BuildStepError, LibraryType, LinkSearchKind};
use std::collections::HashMap;
use std::path::PathBuf;
use std::hash::{Hasher, Hash};
pub struct MesonBuild {
meson_options: HashMap<String, String>
}
impl MesonBuild {
pub fn builder() -> MesonBuildBuilder {
MesonBuildBuilder::new()
}
}
impl BuildStep for MesonBuild {
fn name(&self) -> &str {
"meson build"
}
fn hash(&self, hasher: &mut Box<dyn Hasher>) {
self.meson_options.iter().for_each(|(key, value)| {
key.hash(hasher);
value.hash(hasher);
});
}
fn execute(&mut self, build: &Build, result: &mut BuildResult) -> Result<(), BuildStepError> {
let build_path = build.build_path().to_str().expect("invalid build path");
let source_path = build.source().local_directory().to_str().expect("invalid source path");
{
let mut command = Command::new("meson");
command.arg("setup");
if let Some(prefix) = build.install_prefix() {
command.args(&["--prefix", prefix.to_str().expect("invalid install prefix")]);
}
match build.library_type {
LibraryType::Shared => command.arg("-Ddefault_library=shared"),
LibraryType::Static => command.arg("-Ddefault_library=static"),
};
self.meson_options.iter().for_each(|(key, value)| {
command.arg(format!("-D{}={}", key, value));
});
command.arg(&build_path);
command.arg(&source_path);
execute_build_command(&mut command, "failed to setup build")?;
}
{
let mut command = Command::new("meson");
command.arg("compile");
command.arg("-C");
command.arg(&build_path);
execute_build_command(&mut command, "failed to execute build")?;
}
{
let mut command = Command::new("meson");
command.arg("install");
command.arg("-C");
command.arg(&build_path);
let (stdout, stderr) = execute_build_command(&mut command, "failed to install build")?;
let install_lines = stdout.lines()
.filter(|line| line.starts_with("Installing "));
let mut installed_elements = HashMap::with_capacity(50);
for full_line in install_lines {
let line = &full_line[11..];
let mut elements = line.split(" to ");
let key = elements.next().map(|e| e.to_owned());
let value = elements.next().map(|e| e.to_owned());
if elements.next().is_some() {
return Err(BuildStepError::new(format!("Meson line \"{}\" contains more than one \" to \" parts.", full_line).to_owned(), stdout, stderr));
}
if key.is_none() || value.is_none() {
return Err(BuildStepError::new(format!("Meson line \"{}\" misses the key or value.", full_line).to_owned(), stdout, stderr));
}
installed_elements.insert(key.unwrap(), value.unwrap());
}
installed_elements.iter().for_each(|(key, value)| {
let source = PathBuf::from(key);
if let Some(extension) = source.extension().map(|e| e.to_string_lossy().into_owned()) {
let target = PathBuf::from(value);
if !target.is_dir() {
eprintln!("meson printed install for file \"{:?}\" to \"{:?}\", but target isn't a directory.", source, target);
return;
}
if matches!(extension.as_ref(), "a" | "lib") {
result.add_library(source.file_name().expect("missing source file name").to_string_lossy().into_owned(), Some(LibraryType::Static));
} else if matches!(extension.as_ref(), "so" | "dll") {
result.add_library(source.file_name().expect("missing source file name").to_string_lossy().into_owned(), Some(LibraryType::Shared));
} else {
return;
}
result.add_library_path(target, Some(LinkSearchKind::Native));
}
});
}
Ok(())
}
}
pub struct MesonBuildBuilder {
inner: MesonBuild
}
impl MesonBuildBuilder {
fn new() -> Self {
MesonBuildBuilder{
inner: MesonBuild{
meson_options: HashMap::new()
}
}
}
pub fn meson_option<K, V>(mut self, key: K, value: V) -> Self
where K: Into<String>,
V: Into<String>
{
self.inner.meson_options.insert(key.into(), value.into());
self
}
pub fn build(self) -> MesonBuild {
self.inner
}
}
#[cfg(test)]
mod test {
use crate::build::{BuildBuilder, MesonBuild};
use crate::source::BuildSourceGit;
use std::env;
use crate::util::create_temporary_path;
#[test]
fn test_build_usrsctp() {
let base_url = std::env::current_dir().expect("missing current dir").join("__test_meson");
env::set_var("rbuild_meson-test_library_type", "static");
let source = BuildSourceGit::builder("https://github.com/cisco/libsrtp.git".to_owned())
.checkout_folder(Some(base_url.clone()))
.skip_revision_checkout(true)
.build();
let meson_step = MesonBuild::builder()
.meson_option("sctp_build_programs", "false")
.build();
let build = BuildBuilder::new()
.name("meson-test")
.source(Box::new(source))
.install_prefix(base_url.join("install_root"))
.build_path(base_url.clone())
.add_step(Box::new(meson_step))
.remove_build_dir(false)
.build();
let mut build = build.expect("failed to create build");
match build.execute() {
Err(error) => {
println!("{}", error.pretty_format());
panic!();
},
Ok(result) => result.emit_cargo()
}
}
}