Mono: Android build and shared libraries fixes
Fix location of Mono's shared libraries. Fix build failing if the directory 'platform/android/java/libs/{target}/{abi}' doesn't exist.
This commit is contained in:
parent
f5f7244a2b
commit
9f1a8ce6a2
69
modules/mono/build_scripts/make_android_mono_config.py
Normal file
69
modules/mono/build_scripts/make_android_mono_config.py
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
def generate_compressed_config(config_src, output_dir):
|
||||
import os.path
|
||||
from compat import byte_to_str
|
||||
|
||||
# Header file
|
||||
with open(os.path.join(output_dir, 'android_mono_config.gen.h'), 'w') as header:
|
||||
header.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
|
||||
#ifndef ANDROID_MONO_CONFIG_GEN_H
|
||||
#define ANDROID_MONO_CONFIG_GEN_H
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
String get_godot_android_mono_config();
|
||||
|
||||
#endif // ANDROID_ENABLED
|
||||
|
||||
#endif // ANDROID_MONO_CONFIG_GEN_H
|
||||
''')
|
||||
|
||||
# Source file
|
||||
with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp:
|
||||
with open(config_src, 'rb') as f:
|
||||
buf = f.read()
|
||||
decompr_size = len(buf)
|
||||
import zlib
|
||||
buf = zlib.compress(buf)
|
||||
compr_size = len(buf)
|
||||
|
||||
bytes_seq_str = ''
|
||||
for i, buf_idx in enumerate(range(compr_size)):
|
||||
if i > 0:
|
||||
bytes_seq_str += ', '
|
||||
bytes_seq_str += byte_to_str(buf[buf_idx])
|
||||
|
||||
cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
|
||||
#include "android_mono_config.gen.h"
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
|
||||
#include "core/io/compression.h"
|
||||
#include "core/pool_vector.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// config
|
||||
static const int config_compressed_size = %d;
|
||||
static const int config_uncompressed_size = %d;
|
||||
static const unsigned char config_compressed_data[] = { %s };
|
||||
|
||||
} // namespace
|
||||
|
||||
String get_godot_android_mono_config() {
|
||||
PoolVector<uint8_t> data;
|
||||
data.resize(config_uncompressed_size);
|
||||
PoolVector<uint8_t>::Write w = data.write();
|
||||
Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data,
|
||||
config_compressed_size, Compression::MODE_DEFLATE);
|
||||
String s;
|
||||
if (s.parse_utf8((const char *)w.ptr(), data.size())) {
|
||||
ERR_FAIL_V(String());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // ANDROID_ENABLED
|
||||
''' % (compr_size, decompr_size, bytes_seq_str))
|
@ -23,30 +23,31 @@ def generate_header(src, dst, version_dst):
|
||||
latest_mtime = mtime if mtime > latest_mtime else latest_mtime
|
||||
with open(filepath, 'rb') as f:
|
||||
buf = f.read()
|
||||
decomp_size = len(buf)
|
||||
decompr_size = len(buf)
|
||||
import zlib
|
||||
buf = zlib.compress(buf)
|
||||
compr_size = len(buf)
|
||||
name = str(cs_file_count)
|
||||
header.write('\n')
|
||||
header.write('// ' + filepath_src_rel + '\n')
|
||||
header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
|
||||
header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
|
||||
header.write('static const int _cs_' + name + '_compressed_size = ' + str(compr_size) + ';\n')
|
||||
header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decompr_size) + ';\n')
|
||||
header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
|
||||
for i, buf_idx in enumerate(range(len(buf))):
|
||||
for i, buf_idx in enumerate(range(compr_size)):
|
||||
if i > 0:
|
||||
header.write(', ')
|
||||
header.write(byte_to_str(buf[buf_idx]))
|
||||
header.write(' };\n')
|
||||
inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
|
||||
'CompressedFile(_cs_' + name + '_compressed_size, ' \
|
||||
'GodotCsCompressedFile(_cs_' + name + '_compressed_size, ' \
|
||||
'_cs_' + name + '_uncompressed_size, ' \
|
||||
'_cs_' + name + '_compressed));\n'
|
||||
header.write(' };\n')
|
||||
header.write('\nstruct CompressedFile\n' '{\n'
|
||||
header.write('\nstruct GodotCsCompressedFile\n' '{\n'
|
||||
'\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
|
||||
'\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
|
||||
'\n\tGodotCsCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
|
||||
'\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
|
||||
'\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
|
||||
'\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
|
||||
'\t\tdata = p_data;\n' '\t}\n' '\n\tGodotCsCompressedFile() {}\n' '};\n'
|
||||
'\nvoid get_compressed_files(Map<String, GodotCsCompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
|
||||
)
|
||||
header.write('\n#endif // TOOLS_ENABLED\n')
|
||||
header.write('\n#endif // CS_COMPRESSED_H\n')
|
||||
|
@ -41,7 +41,7 @@ def copy_file(src_dir, dst_dir, name):
|
||||
dst_dir = Dir(dst_dir).abspath
|
||||
|
||||
if not os.path.isdir(dst_dir):
|
||||
os.mkdir(dst_dir)
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
copy(src_path, dst_dir)
|
||||
|
||||
@ -65,6 +65,10 @@ def configure(env, env_mono):
|
||||
# TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
|
||||
raise RuntimeError('This module does not currently support building for android with tools enabled')
|
||||
|
||||
if is_android and mono_static:
|
||||
# When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0
|
||||
raise RuntimeError('Linking Mono statically is not currently supported on Android')
|
||||
|
||||
if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
|
||||
print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
|
||||
|
||||
@ -188,7 +192,7 @@ def configure(env, env_mono):
|
||||
if is_apple:
|
||||
env.Append(LIBS=['iconv', 'pthread'])
|
||||
elif is_android:
|
||||
env.Append(LIBS=['m', 'dl'])
|
||||
pass # Nothing
|
||||
else:
|
||||
env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
|
||||
|
||||
@ -236,6 +240,14 @@ def configure(env, env_mono):
|
||||
mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
|
||||
|
||||
make_template_dir(env, mono_root)
|
||||
elif not tools_enabled and is_android:
|
||||
# Compress Android Mono Config
|
||||
from . import make_android_mono_config
|
||||
config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
|
||||
make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
|
||||
|
||||
# Copy the required shared libraries
|
||||
copy_mono_shared_libs(env, mono_root, None)
|
||||
|
||||
if copy_mono_root:
|
||||
if not mono_root:
|
||||
@ -270,9 +282,8 @@ def make_template_dir(env, mono_root):
|
||||
|
||||
# Copy etc/mono/
|
||||
|
||||
if platform != 'android':
|
||||
template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
|
||||
copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
|
||||
template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
|
||||
copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
|
||||
|
||||
# Copy the required shared libraries
|
||||
|
||||
|
@ -682,19 +682,20 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||
|
||||
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
|
||||
|
||||
String name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (name.empty()) {
|
||||
name = "UnnamedProject";
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
if (appname_safe.empty()) {
|
||||
appname_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
name += ".dll";
|
||||
appname_safe += ".dll";
|
||||
|
||||
if (proj_assembly) {
|
||||
String proj_asm_path = proj_assembly->get_path();
|
||||
|
||||
if (!FileAccess::exists(proj_assembly->get_path())) {
|
||||
// Maybe it wasn't loaded from the default path, so check this as well
|
||||
proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name);
|
||||
proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
|
||||
if (!FileAccess::exists(proj_asm_path))
|
||||
return false; // No assembly to load
|
||||
}
|
||||
@ -702,7 +703,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
|
||||
if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
|
||||
return false; // Already up to date
|
||||
} else {
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name)))
|
||||
if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe)))
|
||||
return false; // No assembly to load
|
||||
}
|
||||
|
||||
|
@ -914,12 +914,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
|
||||
|
||||
// Generate sources from compressed files
|
||||
|
||||
Map<String, CompressedFile> compressed_files;
|
||||
Map<String, GodotCsCompressedFile> compressed_files;
|
||||
get_compressed_files(compressed_files);
|
||||
|
||||
for (Map<String, CompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) {
|
||||
for (Map<String, GodotCsCompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) {
|
||||
const String &file_name = E->key();
|
||||
const CompressedFile &file_data = E->value();
|
||||
const GodotCsCompressedFile &file_data = E->value();
|
||||
|
||||
String output_file = path_join(core_dir, file_name);
|
||||
|
||||
|
@ -63,16 +63,17 @@ bool GodotSharpEditor::_create_project_solution() {
|
||||
pr.step(TTR("Generating C# project..."));
|
||||
|
||||
String path = OS::get_singleton()->get_resource_dir();
|
||||
String name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (name.empty()) {
|
||||
name = "UnnamedProject";
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
if (appname_safe.empty()) {
|
||||
appname_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
String guid = CSharpProject::generate_game_project(path, name);
|
||||
String guid = CSharpProject::generate_game_project(path, appname_safe);
|
||||
|
||||
if (guid.length()) {
|
||||
|
||||
DotNetSolution solution(name);
|
||||
DotNetSolution solution(appname_safe);
|
||||
|
||||
if (!solution.set_path(path)) {
|
||||
show_error_dialog(TTR("Failed to create solution."));
|
||||
@ -81,12 +82,12 @@ bool GodotSharpEditor::_create_project_solution() {
|
||||
|
||||
DotNetSolution::ProjectInfo proj_info;
|
||||
proj_info.guid = guid;
|
||||
proj_info.relpath = name + ".csproj";
|
||||
proj_info.relpath = appname_safe + ".csproj";
|
||||
proj_info.configs.push_back("Debug");
|
||||
proj_info.configs.push_back("Release");
|
||||
proj_info.configs.push_back("Tools");
|
||||
|
||||
solution.add_new_project(name, proj_info);
|
||||
solution.add_new_project(appname_safe, proj_info);
|
||||
|
||||
Error sln_error = solution.save();
|
||||
|
||||
|
@ -106,13 +106,14 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
|
||||
Map<String, String> dependencies;
|
||||
|
||||
String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (project_dll_name.empty()) {
|
||||
project_dll_name = "UnnamedProject";
|
||||
String project_dll_name_safe = OS::get_singleton()->get_safe_dir_name(project_dll_name);
|
||||
if (project_dll_name_safe.empty()) {
|
||||
project_dll_name_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
|
||||
String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll");
|
||||
dependencies.insert(project_dll_name, project_dll_src_path);
|
||||
String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name_safe + ".dll");
|
||||
dependencies.insert(project_dll_name_safe, project_dll_src_path);
|
||||
|
||||
{
|
||||
MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
|
||||
@ -122,10 +123,10 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
|
||||
_GDMONO_SCOPE_DOMAIN_(export_domain);
|
||||
|
||||
GDMonoAssembly *scripts_assembly = NULL;
|
||||
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
|
||||
bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name_safe,
|
||||
project_dll_src_path, &scripts_assembly, /* refonly: */ true);
|
||||
|
||||
ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name);
|
||||
ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name_safe);
|
||||
ERR_FAIL_COND(!load_success);
|
||||
|
||||
Vector<String> search_dirs;
|
||||
|
@ -39,6 +39,10 @@
|
||||
#include "editor/editor_settings.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "utils/android_utils.h"
|
||||
#endif
|
||||
|
||||
namespace GodotSharpDirs {
|
||||
|
||||
String _get_expected_build_config() {
|
||||
@ -129,15 +133,16 @@ private:
|
||||
mono_solutions_dir = mono_user_dir.plus_file("solutions");
|
||||
build_logs_dir = mono_user_dir.plus_file("build_logs");
|
||||
|
||||
String name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (name.empty()) {
|
||||
name = "UnnamedProject";
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
if (appname_safe.empty()) {
|
||||
appname_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
|
||||
|
||||
sln_filepath = base_path.plus_file(name + ".sln");
|
||||
csproj_filepath = base_path.plus_file(name + ".csproj");
|
||||
sln_filepath = base_path.plus_file(appname_safe + ".sln");
|
||||
csproj_filepath = base_path.plus_file(appname_safe + ".csproj");
|
||||
#endif
|
||||
|
||||
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||
@ -150,7 +155,12 @@ private:
|
||||
|
||||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
|
||||
#if __ANDROID__
|
||||
data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
|
||||
#else
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
|
||||
@ -173,15 +183,21 @@ private:
|
||||
|
||||
#else
|
||||
|
||||
String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
|
||||
String data_dir_root = exe_dir.plus_file("data_" + appname);
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
String data_dir_root = exe_dir.plus_file("data_" + appname_safe);
|
||||
if (!DirAccess::exists(data_dir_root)) {
|
||||
data_dir_root = exe_dir.plus_file("data_Godot");
|
||||
}
|
||||
|
||||
String data_mono_root_dir = data_dir_root.plus_file("Mono");
|
||||
data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
|
||||
|
||||
#if __ANDROID__
|
||||
data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
|
||||
#else
|
||||
data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
|
||||
|
@ -56,6 +56,10 @@
|
||||
#include "main/main.h"
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
#include "android_mono_config.gen.h"
|
||||
#endif
|
||||
|
||||
#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. " \
|
||||
"This error is expected if you just upgraded to a newer Godot version. " \
|
||||
"Building the project will update the assembly to the correct version."
|
||||
@ -287,7 +291,11 @@ void GDMono::initialize() {
|
||||
gdmono_debug_init();
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
|
||||
#else
|
||||
mono_config_parse(NULL);
|
||||
#endif
|
||||
|
||||
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
|
||||
|
||||
@ -651,12 +659,13 @@ bool GDMono::_load_project_assembly() {
|
||||
if (project_assembly)
|
||||
return true;
|
||||
|
||||
String name = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
if (name.empty()) {
|
||||
name = "UnnamedProject";
|
||||
String appname = ProjectSettings::get_singleton()->get("application/config/name");
|
||||
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
|
||||
if (appname_safe.empty()) {
|
||||
appname_safe = "UnnamedProject";
|
||||
}
|
||||
|
||||
bool success = load_assembly(name, &project_assembly);
|
||||
bool success = load_assembly(appname_safe, &project_assembly);
|
||||
|
||||
if (success) {
|
||||
mono_assembly_set_main(project_assembly->get_assembly());
|
||||
|
@ -270,7 +270,18 @@ Error GDMonoAssembly::load(bool p_refonly) {
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_array(path);
|
||||
ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
|
||||
|
||||
String image_filename = ProjectSettings::get_singleton()->globalize_path(path);
|
||||
String image_filename;
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
if (path.begins_with("res://")) {
|
||||
image_filename = path.substr(6, path.length());
|
||||
} else {
|
||||
image_filename = ProjectSettings::get_singleton()->globalize_path(path);
|
||||
}
|
||||
#else
|
||||
// FIXME: globalize_path does not work on exported games
|
||||
image_filename = ProjectSettings::get_singleton()->globalize_path(path);
|
||||
#endif
|
||||
|
||||
MonoImageOpenStatus status = MONO_IMAGE_OK;
|
||||
|
||||
|
68
modules/mono/utils/android_utils.cpp
Normal file
68
modules/mono/utils/android_utils.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*************************************************************************/
|
||||
/* android_utils.cpp */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#include "android_utils.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include "platform/android/thread_jandroid.h"
|
||||
|
||||
namespace GDMonoUtils {
|
||||
namespace Android {
|
||||
|
||||
String get_app_native_lib_dir() {
|
||||
JNIEnv *env = ThreadAndroid::get_env();
|
||||
|
||||
jclass activityThreadClass = env->FindClass("android/app/ActivityThread");
|
||||
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
||||
jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread);
|
||||
jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
|
||||
jobject ctx = env->CallObjectMethod(activityThread, getApplication);
|
||||
|
||||
jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo);
|
||||
jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField);
|
||||
|
||||
String result;
|
||||
|
||||
const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
|
||||
if (nativeLibraryDir_utf8) {
|
||||
result.parse_utf8(nativeLibraryDir_utf8);
|
||||
env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Android
|
||||
} // namespace GDMonoUtils
|
||||
|
||||
#endif // __ANDROID__
|
48
modules/mono/utils/android_utils.h
Normal file
48
modules/mono/utils/android_utils.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*************************************************************************/
|
||||
/* android_utils.h */
|
||||
/*************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/*************************************************************************/
|
||||
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef ANDROID_UTILS_H
|
||||
#define ANDROID_UTILS_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include "core/ustring.h"
|
||||
|
||||
namespace GDMonoUtils {
|
||||
namespace Android {
|
||||
|
||||
String get_app_native_lib_dir();
|
||||
|
||||
} // namespace Android
|
||||
} // namespace GDMonoUtils
|
||||
|
||||
#endif // __ANDROID__
|
||||
|
||||
#endif // ANDROID_UTILS_H
|
Loading…
Reference in New Issue
Block a user