1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
use std::collections::HashSet;
use std::path::Path;
use crate::config::PathfinderConfig;
use crate::error::MinglingPathfinderError;
use crate::module_pathf;
use crate::pattern_analyzer;
/// Analyzes the Mingling types of the specified crate directory and generates mapping files to the specified output directory.
///
/// `crate_dir` — crate root directory (i.e., the directory containing Cargo.toml)
/// `output_dir` — directory where mapping files will be written
/// `config` — pathfinder configuration (e.g., dispatch_tree detection)
///
/// Mapping file format per line: `TypeName = crate::module::path::TypeName`
pub fn analyze_and_build_type_mapping_for(
crate_dir: &Path,
output_dir: &Path,
config: &PathfinderConfig,
) -> Result<(), MinglingPathfinderError> {
let module_mapping = module_pathf::analyze(crate_dir)?;
let analyzer = pattern_analyzer::init_with_config(config.clone());
let mut type_mappings: Vec<(String, String)> = Vec::new();
for item in module_mapping {
let file_abs = crate_dir.join(item.file_path());
if !file_abs.is_file() {
continue;
}
let module_path = item.module_path();
let Ok(analyze_items) = analyzer.analyze_file_items(&file_abs) else {
continue;
};
for ai in analyze_items {
let full_path = if ai.module.is_empty() {
format!("{}::{}", module_path, ai.item_name)
} else {
format!("{}::{}::{}", module_path, ai.module, ai.item_name)
};
type_mappings.push((ai.item_name, full_path));
}
}
// Sort by full path (ASCII order)
type_mappings.sort_by(|a, b| a.1.cmp(&b.1));
// Deduplicate by type name, keeping the first occurrence
let mut seen = HashSet::new();
type_mappings.retain(|(name, _)| seen.insert(name.clone()));
// Create output directory
std::fs::create_dir_all(output_dir)?;
// Write files
let output_path = output_dir.join("MAPPING");
let type_using_path = output_dir.join("type_using.rs");
let mut content_mapping = String::new();
for (name, path) in &type_mappings {
content_mapping.push_str(&format!("{name} = {path}\n"));
}
std::fs::write(&output_path, content_mapping)?;
let mut content_using = String::new();
for (_, path) in &type_mappings {
content_using.push_str(&format!("use {path};\n"));
}
std::fs::write(&type_using_path, content_using)?;
Ok(())
}
/// Convenience version to be called from `build.rs`, automatically reading configuration
/// from environment variables.
///
/// Reads `CARGO_PKG_NAME` and `OUT_DIR`, and outputs to `{OUT_DIR}/{CARGO_PKG_NAME}/`.
pub fn analyze_and_build_type_mapping() -> Result<(), MinglingPathfinderError> {
let crate_name = std::env::var("CARGO_PKG_NAME").map_err(|_| {
MinglingPathfinderError::IoError(std::io::Error::new(
std::io::ErrorKind::NotFound,
"CARGO_PKG_NAME not set (not running in build.rs?)",
))
})?;
let out_dir = std::env::var("OUT_DIR").map_err(|_| {
MinglingPathfinderError::IoError(std::io::Error::new(
std::io::ErrorKind::NotFound,
"OUT_DIR not set (not running in build.rs?)",
))
})?;
let crate_dir = std::env::current_dir()?;
let output_dir = Path::new(&out_dir).join(&crate_name);
analyze_and_build_type_mapping_for(&crate_dir, &output_dir, &PathfinderConfig::default())?;
// Notify Cargo to re-run build.rs when source files change
println!("cargo:rerun-if-changed=src/");
println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_OS");
println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH");
Ok(())
}
|