mirror of
https://github.com/godotengine/godot.git
synced 2024-11-21 03:18:37 +08:00
Merge pull request #13130 from endragor/gdnative-android-export
Proper GDNative export on Android
This commit is contained in:
commit
bedcbdd420
@ -554,7 +554,7 @@ bool OS::has_feature(const String &p_feature) {
|
||||
if (sizeof(void *) == 4 && p_feature == "32") {
|
||||
return true;
|
||||
}
|
||||
#if defined(__x86_64) || defined(__x86_64__)
|
||||
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__)
|
||||
if (p_feature == "x86_64") {
|
||||
return true;
|
||||
}
|
||||
|
@ -123,7 +123,13 @@ bool GDNative::initialize() {
|
||||
return false;
|
||||
}
|
||||
#ifdef IPHONE_ENABLED
|
||||
// on iOS we use static linking
|
||||
String path = "";
|
||||
#elif defined(ANDROID_ENABLED)
|
||||
// On Android dynamic libraries are located separately from resource assets,
|
||||
// we should pass library name to dlopen(). The library name is flattened
|
||||
// during export.
|
||||
String path = lib_path.get_file();
|
||||
#else
|
||||
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
|
||||
#endif
|
||||
|
@ -467,52 +467,72 @@ class EditorExportAndroid : public EditorExportPlatform {
|
||||
return zipfi;
|
||||
}
|
||||
|
||||
static Set<String> get_abis() {
|
||||
Set<String> abis;
|
||||
abis.insert("armeabi");
|
||||
abis.insert("armeabi-v7a");
|
||||
abis.insert("arm64-v8a");
|
||||
abis.insert("x86");
|
||||
abis.insert("x86_64");
|
||||
abis.insert("mips");
|
||||
abis.insert("mips64");
|
||||
static Vector<String> get_abis() {
|
||||
// mips and armv6 are dead (especially for games), so not including them
|
||||
Vector<String> abis;
|
||||
abis.push_back("armeabi-v7a");
|
||||
abis.push_back("arm64-v8a");
|
||||
abis.push_back("x86");
|
||||
abis.push_back("x86_64");
|
||||
return abis;
|
||||
}
|
||||
|
||||
static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
|
||||
APKExportData *ed = (APKExportData *)p_userdata;
|
||||
String dst_path = p_path;
|
||||
static Set<String> android_abis = get_abis();
|
||||
|
||||
if (dst_path.ends_with(".so")) {
|
||||
String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name
|
||||
if (android_abis.has(abi)) {
|
||||
dst_path = "lib/" + abi + "/" + dst_path.get_file();
|
||||
} else {
|
||||
String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " +
|
||||
p_path + " does not follow this convention.";
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
return ERR_FILE_BAD_PATH;
|
||||
}
|
||||
} else {
|
||||
dst_path = dst_path.replace_first("res://", "assets/");
|
||||
}
|
||||
|
||||
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
|
||||
zip_fileinfo zipfi = get_zip_fileinfo();
|
||||
|
||||
zipOpenNewFileInZip(ed->apk,
|
||||
dst_path.utf8().get_data(),
|
||||
p_path.utf8().get_data(),
|
||||
&zipfi,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
_should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0,
|
||||
compression_method,
|
||||
Z_DEFAULT_COMPRESSION);
|
||||
|
||||
zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
|
||||
zipCloseFileInZip(ed->apk);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static Error save_apk_so(void *p_userdata, const SharedObject &p_so) {
|
||||
if (!p_so.path.get_file().begins_with("lib")) {
|
||||
String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
return FAILED;
|
||||
}
|
||||
APKExportData *ed = (APKExportData *)p_userdata;
|
||||
Vector<String> abis = get_abis();
|
||||
bool exported = false;
|
||||
for (int i = 0; i < p_so.tags.size(); ++i) {
|
||||
// shared objects can be fat (compatible with multiple ABIs)
|
||||
int start_pos = 0;
|
||||
int abi_index = abis.find(p_so.tags[i]);
|
||||
if (abi_index != -1) {
|
||||
exported = true;
|
||||
start_pos = abi_index + 1;
|
||||
String abi = abis[abi_index];
|
||||
String dst_path = "lib/" + abi + "/" + p_so.path.get_file();
|
||||
Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path);
|
||||
Error store_err = store_in_apk(ed, dst_path, array);
|
||||
ERR_FAIL_COND_V(store_err, store_err);
|
||||
}
|
||||
}
|
||||
if (!exported) {
|
||||
String abis_string = String(" ").join(abis);
|
||||
String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string;
|
||||
ERR_PRINT(err.utf8().get_data());
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) {
|
||||
APKExportData *ed = (APKExportData *)p_userdata;
|
||||
String dst_path = p_path.replace_first("res://", "assets/");
|
||||
|
||||
store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0);
|
||||
ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total);
|
||||
return OK;
|
||||
}
|
||||
@ -935,6 +955,18 @@ class EditorExportAndroid : public EditorExportPlatform {
|
||||
//printf("end\n");
|
||||
}
|
||||
|
||||
static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) {
|
||||
Vector<String> abis = get_abis();
|
||||
Vector<String> enabled_abis;
|
||||
for (int i = 0; i < abis.size(); ++i) {
|
||||
bool is_enabled = p_preset->get("architectures/" + abis[i]);
|
||||
if (is_enabled) {
|
||||
enabled_abis.push_back(abis[i]);
|
||||
}
|
||||
}
|
||||
return enabled_abis;
|
||||
}
|
||||
|
||||
public:
|
||||
enum {
|
||||
MAX_USER_PERMISSIONS = 20
|
||||
@ -951,6 +983,11 @@ public:
|
||||
r_features->push_back("etc");
|
||||
else*/
|
||||
r_features->push_back("etc2");
|
||||
|
||||
Vector<String> abis = get_enabled_abis(p_preset);
|
||||
for (int i = 0; i < abis.size(); ++i) {
|
||||
r_features->push_back(abis[i]);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void get_export_options(List<ExportOption> *r_options) {
|
||||
@ -967,8 +1004,6 @@ public:
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/arm"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/x86"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
|
||||
@ -982,6 +1017,13 @@ public:
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
|
||||
Vector<String> abis = get_abis();
|
||||
for (int i = 0; i < abis.size(); ++i) {
|
||||
String abi = abis[i];
|
||||
bool is_default = (abi == "armeabi-v7a");
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
|
||||
}
|
||||
|
||||
const char **perms = android_perms;
|
||||
while (*perms) {
|
||||
|
||||
@ -1295,10 +1337,6 @@ public:
|
||||
String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
|
||||
zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
|
||||
|
||||
bool export_x86 = p_preset->get("architecture/x86");
|
||||
bool export_arm = p_preset->get("architecture/arm");
|
||||
bool export_arm64 = p_preset->get("architecture/arm64");
|
||||
|
||||
bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
|
||||
bool immersive = p_preset->get("screen/immersive_mode");
|
||||
|
||||
@ -1318,6 +1356,8 @@ public:
|
||||
String release_username = p_preset->get("keystore/release_user");
|
||||
String release_password = p_preset->get("keystore/release_password");
|
||||
|
||||
Vector<String> enabled_abis = get_enabled_abis(p_preset);
|
||||
|
||||
while (ret == UNZ_OK) {
|
||||
|
||||
//get filename
|
||||
@ -1381,25 +1421,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (file == "lib/x86/*.so" && !export_x86) {
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (file.match("lib/armeabi*/*.so") && !export_arm) {
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (file.match("lib/arm64*/*.so") && !export_arm64) {
|
||||
skip = true;
|
||||
if (file.ends_with(".so")) {
|
||||
bool enabled = false;
|
||||
for (int i = 0; i < enabled_abis.size(); ++i) {
|
||||
if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!enabled) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (file.begins_with("META-INF") && _signed) {
|
||||
skip = true;
|
||||
}
|
||||
|
||||
print_line("ADDING: " + file);
|
||||
|
||||
if (!skip) {
|
||||
print_line("ADDING: " + file);
|
||||
|
||||
// Respect decision on compression made by AAPT for the export template
|
||||
const bool uncompressed = info.compression_method == 0;
|
||||
@ -1473,7 +1513,7 @@ public:
|
||||
ed.ep = &ep;
|
||||
ed.apk = unaligned_apk;
|
||||
|
||||
err = export_project_files(p_preset, save_apk_file, &ed);
|
||||
err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user