Browse Source

C#: Unify project name handling and fix issues with the handling of some special characters

Co-authored-by: Raul Santos <[email protected]>
RedworkDE 2 years ago
parent
commit
92f13ba9ea

+ 18 - 10
core/config/project_settings.cpp

@@ -67,14 +67,6 @@ String ProjectSettings::get_resource_path() const {
 	return resource_path;
 	return resource_path;
 }
 }
 
 
-String ProjectSettings::get_safe_project_name() const {
-	String safe_name = OS::get_singleton()->get_safe_dir_name(get("application/config/name"));
-	if (safe_name.is_empty()) {
-		safe_name = "UnnamedProject";
-	}
-	return safe_name;
-}
-
 String ProjectSettings::get_imported_files_path() const {
 String ProjectSettings::get_imported_files_path() const {
 	return get_project_data_path().path_join("imported");
 	return get_project_data_path().path_join("imported");
 }
 }
@@ -930,10 +922,26 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const RBMap<Str
 }
 }
 
 
 Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other params as dictionary and array?
 Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other params as dictionary and array?
-
 	return save_custom(p_file);
 	return save_custom(p_file);
 }
 }
 
 
+#ifdef TOOLS_ENABLED
+bool _csproj_exists(String p_root_dir) {
+	Ref<DirAccess> dir = DirAccess::open(p_root_dir);
+
+	dir->list_dir_begin();
+	String file_name = dir->_get_next();
+	while (file_name != "") {
+		if (!dir->current_is_dir() && file_name.get_extension() == "csproj") {
+			return true;
+		}
+		file_name = dir->_get_next();
+	}
+
+	return false;
+}
+#endif // TOOLS_ENABLED
+
 Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) {
 Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) {
 	ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty.");
 	ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty.");
 
 
@@ -952,7 +960,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
 		}
 		}
 	}
 	}
 	// Check for the existence of a csproj file.
 	// Check for the existence of a csproj file.
-	if (FileAccess::exists(get_resource_path().path_join(get_safe_project_name() + ".csproj"))) {
+	if (_csproj_exists(get_resource_path())) {
 		// If there is a csproj file, add the C# feature if it doesn't already exist.
 		// If there is a csproj file, add the C# feature if it doesn't already exist.
 		if (!project_features.has("C#")) {
 		if (!project_features.has("C#")) {
 			project_features.append("C#");
 			project_features.append("C#");

+ 0 - 1
core/config/project_settings.h

@@ -166,7 +166,6 @@ public:
 	String get_project_data_dir_name() const;
 	String get_project_data_dir_name() const;
 	String get_project_data_path() const;
 	String get_project_data_path() const;
 	String get_resource_path() const;
 	String get_resource_path() const;
-	String get_safe_project_name() const;
 	String get_imported_files_path() const;
 	String get_imported_files_path() const;
 
 
 	static ProjectSettings *get_singleton();
 	static ProjectSettings *get_singleton();

+ 2 - 5
modules/mono/csharp_script.cpp

@@ -62,6 +62,7 @@
 #include "signal_awaiter_utils.h"
 #include "signal_awaiter_utils.h"
 #include "utils/macros.h"
 #include "utils/macros.h"
 #include "utils/naming_utils.h"
 #include "utils/naming_utils.h"
+#include "utils/path_utils.h"
 #include "utils/string_utils.h"
 #include "utils/string_utils.h"
 
 
 #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
 #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var)
@@ -740,11 +741,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
 			return false; // Already up to date
 			return false; // Already up to date
 		}
 		}
 	} else {
 	} else {
-		String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
-
-		if (assembly_name.is_empty()) {
-			assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
-		}
+		String assembly_name = path::get_csharp_project_name();
 
 
 		assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
 		assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
 								.path_join(assembly_name + ".dll");
 								.path_join(assembly_name + ".dll");

+ 0 - 22
modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs

@@ -57,27 +57,5 @@ namespace GodotTools.Core
                    path.StartsWith("\\", StringComparison.Ordinal) ||
                    path.StartsWith("\\", StringComparison.Ordinal) ||
                    path.StartsWith(_driveRoot, StringComparison.Ordinal);
                    path.StartsWith(_driveRoot, StringComparison.Ordinal);
         }
         }
-
-        public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
-        {
-            var invalidChars = new List<string> { ":", "*", "?", "\"", "<", ">", "|" };
-
-            if (allowDirSeparator)
-            {
-                // Directory separators are allowed, but disallow ".." to avoid going up the filesystem
-                invalidChars.Add("..");
-            }
-            else
-            {
-                invalidChars.Add("/");
-            }
-
-            string safeDirName = dirName.Replace("\\", "/").Trim();
-
-            foreach (string invalidChar in invalidChars)
-                safeDirName = safeDirName.Replace(invalidChar, "-");
-
-            return safeDirName;
-        }
     }
     }
 }
 }

+ 1 - 8
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -151,7 +151,7 @@ namespace GodotTools.Export
                 string ridOS = DetermineRuntimeIdentifierOS(platform);
                 string ridOS = DetermineRuntimeIdentifierOS(platform);
                 string ridArch = DetermineRuntimeIdentifierArch(arch);
                 string ridArch = DetermineRuntimeIdentifierArch(arch);
                 string runtimeIdentifier = $"{ridOS}-{ridArch}";
                 string runtimeIdentifier = $"{ridOS}-{ridArch}";
-                string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}";
+                string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{arch}";
                 if (platform == OS.Platforms.MacOS)
                 if (platform == OS.Platforms.MacOS)
                 {
                 {
                     projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
                     projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
@@ -258,12 +258,5 @@ namespace GodotTools.Export
             platform = null;
             platform = null;
             return false;
             return false;
         }
         }
-
-        private static string DetermineDataDirNameForProject()
-        {
-            string appName = (string)ProjectSettings.GetSetting("application/config/name");
-            string appNameSafe = appName.ToSafeDirName();
-            return $"data_{appNameSafe}";
-        }
     }
     }
 }
 }

+ 10 - 8
modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs

@@ -48,21 +48,23 @@ namespace GodotTools.Internals
             }
             }
         }
         }
 
 
-        public static void DetermineProjectLocation()
+
+        public static string CSharpProjectName
         {
         {
-            static string DetermineProjectName()
+            get
             {
             {
-                string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
-                projectAssemblyName = projectAssemblyName.ToSafeDirName();
-                if (string.IsNullOrEmpty(projectAssemblyName))
-                    projectAssemblyName = "UnnamedProject";
-                return projectAssemblyName;
+                Internal.godot_icall_GodotSharpDirs_CSharpProjectName(out godot_string dest);
+                using (dest)
+                    return Marshaling.ConvertStringToManaged(dest);
             }
             }
+        }
 
 
+        public static void DetermineProjectLocation()
+        {
             _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
             _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name");
             if (string.IsNullOrEmpty(_projectAssemblyName))
             if (string.IsNullOrEmpty(_projectAssemblyName))
             {
             {
-                _projectAssemblyName = DetermineProjectName();
+                _projectAssemblyName = CSharpProjectName;
                 ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
                 ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName);
             }
             }
 
 

+ 2 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs

@@ -101,6 +101,8 @@ namespace GodotTools.Internals
 
 
         public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
         public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest);
 
 
+        public static partial void godot_icall_GodotSharpDirs_CSharpProjectName(out godot_string r_dest);
+
         public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
         public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label,
             int amount, bool canCancel);
             int amount, bool canCancel);
 
 

+ 6 - 0
modules/mono/editor/editor_internal_calls.cpp

@@ -49,6 +49,7 @@
 #include "../csharp_script.h"
 #include "../csharp_script.h"
 #include "../godotsharp_dirs.h"
 #include "../godotsharp_dirs.h"
 #include "../utils/macos_utils.h"
 #include "../utils/macos_utils.h"
+#include "../utils/path_utils.h"
 #include "code_completion.h"
 #include "code_completion.h"
 
 
 #include "../interop_types.h"
 #include "../interop_types.h"
@@ -81,6 +82,10 @@ void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) {
 #endif
 #endif
 }
 }
 
 
+void godot_icall_GodotSharpDirs_CSharpProjectName(godot_string *r_dest) {
+	memnew_placement(r_dest, String(path::get_csharp_project_name()));
+}
+
 void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
 void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) {
 	String task = *reinterpret_cast<const String *>(p_task);
 	String task = *reinterpret_cast<const String *>(p_task);
 	String label = *reinterpret_cast<const String *>(p_label);
 	String label = *reinterpret_cast<const String *>(p_label);
@@ -231,6 +236,7 @@ static const void *unmanaged_callbacks[]{
 	(void *)godot_icall_GodotSharpDirs_MonoUserDir,
 	(void *)godot_icall_GodotSharpDirs_MonoUserDir,
 	(void *)godot_icall_GodotSharpDirs_BuildLogsDirs,
 	(void *)godot_icall_GodotSharpDirs_BuildLogsDirs,
 	(void *)godot_icall_GodotSharpDirs_DataEditorToolsDir,
 	(void *)godot_icall_GodotSharpDirs_DataEditorToolsDir,
+	(void *)godot_icall_GodotSharpDirs_CSharpProjectName,
 	(void *)godot_icall_EditorProgress_Create,
 	(void *)godot_icall_EditorProgress_Create,
 	(void *)godot_icall_EditorProgress_Dispose,
 	(void *)godot_icall_EditorProgress_Dispose,
 	(void *)godot_icall_EditorProgress_Step,
 	(void *)godot_icall_EditorProgress_Step,

+ 2 - 2
modules/mono/godotsharp_dirs.cpp

@@ -44,6 +44,7 @@
 #endif
 #endif
 
 
 #include "mono_gd/gd_mono.h"
 #include "mono_gd/gd_mono.h"
+#include "utils/path_utils.h"
 
 
 namespace GodotSharpDirs {
 namespace GodotSharpDirs {
 
 
@@ -139,8 +140,7 @@ private:
 		api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
 		api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
 #else // TOOLS_ENABLED
 #else // TOOLS_ENABLED
 		String arch = Engine::get_singleton()->get_architecture_name();
 		String arch = Engine::get_singleton()->get_architecture_name();
-		String appname = GLOBAL_GET("application/config/name");
-		String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
+		String appname_safe = path::get_csharp_project_name();
 		String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
 		String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
 		if (!DirAccess::exists(data_dir_root)) {
 		if (!DirAccess::exists(data_dir_root)) {
 			data_dir_root = exe_dir.path_join("data_Godot_" + arch);
 			data_dir_root = exe_dir.path_join("data_Godot_" + arch);

+ 3 - 17
modules/mono/mono_gd/gd_mono.cpp

@@ -293,20 +293,10 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
 	return godot_plugins_initialize;
 	return godot_plugins_initialize;
 }
 }
 #else
 #else
-static String get_assembly_name() {
-	String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
-
-	if (assembly_name.is_empty()) {
-		assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
-	}
-
-	return assembly_name;
-}
-
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) {
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
 	godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
 
 
-	String assembly_name = get_assembly_name();
+	String assembly_name = path::get_csharp_project_name();
 
 
 	HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
 	HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
 															 .path_join(assembly_name + ".dll"));
 															 .path_join(assembly_name + ".dll"));
@@ -331,7 +321,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime
 }
 }
 
 
 godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
 godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) {
-	String assembly_name = get_assembly_name();
+	String assembly_name = path::get_csharp_project_name();
 
 
 #if defined(WINDOWS_ENABLED)
 #if defined(WINDOWS_ENABLED)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
@@ -476,11 +466,7 @@ void GDMono::_init_godot_api_hashes() {
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 bool GDMono::_load_project_assembly() {
 bool GDMono::_load_project_assembly() {
-	String assembly_name = GLOBAL_GET("dotnet/project/assembly_name");
-
-	if (assembly_name.is_empty()) {
-		assembly_name = ProjectSettings::get_singleton()->get_safe_project_name();
-	}
+	String assembly_name = path::get_csharp_project_name();
 
 
 	String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
 	String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
 								   .path_join(assembly_name + ".dll");
 								   .path_join(assembly_name + ".dll");

+ 23 - 0
modules/mono/utils/path_utils.cpp

@@ -231,4 +231,27 @@ String relative_to(const String &p_path, const String &p_relative_to) {
 
 
 	return relative_to_impl(path_abs_norm, relative_to_abs_norm);
 	return relative_to_impl(path_abs_norm, relative_to_abs_norm);
 }
 }
+
+String get_csharp_project_name() {
+	String name = ProjectSettings::get_singleton()->get_setting_with_override("dotnet/project/assembly_name");
+	if (name.is_empty()) {
+		name = ProjectSettings::get_singleton()->get_setting_with_override("application/config/name");
+		Vector<String> invalid_chars = Vector<String>({ //
+				// Windows reserved filename chars.
+				":", "*", "?", "\"", "<", ">", "|",
+				// Directory separators.
+				"/", "\\",
+				// Other chars that have been found to break assembly loading.
+				";", "'", "=", "," });
+		name = name.strip_edges();
+		for (int i = 0; i < invalid_chars.size(); i++) {
+			name = name.replace(invalid_chars[i], "-");
+		}
+	}
+	if (name.is_empty()) {
+		name = "UnnamedProject";
+	}
+	return name;
+}
+
 } // namespace path
 } // namespace path

+ 2 - 1
modules/mono/utils/path_utils.h

@@ -31,7 +31,6 @@
 #ifndef MONO_PATH_UTILS_H
 #ifndef MONO_PATH_UTILS_H
 #define MONO_PATH_UTILS_H
 #define MONO_PATH_UTILS_H
 
 
-#include "core/string/string_builder.h"
 #include "core/string/ustring.h"
 #include "core/string/ustring.h"
 
 
 namespace path {
 namespace path {
@@ -58,6 +57,8 @@ String abspath(const String &p_path);
 String realpath(const String &p_path);
 String realpath(const String &p_path);
 
 
 String relative_to(const String &p_path, const String &p_relative_to);
 String relative_to(const String &p_path, const String &p_relative_to);
+
+String get_csharp_project_name();
 } // namespace path
 } // namespace path
 
 
 #endif // MONO_PATH_UTILS_H
 #endif // MONO_PATH_UTILS_H