You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-07 12:30:27 +00:00
Android now (optionally) builds the template when exporting
Added new way to create add-ons Removed old way to create add-ons
This commit is contained in:
@@ -417,6 +417,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||
name = "noname";
|
||||
|
||||
pname = pname.replace("$genname", name);
|
||||
|
||||
return pname;
|
||||
}
|
||||
|
||||
@@ -1143,11 +1144,12 @@ public:
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_package/use_custom_build"), false));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.$genname"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), 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));
|
||||
@@ -1388,21 +1390,25 @@ public:
|
||||
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
|
||||
|
||||
String err;
|
||||
r_missing_templates = find_export_template("android_debug.apk") == String() || find_export_template("android_release.apk") == String();
|
||||
|
||||
if (p_preset->get("custom_package/debug") != "") {
|
||||
if (FileAccess::exists(p_preset->get("custom_package/debug"))) {
|
||||
r_missing_templates = false;
|
||||
} else {
|
||||
err += TTR("Custom debug template not found.") + "\n";
|
||||
if (!bool(p_preset->get("custom_package/use_custom_build"))) {
|
||||
|
||||
r_missing_templates = find_export_template("android_debug.apk") == String() || find_export_template("android_release.apk") == String();
|
||||
|
||||
if (p_preset->get("custom_package/debug") != "") {
|
||||
if (FileAccess::exists(p_preset->get("custom_package/debug"))) {
|
||||
r_missing_templates = false;
|
||||
} else {
|
||||
err += TTR("Custom debug template not found.") + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_preset->get("custom_package/release") != "") {
|
||||
if (FileAccess::exists(p_preset->get("custom_package/release"))) {
|
||||
r_missing_templates = false;
|
||||
} else {
|
||||
err += TTR("Custom release template not found.") + "\n";
|
||||
if (p_preset->get("custom_package/release") != "") {
|
||||
if (FileAccess::exists(p_preset->get("custom_package/release"))) {
|
||||
r_missing_templates = false;
|
||||
} else {
|
||||
err += TTR("Custom release template not found.") + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1435,6 +1441,30 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (bool(p_preset->get("custom_package/use_custom_build"))) {
|
||||
String sdk_path = EditorSettings::get_singleton()->get("export/android/custom_build_sdk_path");
|
||||
if (sdk_path == "") {
|
||||
err += TTR("Custom build requires a valid Android SDK path in Editor Settings.") + "\n";
|
||||
valid = false;
|
||||
} else {
|
||||
Error errn;
|
||||
DirAccess *da = DirAccess::open(sdk_path.plus_file("tools"), &errn);
|
||||
if (errn != OK) {
|
||||
err += TTR("Invalid Android SDK path for custom build in Editor Settings.") + "\n";
|
||||
valid = false;
|
||||
}
|
||||
if (da) {
|
||||
memdelete(da);
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileAccess::exists("res://android/build/build.gradle")) {
|
||||
|
||||
err += TTR("Android project is not installed for compiling. Install from Editor menu.") + "\n";
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool apk_expansion = p_preset->get("apk_expansion/enable");
|
||||
|
||||
if (apk_expansion) {
|
||||
@@ -1473,6 +1503,260 @@ public:
|
||||
return list;
|
||||
}
|
||||
|
||||
void _update_custom_build_project() {
|
||||
|
||||
DirAccessRef da = DirAccess::open("res://android");
|
||||
|
||||
ERR_FAIL_COND(!da);
|
||||
Map<String, List<String> > directory_paths;
|
||||
Map<String, List<String> > manifest_sections;
|
||||
Map<String, List<String> > gradle_sections;
|
||||
da->list_dir_begin();
|
||||
String d = da->get_next();
|
||||
while (d != String()) {
|
||||
|
||||
if (!d.begins_with(".") && d != "build" && da->current_is_dir()) { //a dir and not the build dir
|
||||
//add directories found
|
||||
DirAccessRef ds = DirAccess::open(String("res://android").plus_file(d));
|
||||
if (ds) {
|
||||
ds->list_dir_begin();
|
||||
String sd = ds->get_next();
|
||||
while (sd != String()) {
|
||||
|
||||
if (!sd.begins_with(".") && ds->current_is_dir()) {
|
||||
String key = sd.to_upper();
|
||||
if (!directory_paths.has(key)) {
|
||||
directory_paths[key] = List<String>();
|
||||
}
|
||||
String path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android").plus_file(d).plus_file(sd);
|
||||
directory_paths[key].push_back(path);
|
||||
print_line("Add: " + sd + ":" + path);
|
||||
}
|
||||
|
||||
sd = ds->get_next();
|
||||
}
|
||||
ds->list_dir_end();
|
||||
}
|
||||
//parse manifest
|
||||
{
|
||||
FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("AndroidManifest.conf"), FileAccess::READ);
|
||||
if (f) {
|
||||
|
||||
String section;
|
||||
while (!f->eof_reached()) {
|
||||
String l = f->get_line();
|
||||
String k = l.strip_edges();
|
||||
if (k.begins_with("[")) {
|
||||
section = k.substr(1, k.length() - 2).strip_edges().to_upper();
|
||||
print_line("Section: " + section);
|
||||
} else if (k != String()) {
|
||||
if (!manifest_sections.has(section)) {
|
||||
manifest_sections[section] = List<String>();
|
||||
}
|
||||
manifest_sections[section].push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
f->close();
|
||||
}
|
||||
}
|
||||
//parse gradle
|
||||
{
|
||||
FileAccessRef f = FileAccess::open(String("res://android").plus_file(d).plus_file("gradle.conf"), FileAccess::READ);
|
||||
if (f) {
|
||||
|
||||
String section;
|
||||
while (!f->eof_reached()) {
|
||||
String l = f->get_line().strip_edges();
|
||||
String k = l.strip_edges();
|
||||
if (k.begins_with("[")) {
|
||||
section = k.substr(1, k.length() - 2).strip_edges().to_upper();
|
||||
print_line("Section: " + section);
|
||||
} else if (k != String()) {
|
||||
if (!gradle_sections.has(section)) {
|
||||
gradle_sections[section] = List<String>();
|
||||
}
|
||||
gradle_sections[section].push_back(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
d = da->get_next();
|
||||
}
|
||||
da->list_dir_end();
|
||||
|
||||
{ //fix gradle build
|
||||
|
||||
String new_file;
|
||||
{
|
||||
FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::READ);
|
||||
if (f) {
|
||||
|
||||
while (!f->eof_reached()) {
|
||||
String l = f->get_line();
|
||||
|
||||
if (l.begins_with("//CHUNK_")) {
|
||||
String text = l.replace_first("//CHUNK_", "");
|
||||
int begin_pos = text.find("_BEGIN");
|
||||
if (begin_pos != -1) {
|
||||
text = text.substr(0, begin_pos);
|
||||
text = text.to_upper(); //just in case
|
||||
|
||||
String end_marker = "//CHUNK_" + text + "_END";
|
||||
size_t pos = f->get_position();
|
||||
bool found = false;
|
||||
while (!f->eof_reached()) {
|
||||
l = f->get_line();
|
||||
if (l.begins_with(end_marker)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new_file += "//CHUNK_" + text + "_BEGIN\n";
|
||||
|
||||
if (!found) {
|
||||
ERR_PRINTS("No end marker found in build.gradle for chunk: " + text);
|
||||
f->seek(pos);
|
||||
} else {
|
||||
|
||||
//add chunk lines
|
||||
if (gradle_sections.has(text)) {
|
||||
for (List<String>::Element *E = gradle_sections[text].front(); E; E = E->next()) {
|
||||
new_file += E->get() + "\n";
|
||||
}
|
||||
}
|
||||
new_file += end_marker + "\n";
|
||||
}
|
||||
} else {
|
||||
new_file += l + "\n"; //pass line by
|
||||
}
|
||||
} else if (l.begins_with("//DIR_")) {
|
||||
String text = l.replace_first("//DIR_", "");
|
||||
int begin_pos = text.find("_BEGIN");
|
||||
if (begin_pos != -1) {
|
||||
text = text.substr(0, begin_pos);
|
||||
text = text.to_upper(); //just in case
|
||||
|
||||
String end_marker = "//DIR_" + text + "_END";
|
||||
size_t pos = f->get_position();
|
||||
bool found = false;
|
||||
while (!f->eof_reached()) {
|
||||
l = f->get_line();
|
||||
if (l.begins_with(end_marker)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new_file += "//DIR_" + text + "_BEGIN\n";
|
||||
|
||||
if (!found) {
|
||||
ERR_PRINTS("No end marker found in build.gradle for dir: " + text);
|
||||
f->seek(pos);
|
||||
} else {
|
||||
//add chunk lines
|
||||
if (directory_paths.has(text)) {
|
||||
for (List<String>::Element *E = directory_paths[text].front(); E; E = E->next()) {
|
||||
new_file += ",'" + E->get().replace("'", "\'") + "'";
|
||||
new_file += "\n";
|
||||
}
|
||||
}
|
||||
new_file += end_marker + "\n";
|
||||
}
|
||||
} else {
|
||||
new_file += l + "\n"; //pass line by
|
||||
}
|
||||
|
||||
} else {
|
||||
new_file += l + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileAccessRef f = FileAccess::open("res://android/build/build.gradle", FileAccess::WRITE);
|
||||
f->store_string(new_file);
|
||||
f->close();
|
||||
}
|
||||
|
||||
{ //fix manifest
|
||||
|
||||
String new_file;
|
||||
{
|
||||
FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::READ);
|
||||
if (f) {
|
||||
|
||||
while (!f->eof_reached()) {
|
||||
String l = f->get_line();
|
||||
|
||||
if (l.begins_with("<!--CHUNK_")) {
|
||||
String text = l.replace_first("<!--CHUNK_", "");
|
||||
int begin_pos = text.find("_BEGIN-->");
|
||||
if (begin_pos != -1) {
|
||||
text = text.substr(0, begin_pos);
|
||||
text = text.to_upper(); //just in case
|
||||
|
||||
String end_marker = "<!--CHUNK_" + text + "_END-->";
|
||||
size_t pos = f->get_position();
|
||||
bool found = false;
|
||||
while (!f->eof_reached()) {
|
||||
l = f->get_line();
|
||||
if (l.begins_with(end_marker)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
new_file += "<!--CHUNK_" + text + "_BEGIN-->\n";
|
||||
|
||||
if (!found) {
|
||||
ERR_PRINTS("No end marker found in AndroidManifest.conf for chunk: " + text);
|
||||
f->seek(pos);
|
||||
} else {
|
||||
//add chunk lines
|
||||
if (manifest_sections.has(text)) {
|
||||
for (List<String>::Element *E = manifest_sections[text].front(); E; E = E->next()) {
|
||||
new_file += E->get() + "\n";
|
||||
}
|
||||
}
|
||||
new_file += end_marker + "\n";
|
||||
}
|
||||
} else {
|
||||
new_file += l + "\n"; //pass line by
|
||||
}
|
||||
|
||||
} else if (l.strip_edges().begins_with("<application")) {
|
||||
String last_tag = "android:icon=\"@drawable/icon\"";
|
||||
int last_tag_pos = l.find(last_tag);
|
||||
if (last_tag_pos == -1) {
|
||||
WARN_PRINTS("No adding of application tags because could not find last tag for <application: " + last_tag);
|
||||
new_file += l + "\n";
|
||||
} else {
|
||||
String base = l.substr(0, last_tag_pos + last_tag.length());
|
||||
if (manifest_sections.has("application_tags")) {
|
||||
for (List<String>::Element *E = manifest_sections["application_tags"].front(); E; E = E->next()) {
|
||||
String to_add = E->get().strip_edges();
|
||||
base += " " + to_add + " ";
|
||||
}
|
||||
}
|
||||
base += ">\n";
|
||||
new_file += base;
|
||||
}
|
||||
} else {
|
||||
new_file += l + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileAccessRef f = FileAccess::open("res://android/build/AndroidManifest.xml", FileAccess::WRITE);
|
||||
f->store_string(new_file);
|
||||
f->close();
|
||||
}
|
||||
}
|
||||
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
|
||||
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
@@ -1481,21 +1765,86 @@ public:
|
||||
|
||||
EditorProgress ep("export", "Exporting for Android", 105);
|
||||
|
||||
if (p_debug)
|
||||
src_apk = p_preset->get("custom_package/debug");
|
||||
else
|
||||
src_apk = p_preset->get("custom_package/release");
|
||||
if (bool(p_preset->get("custom_package/use_custom_build"))) { //custom build
|
||||
//re-generate build.gradle and AndroidManifest.xml
|
||||
|
||||
src_apk = src_apk.strip_edges();
|
||||
if (src_apk == "") {
|
||||
if (p_debug) {
|
||||
src_apk = find_export_template("android_debug.apk");
|
||||
} else {
|
||||
src_apk = find_export_template("android_release.apk");
|
||||
{ //test that installed build version is alright
|
||||
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();
|
||||
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));
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
}
|
||||
//build project if custom build is enabled
|
||||
String sdk_path = EDITOR_GET("export/android/custom_build_sdk_path");
|
||||
|
||||
ERR_FAIL_COND_V(sdk_path == "", ERR_UNCONFIGURED);
|
||||
|
||||
_update_custom_build_project();
|
||||
|
||||
OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required
|
||||
|
||||
String build_command;
|
||||
#ifdef WINDOWS_ENABLED
|
||||
build_command = "gradlew.bat";
|
||||
#else
|
||||
build_command = "gradlew";
|
||||
#endif
|
||||
|
||||
String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build");
|
||||
|
||||
build_command = build_path.plus_file(build_command);
|
||||
|
||||
List<String> cmdline;
|
||||
cmdline.push_back("build");
|
||||
cmdline.push_back("-p");
|
||||
cmdline.push_back(build_path);
|
||||
/*{ used for debug
|
||||
int ec;
|
||||
String pipe;
|
||||
OS::get_singleton()->execute(build_command, cmdline, true, NULL, NULL, &ec);
|
||||
print_line("exit code: " + itos(ec));
|
||||
}
|
||||
*/
|
||||
int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline);
|
||||
if (result != 0) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation."));
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
if (p_debug) {
|
||||
src_apk = build_path.plus_file("build/outputs/apk/debug/build-debug-unsigned.apk");
|
||||
} else {
|
||||
src_apk = build_path.plus_file("build/outputs/apk/release/build-release-unsigned.apk");
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(src_apk)) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("No build apk generated at: ") + "\n" + src_apk);
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (p_debug)
|
||||
src_apk = p_preset->get("custom_package/debug");
|
||||
else
|
||||
src_apk = p_preset->get("custom_package/release");
|
||||
|
||||
src_apk = src_apk.strip_edges();
|
||||
if (src_apk == "") {
|
||||
EditorNode::add_io_error("Package not found: " + src_apk);
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
if (p_debug) {
|
||||
src_apk = find_export_template("android_debug.apk");
|
||||
} else {
|
||||
src_apk = find_export_template("android_release.apk");
|
||||
}
|
||||
if (src_apk == "") {
|
||||
EditorNode::add_io_error("Package not found: " + src_apk);
|
||||
return ERR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1975,6 +2324,8 @@ void register_android_exporter() {
|
||||
EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey");
|
||||
EDITOR_DEF("export/android/debug_keystore_pass", "android");
|
||||
EDITOR_DEF("export/android/force_system_user", false);
|
||||
EDITOR_DEF("export/android/custom_build_sdk_path", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/custom_build_sdk_path", PROPERTY_HINT_GLOBAL_DIR, "*.keystore"));
|
||||
|
||||
EDITOR_DEF("export/android/timestamping_authority_url", "");
|
||||
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
|
||||
|
||||
Reference in New Issue
Block a user