Android: Extract libs from pre-built APKs when installing build template
Otherwise we would need to include all of them in android_source.zip, which means building the zip after all libs have been built by SCons (so it would have to be done via gradle or a manual script). By extracting it from the pre-built APKs, we save some disk space in templates archives too.
This commit is contained in:
parent
b1f294b3ac
commit
52024c0e90
2 changed files with 114 additions and 23 deletions
|
@ -542,7 +542,6 @@ void ExportTemplateManager::_notification(int p_what) {
|
||||||
template_list_state->set_text(status);
|
template_list_state->set_text(status);
|
||||||
if (errored) {
|
if (errored) {
|
||||||
set_process(false);
|
set_process(false);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,25 +554,33 @@ void ExportTemplateManager::_notification(int p_what) {
|
||||||
|
|
||||||
bool ExportTemplateManager::can_install_android_template() {
|
bool ExportTemplateManager::can_install_android_template() {
|
||||||
|
|
||||||
return FileAccess::exists(EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG).plus_file("android_source.zip"));
|
const String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||||
|
return FileAccess::exists(templates_dir.plus_file("android_source.zip")) &&
|
||||||
|
FileAccess::exists(templates_dir.plus_file("android_release.apk")) &&
|
||||||
|
FileAccess::exists(templates_dir.plus_file("android_debug.apk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Error ExportTemplateManager::install_android_template() {
|
Error ExportTemplateManager::install_android_template() {
|
||||||
|
|
||||||
|
// To support custom Android builds, we install various things to the project's res://android folder.
|
||||||
|
// First is the Java source code and buildsystem from android_source.zip.
|
||||||
|
// Then we extract the Godot Android libraries from pre-build android_release.apk
|
||||||
|
// and android_debug.apk, to place them in the libs folder.
|
||||||
|
|
||||||
DirAccessRef da = DirAccess::open("res://");
|
DirAccessRef da = DirAccess::open("res://");
|
||||||
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
||||||
//make android dir (if it does not exist)
|
|
||||||
|
|
||||||
|
// Make res://android dir (if it does not exist).
|
||||||
da->make_dir("android");
|
da->make_dir("android");
|
||||||
{
|
{
|
||||||
//add an empty .gdignore file to avoid scan
|
// Add an empty .gdignore file to avoid scan.
|
||||||
FileAccessRef f = FileAccess::open("res://android/.gdignore", FileAccess::WRITE);
|
FileAccessRef f = FileAccess::open("res://android/.gdignore", FileAccess::WRITE);
|
||||||
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
||||||
f->store_line("");
|
f->store_line("");
|
||||||
f->close();
|
f->close();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
//add version, to ensure building won't work if template and Godot version don't match
|
// Add version, to ensure building won't work if template and Godot version don't match.
|
||||||
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
|
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
|
||||||
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
ERR_FAIL_COND_V(!f, ERR_CANT_CREATE);
|
||||||
f->store_line(VERSION_FULL_CONFIG);
|
f->store_line(VERSION_FULL_CONFIG);
|
||||||
|
@ -583,7 +590,10 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
Error err = da->make_dir_recursive("android/build");
|
Error err = da->make_dir_recursive("android/build");
|
||||||
ERR_FAIL_COND_V(err != OK, err);
|
ERR_FAIL_COND_V(err != OK, err);
|
||||||
|
|
||||||
String source_zip = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG).plus_file("android_source.zip");
|
// Uncompress source template.
|
||||||
|
|
||||||
|
const String &templates_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||||
|
const String &source_zip = templates_path.plus_file("android_source.zip");
|
||||||
ERR_FAIL_COND_V(!FileAccess::exists(source_zip), ERR_CANT_OPEN);
|
ERR_FAIL_COND_V(!FileAccess::exists(source_zip), ERR_CANT_OPEN);
|
||||||
|
|
||||||
FileAccess *src_f = NULL;
|
FileAccess *src_f = NULL;
|
||||||
|
@ -593,37 +603,33 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format.");
|
ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android sources not in ZIP format.");
|
||||||
|
|
||||||
int ret = unzGoToFirstFile(pkg);
|
int ret = unzGoToFirstFile(pkg);
|
||||||
|
|
||||||
int total_files = 0;
|
int total_files = 0;
|
||||||
//count files
|
// Count files to unzip.
|
||||||
while (ret == UNZ_OK) {
|
while (ret == UNZ_OK) {
|
||||||
total_files++;
|
total_files++;
|
||||||
ret = unzGoToNextFile(pkg);
|
ret = unzGoToNextFile(pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = unzGoToFirstFile(pkg);
|
ret = unzGoToFirstFile(pkg);
|
||||||
//decompress files
|
|
||||||
ProgressDialog::get_singleton()->add_task("uncompress", TTR("Uncompressing Android Build Sources"), total_files);
|
ProgressDialog::get_singleton()->add_task("uncompress_src", TTR("Uncompressing Android Build Sources"), total_files);
|
||||||
|
|
||||||
Set<String> dirs_tested;
|
Set<String> dirs_tested;
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
while (ret == UNZ_OK) {
|
while (ret == UNZ_OK) {
|
||||||
|
|
||||||
//get filename
|
// Get file path.
|
||||||
unz_file_info info;
|
unz_file_info info;
|
||||||
char fname[16384];
|
char fpath[16384];
|
||||||
ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
|
ret = unzGetCurrentFileInfo(pkg, &info, fpath, 16384, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
String name = fname;
|
String path = fpath;
|
||||||
|
String base_dir = path.get_base_dir();
|
||||||
|
|
||||||
String base_dir = name.get_base_dir();
|
if (!path.ends_with("/")) {
|
||||||
|
|
||||||
if (!name.ends_with("/")) {
|
|
||||||
Vector<uint8_t> data;
|
Vector<uint8_t> data;
|
||||||
data.resize(info.uncompressed_size);
|
data.resize(info.uncompressed_size);
|
||||||
|
|
||||||
//read
|
// Read.
|
||||||
unzOpenCurrentFile(pkg);
|
unzOpenCurrentFile(pkg);
|
||||||
unzReadCurrentFile(pkg, data.ptrw(), data.size());
|
unzReadCurrentFile(pkg, data.ptrw(), data.size());
|
||||||
unzCloseCurrentFile(pkg);
|
unzCloseCurrentFile(pkg);
|
||||||
|
@ -633,7 +639,7 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
dirs_tested.insert(base_dir);
|
dirs_tested.insert(base_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
String to_write = String("res://android/build").plus_file(name);
|
String to_write = String("res://android/build").plus_file(path);
|
||||||
FileAccess *f = FileAccess::open(to_write, FileAccess::WRITE);
|
FileAccess *f = FileAccess::open(to_write, FileAccess::WRITE);
|
||||||
if (f) {
|
if (f) {
|
||||||
f->store_buffer(data.ptr(), data.size());
|
f->store_buffer(data.ptr(), data.size());
|
||||||
|
@ -646,13 +652,96 @@ Error ExportTemplateManager::install_android_template() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressDialog::get_singleton()->task_step("uncompress", name, idx);
|
ProgressDialog::get_singleton()->task_step("uncompress_src", path, idx);
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
ret = unzGoToNextFile(pkg);
|
ret = unzGoToNextFile(pkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressDialog::get_singleton()->end_task("uncompress");
|
ProgressDialog::get_singleton()->end_task("uncompress_src");
|
||||||
|
unzClose(pkg);
|
||||||
|
|
||||||
|
// Extract libs from pre-built APKs.
|
||||||
|
err = _extract_libs_from_apk("release");
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't extract Android libs from android_release.apk.");
|
||||||
|
err = _extract_libs_from_apk("debug");
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't extract Android libs from android_debug.apk.");
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error ExportTemplateManager::_extract_libs_from_apk(const String &p_target_name) {
|
||||||
|
|
||||||
|
const String &templates_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
|
||||||
|
const String &apk_file = templates_path.plus_file("android_" + p_target_name + ".apk");
|
||||||
|
ERR_FAIL_COND_V(!FileAccess::exists(apk_file), ERR_CANT_OPEN);
|
||||||
|
|
||||||
|
FileAccess *src_f = NULL;
|
||||||
|
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
|
||||||
|
|
||||||
|
unzFile pkg = unzOpen2(apk_file.utf8().get_data(), &io);
|
||||||
|
ERR_FAIL_COND_V_MSG(!pkg, ERR_CANT_OPEN, "Android APK can't be extracted.");
|
||||||
|
|
||||||
|
DirAccessRef da = DirAccess::open("res://");
|
||||||
|
ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
|
||||||
|
|
||||||
|
// 8 steps because 4 arches, 2 libs per arch.
|
||||||
|
ProgressDialog::get_singleton()->add_task("extract_libs_from_apk", TTR("Extracting Android Libraries From APKs"), 8);
|
||||||
|
|
||||||
|
int ret = unzGoToFirstFile(pkg);
|
||||||
|
Set<String> dirs_tested;
|
||||||
|
int idx = 0;
|
||||||
|
while (ret == UNZ_OK) {
|
||||||
|
// Get file path.
|
||||||
|
unz_file_info info;
|
||||||
|
char fpath[16384];
|
||||||
|
ret = unzGetCurrentFileInfo(pkg, &info, fpath, 16384, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
|
String path = fpath;
|
||||||
|
String base_dir = path.get_base_dir();
|
||||||
|
String file = path.get_file();
|
||||||
|
|
||||||
|
if (!base_dir.begins_with("lib") || path.ends_with("/")) {
|
||||||
|
ret = unzGoToNextFile(pkg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<uint8_t> data;
|
||||||
|
data.resize(info.uncompressed_size);
|
||||||
|
|
||||||
|
// Read.
|
||||||
|
unzOpenCurrentFile(pkg);
|
||||||
|
unzReadCurrentFile(pkg, data.ptrw(), data.size());
|
||||||
|
unzCloseCurrentFile(pkg);
|
||||||
|
|
||||||
|
// We have a "lib" folder in the APK, but it should be "libs/{release,debug}" in the source dir.
|
||||||
|
String target_base_dir = base_dir.replace_first("lib", String("libs").plus_file(p_target_name));
|
||||||
|
|
||||||
|
if (!dirs_tested.has(base_dir)) {
|
||||||
|
da->make_dir_recursive(String("android/build").plus_file(target_base_dir));
|
||||||
|
dirs_tested.insert(base_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
String to_write = String("res://android/build").plus_file(target_base_dir.plus_file(path.get_file()));
|
||||||
|
FileAccess *f = FileAccess::open(to_write, FileAccess::WRITE);
|
||||||
|
if (f) {
|
||||||
|
f->store_buffer(data.ptr(), data.size());
|
||||||
|
memdelete(f);
|
||||||
|
#ifndef WINDOWS_ENABLED
|
||||||
|
// We can't retrieve Unix permissions from the APK it seems, so simply set 0755 as should be.
|
||||||
|
FileAccess::set_unix_permissions(to_write, 0755);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
ERR_PRINTS("Can't uncompress file: " + to_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDialog::get_singleton()->task_step("extract_libs_from_apk", path, idx);
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
ret = unzGoToNextFile(pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDialog::get_singleton()->end_task("extract_libs_from_apk");
|
||||||
unzClose(pkg);
|
unzClose(pkg);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
|
|
@ -72,6 +72,8 @@ class ExportTemplateManager : public ConfirmationDialog {
|
||||||
virtual void ok_pressed();
|
virtual void ok_pressed();
|
||||||
bool _install_from_file(const String &p_file, bool p_use_progress = true);
|
bool _install_from_file(const String &p_file, bool p_use_progress = true);
|
||||||
|
|
||||||
|
Error _extract_libs_from_apk(const String &p_target_name);
|
||||||
|
|
||||||
void _http_download_mirror_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
|
void _http_download_mirror_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
|
||||||
void _http_download_templates_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
|
void _http_download_templates_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue