aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2023-05-02 16:04:29 -0700
committerDan Albert <danalbert@google.com>2023-05-05 20:05:58 +0000
commit437b7b75b9ba1ecb825e37c8514e28def7ceb7bc (patch)
treeacbf21bbeb7690e55f6a743bd9cb52f5fe67dae6
parent881909920a07d8474febd1fca1c2fe518a9d90bd (diff)
downloadndk-437b7b75b9ba1ecb825e37c8514e28def7ceb7bc.tar.gz
Use the libc++ from the toolchain.
Bug: https://github.com/android/ndk/issues/1530 Test: ./checkbuild.py && ./run_tests.py Change-Id: Id8f99069e1c0bbe532626ebd99054689872caa72
-rw-r--r--docs/changelogs/Changelog-r26.md6
-rwxr-xr-xndk/checkbuild.py297
2 files changed, 142 insertions, 161 deletions
diff --git a/docs/changelogs/Changelog-r26.md b/docs/changelogs/Changelog-r26.md
index 2822cfa64..25200f6ae 100644
--- a/docs/changelogs/Changelog-r26.md
+++ b/docs/changelogs/Changelog-r26.md
@@ -15,13 +15,15 @@ directly, see the [build system maintainers guide].
* KitKat (APIs 19 and 20) is no longer supported. The minimum OS supported by
the NDK is Lollipop (API level 21). See [Issue 1751] for details.
+* libc++ has been updated. The NDK's libc++ now comes directly from our LLVM
+ toolchain, so every future LLVM update is also a libc++ update. Future
+ changelogs will not explicitly mention libc++ updates.
[Issue 1751]: https://github.com/android/ndk/issues/1751
## Changes
* Updated LLVM to clang-r487747b, based on LLVM 17 development.
- * This does **not** include a libc++ update. That work is still ongoing.
* Clang now treats `-Wimplicit-function-declaration` as an error rather than a
warning in C11 and newer. Clang's default C standard is 17, so this is a
change in default behavior compared to older versions of Clang, but is the
@@ -36,6 +38,7 @@ directly, see the [build system maintainers guide].
See https://reviews.llvm.org/D122983 for more details.
* [Issue 1298]: Fixed seccomp error with ASan on x86_64 devices.
+ * [Issue 1530]: Updated libc++ to match LLVM version.
* [Issue 1677]: Fixed Clang crash in optimizer.
* [Issue 1834]: Fixed Clang crash during SVE conversions.
* [Issue 1860]: Fixed miscompilation affecting armv7.
@@ -67,6 +70,7 @@ directly, see the [build system maintainers guide].
[Issue 837]: https://github.com/android/ndk/issues/837
[Issue 1298]: https://github.com/android/ndk/issues/1298
[Issue 1400]: https://github.com/android/ndk/issues/1400
+[Issue 1530]: https://github.com/android/ndk/issues/1530
[Issue 1677]: https://github.com/android/ndk/issues/1677
[Issue 1803]: https://github.com/android/ndk/issues/1803
[Issue 1834]: https://github.com/android/ndk/issues/1834
diff --git a/ndk/checkbuild.py b/ndk/checkbuild.py
index 93b9a7fa9..5db545717 100755
--- a/ndk/checkbuild.py
+++ b/ndk/checkbuild.py
@@ -28,7 +28,6 @@ import json
import logging
import multiprocessing
import os
-import pipes
import re
import shutil
import site
@@ -68,6 +67,7 @@ import ndk.test.spec
import ndk.timer
import ndk.ui
import ndk.workqueue
+from ndk.abis import ALL_ABIS, Abi
from ndk.crtobjectbuilder import CrtObjectBuilder
from ndk.hosts import Host
from ndk.paths import ANDROID_DIR, NDK_DIR
@@ -525,10 +525,13 @@ class Clang(ndk.builds.Module):
)
)
- # Also remove the other libraries that we installed, but they were only
- # installed on Linux.
+ # Remove duplicate install locations of some runtime libraries. The toolchain
+ # artifacts install these to a location the driver doesn't search. We relocate
+ # these as necessary (either in this class or in Toolchain), so clean up the
+ # excess. The Android runtimes are only packaged in the Linux toolchain.
if self.host == Host.Linux:
shutil.rmtree(install_path / "runtimes_ndk_cxx")
+ shutil.rmtree(install_path / "android_libc++")
# Remove CMake package files that should not be exposed.
# For some reason the LLVM install includes CMake modules that expose
@@ -893,71 +896,6 @@ def make_linker_script(path: Path, libs: List[str]) -> None:
@register
-class Libcxx(ndk.builds.Module):
- name = "libc++"
- src = ANDROID_DIR / "toolchain/llvm-project/libcxx"
- install_path = Path("sources/cxx-stl/llvm-libc++")
- notice = src / "LICENSE.TXT"
- notice_group = ndk.builds.NoticeGroup.TOOLCHAIN
- deps = {
- "base-toolchain",
- "ndk-build",
- "ndk-build-shortcut",
- }
-
- @property
- def obj_out(self) -> Path:
- return self.out_dir / "libcxx" / "obj"
-
- @property
- def lib_out(self) -> Path:
- return self.out_dir / "libcxx" / "libs"
-
- def build(self) -> None:
- ndk_build = self.get_dep("ndk-build").get_build_host_install() / "ndk-build"
-
- android_mk = self.src / "Android.mk"
- application_mk = self.src / "Application.mk"
-
- build_cmd = [
- "bash",
- str(ndk_build),
- f"-j{multiprocessing.cpu_count()}",
- "V=1",
- # Since nothing in this build depends on libc++_static, we need to
- # name it to force it to build.
- "APP_MODULES=c++_shared c++_static",
- # Tell ndk-build where all of our makefiles are and where outputs
- # should go. The defaults in ndk-build are only valid if we have a
- # typical ndk-build layout with a jni/{Android,Application}.mk.
- "NDK_PROJECT_PATH=null",
- f"APP_BUILD_SCRIPT={android_mk}",
- f"NDK_APPLICATION_MK={application_mk}",
- f"NDK_OUT={self.obj_out}",
- f"NDK_LIBS_OUT={self.lib_out}",
- # Make sure we don't pick up a cached copy.
- "LIBCXX_FORCE_REBUILD=true",
- ]
-
- print("Running: " + " ".join([pipes.quote(arg) for arg in build_cmd]))
- subprocess.check_call(build_cmd)
-
- def install(self) -> None:
- """Installs headers and makefiles.
-
- The libraries are installed separately, by the Toolchain module."""
- install_root = self.get_install_path()
-
- if install_root.exists():
- shutil.rmtree(install_root)
- install_root.mkdir(parents=True)
-
- shutil.copy2(self.src / "Android.mk", install_root)
- # TODO: Use the includes from sysroot.
- shutil.copytree(self.src / "include", install_root / "include")
-
-
-@register
class LibShaderc(ndk.builds.Module):
name = "libshaderc"
install_path = Path("sources/third_party/shaderc")
@@ -1331,15 +1269,13 @@ def write_clang_wrapper(
@register
-class BaseToolchain(ndk.builds.Module):
- """The subset of the toolchain needed to build other toolchain components.
+class Toolchain(ndk.builds.Module):
+ """The LLVM toolchain.
- libc++ is built using this toolchain, and the full toolchain requires
- libc++. The toolchain is split into BaseToolchain and Toolchain to break
- the cyclic dependency.
+ The toolchain includes Clang, LLD, libc++, and LLVM's binutils.
"""
- name = "base-toolchain"
+ name = "toolchain"
# This is installed to the Clang location to avoid migration pain.
install_path = Path("toolchains/llvm/prebuilt/{host}")
notice_group = ndk.builds.NoticeGroup.TOOLCHAIN
@@ -1358,6 +1294,31 @@ class BaseToolchain(ndk.builds.Module):
yield from Sysroot().notices
yield from SystemStl().notices
+ @property
+ def sysroot_install_path(self) -> Path:
+ return self.get_install_path() / "sysroot"
+
+ def toolchain_libcxx_path_for(self, abi: Abi) -> Path:
+ """Returns the path to the toolchain's NDK libc++ artifacts.
+
+ The toolchain artifacts install all the libc++ artifacts to the android_libc++
+ subdirectory rather than anywhere that the driver can find them (because that's
+ still WIP). These are only included in the Linux artifacts.
+ """
+ # The libc++ directories in the toolchain artifacts use yet another spelling of
+ # each ABI.
+ libcxx_arch_name = {
+ Abi("armeabi-v7a"): "arm",
+ Abi("arm64-v8a"): "aarch64",
+ Abi("x86"): "i386",
+ Abi("x86_64"): "x86_64",
+ }[abi]
+ return (
+ ClangToolchain.path_for_host(Host.Linux)
+ / "android_libc++/ndk"
+ / libcxx_arch_name
+ )
+
def build(self) -> None:
pass
@@ -1367,7 +1328,7 @@ class BaseToolchain(ndk.builds.Module):
sysroot_dir = self.get_dep("sysroot").get_install_path()
system_stl_dir = self.get_dep("system-stl").get_install_path()
- shutil.copytree(sysroot_dir, install_dir / "sysroot", dirs_exist_ok=True)
+ shutil.copytree(sysroot_dir, self.sysroot_install_path, dirs_exist_ok=True)
exe = ".exe" if self.host.is_windows else ""
shutil.copy2(
@@ -1402,6 +1363,105 @@ class BaseToolchain(ndk.builds.Module):
system_stl_inc_src = system_stl_dir / "include"
system_stl_inc_dst = system_stl_hdr_dir / "4.9.x"
shutil.copytree(system_stl_inc_src, system_stl_inc_dst)
+ self.relocate_libcxx()
+ self.create_libcxx_linker_scripts()
+
+ def relocate_libcxx(self) -> None:
+ """Relocate libc++ so its discoverable by the Clang driver.
+
+ The NDK libc++ in the toolchain prebuilts is not installed to a location that
+ the driver is able to find by default. Move it to a driver searched directory.
+ """
+ # The Clang driver automatically uses the following library search directories
+ # (relative to the LLVM install root, for an aarch64-linux-android21 target and
+ # LLVM 17):
+ #
+ # 1. lib/clang/17/lib/linux/aarch64
+ # 2. bin/../sysroot/usr/lib/aarch64-linux-android/21
+ # 3. bin/../sysroot/usr/lib/aarch64-linux-android
+ # 4. bin/../sysroot/usr/lib
+ #
+ # The sysroot directory comes from the platform's sysroot artifact, so it's best
+ # to avoid installing to that (if we install there, the platform's artifact
+ # can't be used directly; it needs to have NDK components installed to it).
+ #
+ # However, AGP (and probably other systems) expect to find libc++_shared.so in
+ # sysroot/usr/lib/$TRIPLE, so we should continue using that path for the time
+ # being. At some point we should move all the libc++ details into the
+ # toolchain's directories so it's easier to use an arbitrary sysroot (e.g. for
+ # previewing Android APIs without needing a whole new NDK), but we can't do that
+ # for the headers yet anyway (see below). Keep compatible for now.
+ usr_lib = self.sysroot_install_path / "usr/lib"
+ for abi in ALL_ABIS:
+ dest = usr_lib / ndk.abis.abi_to_triple(abi)
+ src = self.toolchain_libcxx_path_for(abi) / "lib"
+ for lib in src.iterdir():
+ shutil.copy2(lib, dest / lib.name)
+
+ # libc++ headers for Android will currently only be found in the sysroot:
+ # https://github.com/llvm/llvm-project/blob/c64f10bfe20308ebc7d5d18912cd0ba82a44eaa1/clang/lib/Driver/ToolChains/Gnu.cpp#L3080-L3084
+ #
+ # We ought to revert that driver behavior (which shouldn't be contentious, since
+ # it's our patch in the first place), but for now we'll continue installing the
+ # libc++ headers to the sysroot.
+ src = ClangToolchain.path_for_host(Host.Linux) / "include/c++/v1"
+ dest = self.sysroot_install_path / "usr/include/c++/v1"
+ if dest.exists():
+ shutil.rmtree(dest)
+ dest.parent.mkdir(parents=True, exist_ok=True)
+ shutil.copytree(src, dest)
+
+ # There's also an Android-specific __config_site header that we need to install.
+ shutil.copy2(self.find_libcxx_config_site(), dest / "__config_site")
+
+ def find_libcxx_config_site(self) -> Path:
+ """Finds the __config_site file for the NDK libc++.
+
+ That header exists per-ABI in the android_libc++ directory, but they should all
+ be identical and the driver doesn't search per-ABI include directories for
+ libc++. Verify that they are actually identical and return one of them
+ arbitrarily.
+ """
+ config_sites: list[Path] = []
+ for abi in ALL_ABIS:
+ includes = self.toolchain_libcxx_path_for(abi) / "include"
+ config_sites.extend(includes.glob("**/__config_site"))
+ first = config_sites[0]
+ contents = first.read_bytes()
+ for config_site in config_sites[1:]:
+ if config_site.read_bytes() != contents:
+ raise RuntimeError(
+ f"Expected all NDK __config_site files to be identical. {first} "
+ f"and {config_site} have different contents."
+ )
+ return first
+
+ def create_libcxx_linker_scripts(self) -> None:
+ """Install per-target linker scripts for libc++.so and libc++.a.
+
+ Clang is going to try to use `-lc++`, not `-lc++_shared` or
+ `-lc++_static -lc++abi`. Linker scripts paper over those details.
+
+ These are per-target for historical reasons (pre-21 needed libandroid_support,
+ arm32 needed libunwind). These could probably be reduced to a single linker
+ script now.
+ """
+ install_dir = self.get_install_path()
+ for api in ALL_API_LEVELS:
+ for abi in ndk.abis.iter_abis_for_api(api):
+ triple = ndk.abis.abi_to_triple(abi)
+ dst_dir = install_dir / "sysroot/usr/lib" / triple / str(api)
+
+ static_script = ["-lc++_static", "-lc++abi"]
+ shared_script = ["-lc++_shared"]
+
+ libcxx_so_path = dst_dir / "libc++.so"
+ with open(libcxx_so_path, "w") as script:
+ script.write("INPUT({})".format(" ".join(shared_script)))
+
+ libcxx_a_path = dst_dir / "libc++.a"
+ with open(libcxx_a_path, "w") as script:
+ script.write("INPUT({})".format(" ".join(static_script)))
@register
@@ -1439,82 +1499,6 @@ class Vulkan(ndk.builds.Module):
)
-@register
-class Toolchain(ndk.builds.Module):
- """The complete toolchain.
-
- BaseToolchain installs the core of the toolchain. This module installs the
- STL to that toolchain.
- """
-
- name = "toolchain"
- # This is installed to the Clang location to avoid migration pain.
- install_path = Path("toolchains/llvm/prebuilt/{host}")
- notice_group = ndk.builds.NoticeGroup.TOOLCHAIN
- deps = {
- "base-toolchain",
- "libc++",
- "libc++abi",
- }
-
- @property
- def notices(self) -> Iterator[Path]:
- yield from Libcxx().notices
- yield from Libcxxabi().notices
-
- def build(self) -> None:
- pass
-
- def install(self) -> None:
- install_dir = self.get_install_path()
- libcxx_dir = self.get_dep("libc++").get_install_path()
- libcxxabi_dir = self.get_dep("libc++abi").get_install_path()
-
- libcxx_hdr_dir = install_dir / "sysroot/usr/include/c++"
- libcxx_hdr_dir.mkdir(parents=True)
- libcxx_inc_src = libcxx_dir / "include"
- libcxx_inc_dst = libcxx_hdr_dir / "v1"
- shutil.copytree(libcxx_inc_src, libcxx_inc_dst)
-
- libcxxabi_inc_src = libcxxabi_dir / "include"
- shutil.copytree(libcxxabi_inc_src, libcxx_inc_dst, dirs_exist_ok=True)
-
- for arch in ndk.abis.ALL_ARCHITECTURES:
- triple = ndk.abis.arch_to_triple(arch)
- (abi,) = ndk.abis.arch_to_abis(arch)
- sysroot_dst = install_dir / "sysroot/usr/lib" / triple
-
- shutil.copy2(
- self.out_dir / "libcxx" / "libs" / abi / "libc++_shared.so", sysroot_dst
- )
- static_libs = [
- "libc++_static.a",
- "libc++abi.a",
- ]
-
- for lib in static_libs:
- shutil.copy2(
- self.out_dir / "libcxx" / "obj" / "local" / abi / lib, sysroot_dst
- )
-
- # Also install a libc++.so and libc++.a linker script per API level.
- for api in ALL_API_LEVELS:
- for abi in ndk.abis.iter_abis_for_api(api):
- triple = ndk.abis.abi_to_triple(abi)
- dst_dir = install_dir / "sysroot/usr/lib" / triple / str(api)
-
- static_script = ["-lc++_static", "-lc++abi"]
- shared_script = ["-lc++_shared"]
-
- libcxx_so_path = dst_dir / "libc++.so"
- with open(libcxx_so_path, "w") as script:
- script.write("INPUT({})".format(" ".join(shared_script)))
-
- libcxx_a_path = dst_dir / "libc++.a"
- with open(libcxx_a_path, "w") as script:
- script.write("INPUT({})".format(" ".join(static_script)))
-
-
def make_format_value(value: Any) -> Any:
if isinstance(value, list):
return " ".join(value)
@@ -1710,13 +1694,6 @@ class SystemStl(ndk.builds.PackageModule):
@register
-class Libcxxabi(ndk.builds.PackageModule):
- name = "libc++abi"
- install_path = Path("sources/cxx-stl/llvm-libc++abi")
- src = ANDROID_DIR / "toolchain/llvm-project/libcxxabi"
-
-
-@register
class SimplePerf(ndk.builds.Module):
name = "simpleperf"
install_path = Path("simpleperf")
@@ -1882,7 +1859,7 @@ class Meta(ndk.builds.PackageModule):
no_notice = True
deps = {
- "base-toolchain",
+ "toolchain",
}
@staticmethod
@@ -1934,7 +1911,7 @@ class Meta(ndk.builds.PackageModule):
# only need to scan a single 32-bit architecture since these libraries
# do not vary in availability across architectures.
sysroot_base = (
- self.get_dep("base-toolchain").get_install_path()
+ self.get_dep("toolchain").get_install_path()
/ "sysroot/usr/lib/arm-linux-androideabi"
)