build_utils/build/
meson.rs1use crate::BuildStep;
2use crate::util::execute_build_command;
3use std::process::Command;
4use crate::build::{BuildResult, Build, BuildStepError, LibraryType, LinkSearchKind};
5use std::collections::HashMap;
6use std::path::PathBuf;
7use std::hash::{Hasher, Hash};
8
9pub struct MesonBuild {
10 meson_options: HashMap<String, String>
11}
12
13impl MesonBuild {
14 pub fn builder() -> MesonBuildBuilder {
15 MesonBuildBuilder::new()
16 }
17}
18
19impl BuildStep for MesonBuild {
20 fn name(&self) -> &str {
21 "meson build"
22 }
23
24 fn hash(&self, hasher: &mut Box<dyn Hasher>) {
25 self.meson_options.iter().for_each(|(key, value)| {
26 key.hash(hasher);
27 value.hash(hasher);
28 });
29 }
30
31 fn execute(&mut self, build: &Build, result: &mut BuildResult) -> Result<(), BuildStepError> {
32 let build_path = build.build_path().to_str().expect("invalid build path");
33 let source_path = build.source().local_directory().to_str().expect("invalid source path");
34
35 {
37 let mut command = Command::new("meson");
38 command.arg("setup");
39
40 if let Some(prefix) = build.install_prefix() {
41 command.args(&["--prefix", prefix.to_str().expect("invalid install prefix")]);
42 }
43
44 match build.library_type {
45 LibraryType::Shared => command.arg("-Ddefault_library=shared"),
46 LibraryType::Static => command.arg("-Ddefault_library=static"),
47 };
48
49 self.meson_options.iter().for_each(|(key, value)| {
50 command.arg(format!("-D{}={}", key, value));
51 });
52
53 command.arg(&build_path);
54 command.arg(&source_path);
55
56 execute_build_command(&mut command, "failed to setup build")?;
57 }
58
59 {
61 let mut command = Command::new("meson");
62 command.arg("compile");
63 command.arg("-C");
64 command.arg(&build_path);
65 execute_build_command(&mut command, "failed to execute build")?;
66 }
67
68 {
70 let mut command = Command::new("meson");
71 command.arg("install");
72 command.arg("-C");
73 command.arg(&build_path);
74 let (stdout, stderr) = execute_build_command(&mut command, "failed to install build")?;
75
76 let install_lines = stdout.lines()
77 .filter(|line| line.starts_with("Installing "));
78
79 let mut installed_elements = HashMap::with_capacity(50);
80 for full_line in install_lines {
81 let line = &full_line[11..];
83 let mut elements = line.split(" to ");
84
85 let key = elements.next().map(|e| e.to_owned());
86 let value = elements.next().map(|e| e.to_owned());
87 if elements.next().is_some() {
88 return Err(BuildStepError::new(format!("Meson line \"{}\" contains more than one \" to \" parts.", full_line).to_owned(), stdout, stderr));
89 }
90 if key.is_none() || value.is_none() {
91 return Err(BuildStepError::new(format!("Meson line \"{}\" misses the key or value.", full_line).to_owned(), stdout, stderr));
92 }
93
94 installed_elements.insert(key.unwrap(), value.unwrap());
95 }
96
97 installed_elements.iter().for_each(|(key, value)| {
100 let source = PathBuf::from(key);
101 if let Some(extension) = source.extension().map(|e| e.to_string_lossy().into_owned()) {
102 let target = PathBuf::from(value);
103 if !target.is_dir() {
104 eprintln!("meson printed install for file \"{:?}\" to \"{:?}\", but target isn't a directory.", source, target);
105 return;
106 }
107
108 if matches!(extension.as_ref(), "a" | "lib") {
110 result.add_library(source.file_name().expect("missing source file name").to_string_lossy().into_owned(), Some(LibraryType::Static));
111 } else if matches!(extension.as_ref(), "so" | "dll") {
112 result.add_library(source.file_name().expect("missing source file name").to_string_lossy().into_owned(), Some(LibraryType::Shared));
113 } else {
114 return;
115 }
116 result.add_library_path(target, Some(LinkSearchKind::Native));
117 }
118 });
119 }
120
121 Ok(())
122 }
123}
124
125pub struct MesonBuildBuilder {
126 inner: MesonBuild
127}
128
129impl MesonBuildBuilder {
130 fn new() -> Self {
131 MesonBuildBuilder{
132 inner: MesonBuild{
133 meson_options: HashMap::new()
134 }
135 }
136 }
137
138 pub fn meson_option<K, V>(mut self, key: K, value: V) -> Self
139 where K: Into<String>,
140 V: Into<String>
141 {
142 self.inner.meson_options.insert(key.into(), value.into());
143 self
144 }
145
146 pub fn build(self) -> MesonBuild {
147 self.inner
148 }
149}
150
151#[cfg(test)]
152mod test {
153 use crate::build::{BuildBuilder, MesonBuild};
154 use crate::source::BuildSourceGit;
155 use std::env;
156 use crate::util::create_temporary_path;
157
158 #[test]
159 fn test_build_usrsctp() {
160 let base_url = std::env::current_dir().expect("missing current dir").join("__test_meson");
161
162 env::set_var("rbuild_meson-test_library_type", "static");
163
164 let source = BuildSourceGit::builder("https://github.com/cisco/libsrtp.git".to_owned())
165 .checkout_folder(Some(base_url.clone()))
166 .skip_revision_checkout(true)
167 .build();
168
169 let meson_step = MesonBuild::builder()
170 .meson_option("sctp_build_programs", "false")
171 .build();
172
173 let build = BuildBuilder::new()
175 .name("meson-test")
176 .source(Box::new(source))
177 .install_prefix(base_url.join("install_root"))
178 .build_path(base_url.clone())
179 .add_step(Box::new(meson_step))
180 .remove_build_dir(false)
181 .build();
182
183 let mut build = build.expect("failed to create build");
184 match build.execute() {
185 Err(error) => {
186 println!("{}", error.pretty_format());
187 panic!();
188 },
189 Ok(result) => result.emit_cargo()
190 }
191 }
192}