-
Notifications
You must be signed in to change notification settings - Fork 1
/
build.rs
135 lines (113 loc) · 4.47 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
extern crate autotools;
use autotools::Config;
use std::env;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
use MD5Status::{Mismatch, Unknown};
const PACKAGE_URL: &str = "https://github.com/ivmai/cudd/archive/refs/tags/cudd-3.0.0.tar.gz";
const PACKAGE_MD5: &str = "edca9c69528256ca8ae37be9cedef73f";
#[derive(Debug)]
enum FetchError {
CommandError(std::process::ExitStatus),
IOError(std::io::Error),
PathExists,
}
enum MD5Status {
Match,
Mismatch,
Unknown,
}
impl From<std::io::Error> for FetchError {
fn from(err: std::io::Error) -> FetchError {
FetchError::IOError(err)
}
}
/// Run a command and return (stdout, stderr) if exit status is success.
fn run_command(cmd: &mut Command) -> Result<(String, String), FetchError> {
let output = cmd.output()?;
return if output.status.success() {
Ok((
String::from_utf8(output.stdout).unwrap(),
String::from_utf8(output.stderr).unwrap(),
))
} else {
eprintln!("Command {:?} exited with status {}", cmd, output.status);
Err(FetchError::CommandError(output.status))
};
}
/// Fetch a file from a URL if it does not already exist in out_dir and verify its md5sum if possible.
fn fetch_package(out_dir: &str, url: &str, md5: &str) -> Result<(PathBuf, MD5Status), FetchError> {
let out_path = Path::new(&out_dir);
let target_path = out_path.join(Path::new(url).file_name().unwrap());
let target_path_str = target_path.clone().into_os_string().into_string().unwrap();
match target_path.metadata() {
Err(error) if error.kind() == ErrorKind::NotFound => {
// Path does not exist! Start download...
println!("Downloading {} to {}", url, target_path_str);
let mut command = Command::new("curl");
command.args(&["-L", url, "-o", target_path_str.as_str()]);
run_command(&mut command)?;
}
Ok(data) if data.is_file() => {
println!("{} exists. Skipping download.", target_path_str);
}
Ok(_) => return Err(FetchError::PathExists),
Err(error) => return Err(FetchError::from(error)),
}
// Now run md5 sum check:
let mut command_1 = Command::new("md5sum");
command_1.arg(target_path.clone());
let mut command_2 = Command::new("md5");
command_2.arg(target_path.clone());
let md5_result = run_command(&mut command_1).or_else(|_| run_command(&mut command_2));
let md5_status = match md5_result {
Err(_) => MD5Status::Unknown,
Ok((output, _)) if output.contains(md5) => MD5Status::Match,
_ => MD5Status::Mismatch,
};
Ok((target_path, md5_status))
}
fn main() -> Result<(), String> {
let build_cudd = env::var_os("CARGO_FEATURE_BUILD_CUDD").is_some();
if !build_cudd {
// If silent build is active, don't do anything.
return Ok(());
}
let out_dir = env::var("OUT_DIR")
.map_err(|_| "Environmental variable `OUT_DIR` not defined.".to_string())?;
let (tar_path, md5_status) = fetch_package(&out_dir, PACKAGE_URL, PACKAGE_MD5)
.map_err(|e| format!("Error downloading CUDD package: {:?}.", e))?;
let tar_path_str = tar_path.to_str().unwrap().to_string();
match md5_status {
Unknown => eprintln!("WARNING: MD5 not computed. Package validation skipped."),
Mismatch => return Err("CUDD package MD5 hash mismatch.".to_string()),
_ => (),
}
// Get cudd.tar.gz path without extensions.
let cudd_path = tar_path.with_extension("").with_extension("");
let cudd_path_str = cudd_path.clone().into_os_string().into_string().unwrap();
if !cudd_path.exists() {
// Create the destination directory.
std::fs::create_dir_all(cudd_path.clone())
.map_err(|e| format!("Cannot create CUDD directory: {:?}", e))?;
}
// un-tar package, ignoring the name of the top level folder, dumping into cudd_path instead.
let mut tar_command = Command::new("tar");
tar_command.args(&[
"xf",
&tar_path_str,
"--strip-components=1",
"-C",
&cudd_path_str,
]);
run_command(&mut tar_command).map_err(|e| format!("Error decompressing CUDD: {:?}", e))?;
// Enable dddmp when building.
let build_output = Config::new(cudd_path).enable("dddmp", None).build();
println!(
"cargo:rustc-link-search=native={}",
build_output.join("lib").display()
);
println!("cargo:rustc-link-lib=static=cudd");
Ok(())
}