1use cargo_lock::Lockfile;
30use cargo_manifest::Manifest;
31use itertools::Itertools;
32use std::path::PathBuf;
33
34pub struct ModuleMain {
35 rust_code: String,
36 out_path: PathBuf,
37 crate_name: String,
38 virtual_lib: String,
39}
40
41impl Default for ModuleMain {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl ModuleMain {
48 pub fn new() -> Self {
49 let crate_name = std::env::var("CARGO_PKG_NAME").unwrap().replace('-', "_");
50 let crate_version = std::env::var("CARGO_PKG_VERSION").unwrap();
51 let rustc_version = rustc_version::version().unwrap();
52 let mod_const_name = format!("BP3D_OS_MODULE_{}", crate_name.to_uppercase());
53 let mut manifest_path = PathBuf::from(
54 std::env::var_os("CARGO_MANIFEST_PATH").expect("Failed to get CARGO_MANIFEST_PATH"),
55 );
56 let package =
57 Manifest::from_path(&manifest_path).expect("Failed to read CARGO_MANIFEST_PATH");
58 manifest_path.set_extension("lock");
59 let lock_file = Lockfile::load(&manifest_path).ok();
60 let mut features = Vec::new();
61 let deps_list = package
62 .dependencies
63 .map(|v| {
64 v.iter()
65 .map(|(k, v)| {
66 let dep_version = lock_file
67 .as_ref()
68 .and_then(|v| v.packages.iter().find(|v| v.name.as_ref() == *k))
69 .map(|v| &v.version);
70 let dep_name = k.replace("-", "_");
71 for feature in v.req_features() {
72 features.push(format!("{}/{}", dep_name, feature));
73 }
74 match dep_version {
75 Some(v) => format!("{}={}", dep_name, v),
76 None => format!("{}={}", dep_name, v.req()),
77 }
78 })
79 .join(",")
80 })
81 .unwrap_or("".into());
82 let data = format!(
83 "\"\0BP3D_OS_MODULE|TYPE=RUST|NAME={}|VERSION={}|RUSTC={}|DEPS={}|FEATURES={}\0\"",
84 crate_name,
85 crate_version,
86 rustc_version,
87 deps_list,
88 features.join(",")
89 );
90 let rust_code = format!(
91 "
92 #[unsafe(no_mangle)]
93 #[allow(clippy::manual_c_str_literals)] // The string is enclosed in NULLs and apparently clippy
94 // does not like that...
95 static mut {mod_const_name}: *const std::ffi::c_char = {data}.as_ptr() as _;
96"
97 );
98 let virtual_lib = format!("
99 #[allow(static_mut_refs)]
100 pub static VIRTUAL_MODULE: bp3d_os::module::library::types::VirtualLibrary = bp3d_os::module::library::types::VirtualLibrary::new(\"{crate_name}\", &[
101 (\"{mod_const_name}\", unsafe {{ &{mod_const_name} as *const *const i8 as *const std::ffi::c_void }})");
102 let out_path =
103 PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("bp3d_os_module.rs");
104 let this = Self {
105 rust_code,
106 out_path,
107 crate_name,
108 virtual_lib,
109 };
110 this.add_init().add_uninit()
111 }
112
113 pub fn add_export(mut self, func_name: impl AsRef<str>) -> Self {
114 let func_name = func_name.as_ref();
115 self.virtual_lib += &format!(",\n (\"{func_name}\", {func_name} as _)");
116 self
117 }
118
119 fn add_init(mut self) -> Self {
120 let motherfuckingrust = "extern \"C\"";
121 let crate_name = &self.crate_name;
122 let rust_code = format!(
123 r"
124 #[unsafe(no_mangle)]
125 #[inline(never)]
126 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_init(loader: &'static std::sync::Mutex<bp3d_os::module::loader::ModuleLoader>) {{
127 bp3d_os::module::loader::ModuleLoader::install_from_existing(loader);
128 }}
129"
130 );
131 self.rust_code += &rust_code;
132 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_init");
133 self.add_export(motherfuckingrust)
134 }
135
136 fn add_uninit(mut self) -> Self {
137 let motherfuckingrust = "extern \"C\"";
138 let crate_name = &self.crate_name;
139 let rust_code = format!(
140 r"
141 #[unsafe(no_mangle)]
142 #[inline(never)]
143 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_uninit() {{
144 bp3d_os::module::loader::ModuleLoader::uninstall();
145 }}
146"
147 );
148 self.rust_code += &rust_code;
149 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_uninit");
150 self.add_export(motherfuckingrust)
151 }
152
153 pub fn add_open(mut self) -> Self {
154 let motherfuckingrust = "extern \"C\"";
155 let crate_name = &self.crate_name;
156 let rust_code = format!(
157 r"
158 #[unsafe(no_mangle)]
159 #[inline(never)]
160 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_open() {{
161 module_open();
162 }}
163"
164 );
165 self.rust_code += &rust_code;
166 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_open");
167 self.add_export(motherfuckingrust)
168 }
169
170 pub fn add_close(mut self) -> Self {
171 let motherfuckingrust = "extern \"C\"";
172 let crate_name = &self.crate_name;
173 let rust_code = format!(
174 r"
175 #[unsafe(no_mangle)]
176 #[inline(never)]
177 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_close() {{
178 module_close();
179 }}
180"
181 );
182 self.rust_code += &rust_code;
183 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_close");
184 self.add_export(motherfuckingrust)
185 }
186
187 pub fn add_bp3d_debug(mut self) -> Self {
188 let motherfuckingrust = "extern \"Rust\"";
189 let crate_name = &self.crate_name;
190 let rust_code = format!(
191 r"
192 #[unsafe(no_mangle)]
193 #[inline(never)]
194 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_init_bp3d_debug(engine: &'static dyn bp3d_debug::engine::Engine) {{
195 bp3d_debug::engine::set(engine);
196 }}
197"
198 );
199 self.rust_code += &rust_code;
200 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_init_bp3d_debug");
201 self.add_export(motherfuckingrust)
202 }
203
204 pub fn build(mut self) {
205 self.virtual_lib += "\n ]);";
206 self.rust_code += &self.virtual_lib;
207 std::fs::write(&self.out_path, self.rust_code).unwrap();
208 #[cfg(unix)]
209 {
210 let crate_name = self.crate_name;
211 #[cfg(target_vendor = "apple")]
212 println!("cargo::rustc-link-arg-cdylib=-Wl,-install_name,@rpath/lib{crate_name}.dylib");
213 #[cfg(all(unix, not(target_vendor = "apple")))]
214 println!("cargo::rustc-link-arg-cdylib=-Wl,-soname,lib{crate_name}.so");
215 }
216 println!(
217 "cargo:rustc-env=BP3D_OS_MODULE_MAIN={}",
218 self.out_path.display()
219 );
220 }
221}