1extern crate cc;
2
3use std::env;
4use std::ffi::OsStr;
5use std::fs;
6use std::path::{Path, PathBuf};
7use std::process::Command;
8
9pub fn source_dir() -> PathBuf {
10 Path::new(env!("CARGO_MANIFEST_DIR")).join("gmssl")
11}
12
13pub fn version() -> &'static str {
14 env!("CARGO_PKG_VERSION")
15}
16
17pub struct Build {
18 out_dir: Option<PathBuf>,
19 target: Option<String>,
20 host: Option<String>,
21}
22
23pub struct Artifacts {
24 include_dir: PathBuf,
25 lib_dir: PathBuf,
26 bin_dir: PathBuf,
27 libs: Vec<String>,
28 target: String,
29}
30
31impl Build {
32 pub fn new() -> Build {
33 Build {
34 out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s).join("gmssl-build")),
35 target: env::var("TARGET").ok(),
36 host: env::var("HOST").ok(),
37 }
38 }
39
40 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Build {
41 self.out_dir = Some(path.as_ref().to_path_buf());
42 self
43 }
44
45 pub fn target(&mut self, target: &str) -> &mut Build {
46 self.target = Some(target.to_string());
47 self
48 }
49
50 pub fn host(&mut self, host: &str) -> &mut Build {
51 self.host = Some(host.to_string());
52 self
53 }
54
55 fn cmd_make(&self) -> Command {
56 let host = &self.host.as_ref().expect("HOST dir not set")[..];
57 if host.contains("dragonfly")
58 || host.contains("freebsd")
59 || host.contains("openbsd")
60 || host.contains("solaris")
61 || host.contains("illumos")
62 {
63 Command::new("gmake")
64 } else {
65 Command::new("make")
66 }
67 }
68
69
70 fn cmd_cmake(&self) -> Command {
71 Command::new("cmake")
72 }
73
74 #[cfg(windows)]
75 fn check_env_var(&self, var_name: &str) -> Option<bool> {
76 env::var_os(var_name).map(|s| {
77 if s == "1" {
78 println!(
80 "{}: nasm.exe is force enabled by the \
81 'OPENSSL_RUST_USE_NASM' env var.",
82 env!("CARGO_PKG_NAME")
83 );
84 true
85 } else if s == "0" {
86 println!(
88 "{}: nasm.exe is force disabled by the \
89 'OPENSSL_RUST_USE_NASM' env var.",
90 env!("CARGO_PKG_NAME")
91 );
92 false
93 } else {
94 panic!(
95 "The environment variable {} is set to an unacceptable value: {:?}",
96 var_name, s
97 );
98 }
99 })
100 }
101
102 #[cfg(windows)]
103 fn is_nasm_ready(&self) -> bool {
104 self.check_env_var("OPENSSL_RUST_USE_NASM")
105 .unwrap_or_else(|| {
106 let wherenasm = Command::new("cmd")
108 .args(&["/C", "where nasm"])
109 .output()
110 .expect("Failed to execute `cmd`.");
111 wherenasm.status.success()
112 })
113 }
114
115 #[cfg(not(windows))]
116 fn is_nasm_ready(&self) -> bool {
117 false
119 }
120
121 pub fn build(&mut self) -> Artifacts {
122 let target = &self.target.as_ref().expect("TARGET dir not set")[..];
123
124
125
126 let host = &self.host.as_ref().expect("HOST dir not set")[..];
127 let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
128
129 self.run_command(Command::new("ls"),format!("{:?}",out_dir).as_str());
130
131 let build_dir = out_dir.join("build");
132
133
134 let install_dir = out_dir.join("install");
135
136 if build_dir.exists() {
137 fs::remove_dir_all(&build_dir).unwrap();
138 }
139 if install_dir.exists() {
140 fs::remove_dir_all(&install_dir).unwrap();
141 }
142
143 let inner_dir = build_dir.join("src");
144 fs::create_dir_all(&inner_dir).unwrap();
145 cp_r(&source_dir(), &inner_dir);
146
147 let mut ls_src = Command::new("ls");
148 ls_src.arg(&inner_dir);
149 self.run_command(ls_src,"display_target");
150
151 let mut display_target = Command::new("echo");
152 display_target.arg(&source_dir());
153 display_target.arg(&inner_dir);
154 display_target.arg(&build_dir);
155 display_target.arg(&install_dir);
156 self.run_command(display_target,"display_target");
157
158 if target.contains("msvc") {
165 let mut build =
166 cc::windows_registry::find(target, "nmake.exe").expect("failed to find nmake");
167 build.arg("build_libs").current_dir(&inner_dir);
168 self.run_command(build, "building OpenSSL");
169
170 let mut install =
171 cc::windows_registry::find(target, "nmake.exe").expect("failed to find nmake");
172 install.arg("install_dev").current_dir(&inner_dir);
173 self.run_command(install, "installing OpenSSL");
174 } else {
175
176 let mut depend = self.cmd_cmake();
177 depend.arg("src").current_dir(&build_dir);
178 let path = install_dir.as_path().to_str().unwrap();
179 depend.arg(format!("-DCMAKE_INSTALL_PREFIX={}",path));
180
181 self.run_command(depend, "building OpenSSL dependencies");
182
183 let mut build = self.cmd_make();
184 build.current_dir(&build_dir);
185 if !cfg!(windows) {
186 if let Some(s) = env::var_os("CARGO_MAKEFLAGS") {
187 build.env("MAKEFLAGS", s);
188 }
189 }
190
191self.run_command(build, "building OpenSSL");
200
201 let mut install = self.cmd_make();
202 install.arg("install").current_dir(&build_dir);
203 self.run_command(install, "installing OpenSSL");
204 }
205
206 let libs = if target.contains("msvc") {
207 vec!["libssl".to_string(), "libcrypto".to_string()]
208 } else {
209 vec!["gmssl".to_string()]
210 };
211
212 fs::remove_dir_all(&inner_dir).unwrap();
213
214 Artifacts {
215 lib_dir: install_dir.join("lib"),
216 bin_dir: install_dir.join("bin"),
217 include_dir: install_dir.join("include"),
218 libs: libs,
219 target: target.to_string(),
220 }
221 }
222
223 fn run_command(&self, mut command: Command, desc: &str) {
224 println!("running {:?}", command);
225 let status = command.status();
226
227 let (status_or_failed, error) = match status {
228 Ok(status) if status.success() => return,
229 Ok(status) => ("Exit status", format!("{}", status)),
230 Err(failed) => ("Failed to execute", format!("{}", failed)),
231 };
232 panic!(
233 "
234
235
236Error {}:
237 Command: {:?}
238 {}: {}
239
240
241 ",
242 desc, command, status_or_failed, error
243 );
244 }
245}
246
247fn cp_r(src: &Path, dst: &Path) {
248 for f in fs::read_dir(src).unwrap() {
249 let f = f.unwrap();
250 let path = f.path();
251 let name = path.file_name().unwrap();
252
253 if name.to_str() == Some(".git") {
256 continue;
257 }
258
259 let dst = dst.join(name);
260 if f.file_type().unwrap().is_dir() {
261 fs::create_dir_all(&dst).unwrap();
262 cp_r(&path, &dst);
263 } else {
264 let _ = fs::remove_file(&dst);
265 fs::copy(&path, &dst).unwrap();
266 }
267 }
268}
269
270fn sanitize_sh(path: &Path) -> String {
271 if !cfg!(windows) {
272 return path.to_str().unwrap().to_string();
273 }
274 let path = path.to_str().unwrap().replace("\\", "/");
275 return change_drive(&path).unwrap_or(path);
276
277 fn change_drive(s: &str) -> Option<String> {
278 let mut ch = s.chars();
279 let drive = ch.next().unwrap_or('C');
280 if ch.next() != Some(':') {
281 return None;
282 }
283 if ch.next() != Some('/') {
284 return None;
285 }
286 Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
287 }
288}
289
290impl Artifacts {
291 pub fn include_dir(&self) -> &Path {
292 &self.include_dir
293 }
294
295 pub fn lib_dir(&self) -> &Path {
296 &self.lib_dir
297 }
298
299 pub fn libs(&self) -> &[String] {
300 &self.libs
301 }
302
303 pub fn print_cargo_metadata(&self) {
304 println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
305 for lib in self.libs.iter() {
306 println!("cargo:rustc-link-lib={}", lib);
307 }
308 println!("cargo:include={}", self.include_dir.display());
309 println!("cargo:lib={}", self.lib_dir.display());
310 if self.target.contains("msvc") {
311 println!("cargo:rustc-link-lib=user32");
312 } else if self.target == "wasm32-wasi" {
313 println!("cargo:rustc-link-lib=wasi-emulated-signal");
314 println!("cargo:rustc-link-lib=wasi-emulated-process-clocks");
315 println!("cargo:rustc-link-lib=wasi-emulated-mman");
316 println!("cargo:rustc-link-lib=wasi-emulated-getpid");
317 }
318 }
319}