minutus_mruby_build_utils/
lib.rs1use anyhow::{anyhow, Result};
2use std::path::{Path, PathBuf};
3
4pub struct MRubyManager {
6 workdir: Option<PathBuf>,
7 mruby_version: Option<String>,
8 do_link: bool,
9 build_config: Option<PathBuf>,
10 do_download: bool,
11}
12
13impl MRubyManager {
14 pub fn new() -> Self {
17 Self {
18 workdir: None,
19 mruby_version: None,
20 do_link: true,
21 build_config: None,
22 do_download: true,
23 }
24 }
25
26 pub fn workdir(mut self, path: &Path) -> Self {
28 self.workdir = Some(path.to_path_buf());
29 self
30 }
31
32 pub fn mruby_version(mut self, mruby_version: &str) -> Self {
34 self.mruby_version = Some(mruby_version.to_string());
35 self
36 }
37
38 pub fn build_config(mut self, build_config: &Path) -> Self {
40 self.build_config = Some(build_config.to_path_buf());
41 self
42 }
43
44 pub fn link(mut self, doit: bool) -> Self {
50 self.do_link = doit;
51 self
52 }
53
54 pub fn download(mut self, doit: bool) -> Self {
58 self.do_download = doit;
59 self
60 }
61
62 pub fn run(self) {
64 let workdir = self.workdir.unwrap_or_else(|| {
65 let out_dir = std::env::var("OUT_DIR")
66 .expect("Could not fetch \"OUT_DIR\" environment variable.");
67 Path::new(&out_dir).to_path_buf()
68 });
69 let mruby_version = self
70 .mruby_version
71 .map(String::from)
72 .expect("mruby_version is not set.");
73 let build_config = self
74 .build_config
75 .unwrap_or(Path::new("default").to_path_buf()); if self.do_download {
78 download_mruby(&workdir, &mruby_version);
79 }
80 build_mruby(&workdir, &build_config);
81
82 if self.do_link {
83 link_mruby(&workdir);
84 }
85 }
86}
87
88fn build_mruby(workdir: &Path, path: &Path) {
89 let c = &[
90 "rake",
91 "all",
92 &format!("MRUBY_CONFIG={}", path.to_string_lossy()),
93 ];
94 run_command(&workdir.join("mruby"), c).unwrap();
95}
96
97fn link_mruby(workdir: &Path) {
98 let mruby_config = workdir.join("mruby").join("bin").join("mruby-config");
99 let ldflags_before_libs = run_command(
100 workdir,
101 &[mruby_config.to_str().unwrap(), "--ldflags-before-libs"],
102 )
103 .unwrap();
104 let ldflags = run_command(workdir, &[mruby_config.to_str().unwrap(), "--ldflags"]).unwrap();
105 let libs = run_command(workdir, &[mruby_config.to_str().unwrap(), "--libs"]).unwrap();
106 println!(
107 "cargo:rustc-flags={} {} {}",
108 ldflags_before_libs.trim(),
109 ldflags.trim(),
110 libs.trim()
111 );
112
113 if cc::Build::new()
115 .is_flag_supported("-Wl,--no-as-needed")
116 .unwrap()
117 {
118 println!("cargo:rustc-link-arg=-Wl,--no-as-needed");
119 println!("cargo:rustc-link-arg=-lmruby");
120 }
121}
122
123pub fn download_mruby(workdir: &Path, mruby_version: &str) {
125 if workdir.join("mruby").exists() {
126 return;
127 }
128
129 let url = if mruby_version == "master" {
130 String::from("https://github.com/mruby/mruby/archive/refs/heads/master.tar.gz")
131 } else {
132 format!(
133 "https://github.com/mruby/mruby/archive/refs/tags/{}.tar.gz",
134 mruby_version
135 )
136 };
137
138 let resp = reqwest::blocking::get(url).unwrap();
139 let tar_gz = resp.bytes().unwrap();
140 let tar = {
141 use bytes::Buf;
142 flate2::read::GzDecoder::new(tar_gz.reader())
143 };
144 let mut archive = tar::Archive::new(tar);
145 archive.unpack(&workdir).unwrap();
146
147 std::fs::rename(
148 workdir.join(format!("mruby-{}", mruby_version)),
149 workdir.join("mruby"),
150 )
151 .unwrap();
152}
153
154fn run_command(current_dir: &Path, cmd: &[&str]) -> Result<String> {
155 println!("Start: {:?}", cmd);
156
157 let output = std::process::Command::new(cmd[0])
158 .args(&cmd[1..])
159 .current_dir(current_dir)
160 .output()?;
161
162 if output.status.success() {
163 Ok(String::from_utf8_lossy(&output.stdout).to_string())
164 } else {
165 Err(anyhow!(format!(
166 "Executing {:?} failed: {}, {}",
167 cmd,
168 String::from_utf8_lossy(&output.stdout),
169 String::from_utf8_lossy(&output.stderr)
170 )))
171 }
172}