Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add GDNative Framework support for macOS, add Unix symlink API. #46860

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/io/file_access_pack.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ class DirAccessPack : public DirAccess {
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);

virtual bool is_link(String p_file) { return false; }
virtual String read_link(String p_file) { return p_file; }
virtual Error create_link(String p_source, String p_target) { return FAILED; }

uint64_t get_space_left();

virtual String get_filesystem_type() const;
Expand Down
12 changes: 7 additions & 5 deletions core/os/dir_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,17 @@ class DirChanger {
}
};

Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags) {
Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) {
List<String> dirs;

String curdir = get_current_dir();
list_dir_begin();
String n = get_next();
while (n != String()) {
if (n != "." && n != "..") {
if (current_is_dir()) {
if (p_copy_links && is_link(get_current_dir().plus_file(n))) {
create_link(read_link(get_current_dir().plus_file(n)), p_to + n);
} else if (current_is_dir()) {
dirs.push_back(n);
} else {
const String &rel_path = n;
Expand Down Expand Up @@ -374,7 +376,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
Error err = change_dir(E->get());
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");

err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags);
err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
if (err) {
change_dir("..");
ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
Expand All @@ -386,7 +388,7 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag
return OK;
}

Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) {
Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) {
ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");

DirAccess *target_da = DirAccess::create_for_path(p_to);
Expand All @@ -405,7 +407,7 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) {
}

DirChanger dir_changer(this, p_from);
Error err = _copy_dir(target_da, p_to, p_chmod_flags);
Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links);
memdelete(target_da);

return err;
Expand Down
8 changes: 6 additions & 2 deletions core/os/dir_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DirAccess {
AccessType _access_type;
static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object

Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags);
Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links);

protected:
String _get_root_path() const;
Expand Down Expand Up @@ -89,11 +89,15 @@ class DirAccess {
static bool exists(String p_dir);
virtual uint64_t get_space_left() = 0;

Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1);
Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false);
virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1);
virtual Error rename(String p_from, String p_to) = 0;
virtual Error remove(String p_name) = 0;

virtual bool is_link(String p_file) = 0;
virtual String read_link(String p_file) = 0;
virtual Error create_link(String p_source, String p_target) = 0;

// Meant for editor code when we want to quickly remove a file without custom
// handling (e.g. removing a cache file).
static void remove_file_or_error(String p_path) {
Expand Down
43 changes: 43 additions & 0 deletions drivers/unix/dir_access_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,49 @@ Error DirAccessUnix::remove(String p_path) {
}
}

bool DirAccessUnix::is_link(String p_file) {
if (p_file.is_rel_path())
p_file = get_current_dir().plus_file(p_file);

p_file = fix_path(p_file);

struct stat flags;
if ((lstat(p_file.utf8().get_data(), &flags) != 0))
return FAILED;

return S_ISLNK(flags.st_mode);
}

String DirAccessUnix::read_link(String p_file) {
if (p_file.is_rel_path())
p_file = get_current_dir().plus_file(p_file);

p_file = fix_path(p_file);

char buf[256];
memset(buf, 0, 256);
ssize_t len = readlink(p_file.utf8().get_data(), buf, sizeof(buf));
String link;
if (len > 0) {
link.parse_utf8(buf, len);
}
return link;
}

Error DirAccessUnix::create_link(String p_source, String p_target) {
if (p_target.is_rel_path())
p_target = get_current_dir().plus_file(p_target);

p_source = fix_path(p_source);
p_target = fix_path(p_target);

if (symlink(p_source.utf8().get_data(), p_target.utf8().get_data()) == 0) {
return OK;
} else {
return FAILED;
}
}

uint64_t DirAccessUnix::get_space_left() {
#ifndef NO_STATVFS
struct statvfs vfs;
Expand Down
4 changes: 4 additions & 0 deletions drivers/unix/dir_access_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ class DirAccessUnix : public DirAccess {
virtual Error rename(String p_path, String p_new_path);
virtual Error remove(String p_path);

virtual bool is_link(String p_file);
virtual String read_link(String p_file);
virtual Error create_link(String p_source, String p_target);

virtual uint64_t get_space_left();

virtual String get_filesystem_type() const;
Expand Down
4 changes: 4 additions & 0 deletions drivers/windows/dir_access_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ class DirAccessWindows : public DirAccess {
virtual Error rename(String p_path, String p_new_path);
virtual Error remove(String p_path);

virtual bool is_link(String p_file) { return false; };
virtual String read_link(String p_file) { return p_file; };
virtual Error create_link(String p_source, String p_target) { return FAILED; };

uint64_t get_space_left();

virtual String get_filesystem_type() const;
Expand Down
12 changes: 11 additions & 1 deletion modules/gdnative/gdnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "core/global_constants.h"
#include "core/io/file_access_encrypted.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"
Expand Down Expand Up @@ -325,9 +326,18 @@ bool GDNative::initialize() {
// On OSX the exported libraries are located under the Frameworks directory.
// So we need to replace the library path.
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
if (!FileAccess::exists(path)) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);

if (!da->file_exists(path) && !da->dir_exists(path)) {
path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file());
}

if (da->dir_exists(path)) { // Target library is a ".framework", add library base name to the path.
path = path.plus_file(path.get_file().get_basename());
}

memdelete(da);

#else
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
#endif
Expand Down
7 changes: 3 additions & 4 deletions modules/gdnative/gdnative_library_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ void GDNativeLibraryEditor::_on_item_button(Object *item, int column, int id) {

if (id == BUTTON_SELECT_DEPENDENCES) {
mode = EditorFileDialog::MODE_OPEN_FILES;
} else if (treeItem->get_text(0) == "iOS") {
} else if (treeItem->get_text(0) == "iOS" || treeItem->get_text(0) == "macOS") {
mode = EditorFileDialog::MODE_OPEN_ANY;
}

Expand Down Expand Up @@ -286,10 +286,9 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platforms["X11"] = platform_linux;

NativePlatformConfig platform_osx;
platform_osx.name = "Mac OSX";
platform_osx.name = "macOS";
platform_osx.entries.push_back("64");
platform_osx.entries.push_back("32");
platform_osx.library_extension = "*.dylib";
platform_osx.library_extension = "*.framework; Framework, *.dylib; Dynamic Library";
platforms["OSX"] = platform_osx;

NativePlatformConfig platform_haiku;
Expand Down
4 changes: 4 additions & 0 deletions platform/android/dir_access_jandroid.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ class DirAccessJAndroid : public DirAccess {
virtual Error rename(String p_from, String p_to);
virtual Error remove(String p_name);

virtual bool is_link(String p_file) { return false; }
virtual String read_link(String p_file) { return p_file; }
virtual Error create_link(String p_source, String p_target) { return FAILED; }

virtual String get_filesystem_type() const;

uint64_t get_space_left();
Expand Down
60 changes: 57 additions & 3 deletions platform/osx/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,9 +820,22 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (err == OK) {
DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file());
String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
if (da->dir_exists(src_path)) {
#ifndef UNIX_ENABLED
WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!");
#endif
print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
if (err == OK) {
err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true);
}
} else {
print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file());
}
if (err == OK && sign_enabled) {
err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file(), ent_path);
err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path);
}
}
memdelete(da);
Expand Down Expand Up @@ -898,7 +911,48 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
if (f == "." || f == "..") {
continue;
}
if (da->current_is_dir()) {
if (da->is_link(f)) {
OS::Time time = OS::get_singleton()->get_time();
OS::Date date = OS::get_singleton()->get_date();

zip_fileinfo zipfi;
zipfi.tmz_date.tm_hour = time.hour;
zipfi.tmz_date.tm_mday = date.day;
zipfi.tmz_date.tm_min = time.min;
zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/
zipfi.tmz_date.tm_sec = time.sec;
zipfi.tmz_date.tm_year = date.year;
zipfi.dosDate = 0;
// 0120000: symbolic link type
// 0000755: permissions rwxr-xr-x
// 0000644: permissions rw-r--r--
uint32_t _mode = 0120644;
zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
zipfi.internal_fa = 0;

zipOpenNewFileInZip4(p_zip,
p_folder.plus_file(f).utf8().get_data(),
&zipfi,
nullptr,
0,
nullptr,
0,
nullptr,
Z_DEFLATED,
Z_DEFAULT_COMPRESSION,
0,
-MAX_WBITS,
DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY,
nullptr,
0,
0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
0);

String target = da->read_link(f);
zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
zipCloseFileInZip(p_zip);
} else if (da->current_is_dir()) {
_zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
} else {
bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
Expand Down