Proper GDNative export on Android

This commit is contained in:
Ruslan Mustakov 2017-11-21 01:12:36 +07:00
parent 5ff84070ca
commit ebf9b80a47
3 changed files with 97 additions and 51 deletions

View file

@ -548,7 +548,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;
}

View file

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

View file

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