Skip to main content

rusteal_codegen/cpp_gen/
mod.rs

1// C++ code generation orchestrator.
2
3pub mod wrapper;
4pub mod func_ids;
5pub mod fill_table;
6
7use std::collections::{BTreeMap, HashSet};
8use std::path::Path;
9
10use crate::context::CodegenContext;
11use crate::write_if_changed;
12
13/// Generate all C++ code into the output directory.
14pub fn generate(ctx: &CodegenContext, out_dir: &Path) {
15    std::fs::create_dir_all(out_dir).expect("Failed to create C++ output directory");
16
17    // Group func entries by (module, class) for per-file generation
18    let mut by_class: BTreeMap<(String, String), Vec<&crate::context::FuncEntry>> = BTreeMap::new();
19    for entry in &ctx.func_table {
20        by_class
21            .entry((entry.module_name.clone(), entry.class_name.clone()))
22            .or_default()
23            .push(entry);
24    }
25
26    // Generate per-class wrapper files
27    let mut written: HashSet<String> = HashSet::new();
28    for ((module, class), entries) in &by_class {
29        let code = wrapper::generate_wrapper_file(entries, ctx);
30        let filename = format!("RustealFunc_{}_{}.cpp", module, class);
31        write_if_changed(&out_dir.join(&filename), &code)
32            .unwrap_or_else(|e| panic!("Failed to write {filename}: {e}"));
33        written.insert(filename);
34    }
35    remove_stale_wrappers(out_dir, &written);
36
37    // Generate RustealFuncIds.h
38    let ids_code = func_ids::generate_cpp_func_ids(&ctx.func_table);
39    write_if_changed(&out_dir.join("RustealFuncIds.h"), &ids_code)
40        .expect("Failed to write RustealFuncIds.h");
41
42    // Generate RustealFillFuncTable.cpp
43    let fill_code = fill_table::generate_fill_table(&ctx.cpp_prefix, &ctx.func_table, &by_class);
44    write_if_changed(&out_dir.join("RustealFillFuncTable.cpp"), &fill_code)
45        .expect("Failed to write RustealFillFuncTable.cpp");
46}
47
48/// Delete the per-class wrapper files a previous run left and this one did not write. UBT
49/// compiles every .cpp in the output directory, so a class that lost all its exportable
50/// functions (blocklist, feature change, engine upgrade) would otherwise keep a stale
51/// wrapper that no longer links.
52fn remove_stale_wrappers(out_dir: &Path, written: &HashSet<String>) {
53    let Ok(entries) = std::fs::read_dir(out_dir) else {
54        return;
55    };
56    for entry in entries.flatten() {
57        let name = entry.file_name();
58        let name = name.to_string_lossy();
59        if name.starts_with("RustealFunc_") && name.ends_with(".cpp") && !written.contains(name.as_ref()) {
60            std::fs::remove_file(entry.path())
61                .unwrap_or_else(|e| panic!("Failed to remove stale {name}: {e}"));
62        }
63    }
64}