1#![cfg_attr(published_docs, feature(doc_cfg))]
2#![forbid(unsafe_code)]
16
17mod context;
18mod conv;
19mod formatter;
20mod generator;
21mod models;
22mod special_cases;
23mod util;
24
25#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
26mod tests;
27
28use std::path::{Path, PathBuf};
29
30use proc_macro2::TokenStream;
31
32use crate::context::Context;
33use crate::generator::builtins::generate_builtin_class_files;
34use crate::generator::classes::generate_class_files;
35use crate::generator::extension_interface::generate_sys_interface_file;
36use crate::generator::native_structures::generate_native_structures_files;
37use crate::generator::utility_functions::generate_utilities_file;
38use crate::generator::{
39 generate_core_central_file, generate_core_mod_file, generate_sys_builtin_lifecycle_file,
40 generate_sys_builtin_methods_file, generate_sys_central_file, generate_sys_classes_file,
41 generate_sys_module_file, generate_sys_utilities_file, virtual_definitions,
42};
43use crate::models::domain::{ApiView, ExtensionApi};
44use crate::models::json::{load_extension_api, JsonExtensionApi};
45
46pub type SubmitFn = dyn FnMut(PathBuf, TokenStream);
47
48#[cfg(not(feature = "codegen-full"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-full"))))]
49pub const IS_CODEGEN_FULL: bool = false;
50
51#[cfg(feature = "codegen-full")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-full")))]
53pub const IS_CODEGEN_FULL: bool = true;
54
55#[cfg(all(feature = "experimental-required-objs", before_api = "4.6"))] #[cfg_attr(published_docs, doc(cfg(all(feature = "experimental-required-objs", before_api = "4.6"))))]
56fn __feature_warning() {
57 #[must_use = "The `experimental-required-objs` feature needs at least Godot 4.6-dev version"]
59 fn feature_has_no_effect() -> i32 {
60 1
61 }
62
63 feature_has_no_effect();
64}
65
66fn write_file(path: &Path, contents: String) {
67 let dir = path.parent().unwrap();
68 let _ = std::fs::create_dir_all(dir);
69
70 std::fs::write(path, contents)
71 .unwrap_or_else(|e| panic!("failed to write code file to {};\n\t{}", path.display(), e));
72}
73
74#[cfg(not(feature = "codegen-rustfmt"))] #[cfg_attr(published_docs, doc(cfg(not(feature = "codegen-rustfmt"))))]
75fn submit_fn(path: PathBuf, tokens: TokenStream) {
76 write_file(&path, formatter::format_tokens(tokens));
77}
78
79#[cfg(feature = "codegen-rustfmt")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-rustfmt")))]
80mod rustfmt {
81 use std::process::Command;
82 use std::sync::Mutex;
83
84 use super::*;
85
86 pub fn submit_fn(path: PathBuf, tokens: TokenStream) {
87 write_file(&path, tokens.to_string());
88 FILES_TO_RUSTFMT.lock().unwrap().push(path);
89 }
90
91 pub fn rustfmt_files() {
92 let out_files = FILES_TO_RUSTFMT.lock().unwrap();
93 println!("Format {} generated files...", out_files.len());
94
95 for files in out_files.chunks(20) {
96 let mut command = Command::new("rustfmt");
97 command.arg("--edition");
98 command.arg("2021");
99
100 for file in files {
101 command.arg(file);
102 }
103
104 let status = command.status().expect("failed to invoke rustfmt");
105 if !status.success() {
106 panic!("rustfmt failed on {command:?}");
107 }
108 }
109
110 println!("Rustfmt completed successfully");
111 }
112
113 static FILES_TO_RUSTFMT: Mutex<Vec<PathBuf>> = Mutex::new(Vec::new());
114}
115
116#[cfg(feature = "codegen-rustfmt")] #[cfg_attr(published_docs, doc(cfg(feature = "codegen-rustfmt")))]
117pub(crate) use rustfmt::*;
118
119pub fn generate_sys_files(
120 sys_gen_path: &Path,
121 h_path: &Path,
122 watch: &mut godot_bindings::StopWatch,
123) {
124 let json_api = load_extension_api(watch);
125
126 let mut ctx = Context::build_from_api(&json_api);
127 watch.record("build_context");
128
129 let api = ExtensionApi::from_json(&json_api, &mut ctx);
130 watch.record("map_domain_models");
131
132 generate_sys_central_file(&api, sys_gen_path, &mut submit_fn);
137 watch.record("generate_central_file");
138
139 generate_sys_builtin_methods_file(&api, sys_gen_path, &mut ctx, &mut submit_fn);
140 watch.record("generate_builtin_methods_file");
141
142 generate_sys_builtin_lifecycle_file(&api, sys_gen_path, &mut submit_fn);
143 watch.record("generate_builtin_lifecycle_file");
144
145 generate_sys_classes_file(&api, sys_gen_path, watch, &mut ctx, &mut submit_fn);
146 generate_sys_utilities_file(&api, sys_gen_path, &mut submit_fn);
149 watch.record("generate_utilities_file");
150
151 let is_godot_4_0 = api.godot_version.major == 4 && api.godot_version.minor == 0;
152 generate_sys_interface_file(h_path, sys_gen_path, is_godot_4_0, &mut submit_fn);
153 watch.record("generate_interface_file");
154
155 generate_sys_module_file(sys_gen_path, &mut submit_fn);
156 watch.record("generate_module_file");
157
158 #[cfg(feature = "codegen-rustfmt")]
159 {
160 rustfmt_files();
161 watch.record("rustfmt");
162 }
163}
164
165pub fn generate_core_files(core_gen_path: &Path) {
166 let mut watch = godot_bindings::StopWatch::start();
167
168 generate_core_mod_file(core_gen_path, &mut submit_fn);
169
170 let json_api = load_extension_api(&mut watch);
171 let mut ctx = Context::build_from_api(&json_api);
172 watch.record("build_context");
173
174 let api = ExtensionApi::from_json(&json_api, &mut ctx);
175 let view = ApiView::new(&api);
176 watch.record("map_domain_models");
177
178 generate_class_files(
185 &api,
186 &mut ctx,
187 &view,
188 &core_gen_path.join("classes"),
189 &mut submit_fn,
190 );
191 watch.record("generate_class_files");
192
193 generate_builtin_class_files(
194 &api,
195 &mut ctx,
196 &core_gen_path.join("builtin_classes"),
197 &mut submit_fn,
198 );
199 watch.record("generate_builtin_class_files");
200
201 generate_native_structures_files(
202 &api,
203 &mut ctx,
204 &core_gen_path.join("native"),
205 &mut submit_fn,
206 );
207 watch.record("generate_native_structures_files");
208
209 generate_core_central_file(&api, &mut ctx, core_gen_path, &mut submit_fn);
213 watch.record("generate_central_file");
214
215 generate_utilities_file(&api, core_gen_path, &mut submit_fn);
216 watch.record("generate_utilities_file");
217
218 let code = virtual_definitions::make_virtual_definitions_file(&api, &mut ctx);
222 submit_fn(core_gen_path.join("virtuals.rs"), code);
223 watch.record("generate_virtual_definitions");
224
225 #[cfg(feature = "codegen-rustfmt")]
226 {
227 rustfmt_files();
228 watch.record("rustfmt");
229 }
230
231 watch.write_stats_to(&core_gen_path.join("codegen-stats.txt"));
232}