Mono/C#: Allow debugging exported games
- Include PDB files in exported games. - Release export templates also allow debugging now. Right now the only way to enable debugging in exported games is with the environment variables, which may be cumbersome or not even possible on some platforms.
This commit is contained in:
parent
bb9d89d78c
commit
71fc87e101
|
@ -168,13 +168,13 @@ namespace GodotTools.Export
|
||||||
|
|
||||||
// Add dependency assemblies
|
// Add dependency assemblies
|
||||||
|
|
||||||
var dependencies = new Godot.Collections.Dictionary<string, string>();
|
var assemblies = new Godot.Collections.Dictionary<string, string>();
|
||||||
|
|
||||||
string projectDllName = GodotSharpEditor.ProjectAssemblyName;
|
string projectDllName = GodotSharpEditor.ProjectAssemblyName;
|
||||||
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
|
||||||
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
|
||||||
|
|
||||||
dependencies[projectDllName] = projectDllSrcPath;
|
assemblies[projectDllName] = projectDllSrcPath;
|
||||||
|
|
||||||
if (platform == OS.Platforms.Android)
|
if (platform == OS.Platforms.Android)
|
||||||
{
|
{
|
||||||
|
@ -184,15 +184,15 @@ namespace GodotTools.Export
|
||||||
if (!File.Exists(monoAndroidAssemblyPath))
|
if (!File.Exists(monoAndroidAssemblyPath))
|
||||||
throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
|
throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
|
||||||
|
|
||||||
dependencies["Mono.Android"] = monoAndroidAssemblyPath;
|
assemblies["Mono.Android"] = monoAndroidAssemblyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
string bclDir = DeterminePlatformBclDir(platform);
|
string bclDir = DeterminePlatformBclDir(platform);
|
||||||
|
|
||||||
var initialDependencies = dependencies.Duplicate();
|
var initialAssemblies = assemblies.Duplicate();
|
||||||
internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, bclDir, dependencies);
|
internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
|
||||||
|
|
||||||
AddI18NAssemblies(dependencies, bclDir);
|
AddI18NAssemblies(assemblies, bclDir);
|
||||||
|
|
||||||
string outputDataDir = null;
|
string outputDataDir = null;
|
||||||
|
|
||||||
|
@ -211,22 +211,34 @@ namespace GodotTools.Export
|
||||||
Directory.CreateDirectory(outputDataGameAssembliesDir);
|
Directory.CreateDirectory(outputDataGameAssembliesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var dependency in dependencies)
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
void AddToAssembliesDir(string fileSrcPath)
|
||||||
{
|
{
|
||||||
string dependSrcPath = dependency.Value;
|
|
||||||
|
|
||||||
if (assembliesInsidePck)
|
if (assembliesInsidePck)
|
||||||
{
|
{
|
||||||
string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
|
string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile());
|
||||||
AddFile(dependSrcPath, dependDstPath);
|
AddFile(fileSrcPath, fileDstPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile());
|
Debug.Assert(outputDataDir != null);
|
||||||
File.Copy(dependSrcPath, dependDstPath);
|
string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile());
|
||||||
|
File.Copy(fileSrcPath, fileDstPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string assemblySrcPath = assembly.Value;
|
||||||
|
|
||||||
|
string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null);
|
||||||
|
string pdbSrcPath = assemblyPathWithoutExtension + ".pdb";
|
||||||
|
|
||||||
|
AddToAssembliesDir(assemblySrcPath);
|
||||||
|
|
||||||
|
if (File.Exists(pdbSrcPath))
|
||||||
|
AddToAssembliesDir(pdbSrcPath);
|
||||||
|
}
|
||||||
|
|
||||||
// AOT compilation
|
// AOT compilation
|
||||||
bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
|
bool aotEnabled = platform == OS.Platforms.iOS || (bool)ProjectSettings.GetSetting("mono/export/aot/enabled");
|
||||||
|
|
||||||
|
@ -254,7 +266,7 @@ namespace GodotTools.Export
|
||||||
ToolchainPath = aotToolchainPath
|
ToolchainPath = aotToolchainPath
|
||||||
};
|
};
|
||||||
|
|
||||||
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, dependencies);
|
AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +378,7 @@ namespace GodotTools.Export
|
||||||
if (PlatformRequiresCustomBcl(platform))
|
if (PlatformRequiresCustomBcl(platform))
|
||||||
throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
|
throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
|
||||||
|
|
||||||
platformBclDir = typeof(object).Assembly.Location; // Use the one we're running on
|
platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +437,7 @@ namespace GodotTools.Export
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialDependencies,
|
private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
|
||||||
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
|
string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,14 +231,14 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies,
|
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
|
||||||
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) {
|
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
|
||||||
Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies);
|
Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
|
||||||
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
|
String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
|
||||||
String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
|
String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
|
||||||
Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
|
Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
|
||||||
|
|
||||||
return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, dependencies);
|
return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
|
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
|
||||||
|
|
|
@ -50,13 +50,13 @@ String get_assemblyref_name(MonoImage *p_image, int index) {
|
||||||
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
|
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
|
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
|
||||||
MonoImage *image = p_assembly->get_image();
|
MonoImage *image = p_assembly->get_image();
|
||||||
|
|
||||||
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
|
||||||
String ref_name = get_assemblyref_name(image, i);
|
String ref_name = get_assemblyref_name(image, i);
|
||||||
|
|
||||||
if (r_dependencies.has(ref_name))
|
if (r_assembly_dependencies.has(ref_name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
GDMonoAssembly *ref_assembly = nullptr;
|
GDMonoAssembly *ref_assembly = nullptr;
|
||||||
|
@ -93,17 +93,17 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
|
||||||
ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
|
||||||
|
|
||||||
// Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
|
// Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
|
||||||
r_dependencies[ref_name] = path;
|
r_assembly_dependencies[ref_name] = path;
|
||||||
|
|
||||||
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
|
Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
|
||||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
|
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
|
||||||
const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
|
const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) {
|
||||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
|
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
|
||||||
ERR_FAIL_NULL_V(export_domain, FAILED);
|
ERR_FAIL_NULL_V(export_domain, FAILED);
|
||||||
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
|
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
|
||||||
|
@ -113,16 +113,16 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencie
|
||||||
Vector<String> search_dirs;
|
Vector<String> search_dirs;
|
||||||
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
|
GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
|
||||||
|
|
||||||
for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) {
|
for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
|
||||||
String assembly_name = *key;
|
String assembly_name = *key;
|
||||||
String assembly_path = p_initial_dependencies[*key];
|
String assembly_path = p_initial_assemblies[*key];
|
||||||
|
|
||||||
GDMonoAssembly *assembly = nullptr;
|
GDMonoAssembly *assembly = nullptr;
|
||||||
bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
|
bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
|
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
|
||||||
|
|
||||||
Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies);
|
Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies);
|
||||||
if (err != OK)
|
if (err != OK)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,8 @@ namespace GodotSharpExport {
|
||||||
|
|
||||||
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
|
Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
|
||||||
|
|
||||||
Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
|
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
|
||||||
const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies);
|
const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
|
||||||
|
|
||||||
} // namespace GodotSharpExport
|
} // namespace GodotSharpExport
|
||||||
|
|
||||||
|
|
|
@ -129,12 +129,8 @@ void gd_mono_profiler_init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(DEBUG_ENABLED)
|
|
||||||
|
|
||||||
void gd_mono_debug_init() {
|
void gd_mono_debug_init() {
|
||||||
|
|
||||||
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
|
|
||||||
|
|
||||||
CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
|
CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
@ -159,6 +155,10 @@ void gd_mono_debug_init() {
|
||||||
return; // Exported games don't use the project settings to setup the debugger agent
|
return; // Exported games don't use the project settings to setup the debugger agent
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Debugging enabled
|
||||||
|
|
||||||
|
mono_debug_init(MONO_DEBUG_FORMAT_MONO);
|
||||||
|
|
||||||
// --debugger-agent=help
|
// --debugger-agent=help
|
||||||
const char *options[] = {
|
const char *options[] = {
|
||||||
"--soft-breakpoints",
|
"--soft-breakpoints",
|
||||||
|
@ -167,7 +167,6 @@ void gd_mono_debug_init() {
|
||||||
mono_jit_parse_options(2, (char **)options);
|
mono_jit_parse_options(2, (char **)options);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // defined(DEBUG_ENABLED)
|
|
||||||
#endif // !defined(JAVASCRIPT_ENABLED)
|
#endif // !defined(JAVASCRIPT_ENABLED)
|
||||||
|
|
||||||
#if defined(JAVASCRIPT_ENABLED)
|
#if defined(JAVASCRIPT_ENABLED)
|
||||||
|
@ -175,6 +174,7 @@ MonoDomain *gd_initialize_mono_runtime() {
|
||||||
const char *vfs_prefix = "managed";
|
const char *vfs_prefix = "managed";
|
||||||
int enable_debugging = 0;
|
int enable_debugging = 0;
|
||||||
|
|
||||||
|
// TODO: Provide a way to enable debugging on WASM release builds.
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
enable_debugging = 1;
|
enable_debugging = 1;
|
||||||
#endif
|
#endif
|
||||||
|
@ -185,9 +185,7 @@ MonoDomain *gd_initialize_mono_runtime() {
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
MonoDomain *gd_initialize_mono_runtime() {
|
MonoDomain *gd_initialize_mono_runtime() {
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
gd_mono_debug_init();
|
gd_mono_debug_init();
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
|
#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
|
||||||
// I don't know whether this actually matters or not
|
// I don't know whether this actually matters or not
|
||||||
|
|
Loading…
Reference in New Issue