Skip to content

Godot-cpp compile for Android #328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,16 @@ $ cd godot-cpp
$ scons platform=<your platform> generate_bindings=yes
$ cd ..
```
For android:
Download the latest [Android NDK](https://developer.android.com/ndk/downloads) from the official website and set NDK path.
```
$ scons platform=android generate_bindings=yes ANDROID_NDK_ROOT="/PATH-TO-ANDROID-NDK/" android_arch=< >
```
`android_arch` can be `armv7, arm64v8, x86, x86_64`.
`ANDROID_NDK_ROOT` can also be set in the environment variables of your computer if you do not want to include it in your Scons call.


> Replace `<your platform>` with either `windows`, `linux` or `osx`.
> Replace `<your platform>` with either `windows`, `linux`, `osx` or `android`.

> Include `use_llvm=yes` for using clang++

Expand Down Expand Up @@ -189,6 +197,19 @@ $ link /nologo /dll /out:bin\libtest.dll /implib:bin\libsimple.lib src\init.obj
*macOS*
For OSX you need to find out what compiler flags need to be used.

*Android*
```
$ cd SimpleLibrary
$ aarch64-linux-android29-clang -fPIC -o src/init.os -c src/init.cpp -g -O3 -std=c++14 -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Igodot-cpp/godot_headers
$ aarch64-linux-android29-clang -o bin/libtest.so -shared src/init.os -Lgodot-cpp/bin -l<name of the godot-cpp>
```
> use `armv7a-linux-androideabi29-clang` for 32 bit armeabi-v7a library

> This creates the file `libtest.so` in your `SimpleLibrary/bin` directory.

> You will need to replace `<name of the godot-cpp>` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library)


### Creating `.gdnlib` and `.gdns` files
follow [godot_header/README.md](https://github.com/GodotNativeTools/godot_headers/blob/master/README.md#how-do-i-use-native-scripts-from-the-editor) to create the `.gdns`

Expand Down
121 changes: 115 additions & 6 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@
import os
import sys

# Workaround for MinGW. See:
# http://www.scons.org/wiki/LongCmdLinesOnWin32
if (os.name=="nt"):
import subprocess

def mySubProcess(cmdline,env):
#print "SPAWNED : " + cmdline
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, startupinfo=startupinfo, shell = False, env = env)
data, err = proc.communicate()
rv = proc.wait()
if rv:
print("=====")
print(err.decode("utf-8"))
print("=====")
return rv

def mySpawn(sh, escape, cmd, args, env):

newargs = ' '.join(args[1:])
cmdline = cmd + " " + newargs

rv=0
if len(cmdline) > 32000 and cmd.endswith("ar") :
cmdline = cmd + " " + args[1] + " " + args[2] + " "
for i in range(3,len(args)) :
rv = mySubProcess( cmdline + args[i], env )
if rv :
break
else:
rv = mySubProcess( cmdline, env )

return rv

def add_sources(sources, dir, extension):
for f in os.listdir(dir):
Expand All @@ -29,7 +64,7 @@ opts.Add(EnumVariable(
'platform',
'Target platform',
host_platform,
allowed_values=('linux', 'osx', 'windows'),
allowed_values=('linux', 'osx', 'windows', 'android'),
ignorecase=2
))
opts.Add(EnumVariable(
Expand Down Expand Up @@ -73,16 +108,33 @@ opts.Add(BoolVariable(
'Generate GDNative API bindings',
False
))
opts.Add(EnumVariable(
'android_arch',
'Target Android architecture',
'armv7',
['armv7','arm64v8','x86','x86_64']
))
opts.Add(
'android_api_level',
'Target Android API level',
'18' if ARGUMENTS.get("android_arch", 'armv7') in ['armv7', 'x86'] else '21'
)
opts.Add(
'ANDROID_NDK_ROOT',
'Path to your Android NDK installation. By default, uses ANDROID_NDK_ROOT from your defined environment variables.',
os.environ.get("ANDROID_NDK_ROOT", None)
)

env = Environment()
env = Environment(ENV = os.environ)
opts.Update(env)
Help(opts.GenerateHelpText(env))

is64 = sys.maxsize > 2**32
if (
env['TARGET_ARCH'] == 'amd64' or
env['TARGET_ARCH'] == 'emt64' or
env['TARGET_ARCH'] == 'x86_64'
env['TARGET_ARCH'] == 'x86_64' or
env['TARGET_ARCH'] == 'arm64-v8a'
):
is64 = True

Expand All @@ -92,7 +144,7 @@ if env['bits'] == 'default':
# This makes sure to keep the session environment variables on Windows.
# This way, you can run SCons in a Visual Studio 2017 prompt and it will find
# all the required tools
if host_platform == 'windows':
if host_platform == 'windows' and env['platform'] != 'android':
if env['bits'] == '64':
env = Environment(TARGET_ARCH='amd64')
elif env['bits'] == '32':
Expand Down Expand Up @@ -173,6 +225,62 @@ elif env['platform'] == 'windows':
'-static-libgcc',
'-static-libstdc++',
])
elif env['platform'] == 'android':
if host_platform == 'windows':
env = env.Clone(tools=['mingw'])
env["SPAWN"] = mySpawn

# Verify NDK root
if not 'ANDROID_NDK_ROOT' in env:
raise ValueError("To build for Android, ANDROID_NDK_ROOT must be defined. Please set ANDROID_NDK_ROOT to the root folder of your Android NDK installation.")

# Validate API level
api_level = int(env['android_api_level'])
if env['android_arch'] in ['x86_64', 'arm64v8'] and api_level < 21:
print("WARN: 64-bit Android architectures require an API level of at least 21; setting android_api_level=21")
env['android_api_level'] = '21'
api_level = 21

# Setup toolchain
toolchain = env['ANDROID_NDK_ROOT'] + "/toolchains/llvm/prebuilt/"
if host_platform == "windows":
toolchain += "windows"
import platform as pltfm
if pltfm.machine().endswith("64"):
toolchain += "-x86_64"
elif host_platform == "linux":
toolchain += "linux-x86_64"
elif host_platform == "osx":
toolchain += "darwin-x86_64"
env.PrependENVPath('PATH', toolchain + "/bin") # This does nothing half of the time, but we'll put it here anyways

# Get architecture info
arch_info_table = {
"armv7" : {
"march":"armv7-a", "target":"armv7a-linux-androideabi", "tool_path":"arm-linux-androideabi", "compiler_path":"armv7a-linux-androideabi",
"ccflags" : ['-mfpu=neon']
},
"arm64v8" : {
"march":"armv8-a", "target":"aarch64-linux-android", "tool_path":"aarch64-linux-android", "compiler_path":"aarch64-linux-android",
"ccflags" : []
},
"x86" : {
"march":"i686", "target":"i686-linux-android", "tool_path":"i686-linux-android", "compiler_path":"i686-linux-android",
"ccflags" : ['-mstackrealign']
},
"x86_64" : {"march":"x86-64", "target":"x86_64-linux-android", "tool_path":"x86_64-linux-android", "compiler_path":"x86_64-linux-android",
"ccflags" : []
}
}
arch_info = arch_info_table[env['android_arch']]

# Setup tools
env['CC'] = toolchain + "/bin/clang"
env['CXX'] = toolchain + "/bin/clang++"
env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar"

env.Append(CCFLAGS=['--target=' + arch_info['target'] + env['android_api_level'], '-march=' + arch_info['march'], '-fPIC'])#, '-fPIE', '-fno-addrsig', '-Oz'])
env.Append(CCFLAGS=arch_info['ccflags'])

env.Append(CPPPATH=[
'.',
Expand Down Expand Up @@ -202,10 +310,11 @@ add_sources(sources, 'src/core', 'cpp')
add_sources(sources, 'src/gen', 'cpp')

library = env.StaticLibrary(
target='bin/' + 'libgodot-cpp.{}.{}.{}'.format(
target='bin/' + 'libgodot-cpp.{}.{}.{}{}'.format(
env['platform'],
env['target'],
env['bits'],
env['bits'] if env['platform'] != 'android' else env['android_arch'],
env['LIBSUFFIX']
), source=sources
)
Default(library)