1extern crate cc;
2
3use std::collections::HashMap;
4use std::env;
5use std::fs::File;
6use std::io::Write;
7use std::path::Path;
8use std::process::Command;
9use which::which;
10
11pub struct NoFileYet;
12pub struct WithEclairFile<'a>(&'a str);
13
14pub struct Build<'a, T = NoFileYet> {
15 clang_compiler: &'a str,
16 eclair_compiler: &'a str,
17 datalog_dir: Option<&'a str>, eclair_file: T,
19}
20
21impl<'a> Build<'a, NoFileYet> {
22 pub fn new() -> Self {
23 Build {
24 clang_compiler: "clang",
25 eclair_compiler: "eclair",
26 eclair_file: NoFileYet,
27 datalog_dir: None,
28 }
29 }
30
31 pub fn file(self, path: &str) -> Build<'a, WithEclairFile> {
33 Build {
34 clang_compiler: self.clang_compiler,
35 eclair_compiler: self.eclair_compiler,
36 datalog_dir: self.datalog_dir,
37 eclair_file: WithEclairFile(path),
38 }
39 }
40}
41
42impl<'a, T> Build<'a, T> {
43 pub fn eclair(mut self, path: &'a str) -> Self {
44 self.eclair_compiler = path;
45 self
46 }
47
48 pub fn clang(mut self, path: &'a str) -> Self {
51 self.clang_compiler = path;
52 self
53 }
54
55 pub fn datalog_dir(mut self, path: &'a str) -> Self {
56 self.datalog_dir = path.into();
57 self
58 }
59}
60
61impl<'a> Build<'a, WithEclairFile<'a>> {
62 pub fn compile(self) {
63 self.check_valid_setup();
64
65 println!("cargo:rerun-if-changed={}", self.eclair_file.0);
66
67 let llvm_output_file = self.eclair_compile();
68 self.llvm_compile(llvm_output_file);
69 }
70
71 fn check_valid_setup(&self) {
72 let _ = which(&self.eclair_compiler).expect("Could not locate eclair compiler!");
73 let _ = which(&self.clang_compiler).expect("Could not locate clang compiler!");
74 }
75
76 fn eclair_compile(&self) -> std::path::PathBuf {
77 let output = self.llvm_output_file();
78
79 let mut env = HashMap::new();
80 if let Some(dl_dir) = &self.datalog_dir {
81 env.insert("DATALOG_DIR", dl_dir);
82 }
83
84 let llvm_ir = Command::new(&self.eclair_compiler)
85 .args(["compile", &self.eclair_file.0, "--emit", "llvm"])
86 .envs(env)
87 .output()
88 .unwrap();
89
90 let mut f = File::create(&output).unwrap();
91 f.write_all(llvm_ir.stdout.as_slice())
92 .expect("Failed to write LLVM IR generated by Eclair to file");
93
94 output
95 }
96
97 fn llvm_compile(self, llvm_file: std::path::PathBuf) {
98 let lib_name = "eclair";
101 cc::Build::new()
102 .compiler(self.clang_compiler)
103 .file(llvm_file)
104 .compile(lib_name);
105 }
106
107 fn llvm_output_file(&self) -> std::path::PathBuf {
108 let out_dir = env::var("OUT_DIR").unwrap();
109 Path::new(&out_dir).join("program.ll")
110 }
111}