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

Co-authored-by: Raul Santos <raulsntos@gmail.com>
This commit is contained in:
RedworkDE 2023-03-06 21:39:24 +01:00
parent 33957aee69
commit 92f13ba9ea
12 changed files with 71 additions and 76 deletions

View File

@ -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#");

View File

@ -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();

View File

@ -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"); String assembly_name = path::get_csharp_project_name();
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_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");

View File

@ -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;
}
} }
} }

View File

@ -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}";
}
} }
} }

View File

@ -48,21 +48,23 @@ namespace GodotTools.Internals
} }
} }
public static string CSharpProjectName
{
get
{
Internal.godot_icall_GodotSharpDirs_CSharpProjectName(out godot_string dest);
using (dest)
return Marshaling.ConvertStringToManaged(dest);
}
}
public static void DetermineProjectLocation() public static void DetermineProjectLocation()
{ {
static string DetermineProjectName()
{
string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
projectAssemblyName = projectAssemblyName.ToSafeDirName();
if (string.IsNullOrEmpty(projectAssemblyName))
projectAssemblyName = "UnnamedProject";
return projectAssemblyName;
}
_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);
} }

View File

@ -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);

View File

@ -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,

View File

@ -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 = path::get_csharp_project_name();
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
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);

View File

@ -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"); String assembly_name = path::get_csharp_project_name();
if (assembly_name.is_empty()) {
assembly_name = ProjectSettings::get_singleton()->get_safe_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");

View File

@ -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

View File

@ -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