Merge pull request #66952 from bruvzg/macos_net_export

Fix macOS .NET export.
This commit is contained in:
Rémi Verschelde 2022-10-12 22:52:59 +02:00 committed by GitHub
commit f2248969db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 156 additions and 266 deletions

View File

@ -144,6 +144,7 @@ public:
};
virtual Ref<EditorExportPreset> create_preset();
virtual bool is_executable(const String &p_path) const { return false; }
virtual void clear_messages() { messages.clear(); }
virtual void add_message(ExportMessageType p_type, const String &p_category, const String &p_message) {

View File

@ -185,10 +185,12 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset>
String src_path = ProjectSettings::get_singleton()->globalize_path(so_files[i].path);
String target_path;
if (so_files[i].target.is_empty()) {
target_path = p_path.get_base_dir().path_join(src_path.get_file());
target_path = p_path.get_base_dir();
} else {
target_path = p_path.get_base_dir().path_join(so_files[i].target).path_join(src_path.get_file());
target_path = p_path.get_base_dir().path_join(so_files[i].target);
da->make_dir_recursive(target_path);
}
target_path = target_path.path_join(src_path.get_file());
if (da->dir_exists(src_path)) {
err = da->make_dir_recursive(target_path);

View File

@ -17,6 +17,8 @@ namespace GodotTools.Export
{
public partial class ExportPlugin : EditorExportPlugin
{
private List<string> _tempFolders = new List<string>();
public void RegisterExportSettings()
{
// TODO: These would be better as export preset options, but that doesn't seem to be supported yet
@ -111,62 +113,78 @@ namespace GodotTools.Export
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
// TODO: This works for now, as we only implemented support for x86 family desktop so far, but it needs to be fixed
string arch = features.Contains("x86_64") ? "x86_64" : "x86";
string ridOS = DetermineRuntimeIdentifierOS(platform);
string ridArch = DetermineRuntimeIdentifierArch(arch);
string runtimeIdentifier = $"{ridOS}-{ridArch}";
// Create temporary publish output directory
string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
$"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
if (!Directory.Exists(publishOutputTempDir))
Directory.CreateDirectory(publishOutputTempDir);
// Execute dotnet publish
if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
runtimeIdentifier, publishOutputTempDir))
var archs = new List<string>();
if (features.Contains("x86_64"))
{
throw new InvalidOperationException("Failed to build project.");
archs.Add("x86_64");
}
else if (features.Contains("x86_32"))
{
archs.Add("x86_32");
}
else if (features.Contains("arm64"))
{
archs.Add("arm64");
}
else if (features.Contains("universal"))
{
if (platform == OS.Platforms.MacOS)
{
archs.Add("x86_64");
archs.Add("arm64");
}
}
string soExt = ridOS switch
foreach (var arch in archs)
{
OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
_ => "so"
};
string ridOS = DetermineRuntimeIdentifierOS(platform);
string ridArch = DetermineRuntimeIdentifierArch(arch);
string runtimeIdentifier = $"{ridOS}-{ridArch}";
string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}";
if (platform == OS.Platforms.MacOS)
{
projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
}
if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
// NativeAOT shared library output
&& !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
{
throw new NotSupportedException(
"Publish succeeded but project assembly not found in the output directory");
}
// Create temporary publish output directory
// Copy all files from the dotnet publish output directory to
// a data directory next to the Godot output executable.
string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
$"{Process.GetCurrentProcess().Id}-{buildConfig}-{runtimeIdentifier}");
string outputDataDir = Path.Combine(outputDir, DetermineDataDirNameForProject());
_tempFolders.Add(publishOutputTempDir);
if (Directory.Exists(outputDataDir))
Directory.Delete(outputDataDir, recursive: true); // Clean first
if (!Directory.Exists(publishOutputTempDir))
Directory.CreateDirectory(publishOutputTempDir);
Directory.CreateDirectory(outputDataDir);
// Execute dotnet publish
foreach (string dir in Directory.GetDirectories(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(publishOutputTempDir.Length + 1)));
}
if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
runtimeIdentifier, publishOutputTempDir))
{
throw new InvalidOperationException("Failed to build project.");
}
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
File.Copy(file, Path.Combine(outputDataDir, file.Substring(publishOutputTempDir.Length + 1)));
string soExt = ridOS switch
{
OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
_ => "so"
};
if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
// NativeAOT shared library output
&& !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
{
throw new NotSupportedException(
"Publish succeeded but project assembly not found in the output directory");
}
// Add to the exported project shared object list.
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
{
AddSharedObject(file, tags: null, projectDataDirName);
}
}
}
@ -198,6 +216,12 @@ namespace GodotTools.Export
if (Directory.Exists(aotTempDir))
Directory.Delete(aotTempDir, recursive: true);
foreach (string folder in _tempFolders)
{
Directory.Delete(folder, recursive: true);
}
_tempFolders.Clear();
// TODO: The following is just a workaround until the export plugins can be made to abort with errors
// We check for empty as well, because it's set to empty after hot-reloading

View File

@ -94,138 +94,63 @@ String _get_mono_user_dir() {
class _GodotSharpDirs {
public:
String res_data_dir;
String res_metadata_dir;
String res_config_dir;
String res_temp_dir;
String res_temp_assemblies_base_dir;
String res_temp_assemblies_dir;
String mono_user_dir;
String mono_logs_dir;
String api_assemblies_base_dir;
String api_assemblies_dir;
#ifdef TOOLS_ENABLED
String mono_solutions_dir;
String build_logs_dir;
String data_editor_tools_dir;
#else
// Equivalent of res_assemblies_dir, but in the data directory rather than in 'res://'.
// Only defined on export templates. Used when exporting assemblies outside of PCKs.
String data_game_assemblies_dir;
#endif
String data_mono_etc_dir;
String data_mono_lib_dir;
#ifdef WINDOWS_ENABLED
String data_mono_bin_dir;
#endif
private:
_GodotSharpDirs() {
res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono");
String res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().path_join("mono");
res_metadata_dir = res_data_dir.path_join("metadata");
res_config_dir = res_data_dir.path_join("etc").path_join("mono");
// TODO use paths from csproj
res_temp_dir = res_data_dir.path_join("temp");
res_temp_assemblies_base_dir = res_temp_dir.path_join("bin");
res_temp_assemblies_dir = res_temp_assemblies_base_dir.path_join(_get_expected_build_config());
api_assemblies_base_dir = res_data_dir.path_join("assemblies");
res_temp_assemblies_dir = res_data_dir.path_join("temp").path_join("bin").path_join(_get_expected_build_config());
#ifdef WEB_ENABLED
mono_user_dir = "user://";
#else
mono_user_dir = _get_mono_user_dir();
#endif
mono_logs_dir = mono_user_dir.path_join("mono_logs");
#ifdef TOOLS_ENABLED
mono_solutions_dir = mono_user_dir.path_join("solutions");
build_logs_dir = mono_user_dir.path_join("build_logs");
String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
#endif
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
String res_dir = OS::get_singleton()->get_bundle_resource_dir();
#ifdef TOOLS_ENABLED
String data_dir_root = exe_dir.path_join("GodotSharp");
data_editor_tools_dir = data_dir_root.path_join("Tools");
api_assemblies_base_dir = data_dir_root.path_join("Api");
String data_mono_root_dir = data_dir_root.path_join("Mono");
data_mono_etc_dir = data_mono_root_dir.path_join("etc");
#ifdef ANDROID_ENABLED
data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
#else
data_mono_lib_dir = data_mono_root_dir.path_join("lib");
#endif
#ifdef WINDOWS_ENABLED
data_mono_bin_dir = data_mono_root_dir.path_join("bin");
#endif
String api_assemblies_base_dir = data_dir_root.path_join("Api");
build_logs_dir = mono_user_dir.path_join("build_logs");
#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
data_editor_tools_dir = exe_dir.path_join("../Resources/GodotSharp/Tools");
data_editor_tools_dir = res_dir.path_join("GodotSharp").path_join("Tools");
}
if (!DirAccess::exists(api_assemblies_base_dir)) {
api_assemblies_base_dir = exe_dir.path_join("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc");
data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib");
api_assemblies_base_dir = res_dir.path_join("GodotSharp").path_join("Api");
}
#endif
#else
api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
#else // TOOLS_ENABLED
String arch = Engine::get_singleton()->get_architecture_name();
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
String data_dir_root = exe_dir.path_join("data_" + appname_safe);
String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch);
if (!DirAccess::exists(data_dir_root)) {
data_dir_root = exe_dir.path_join("data_Godot");
data_dir_root = exe_dir.path_join("data_Godot_" + arch);
}
String data_mono_root_dir = data_dir_root.path_join("Mono");
data_mono_etc_dir = data_mono_root_dir.path_join("etc");
#ifdef ANDROID_ENABLED
data_mono_lib_dir = gdmono::android::support::get_app_native_lib_dir();
#else
data_mono_lib_dir = data_mono_root_dir.path_join("lib");
data_game_assemblies_dir = data_dir_root.path_join("Assemblies");
#endif
#ifdef WINDOWS_ENABLED
data_mono_bin_dir = data_mono_root_dir.path_join("bin");
#endif
#ifdef MACOS_ENABLED
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/etc");
data_mono_lib_dir = exe_dir.path_join("../Resources/GodotSharp/Mono/lib");
if (!DirAccess::exists(data_dir_root)) {
data_dir_root = res_dir.path_join("data_" + appname_safe + "_" + arch);
}
if (!DirAccess::exists(data_game_assemblies_dir)) {
data_game_assemblies_dir = exe_dir.path_join("../Resources/GodotSharp/Assemblies");
if (!DirAccess::exists(data_dir_root)) {
data_dir_root = res_dir.path_join("data_Godot_" + arch);
}
#endif
#endif
#ifdef TOOLS_ENABLED
api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config());
#else
api_assemblies_dir = data_dir_root;
#endif
}
@ -237,26 +162,10 @@ public:
}
};
String get_res_data_dir() {
return _GodotSharpDirs::get_singleton().res_data_dir;
}
String get_res_metadata_dir() {
return _GodotSharpDirs::get_singleton().res_metadata_dir;
}
String get_res_config_dir() {
return _GodotSharpDirs::get_singleton().res_config_dir;
}
String get_res_temp_dir() {
return _GodotSharpDirs::get_singleton().res_temp_dir;
}
String get_res_temp_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_base_dir;
}
String get_res_temp_assemblies_dir() {
return _GodotSharpDirs::get_singleton().res_temp_assemblies_dir;
}
@ -265,23 +174,11 @@ String get_api_assemblies_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_dir;
}
String get_api_assemblies_base_dir() {
return _GodotSharpDirs::get_singleton().api_assemblies_base_dir;
}
String get_mono_user_dir() {
return _GodotSharpDirs::get_singleton().mono_user_dir;
}
String get_mono_logs_dir() {
return _GodotSharpDirs::get_singleton().mono_logs_dir;
}
#ifdef TOOLS_ENABLED
String get_mono_solutions_dir() {
return _GodotSharpDirs::get_singleton().mono_solutions_dir;
}
String get_build_logs_dir() {
return _GodotSharpDirs::get_singleton().build_logs_dir;
}
@ -289,23 +186,6 @@ String get_build_logs_dir() {
String get_data_editor_tools_dir() {
return _GodotSharpDirs::get_singleton().data_editor_tools_dir;
}
#else
String get_data_game_assemblies_dir() {
return _GodotSharpDirs::get_singleton().data_game_assemblies_dir;
}
#endif
String get_data_mono_etc_dir() {
return _GodotSharpDirs::get_singleton().data_mono_etc_dir;
}
String get_data_mono_lib_dir() {
return _GodotSharpDirs::get_singleton().data_mono_lib_dir;
}
#ifdef WINDOWS_ENABLED
String get_data_mono_bin_dir() {
return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
}
#endif
} // namespace GodotSharpDirs

View File

@ -35,34 +35,18 @@
namespace GodotSharpDirs {
String get_res_data_dir();
String get_res_metadata_dir();
String get_res_config_dir();
String get_res_temp_dir();
String get_res_temp_assemblies_base_dir();
String get_res_temp_assemblies_dir();
String get_api_assemblies_dir();
String get_api_assemblies_base_dir();
String get_mono_user_dir();
String get_mono_logs_dir();
#ifdef TOOLS_ENABLED
String get_mono_solutions_dir();
String get_build_logs_dir();
String get_data_editor_tools_dir();
#else
String get_data_game_assemblies_dir();
#endif
String get_data_mono_etc_dir();
String get_data_mono_lib_dir();
#ifdef WINDOWS_ENABLED
String get_data_mono_bin_dir();
#endif
} // namespace GodotSharpDirs
#endif // GODOTSHARP_DIRS_H

View File

@ -31,6 +31,8 @@
#include "export_plugin.h"
#include "codesign.h"
#include "lipo.h"
#include "macho.h"
#include "core/string/translation.h"
#include "editor/editor_node.h"
@ -754,6 +756,7 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
if (extensions_to_sign.is_empty()) {
extensions_to_sign.push_back("dylib");
extensions_to_sign.push_back("framework");
extensions_to_sign.push_back("");
}
Error dir_access_error;
@ -778,6 +781,10 @@ Error EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPres
if (code_sign_error != OK) {
return code_sign_error;
}
if (is_executable(current_file_path)) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(current_file_path, 0755);
}
} else if (dir_access->current_is_dir()) {
Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) };
if (code_sign_error != OK) {
@ -799,6 +806,14 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
const String &p_in_app_path, bool p_sign_enabled,
const Ref<EditorExportPreset> &p_preset, const String &p_ent_path,
bool p_should_error_on_non_code_sign) {
static Vector<String> extensions_to_sign;
if (extensions_to_sign.is_empty()) {
extensions_to_sign.push_back("dylib");
extensions_to_sign.push_back("framework");
extensions_to_sign.push_back("");
}
Error err{ OK };
if (dir_access->dir_exists(p_src_path)) {
#ifndef UNIX_ENABLED
@ -818,7 +833,13 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
// If it is a directory, find and sign all dynamic libraries.
err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign);
} else {
err = _code_sign(p_preset, p_in_app_path, p_ent_path, false);
if (extensions_to_sign.find(p_in_app_path.get_extension()) > -1) {
err = _code_sign(p_preset, p_in_app_path, p_ent_path, false);
}
if (is_executable(p_in_app_path)) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(p_in_app_path, 0755);
}
}
}
return err;
@ -877,6 +898,17 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str
return OK;
}
bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
uint16_t magic = fb->get_16();
return (magic == 0x2123);
}
bool EditorExportPlatformMacOS::is_executable(const String &p_path) const {
return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path);
}
Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
if (f.is_null()) {
@ -1158,11 +1190,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
// Now process our template.
bool found_binary = false;
Vector<String> dylibs_found;
while (ret == UNZ_OK && err == OK) {
bool is_execute = false;
// Get filename.
unz_file_info info;
char fname[16384];
@ -1219,7 +1248,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
continue; // skip
}
found_binary = true;
is_execute = true;
file = "Contents/MacOS/" + pkg_name;
}
@ -1251,25 +1279,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
}
if (data.size() > 0) {
if (file.find("/data.mono.macos.release_debug." + architecture + "/") != -1) {
if (!p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
file = file.replace("/data.mono.macos.release_debug." + architecture + "/", "/GodotSharp/");
}
if (file.find("/data.mono.macos.release." + architecture + "/") != -1) {
if (p_debug) {
ret = unzGoToNextFile(src_pkg_zip);
continue; // skip
}
file = file.replace("/data.mono.macos.release." + architecture + "/", "/GodotSharp/");
}
if (file.ends_with(".dylib")) {
dylibs_found.push_back(file);
}
print_verbose("ADDING: " + file + " size: " + itos(data.size()));
// Write it into our application bundle.
@ -1285,7 +1294,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (f.is_valid()) {
f->store_buffer(data.ptr(), data.size());
f.unref();
if (is_execute) {
if (is_executable(file)) {
// chmod with 0755 if the file is executable.
FileAccess::set_unix_permissions(file, 0755);
}
@ -1324,12 +1333,35 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
return ERR_SKIP;
}
// See if we can code sign our new package.
bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
bool ad_hoc = false;
int codesign_tool = p_preset->get("codesign/codesign");
switch (codesign_tool) {
case 1: { // built-in ad-hoc
ad_hoc = true;
} break;
case 2: { // "rcodesign"
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
} break;
#endif
default: {
};
}
String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
Vector<SharedObject> shared_objects;
err = save_pack(p_preset, p_debug, pack_path, &shared_objects);
// See if we can code sign our new package.
bool sign_enabled = (p_preset->get("codesign/codesign").operator int() > 0);
bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
if (!shared_objects.is_empty() && sign_enabled && ad_hoc && !lib_validation) {
add_message(EXPORT_MESSAGE_INFO, TTR("Entitlements Modified"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries."));
lib_validation = true;
}
String ent_path = p_preset->get("codesign/entitlements/custom_file");
String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements");
@ -1365,7 +1397,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
}
}
if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) {
if (lib_validation) {
ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>");
ent_f->store_line("<true/>");
}
@ -1495,32 +1527,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
}
}
bool ad_hoc = false;
int codesign_tool = p_preset->get("codesign/codesign");
switch (codesign_tool) {
case 1: { // built-in ad-hoc
ad_hoc = true;
} break;
case 2: { // "rcodesign"
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-");
} break;
#endif
default: {
};
}
if (err == OK) {
bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation");
if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries."));
err = ERR_CANT_CREATE;
}
}
if (err == OK) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
@ -1529,8 +1535,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file();
err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true);
} else {
String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target).path_join(src_path.get_file());
err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, false);
String path_in_app = tmp_app_path_name.path_join(shared_objects[i].target);
tmp_app_dir->make_dir_recursive(path_in_app);
err = _copy_and_sign_files(da, src_path, path_in_app.path_join(src_path.get_file()), sign_enabled, p_preset, ent_path, false);
}
if (err != OK) {
break;
@ -1546,14 +1553,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
}
}
if (sign_enabled) {
for (int i = 0; i < dylibs_found.size(); i++) {
if (err == OK) {
err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path, false);
}
}
}
if (err == OK && sign_enabled) {
if (ep.step(TTR("Code signing bundle"), 2)) {
return ERR_SKIP;
@ -1683,8 +1682,6 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
} else if (da->current_is_dir()) {
_zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
} else {
bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command");
OS::DateTime dt = OS::get_singleton()->get_datetime();
zip_fileinfo zipfi;
@ -1698,7 +1695,7 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
// 0100000: regular file type
// 0000755: permissions rwxr-xr-x
// 0000644: permissions rw-r--r--
uint32_t _mode = (is_executable ? 0100755 : 0100644);
uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644);
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
zipfi.internal_fa = 0;

View File

@ -97,6 +97,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
return true;
}
bool is_shbang(const String &p_path) const;
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@ -108,6 +109,7 @@ public:
virtual String get_os_name() const override { return "macOS"; }
virtual Ref<Texture2D> get_logo() const override { return logo; }
virtual bool is_executable(const String &p_path) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
if (use_dmg()) {