Add verbose logging to help with troubleshooting the Android build/export process.

This commit is contained in:
Fredia Huya-Kouadio 2021-02-16 16:33:15 -08:00
parent f91c4c0899
commit 9714abf8cb
2 changed files with 80 additions and 3 deletions

View file

@ -775,6 +775,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) {
print_verbose("Building temporary manifest..");
String manifest_text =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
@ -795,6 +796,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
manifest_text += _get_application_tag(p_preset, plugins_names);
manifest_text += "</manifest>\n";
String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text);
store_string_at_path(manifest_path, manifest_text);
}
@ -1476,14 +1479,19 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
if (!project_splash_path.is_empty()) {
splash_image.instance();
print_verbose("Loading splash image: " + project_splash_path);
const Error err = ImageLoader::load_image(project_splash_path, splash_image);
if (err) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")");
}
splash_image.unref();
}
}
if (splash_image.is_null()) {
// Use the default
print_verbose("Using default splash image.");
splash_image = Ref<Image>(memnew(Image(boot_splash_png)));
}
@ -1494,6 +1502,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
bg_color = boot_splash_bg_color;
}
print_verbose("Creating splash background color image.");
splash_bg_color_image.instance();
splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format());
splash_bg_color_image->fill(bg_color);
@ -1508,19 +1517,24 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
// Regular icon: user selection -> project icon -> default.
String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
print_verbose("Loading regular icon from " + path);
if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) {
print_verbose("- falling back to project icon: " + project_icon_path);
ImageLoader::load_image(project_icon_path, icon);
}
// Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges();
print_verbose("Loading adaptive foreground icon from " + path);
if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) {
print_verbose("- falling back to using the regular icon");
foreground = icon;
}
// Adaptive background: user selection -> default.
path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges();
if (!path.is_empty()) {
print_verbose("Loading adaptive background icon from " + path);
ImageLoader::load_image(path, background);
}
}
@ -1542,6 +1556,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
const Ref<Image> &background) {
// Store the splash image
if (splash_image.is_valid() && !splash_image->is_empty()) {
print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH));
Vector<uint8_t> data;
_load_image_data(splash_image, data);
store_image(SPLASH_IMAGE_EXPORT_PATH, data);
@ -1549,6 +1564,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
// Store the splash bg color image
if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH));
Vector<uint8_t> data;
_load_image_data(splash_bg_color_image, data);
store_image(SPLASH_BG_COLOR_PATH, data);
@ -1559,12 +1575,14 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
for (int i = 0; i < icon_densities_count; ++i) {
if (main_image.is_valid() && !main_image->is_empty()) {
print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path);
Vector<uint8_t> data;
_process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data);
store_image(launcher_icons[i], data);
}
if (foreground.is_valid() && !foreground->is_empty()) {
print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path);
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground,
launcher_adaptive_icon_foregrounds[i].dimensions, data);
@ -1572,6 +1590,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
if (background.is_valid() && !background->is_empty()) {
print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path);
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background,
launcher_adaptive_icon_backgrounds[i].dimensions, data);
@ -2239,6 +2258,7 @@ public:
String release_password = p_preset->get("keystore/release_password");
String apksigner = get_apksigner_path();
print_verbose("Starting signing of the " + export_label + " binary using " + apksigner);
if (!FileAccess::exists(apksigner)) {
EditorNode::add_io_error("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting " + export_label + " is unsigned.");
return OK;
@ -2287,6 +2307,10 @@ public:
args.push_back("--ks-key-alias");
args.push_back(user);
args.push_back(export_path);
if (p_debug) {
// We only print verbose logs for debug builds to avoid leaking release keystore credentials.
print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
int retval;
OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
if (retval) {
@ -2302,24 +2326,41 @@ public:
args.push_back("verify");
args.push_back("--verbose");
args.push_back(export_path);
if (p_debug) {
print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" ")));
}
OS::get_singleton()->execute(apksigner, args, nullptr, &retval);
if (retval) {
EditorNode::add_io_error("'apksigner' verification of " + export_label + " failed.");
return ERR_CANT_CREATE;
}
print_verbose("Successfully completed signing build.");
return OK;
}
void _clear_assets_directory() {
DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
if (da_res->dir_exists("res://android/build/assets")) {
print_verbose("Clearing assets directory..");
DirAccessRef da_assets = DirAccess::open("res://android/build/assets");
da_assets->erase_contents_recursive();
da_res->remove("res://android/build/assets");
}
}
String join_list(List<String> parts, const String &separator) const {
String ret;
for (int i = 0; i < parts.size(); ++i) {
if (i > 0) {
ret += separator;
}
ret += parts[i];
}
return ret;
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override {
int export_format = int(p_preset->get("custom_template/export_format"));
bool should_sign = p_preset->get("package/signed");
@ -2339,6 +2380,15 @@ public:
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<String> enabled_abis = get_enabled_abis(p_preset);
print_verbose("Exporting for Android...");
print_verbose("- debug build: " + bool_to_string(p_debug));
print_verbose("- export path: " + p_path);
print_verbose("- export format: " + itos(export_format));
print_verbose("- sign build: " + bool_to_string(should_sign));
print_verbose("- custom build enabled: " + bool_to_string(use_custom_build));
print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion));
print_verbose("- enabled abis: " + String(",").join(enabled_abis));
Ref<Image> splash_image;
Ref<Image> splash_bg_color_image;
load_splash_refs(splash_image, splash_bg_color_image);
@ -2374,14 +2424,17 @@ public:
}
if (use_custom_build) {
print_verbose("Starting custom build..");
//test that installed build version is alright
{
print_verbose("Checking build version..");
FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ);
if (!f) {
EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
String version = f->get_line().strip_edges();
print_verbose("- build version: " + version);
f->close();
if (version != VERSION_FULL_CONFIG) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
@ -2389,7 +2442,8 @@ public:
}
}
String sdk_path = EDITOR_GET("export/android/android_sdk_path");
ERR_FAIL_COND_V_MSG(sdk_path == "", ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
print_verbose("Android sdk path: " + sdk_path);
// TODO: should we use "package/name" or "application/config/name"?
String project_name = get_project_name(p_preset->get("package/name"));
@ -2405,20 +2459,24 @@ public:
//stores all the project files inside the Gradle project directory. Also includes all ABIs
_clear_assets_directory();
if (!apk_expansion) {
print_verbose("Exporting project files..");
err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, NULL, ignore_so_file);
if (err != OK) {
EditorNode::add_io_error("Could not export project files to gradle project\n");
return err;
}
} else {
print_verbose("Saving apk expansion file..");
err = save_apk_expansion_file(p_preset, p_path);
if (err != OK) {
EditorNode::add_io_error("Could not write expansion package file!");
return err;
}
}
print_verbose("Storing command line flags..");
store_file_at_path("res://android/build/assets/_cl_", command_line_flags);
print_verbose("Updating ANDROID_HOME environment to " + sdk_path);
OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
String build_command;
@ -2458,6 +2516,8 @@ public:
cmdline.push_back(apk_build_command);
}
cmdline.push_back("-p"); // argument to specify the start directory.
cmdline.push_back(build_path); // start directory.
cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code.
cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name.
@ -2467,6 +2527,13 @@ public:
cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies.
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
// NOTE: The release keystore is not included in the verbose logging
// to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting.
// Any non-sensitive additions to the command line arguments must be done above this section.
// Sensitive additions must be done below the logging statement.
print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" ")));
if (should_sign && !p_debug) {
// Pass the release keystore info as well
String release_keystore = p_preset->get("keystore/release");
@ -2481,8 +2548,6 @@ public:
cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias.
cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specity the release keystore password.
}
cmdline.push_back("-p"); // argument to specify the start directory.
cmdline.push_back(build_path); // start directory.
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
if (result != 0) {
@ -2513,15 +2578,18 @@ public:
copy_args.push_back("-Pexport_path=file:" + export_path);
copy_args.push_back("-Pexport_filename=" + export_filename);
print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" ")));
int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args);
if (copy_result != 0) {
EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs."));
return ERR_CANT_CREATE;
}
print_verbose("Successfully completed Android custom build.");
return OK;
}
// This is the start of the Legacy build system
print_verbose("Starting legacy build system..");
if (p_debug)
src_apk = p_preset->get("custom_template/debug");
else

View file

@ -146,6 +146,9 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
String dir = p_path.get_base_dir();
Error err = create_directory(dir);
if (err != OK) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to write data into " + p_path);
}
return err;
}
FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE);
@ -162,12 +165,14 @@ Error store_string_at_path(const String &p_path, const String &p_data) {
// This method will be called ONLY when custom build is enabled.
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) {
String dst_path = p_path.replace_first("res://", "res://android/build/assets/");
print_verbose("Saving project files from " + p_path + " into " + dst_path);
Error err = store_file_at_path(dst_path, p_data);
return err;
}
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true));
store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
@ -175,6 +180,9 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
// Searches the Gradle project res/ directory to find all supported locales
DirAccessRef da = DirAccess::open("res://android/build/res");
if (!da) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to open Android resources directory.");
}
return ERR_CANT_OPEN;
}
da->list_dir_begin();
@ -193,6 +201,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
if (ProjectSettings::get_singleton()->has_setting(property_name)) {
String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true));
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
store_string_at_path(locale_directory, processed_xml_string);
} else {
// TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch