Merge pull request #13130 from endragor/gdnative-android-export
Proper GDNative export on Android
This commit is contained in:
commit
bedcbdd420
3 changed files with 97 additions and 51 deletions
|
@ -554,7 +554,7 @@ bool OS::has_feature(const String &p_feature) {
|
||||||
if (sizeof(void *) == 4 && p_feature == "32") {
|
if (sizeof(void *) == 4 && p_feature == "32") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#if defined(__x86_64) || defined(__x86_64__)
|
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__)
|
||||||
if (p_feature == "x86_64") {
|
if (p_feature == "x86_64") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,13 @@ bool GDNative::initialize() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#ifdef IPHONE_ENABLED
|
#ifdef IPHONE_ENABLED
|
||||||
|
// on iOS we use static linking
|
||||||
String path = "";
|
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
|
#else
|
||||||
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
|
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -467,52 +467,72 @@ class EditorExportAndroid : public EditorExportPlatform {
|
||||||
return zipfi;
|
return zipfi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Set<String> get_abis() {
|
static Vector<String> get_abis() {
|
||||||
Set<String> abis;
|
// mips and armv6 are dead (especially for games), so not including them
|
||||||
abis.insert("armeabi");
|
Vector<String> abis;
|
||||||
abis.insert("armeabi-v7a");
|
abis.push_back("armeabi-v7a");
|
||||||
abis.insert("arm64-v8a");
|
abis.push_back("arm64-v8a");
|
||||||
abis.insert("x86");
|
abis.push_back("x86");
|
||||||
abis.insert("x86_64");
|
abis.push_back("x86_64");
|
||||||
abis.insert("mips");
|
|
||||||
abis.insert("mips64");
|
|
||||||
return abis;
|
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) {
|
static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) {
|
||||||
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/");
|
|
||||||
}
|
|
||||||
|
|
||||||
zip_fileinfo zipfi = get_zip_fileinfo();
|
zip_fileinfo zipfi = get_zip_fileinfo();
|
||||||
|
|
||||||
zipOpenNewFileInZip(ed->apk,
|
zipOpenNewFileInZip(ed->apk,
|
||||||
dst_path.utf8().get_data(),
|
p_path.utf8().get_data(),
|
||||||
&zipfi,
|
&zipfi,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
_should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0,
|
compression_method,
|
||||||
Z_DEFAULT_COMPRESSION);
|
Z_DEFAULT_COMPRESSION);
|
||||||
|
|
||||||
zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
|
zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size());
|
||||||
zipCloseFileInZip(ed->apk);
|
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);
|
ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -935,6 +955,18 @@ class EditorExportAndroid : public EditorExportPlatform {
|
||||||
//printf("end\n");
|
//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:
|
public:
|
||||||
enum {
|
enum {
|
||||||
MAX_USER_PERMISSIONS = 20
|
MAX_USER_PERMISSIONS = 20
|
||||||
|
@ -951,6 +983,11 @@ public:
|
||||||
r_features->push_back("etc");
|
r_features->push_back("etc");
|
||||||
else*/
|
else*/
|
||||||
r_features->push_back("etc2");
|
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) {
|
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/name"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"), ""));
|
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, "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::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::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true));
|
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/SALT"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
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;
|
const char **perms = android_perms;
|
||||||
while (*perms) {
|
while (*perms) {
|
||||||
|
|
||||||
|
@ -1295,10 +1337,6 @@ public:
|
||||||
String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk");
|
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);
|
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 use_32_fb = p_preset->get("graphics/32_bits_framebuffer");
|
||||||
bool immersive = p_preset->get("screen/immersive_mode");
|
bool immersive = p_preset->get("screen/immersive_mode");
|
||||||
|
|
||||||
|
@ -1318,6 +1356,8 @@ public:
|
||||||
String release_username = p_preset->get("keystore/release_user");
|
String release_username = p_preset->get("keystore/release_user");
|
||||||
String release_password = p_preset->get("keystore/release_password");
|
String release_password = p_preset->get("keystore/release_password");
|
||||||
|
|
||||||
|
Vector<String> enabled_abis = get_enabled_abis(p_preset);
|
||||||
|
|
||||||
while (ret == UNZ_OK) {
|
while (ret == UNZ_OK) {
|
||||||
|
|
||||||
//get filename
|
//get filename
|
||||||
|
@ -1381,25 +1421,25 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == "lib/x86/*.so" && !export_x86) {
|
if (file.ends_with(".so")) {
|
||||||
skip = true;
|
bool enabled = false;
|
||||||
}
|
for (int i = 0; i < enabled_abis.size(); ++i) {
|
||||||
|
if (file.begins_with("lib/" + enabled_abis[i] + "/")) {
|
||||||
if (file.match("lib/armeabi*/*.so") && !export_arm) {
|
enabled = true;
|
||||||
skip = true;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (file.match("lib/arm64*/*.so") && !export_arm64) {
|
if (!enabled) {
|
||||||
skip = true;
|
skip = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.begins_with("META-INF") && _signed) {
|
if (file.begins_with("META-INF") && _signed) {
|
||||||
skip = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_line("ADDING: " + file);
|
|
||||||
|
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
|
print_line("ADDING: " + file);
|
||||||
|
|
||||||
// Respect decision on compression made by AAPT for the export template
|
// Respect decision on compression made by AAPT for the export template
|
||||||
const bool uncompressed = info.compression_method == 0;
|
const bool uncompressed = info.compression_method == 0;
|
||||||
|
@ -1473,7 +1513,7 @@ public:
|
||||||
ed.ep = &ep;
|
ed.ep = &ep;
|
||||||
ed.apk = unaligned_apk;
|
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 a new issue