Skip to main content

unity_solution_generator/
defines.rs

1use crate::io::read_file;
2use crate::paths::join_path;
3
4/// Generate Unity's `UNITY_X_Y_Z`, `UNITY_X_Y`, `UNITY_X`, and `UNITY_X_Y_OR_NEWER`
5/// version defines for the given Unity version string (e.g. `6000.2.7f2`).
6pub fn generate_version_defines(version: &str) -> Vec<String> {
7    let parts: Vec<&str> = version.split('.').collect();
8    if parts.len() < 3 {
9        return Vec::new();
10    }
11    let Ok(major) = parts[0].parse::<i32>() else {
12        return Vec::new();
13    };
14    let Ok(minor) = parts[1].parse::<i32>() else {
15        return Vec::new();
16    };
17    let mut patch_str = String::new();
18    for ch in parts[2].chars() {
19        if !ch.is_ascii_digit() {
20            break;
21        }
22        patch_str.push(ch);
23    }
24    let Ok(patch) = patch_str.parse::<i32>() else {
25        return Vec::new();
26    };
27
28    let mut defines = vec![
29        format!("UNITY_{}_{}_{}", major, minor, patch),
30        format!("UNITY_{}_{}", major, minor),
31        format!("UNITY_{}", major),
32    ];
33
34    const VERSION_POINTS: &[(i32, i32)] = &[
35        (5, 3), (5, 4), (5, 5), (5, 6),
36        (2017, 1), (2017, 2), (2017, 3), (2017, 4),
37        (2018, 1), (2018, 2), (2018, 3), (2018, 4),
38        (2019, 1), (2019, 2), (2019, 3), (2019, 4),
39        (2020, 1), (2020, 2), (2020, 3),
40        (2021, 1), (2021, 2), (2021, 3),
41        (2022, 1), (2022, 2), (2022, 3),
42        (2023, 1), (2023, 2), (2023, 3),
43        (2024, 1), (2024, 2), (2024, 3),
44        (2025, 1), (2025, 2), (2025, 3),
45    ];
46    for &(maj, min) in VERSION_POINTS {
47        if major > maj || (major == maj && minor >= min) {
48            defines.push(format!("UNITY_{}_{}_OR_NEWER", maj, min));
49        }
50    }
51    if major >= 6000 {
52        for m in 0..=minor {
53            defines.push(format!("UNITY_6000_{}_OR_NEWER", m));
54        }
55    }
56    defines
57}
58
59/// Stable superset of feature defines emitted by Unity 6.x for managed code.
60/// Mirrors `defaultFeatureDefines` in the Swift source — keep in sync.
61pub const DEFAULT_FEATURE_DEFINES: &[&str] = &[
62    "UNITY_INCLUDE_TESTS",
63    "ENABLE_AR", "ENABLE_AUDIO", "ENABLE_CACHING", "ENABLE_CLOTH",
64    "ENABLE_EVENT_QUEUE", "ENABLE_MICROPHONE", "ENABLE_MULTIPLE_DISPLAYS",
65    "ENABLE_PHYSICS", "ENABLE_TEXTURE_STREAMING", "ENABLE_LZMA",
66    "ENABLE_UNITYEVENTS", "ENABLE_VR", "ENABLE_WEBCAM",
67    "ENABLE_UNITYWEBREQUEST", "ENABLE_WWW",
68    "ENABLE_CLOUD_SERVICES", "ENABLE_CLOUD_SERVICES_ADS",
69    "ENABLE_CLOUD_SERVICES_USE_WEBREQUEST", "ENABLE_UNITY_CONSENT",
70    "ENABLE_UNITY_CLOUD_IDENTIFIERS",
71    "ENABLE_CLOUD_SERVICES_CRASH_REPORTING",
72    "ENABLE_CLOUD_SERVICES_NATIVE_CRASH_REPORTING",
73    "ENABLE_CLOUD_SERVICES_PURCHASING",
74    "ENABLE_CLOUD_SERVICES_ANALYTICS", "ENABLE_CLOUD_SERVICES_BUILD",
75    "ENABLE_EDITOR_GAME_SERVICES",
76    "ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT",
77    "ENABLE_CLOUD_LICENSE", "ENABLE_EDITOR_HUB_LICENSE",
78    "ENABLE_CLOUD_SERVICES_ENGINE_DIAGNOSTICS",
79    "ENABLE_WEBSOCKET_CLIENT",
80    "ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API",
81    "ENABLE_DIRECTOR_AUDIO", "ENABLE_DIRECTOR_TEXTURE",
82    "ENABLE_MANAGED_JOBS", "ENABLE_MANAGED_TRANSFORM_JOBS",
83    "ENABLE_MANAGED_ANIMATION_JOBS", "ENABLE_MANAGED_AUDIO_JOBS",
84    "ENABLE_ENGINE_CODE_STRIPPING", "ENABLE_ONSCREEN_KEYBOARD",
85    "ENABLE_MANAGED_UNITYTLS", "INCLUDE_DYNAMIC_GI",
86    "ENABLE_SCRIPTING_GC_WBARRIERS", "PLATFORM_SUPPORTS_MONO",
87    "ENABLE_MARSHALLING_TESTS", "ENABLE_VIDEO",
88    "ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK",
89    "ENABLE_ACCELERATOR_CLIENT_DEBUGGING", "ENABLE_ACCESSIBILITY",
90    "TEXTCORE_1_0_OR_NEWER",
91    "TEXTCORE_FONT_ENGINE_1_5_OR_NEWER", "TEXTCORE_TEXT_ENGINE_1_5_OR_NEWER",
92    "EDITOR_ONLY_NAVMESH_BUILDER_DEPRECATED",
93    "ENABLE_EGL", "ENABLE_NETWORK", "ENABLE_RUNTIME_GI",
94    "ENABLE_CRUNCH_TEXTURE_COMPRESSION", "ENABLE_FIREBASE_IDENTIFIERS",
95    "UNITY_CAN_SHOW_SPLASH_SCREEN",
96    "UNITY_HAS_GOOGLEVR", "UNITY_HAS_TANGO",
97    "ENABLE_SPATIALTRACKING", "ENABLE_ETC_COMPRESSION",
98    "PLATFORM_EXTENDS_VULKAN_DEVICE", "PLATFORM_HAS_MULTIPLE_SWAPCHAINS",
99    "PLATFORM_UPDATES_TIME_OUTSIDE_OF_PLAYER_LOOP",
100    "PLATFORM_EXTENDS_VULKAN_PIPELINE_CACHE",
101    "PLATFORM_SUPPORTS_SPLIT_GRAPHICS_JOBS",
102    "PLATFORM_HAS_ADDITIONAL_API_CHECKS",
103    "PLATFORM_HAS_GRAPHICS_JOBS_SUPPORT_CHECK_OVERRIDE",
104    "PLATFORM_IMPLEMENTS_INSIGHTS_ANR",
105    "PLATFORM_SUPPORTS_INSIGHTS_DEVICE_INFO",
106    "ENABLE_ANDROID_ADVERTISING_IDS",
107    "PLATFORM_HAS_BUGGY_MSAA_RESOLVE",
108    "ENABLE_ANDROID_APP_SET_ID",
109    "ENABLE_INSIGHTS_PLATFORM_SPECIFIC_RESOURCES",
110    "ENABLE_UNITYADS_RUNTIME", "UNITY_UNITYADS_API",
111    "ENABLE_MONO", "NET_STANDARD_2_0", "NET_STANDARD", "NET_STANDARD_2_1",
112    "NETSTANDARD", "NETSTANDARD2_1",
113    "ENABLE_PROFILER", "ENABLE_UNITY_COLLECTIONS_CHECKS", "ENABLE_BURST_AOT",
114    "UNITY_TEAM_LICENSE", "UNITY_PRO_LICENSE",
115    "ENABLE_CUSTOM_RENDER_TEXTURE", "ENABLE_DIRECTOR",
116    "ENABLE_LOCALIZATION", "ENABLE_SPRITES", "ENABLE_TERRAIN",
117    "ENABLE_TILEMAP", "ENABLE_TIMELINE",
118    "ENABLE_LEGACY_INPUT_MANAGER",
119    "CSHARP_7_OR_LATER", "CSHARP_7_3_OR_NEWER",
120];
121
122/// Parse `scriptingDefineSymbols:` from `ProjectSettings.asset`. Returns the
123/// union (sorted, deduplicated) across all platforms.
124pub fn parse_scripting_defines(project_root: &str) -> Vec<String> {
125    let path = join_path(project_root, "ProjectSettings/ProjectSettings.asset");
126    let Ok(content) = read_file(&path) else {
127        return Vec::new();
128    };
129
130    let mut in_section = false;
131    let mut all = std::collections::BTreeSet::new();
132
133    for line in content.split('\n') {
134        if line.starts_with("  scriptingDefineSymbols:") {
135            in_section = true;
136            continue;
137        }
138        if !in_section {
139            continue;
140        }
141        if !line.starts_with("    ") {
142            break;
143        }
144        if let Some(colon) = line.find(':') {
145            let value = line[colon + 1..].trim();
146            if !value.is_empty() {
147                for d in value.split(';') {
148                    let d = d.trim();
149                    if !d.is_empty() {
150                        all.insert(d.to_string());
151                    }
152                }
153            }
154        }
155    }
156    all.into_iter().collect()
157}