Skip to content

Commit

Permalink
Add support for Android NDK r21
Browse files Browse the repository at this point in the history
This is the essentially the NDK that the version of SpiderMonkey
supports. This makes a few changes to the SpiderMonkey source as well:

1. Pulls in a change from a more recent version so that the correct
   version of the clock functions are used.
2. Disables all attempts by SpiderMonkey to set the sysroot, Android
   platform includes, and the STL. Modern NDKs don't require these
   flags, they often just break mysteriously if not used perfectly.
   SpiderMonkey upstream is starting to use them less, but is still
   pretty far in the past. This makes everything about the Android build
   simpler.

In addition, some compilation errors for 32-bit machines are fixed.
  • Loading branch information
mrobinson committed Aug 17, 2023
1 parent 6993a07 commit 6ccb2bb
Show file tree
Hide file tree
Showing 12 changed files with 483 additions and 251 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ jobs:
shell: cmd
run: |
cargo test --verbose ${{ matrix.features }}
android:
runs-on: ubuntu-latest
- uses: actions/checkout@v3
- name: Install NDK
uses: nttld/setup-ndk@v1
with:
ndk-version: r21d
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
targets: armv7-linux-androideabi
- name: Build
run: |
./build-android cargo build --target="armv7-linux-androideabi"
Integrity:
runs-on: ubuntu-latest
steps:
Expand Down
128 changes: 128 additions & 0 deletions android-build
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import platform
import shutil
import subprocess
import sys

from typing import Dict, Optional


SUPPORTED_NDK_VERSION = '21'
API_LEVEL = '30'


def get_host_string() -> str:
os_type = platform.system().lower()
if os_type not in ["linux", "darwin"]:
raise Exception("Android cross builds are only supported on Linux and macOS.")

cpu_type = platform.machine().lower()
host_suffix = "unknown"
if cpu_type in ["i386", "i486", "i686", "i768", "x86"]:
host_suffix = "x86"
elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]:
host_suffix = "x86_64"
return os_type + "-" + host_suffix


def check_output(*args, **kwargs) -> str:
return subprocess.check_output(*args, **kwargs).decode("utf-8").strip()


def get_target_from_args() -> Optional[str]:
for arg in sys.argv:
if arg.startswith("--target="):
return arg.replace("--target=", "")
return None


def set_toolchain_binaries_in_env(toolchain_dir: str, env: Dict[str, str]):
cc = os.path.join(toolchain_dir, "bin", "clang")
cxx = os.path.join(toolchain_dir, "bin", "clang++")
ar = check_output([cc, "--print-prog-name=ar"])
objcopy = check_output([cc, "--print-prog-name=objcopy"])
ranlib = check_output([cc, "--print-prog-name=ranlib"])
strip = check_output([cc, "--print-prog-name=strip"])
yasm = check_output([cc, "--print-prog-name=yasm"])
host_cc = env.get('HOST_CC') or shutil.which("clang") or shutil.which("gcc")
host_cxx = env.get('HOST_CXX') or shutil.which("clang++") or shutil.which("g++")

assert(host_cc)
assert(host_cxx)

env["AR"] = ar
env["CC"] = cc
env["CPP"] = f"{cc} -E"
env["CXX"] = cxx
env["HOST_CC"] = host_cc
env["HOST_CXX"] = host_cxx
env["OBJCOPY"] = objcopy
env["PKG_CONFIG"] = "/usr/bin/pkg-config"
env["RANLIB"] = ranlib
env["STRIP"] = strip
env["YASM"] = yasm


def set_compiler_flags_in_env(toolchain_dir: str, env: Dict[str, str]):
target = get_target_from_args()
if target == "armv7-linux-androideabi":
ndk_target_name = "armv7a-linux-androideabi"
elif target == "aarch64-linux-android":
ndk_target_name = "aarch64-linux-androideabi"
elif target == "i686-linux-android" or target == "x86_64-linux-android":
ndk_target_name = target
else:
raise Exception(f"Unknown target {target}")

# Including the api level in the target name means that we will not have to
# define the Android API level when calling the compiler.
target_arg = f"--target={ndk_target_name}{API_LEVEL}"
stl_include_dir = os.path.join(toolchain_dir, "sysroot", "usr", "include", "c++", "v1")
stl_arg = f"-I{stl_include_dir}"

env["CFLAGS"] = target_arg
env["CPPFLAGS"] = stl_arg
env["CXXFLAGS"] = " ".join([target_arg, stl_arg])

def create_environment_for_build() -> Dict[str, str]:
env = os.environ.copy()
if "ANDROID_NDK_HOME" not in env:
raise Exception("Please set the ANDROID_NDK_HOME environment variable.")

ndk_home_dir = env["ANDROID_NDK_HOME"]

# Check if the NDK version is 21
if not os.path.isfile(os.path.join(ndk_home_dir, 'source.properties')):
raise Exception(
"ANDROID_NDK should have file `source.properties`.\n" +
"The environment variable ANDROID_NDK_HOME may be set at a wrong path."
)

with open(os.path.join(ndk_home_dir, 'source.properties'), encoding="utf8") as ndk_properties:
version_found = ndk_properties.readlines()[1].split(' = ')[1].split('.')[0]
if version_found != SUPPORTED_NDK_VERSION:
raise Exception(
"Servo and dependencies currently only support NDK version " +
f"{SUPPORTED_NDK_VERSION}. Found {version_found}"
)

# Add the toolchain to the path.
host_string = get_host_string()
toolchain_dir = os.path.join(ndk_home_dir, "toolchains", "llvm", "prebuilt", host_string)
env['PATH'] = os.pathsep.join([os.path.join(toolchain_dir, "bin"), env["PATH"]])

set_toolchain_binaries_in_env(toolchain_dir, env)
set_compiler_flags_in_env(toolchain_dir, env)

# This environment variable is only used by the mozjs build.
env["ANDROID_API_LEVEL"] = API_LEVEL

return env

if __name__ == "__main__":
subprocess.run(sys.argv[1:], env=create_environment_for_build())

This file was deleted.

90 changes: 90 additions & 0 deletions mozjs/etc/patches/0026-Add-support-for-NDKr21.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4
index d4967864d..30eba4ff5 100644
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -8,12 +8,14 @@ AC_DEFUN([MOZ_ANDROID_NDK],
case "$target" in
*-android*|*-linuxandroid*)
dnl $android_* will be set for us by Python configure.
- directory_include_args="-isystem $android_system -isystem $android_sysroot/usr/include"
+ dnl
+ dnl See the mozjs note android-nkd.configure for why these lines are commented out.
+ dnl directory_include_args="-isystem $android_system -isystem $android_sysroot/usr/include"

# clang will do any number of interesting things with host tools unless we tell
# it to use the NDK tools.
- extra_opts="--gcc-toolchain=$(dirname $(dirname $TOOLCHAIN_PREFIX))"
- CPPFLAGS="$extra_opts -D__ANDROID_API__=$android_version $CPPFLAGS"
+ dnl extra_opts="--gcc-toolchain=$(dirname $(dirname $TOOLCHAIN_PREFIX))"
+ dnl CPPFLAGS="$extra_opts -D__ANDROID_API__=$android_version $CPPFLAGS"
ASFLAGS="$extra_opts $ASFLAGS"
LDFLAGS="$extra_opts $LDFLAGS"

diff --git a/build/moz.configure/android-ndk.configure b/build/moz.configure/android-ndk.configure
index 7448743a4..6c60d9713 100644
--- a/build/moz.configure/android-ndk.configure
+++ b/build/moz.configure/android-ndk.configure
@@ -380,6 +380,20 @@ def extra_toolchain_flags(
):
if not android_sysroot:
return []
+
+ # Note for mozjs:
+ # Normally Gecko passes all the arguments below to the compiler. The truth
+ # is though that modern NDKs do not need them. They are all handled by the
+ # special NDK-provided compiler wrappers [1]. The mozjs build expects to
+ # use these wrappers and any small mistake in the way the compiler is
+ # invoked will cause any number of subtle build errors. We are simple
+ # people in mozjs land. Let's not even try to get this right and rely on
+ # the NDK to properly invoke the compiler.
+ #
+ # 1. https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#Clang
+ if android_sysroot:
+ return []
+
flags = [
"-isystem",
android_system,
diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure
index ec482be95..42873cf6e 100755
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1692,11 +1692,13 @@ def linker_ldflags(
sysroot.path, multiarch_dir, sysroot.stdcxx_version
)
)
- if android_platform:
- flags.append("-L{}/usr/lib".format(android_platform))
- flags.append("-Wl,--rpath-link={}/usr/lib".format(android_platform))
- flags.append("--sysroot")
- flags.append(android_platform)
+ # mozjs: The NDK compiler / linker wrappers take care of properly setting these variables.
+ # See the mozjs note in android-ndk.configure.
+ # if android_platform:
+ # flags.append("-L{}/usr/lib".format(android_platform))
+ # flags.append("-Wl,--rpath-link={}/usr/lib".format(android_platform))
+ # flags.append("--sysroot")
+ # flags.append(android_platform)
return flags


diff --git a/mozglue/misc/ConditionVariable_posix.cpp b/mozglue/misc/ConditionVariable_posix.cpp
index fcc404713..6f6076db7 100644
--- a/mozglue/misc/ConditionVariable_posix.cpp
+++ b/mozglue/misc/ConditionVariable_posix.cpp
@@ -20,10 +20,13 @@ using mozilla::TimeDuration;

static const long NanoSecPerSec = 1000000000;

-// Android 32-bit & macOS 10.12 has the clock functions, but not
+// mozjs patch note: This following patched lines come from the upstream version
+// of SpiderMonkey:
+//
+// Android 4.4 or earlier & macOS 10.12 has the clock functions, but not
// pthread_condattr_setclock.
#if defined(HAVE_CLOCK_MONOTONIC) && \
- !(defined(__ANDROID__) && !defined(__LP64__)) && !defined(__APPLE__)
+ !(defined(__ANDROID__) && __ANDROID_API__ < 21) && !defined(__APPLE__)
# define CV_USE_CLOCK_API
#endif

39 changes: 16 additions & 23 deletions mozjs/makefile.cargo
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ifneq ($(HOST),$(TARGET))
ifeq (armv7-linux-androideabi,$(TARGET))
# Reset TARGET variable because armv7 target name used by Rust is not
# the same as the target name needed for the CXX toolchain.
TARGET = arm-linux-androideabi
TARGET = armv7a-linux-androideabi
CONFIGURE_FLAGS += \
--with-arch=armv7-a \
--with-fpu=neon \
Expand All @@ -56,35 +56,28 @@ ifneq ($(HOST),$(TARGET))
endif

ifeq (android,$(findstring android,$(TARGET)))
ifneq (,$(STLPORT_CPPFLAGS))
CONFIGURE_FLAGS += STLPORT_CPPFLAGS="$(STLPORT_CPPFLAGS)"
endif
ifneq (,$(ANDROID_NDK))
CONFIGURE_FLAGS += --with-android-ndk=$(ANDROID_NDK)
endif
ifneq (,$(ANDROID_NDK_VERSION))
CONFIGURE_FLAGS += --with-android-ndk-version=$(ANDROID_NDK_VERSION)
endif
ifneq (,$(ANDROID_VERSION))
CONFIGURE_FLAGS += --with-android-version=$(ANDROID_VERSION)
endif
ifneq (,$(ANDROID_PLATFORM_DIR))
CONFIGURE_FLAGS += --with-android-platform=$(ANDROID_PLATFORM_DIR)
endif
ifneq (,$(ANDROID_TOOLCHAIN_DIR))
CONFIGURE_FLAGS += --with-android-toolchain=$(ANDROID_TOOLCHAIN_DIR)
endif
ifneq (,$(ANDROID_CLANG))
CONFIGURE_FLAGS += --with-android-clang=$(ANDROID_CLANG)
endif
# The target must be suffixed by the Android API level to avoid having
# to define it in the compiler arguments.
TARGET := $(TARGET)$(ANDROID_API_LEVEL)

# STLPORT_CPPFLAGS and the toolchain directory are set here just to
# make the SpiderMonkey build system happy. rust-mozjs patches have
# disabled the actual use of these values, as the NDKs compiler should
# take care of setting these flags internally.
CONFIGURE_FLAGS += \
--without-system-zlib \
--with-android-version=$(ANDROID_API_LEVEL) \
--with-android-platform="$(ANDROID_NDK_HOME)/sysroot" \
STLPORT_CPPFLAGS=" " \
$(NULL)
endif

ifeq ($(WINDOWS),)
CC ?= $(TARGET)-gcc
CPP ?= $(TARGET)-gcc -E
CXX ?= $(TARGET)-g++
AR ?= $(TARGET)-ar
CONFIGURE_FLAGS += --target=$(TARGET) --disable-gold
CONFIGURE_FLAGS += --target=$(TARGET)
endif

else
Expand Down
8 changes: 5 additions & 3 deletions mozjs/mozjs/build/autoconf/android.m4
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ AC_DEFUN([MOZ_ANDROID_NDK],
case "$target" in
*-android*|*-linuxandroid*)
dnl $android_* will be set for us by Python configure.
directory_include_args="-isystem $android_system -isystem $android_sysroot/usr/include"
dnl
dnl See the mozjs note android-nkd.configure for why these lines are commented out.
dnl directory_include_args="-isystem $android_system -isystem $android_sysroot/usr/include"
# clang will do any number of interesting things with host tools unless we tell
# it to use the NDK tools.
extra_opts="--gcc-toolchain=$(dirname $(dirname $TOOLCHAIN_PREFIX))"
CPPFLAGS="$extra_opts -D__ANDROID_API__=$android_version $CPPFLAGS"
dnl extra_opts="--gcc-toolchain=$(dirname $(dirname $TOOLCHAIN_PREFIX))"
dnl CPPFLAGS="$extra_opts -D__ANDROID_API__=$android_version $CPPFLAGS"
ASFLAGS="$extra_opts $ASFLAGS"
LDFLAGS="$extra_opts $LDFLAGS"
Expand Down
16 changes: 14 additions & 2 deletions mozjs/mozjs/build/moz.configure/android-ndk.configure
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,25 @@ def extra_toolchain_flags(
):
if not android_sysroot:
return []

# Note for mozjs:
# Normally Gecko passes all the arguments below to the compiler. The truth
# is though that modern NDKs do not need them. They are all handled by the
# special NDK-provided compiler wrappers [1]. The mozjs build expects to
# use these wrappers and any small mistake in the way the compiler is
# invoked will cause any number of subtle build errors. We are simple
# people in mozjs land. Let's not even try to get this right and rely on
# the NDK to properly invoke the compiler.
#
# 1. https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#Clang
if android_sysroot:
return []

flags = [
"-isystem",
android_system,
"-isystem",
os.path.join(android_sysroot, "usr", "include"),
"--sysroot",
android_sysroot,
"--gcc-toolchain={}".format(toolchain_dir),
"-D__ANDROID_API__=%d" % android_version,
]
Expand Down
12 changes: 7 additions & 5 deletions mozjs/mozjs/build/moz.configure/toolchain.configure
Original file line number Diff line number Diff line change
Expand Up @@ -1692,11 +1692,13 @@ def linker_ldflags(
sysroot.path, multiarch_dir, sysroot.stdcxx_version
)
)
if android_platform:
flags.append("-L{}/usr/lib".format(android_platform))
flags.append("-Wl,--rpath-link={}/usr/lib".format(android_platform))
flags.append("--sysroot")
flags.append(android_platform)
# mozjs: The NDK compiler / linker wrappers take care of properly setting these variables.
# See the mozjs note in android-ndk.configure.
# if android_platform:
# flags.append("-L{}/usr/lib".format(android_platform))
# flags.append("-Wl,--rpath-link={}/usr/lib".format(android_platform))
# flags.append("--sysroot")
# flags.append(android_platform)
return flags


Expand Down
Loading

0 comments on commit 6ccb2bb

Please sign in to comment.