1use std::env;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5pub struct Build {
6 out_dir: Option<PathBuf>,
7 target: Option<String>,
8 host: Option<String>,
9 max_stack_size: Option<usize>,
11 use_longjmp: Option<bool>,
13}
14
15pub struct Artifacts {
16 lib_dir: PathBuf,
17 libs: Vec<String>,
18 cpp_stdlib: Option<String>,
19}
20
21impl Build {
22 #[allow(clippy::new_without_default)]
23 pub fn new() -> Build {
24 Build {
25 out_dir: env::var_os("OUT_DIR").map(|s| PathBuf::from(s).join("pluto-build")),
26 target: env::var("TARGET").ok(),
27 host: env::var("HOST").ok(),
28 max_stack_size: None,
29 use_longjmp: None,
30 }
31 }
32
33 pub fn out_dir<P: AsRef<Path>>(&mut self, path: P) -> &mut Build {
34 self.out_dir = Some(path.as_ref().to_path_buf());
35 self
36 }
37
38 pub fn target(&mut self, target: &str) -> &mut Build {
39 self.target = Some(target.to_string());
40 self
41 }
42
43 pub fn host(&mut self, host: &str) -> &mut Build {
44 self.host = Some(host.to_string());
45 self
46 }
47
48 pub fn set_max_stack_size(&mut self, size: usize) -> &mut Build {
49 self.max_stack_size = Some(size);
50 self
51 }
52
53 pub fn use_longjmp(&mut self, r#use: bool) -> &mut Build {
54 self.use_longjmp = Some(r#use);
55 self
56 }
57
58 pub fn build(&mut self) -> Artifacts {
59 let target = &self.target.as_ref().expect("TARGET not set")[..];
60 let host = &self.host.as_ref().expect("HOST not set")[..];
61 let out_dir = self.out_dir.as_ref().expect("OUT_DIR not set");
62
63 let pluto_source_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("pluto");
64 let soup_source_dir = pluto_source_dir.join("vendor").join("Soup");
65
66 if out_dir.exists() {
68 fs::remove_dir_all(out_dir).unwrap();
69 }
70
71 let mut config = cc::Build::new();
73 config
74 .target(target)
75 .host(host)
76 .warnings(false)
77 .cargo_metadata(false)
78 .std("c++17")
79 .flag_if_supported("-fvisibility=hidden")
80 .flag_if_supported("-fno-rtti")
81 .flag_if_supported("-Wno-multichar")
82 .cpp(true);
83
84 if let Some(max_stack_size) = self.max_stack_size {
85 config.define("LUAI_MAXSTACK", &*max_stack_size.to_string());
86 }
87
88 if let Some(true) = self.use_longjmp {
89 config.define("LUA_USE_LONGJMP", "1");
90 }
91
92 if cfg!(debug_assertions) {
93 config.define("LUA_USE_APICHECK", None);
94 } else {
95 config.define("NDEBUG", None);
96 config.opt_level(2);
97 config.flag_if_supported("-fno-math-errno");
99 }
100
101 let soup_lib_name = "soup";
103 let mut soup_config = config.clone();
104 soup_config.add_files_by_ext(&soup_source_dir.join("soup"), "cpp");
105 match target {
106 _ if target.contains("x86_64") => {
107 soup_config
108 .define("SOUP_USE_INTRIN", None)
109 .add_files_by_ext(&soup_source_dir.join("Intrin"), "cpp")
110 .flag_if_supported("-maes")
111 .flag_if_supported("-mpclmul")
112 .flag_if_supported("-mrdrnd")
113 .flag_if_supported("-mrdseed")
114 .flag_if_supported("-msha")
115 .flag_if_supported("-msse4.1");
116 }
117 _ if target.contains("aarch64") => {
118 soup_config
119 .define("SOUP_USE_INTRIN", None)
120 .add_files_by_ext(&soup_source_dir.join("Intrin"), "cpp")
121 .flag_if_supported("-march=armv8-a+crypto+crc");
122 }
123 _ => {}
124 }
125 soup_config.out_dir(out_dir).compile(soup_lib_name);
126
127 let pluto_lib_name = "pluto";
129 config
130 .add_files_by_ext(&pluto_source_dir, "cpp")
131 .out_dir(out_dir)
132 .compile(pluto_lib_name);
133
134 Artifacts {
135 lib_dir: out_dir.to_path_buf(),
136 libs: vec![pluto_lib_name.to_string(), soup_lib_name.to_string()],
137 cpp_stdlib: Self::get_cpp_link_stdlib(target, host),
138 }
139 }
140
141 fn get_cpp_link_stdlib(target: &str, host: &str) -> Option<String> {
150 let kind = if host == target { "HOST" } else { "TARGET" };
152 let res = env::var(format!("CXXSTDLIB_{target}"))
153 .or_else(|_| env::var(format!("CXXSTDLIB_{}", target.replace('-', "_"))))
154 .or_else(|_| env::var(format!("{kind}_CXXSTDLIB")))
155 .or_else(|_| env::var("CXXSTDLIB"))
156 .ok();
157 if res.is_some() {
158 return res;
159 }
160
161 if target.contains("msvc") {
162 None
163 } else if target.contains("apple") | target.contains("freebsd") | target.contains("openbsd")
164 {
165 Some("c++".to_string())
166 } else if target.contains("android") {
167 Some("c++_shared".to_string())
168 } else {
169 Some("stdc++".to_string())
170 }
171 }
172}
173
174impl Artifacts {
175 pub fn lib_dir(&self) -> &Path {
176 &self.lib_dir
177 }
178
179 pub fn libs(&self) -> &[String] {
180 &self.libs
181 }
182
183 pub fn print_cargo_metadata(&self) {
184 println!("cargo:rustc-link-search=native={}", self.lib_dir.display());
185 for lib in self.libs.iter() {
186 println!("cargo:rustc-link-lib=static={}", lib);
187 }
188 if let Some(ref cpp_stdlib) = self.cpp_stdlib {
189 println!("cargo:rustc-link-lib={}", cpp_stdlib);
190 }
191 }
192}
193
194trait AddFilesByExt {
195 fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self;
196}
197
198impl AddFilesByExt for cc::Build {
199 fn add_files_by_ext(&mut self, dir: &Path, ext: &str) -> &mut Self {
200 for entry in fs::read_dir(dir)
201 .unwrap()
202 .filter_map(|e| e.ok())
203 .filter(|e| e.path().extension() == Some(ext.as_ref()))
204 {
205 self.file(entry.path());
206 }
207 self
208 }
209}