Skip to content

Commit

Permalink
build: rewritten the Android build system
Browse files Browse the repository at this point in the history
Completely rewritten the Android build system using Python

Co-Authored-By: 东灯 <43312495+Lampese@users.noreply.github.com>
Co-Authored-By: Feng Yu <F3n67u@outlook.com>
PR-URL: nodejs/node#44207
Refs: nodejs/node#36287
Reviewed-By: Feng Yu <F3n67u@outlook.com>
Reviewed-By: Christian Clauss <cclauss@me.com>
  • Loading branch information
3 people authored and guangwong committed Jan 3, 2023
1 parent 71144b8 commit 5cfab0c
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 83 deletions.
118 changes: 35 additions & 83 deletions android-configure
Original file line number Diff line number Diff line change
@@ -1,83 +1,35 @@
#!/bin/bash

# In order to cross-compile node for Android using NDK, run:
# source android-configure <path_to_ndk> [arch]
#
# By running android-configure with source, will allow environment variables to
# be persistent in current session. This is useful for installing native node
# modules with npm. Also, don't forget to set the arch in npm config using
# 'npm config set arch=<arch>'

if [ $# -ne 3 ]; then
echo "$0 should have 3 parameters: ndk_path, target_arch and sdk_version"
return 1
fi

NDK_PATH=$1
ARCH="$2"
ANDROID_SDK_VERSION=$3

if [ $ANDROID_SDK_VERSION -lt 24 ]; then
echo "$ANDROID_SDK_VERSION should equal or later than 24 (Android 7.0)"
fi

case $ARCH in
arm)
DEST_CPU="arm"
TOOLCHAIN_NAME="armv7a-linux-androideabi"
;;
x86)
DEST_CPU="ia32"
TOOLCHAIN_NAME="i686-linux-android"
;;
x86_64)
DEST_CPU="x64"
TOOLCHAIN_NAME="x86_64-linux-android"
ARCH="x64"
;;
arm64|aarch64)
DEST_CPU="arm64"
TOOLCHAIN_NAME="aarch64-linux-android"
ARCH="arm64"
;;
*)
echo "Unsupported architecture provided: $ARCH"
return 1
;;
esac

HOST_OS="linux"
HOST_ARCH="x86_64"
export CC_host=$(command -v gcc)
export CXX_host=$(command -v g++)

host_gcc_version=$($CC_host --version | grep gcc | awk '{print $NF}')
major=$(echo $host_gcc_version | awk -F . '{print $1}')
minor=$(echo $host_gcc_version | awk -F . '{print $2}')
if [ -z $major ] || [ -z $minor ] || [ $major -lt 6 ] || ( [ $major -eq 6 ] && [ $minor -lt 3 ] ); then
echo "host gcc $host_gcc_version is too old, need gcc 6.3.0"
return 1
fi

SUFFIX="$TOOLCHAIN_NAME$ANDROID_SDK_VERSION"
TOOLCHAIN=$NDK_PATH/toolchains/llvm/prebuilt/$HOST_OS-$HOST_ARCH

export PATH=$TOOLCHAIN/bin:$PATH
export CC=$TOOLCHAIN/bin/$SUFFIX-clang
export CXX=$TOOLCHAIN/bin/$SUFFIX-clang++


GYP_DEFINES="target_arch=$ARCH"
GYP_DEFINES+=" v8_target_arch=$ARCH"
GYP_DEFINES+=" android_target_arch=$ARCH"
GYP_DEFINES+=" host_os=$HOST_OS OS=android"
export GYP_DEFINES

if [ -f "configure" ]; then
./configure \
--dest-cpu=$DEST_CPU \
--dest-os=android \
--without-snapshot \
--openssl-no-asm \
--cross-compiling
fi
#!/bin/sh

# Locate an acceptable Python interpreter and then re-execute the script.
# Note that the mix of single and double quotes is intentional,
# as is the fact that the ] goes on a new line.
_=[ 'exec' '/bin/sh' '-c' '''
command -v python3.10 >/dev/null && exec python3.10 "$0" "$@"
command -v python3.9 >/dev/null && exec python3.9 "$0" "$@"
command -v python3.8 >/dev/null && exec python3.8 "$0" "$@"
command -v python3.7 >/dev/null && exec python3.7 "$0" "$@"
command -v python3.6 >/dev/null && exec python3.6 "$0" "$@"
command -v python3 >/dev/null && exec python3 "$0" "$@"
exec python "$0" "$@"
''' "$0" "$@"
]
del _

import sys
try:
from shutil import which
except ImportError:
from distutils.spawn import find_executable as which

print('Node.js android configure: Found Python {}.{}.{}...'.format(*sys.version_info))
acceptable_pythons = ((3, 10), (3, 9), (3, 8), (3, 7), (3, 6))
if sys.version_info[:2] in acceptable_pythons:
import android_configure
else:
python_cmds = ['python{}.{}'.format(*vers) for vers in acceptable_pythons]
sys.stderr.write('Please use {}.\n'.format(' or '.join(python_cmds)))
for python_cmd in python_cmds:
python_cmd_path = which(python_cmd)
if python_cmd_path and 'pyenv/shims' not in python_cmd_path:
sys.stderr.write('\t{} {}\n'.format(python_cmd_path, ' '.join(sys.argv[:1])))
sys.exit(1)
26 changes: 26 additions & 0 deletions android-patches/trap-handler.h.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--- trap-handler.h 2022-08-11 09:01:23.384000000 +0800
+++ fixed-trap-handler.h 2022-08-11 09:09:15.352000000 +0800
@@ -17,23 +17,7 @@
namespace internal {
namespace trap_handler {

-// X64 on Linux, Windows, MacOS, FreeBSD.
-#if V8_HOST_ARCH_X64 && V8_TARGET_ARCH_X64 && \
- ((V8_OS_LINUX && !V8_OS_ANDROID) || V8_OS_WIN || V8_OS_DARWIN || \
- V8_OS_FREEBSD)
-#define V8_TRAP_HANDLER_SUPPORTED true
-// Arm64 (non-simulator) on Mac.
-#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_ARM64 && V8_OS_DARWIN
-#define V8_TRAP_HANDLER_SUPPORTED true
-// Arm64 simulator on x64 on Linux, Mac, or Windows.
-#elif V8_TARGET_ARCH_ARM64 && V8_HOST_ARCH_X64 && \
- (V8_OS_LINUX || V8_OS_DARWIN)
-#define V8_TRAP_HANDLER_VIA_SIMULATOR
-#define V8_TRAP_HANDLER_SUPPORTED true
-// Everything else is unsupported.
-#else
#define V8_TRAP_HANDLER_SUPPORTED false
-#endif

// Setup for shared library export.
#if defined(BUILDING_V8_SHARED) && defined(V8_OS_WIN)
76 changes: 76 additions & 0 deletions android_configure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import platform
import sys
import os

# TODO: In next version, it will be a JSON file listing all the patches, and then it will iterate through to apply them.
def patch_android():
print("- Patches List -")
print("[1] [deps/v8/src/trap-handler/trap-handler.h] related to https://github.com/nodejs/node/issues/36287")
if platform.system() == "Linux":
os.system('patch -f ./deps/v8/src/trap-handler/trap-handler.h < ./android-patches/trap-handler.h.patch')
print("\033[92mInfo: \033[0m" + "Tried to patch.")

if platform.system() == "Windows":
print("android-configure is not supported on Windows yet.")
sys.exit(1)

if len(sys.argv) == 2 and sys.argv[1] == "patch":
patch_android()
sys.exit(0)

if len(sys.argv) != 4:
print("Usage: ./android-configure [patch] <path to the Android NDK> <Android SDK version> <target architecture>")
sys.exit(1)

if not os.path.exists(sys.argv[1]) or not os.listdir(sys.argv[1]):
print("\033[91mError: \033[0m" + "Invalid path to the Android NDK")
sys.exit(1)

if int(sys.argv[2]) < 24:
print("\033[91mError: \033[0m" + "Android SDK version must be at least 24 (Android 7.0)")
sys.exit(1)

android_ndk_path = sys.argv[1]
android_sdk_version = sys.argv[2]
arch = sys.argv[3]

if arch == "arm":
DEST_CPU = "arm"
TOOLCHAIN_PREFIX = "armv7a-linux-androideabi"
elif arch in ("aarch64", "arm64"):
DEST_CPU = "arm64"
TOOLCHAIN_PREFIX = "aarch64-linux-android"
arch = "arm64"
elif arch == "x86":
DEST_CPU = "ia32"
TOOLCHAIN_PREFIX = "i686-linux-android"
elif arch == "x86_64":
DEST_CPU = "x64"
TOOLCHAIN_PREFIX = "x86_64-linux-android"
arch = "x64"
else:
print("\033[91mError: \033[0m" + "Invalid target architecture, must be one of: arm, arm64, aarch64, x86, x86_64")
sys.exit(1)

print("\033[92mInfo: \033[0m" + "Configuring for " + DEST_CPU + "...")

if platform.system() == "Darwin":
host_os = "darwin"
toolchain_path = android_ndk_path + "/toolchains/llvm/prebuilt/darwin-x86_64"

elif platform.system() == "Linux":
host_os = "linux"
toolchain_path = android_ndk_path + "/toolchains/llvm/prebuilt/linux-x86_64"

os.environ['PATH'] += os.pathsep + toolchain_path + "/bin"
os.environ['CC'] = toolchain_path + "/bin/" + TOOLCHAIN_PREFIX + android_sdk_version + "-" + "clang"
os.environ['CXX'] = toolchain_path + "/bin/" + TOOLCHAIN_PREFIX + android_sdk_version + "-" + "clang++"

GYP_DEFINES = "target_arch=" + arch
GYP_DEFINES += " v8_target_arch=" + arch
GYP_DEFINES += " android_target_arch=" + arch
GYP_DEFINES += " host_os=" + host_os + " OS=android"
os.environ['GYP_DEFINES'] = GYP_DEFINES

if os.path.exists("./configure"):
os.system("./configure --dest-cpu=" + DEST_CPU + " --dest-os=android --openssl-no-asm --cross-compiling")

0 comments on commit 5cfab0c

Please sign in to comment.