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 for feature in v.req_features() {
71 features.push(format!("{}/{}", k, feature));
72 }
73 match dep_version {
74 Some(v) => format!("{}={}", k, v),
75 None => format!("{}={}", k, v.req()),
76 }
77 })
78 .join(",")
79 })
80 .unwrap_or("".into());
81 let data = format!(
82 "\"\0BP3D_OS_MODULE|TYPE=RUST|NAME={}|VERSION={}|RUSTC={}|DEPS={}|FEATURES={}\0\"",
83 crate_name,
84 crate_version,
85 rustc_version,
86 deps_list,
87 features.join(",")
88 );
89 let rust_code = format!(
90 "
91 #[unsafe(no_mangle)]
92 #[allow(clippy::manual_c_str_literals)] // The string is enclosed in NULLs and apparently clippy
93 // does not like that...
94 static mut {mod_const_name}: *const std::ffi::c_char = {data}.as_ptr() as _;
95"
96 );
97 let virtual_lib = format!("
98 #[allow(static_mut_refs)]
99 pub static VIRTUAL_MODULE: bp3d_os::module::library::types::VirtualLibrary = bp3d_os::module::library::types::VirtualLibrary::new(\"{crate_name}\", &[
100 (\"{mod_const_name}\", unsafe {{ &{mod_const_name} as *const *const i8 as *const std::ffi::c_void }})");
101 let out_path =
102 PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("bp3d_os_module.rs");
103 let this = Self {
104 rust_code,
105 out_path,
106 crate_name,
107 virtual_lib,
108 };
109 this.add_init().add_uninit()
110 }
111
112 pub fn add_export(mut self, func_name: impl AsRef<str>) -> Self {
113 let func_name = func_name.as_ref();
114 self.virtual_lib += &format!(",\n (\"{func_name}\", {func_name} as _)");
115 self
116 }
117
118 fn add_init(mut self) -> Self {
119 let motherfuckingrust = "extern \"C\"";
120 let crate_name = &self.crate_name;
121 let rust_code = format!(
122 r"
123 #[unsafe(no_mangle)]
124 #[inline(never)]
125 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_init(loader: &'static std::sync::Mutex<bp3d_os::module::loader::ModuleLoader>) {{
126 bp3d_os::module::loader::ModuleLoader::install_from_existing(loader);
127 }}
128"
129 );
130 self.rust_code += &rust_code;
131 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_init");
132 self.add_export(motherfuckingrust)
133 }
134
135 fn add_uninit(mut self) -> Self {
136 let motherfuckingrust = "extern \"C\"";
137 let crate_name = &self.crate_name;
138 let rust_code = format!(
139 r"
140 #[unsafe(no_mangle)]
141 #[inline(never)]
142 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_uninit() {{
143 bp3d_os::module::loader::ModuleLoader::uninstall();
144 }}
145"
146 );
147 self.rust_code += &rust_code;
148 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_uninit");
149 self.add_export(motherfuckingrust)
150 }
151
152 pub fn add_open(mut self) -> Self {
153 let motherfuckingrust = "extern \"C\"";
154 let crate_name = &self.crate_name;
155 let rust_code = format!(
156 r"
157 #[unsafe(no_mangle)]
158 #[inline(never)]
159 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_open() {{
160 module_open();
161 }}
162"
163 );
164 self.rust_code += &rust_code;
165 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_open");
166 self.add_export(motherfuckingrust)
167 }
168
169 pub fn add_close(mut self) -> Self {
170 let motherfuckingrust = "extern \"C\"";
171 let crate_name = &self.crate_name;
172 let rust_code = format!(
173 r"
174 #[unsafe(no_mangle)]
175 #[inline(never)]
176 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_close() {{
177 module_close();
178 }}
179"
180 );
181 self.rust_code += &rust_code;
182 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_close");
183 self.add_export(motherfuckingrust)
184 }
185
186 pub fn add_bp3d_debug(mut self) -> Self {
187 let motherfuckingrust = "extern \"Rust\"";
188 let crate_name = &self.crate_name;
189 let rust_code = format!(
190 r"
191 #[unsafe(no_mangle)]
192 #[inline(never)]
193 pub {motherfuckingrust} fn bp3d_os_module_{crate_name}_init_bp3d_debug(engine: &'static dyn bp3d_debug::engine::Engine) {{
194 bp3d_debug::engine::set(engine);
195 }}
196"
197 );
198 self.rust_code += &rust_code;
199 let motherfuckingrust = format!("bp3d_os_module_{crate_name}_init_bp3d_debug");
200 self.add_export(motherfuckingrust)
201 }
202
203 pub fn build(mut self) {
204 self.virtual_lib += "\n ]);";
205 self.rust_code += &self.virtual_lib;
206 std::fs::write(&self.out_path, self.rust_code).unwrap();
207 #[cfg(unix)]
208 {
209 let crate_name = self.crate_name;
210 #[cfg(target_vendor = "apple")]
211 println!("cargo::rustc-link-arg-cdylib=-Wl,-install_name,@rpath/lib{crate_name}.dylib");
212 #[cfg(all(unix, not(target_vendor = "apple")))]
213 println!("cargo::rustc-link-arg-cdylib=-Wl,-soname,lib{crate_name}.so");
214 }
215 println!(
216 "cargo:rustc-env=BP3D_OS_MODULE_MAIN={}",
217 self.out_path.display()
218 );
219 }
220}