Java tutorial
/* * Copyright 2014-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.android; import com.facebook.buck.cxx.CompilerProvider; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.CxxToolProvider; import com.facebook.buck.cxx.DefaultLinkerProvider; import com.facebook.buck.cxx.ElfSharedLibraryInterfaceFactory; import com.facebook.buck.cxx.GnuArchiver; import com.facebook.buck.cxx.GnuLinker; import com.facebook.buck.cxx.Linker; import com.facebook.buck.cxx.LinkerProvider; import com.facebook.buck.cxx.MungingDebugPathSanitizer; import com.facebook.buck.cxx.PosixNmSymbolNameTool; import com.facebook.buck.cxx.PrefixMapDebugPathSanitizer; import com.facebook.buck.cxx.PreprocessorProvider; import com.facebook.buck.io.ExecutableFinder; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.ImmutableFlavor; import com.facebook.buck.rules.ConstantToolProvider; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.ToolProvider; import com.facebook.buck.rules.VersionedTool; import com.facebook.buck.util.environment.Platform; import com.facebook.infer.annotation.Assertions; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.Set; public class NdkCxxPlatforms { /** * Magic path prefix we use to denote the machine-specific location of the Android NDK. Why "@"? * It's uncommon enough to mark that path element as special while not being a metacharacter in * either make, shell, or regular expression syntax. * <p/> * We also have prefixes for tool specific paths, even though they're sub-paths of * `@ANDROID_NDK_ROOT@`. This is to sanitize host-specific sub-directories in the toolchain * (e.g. darwin-x86_64) which would otherwise break determinism and caching when using * cross-compilation. */ public static final String ANDROID_NDK_ROOT = "@ANDROID_NDK_ROOT@"; /** * Magic string we substitute into debug paths in place of the build-host name, erasing the * difference between say, building on Darwin and building on Linux. */ public static final String BUILD_HOST_SUBST = "@BUILD_HOST@"; public static final NdkCxxPlatformCompiler.Type DEFAULT_COMPILER_TYPE = NdkCxxPlatformCompiler.Type.GCC; public static final String DEFAULT_TARGET_APP_PLATFORM = "android-9"; public static final ImmutableSet<String> DEFAULT_CPU_ABIS = ImmutableSet.of("arm", "armv7", "x86"); public static final NdkCxxPlatforms.CxxRuntime DEFAULT_CXX_RUNTIME = NdkCxxPlatforms.CxxRuntime.GNUSTL; private static final ImmutableMap<Platform, Host> BUILD_PLATFORMS = ImmutableMap.of(Platform.LINUX, Host.LINUX_X86_64, Platform.MACOS, Host.DARWIN_X86_64, Platform.WINDOWS, Host.WINDOWS_X86_64); // Utility class, do not instantiate. private NdkCxxPlatforms() { } static int getNdkMajorVersion(String ndkVersion) { return ndkVersion.startsWith("r9") ? 9 : ndkVersion.startsWith("r10") ? 10 : ndkVersion.startsWith("11.") ? 11 : ndkVersion.startsWith("12.") ? 12 : ndkVersion.startsWith("13.") ? 13 : -1; } public static String getDefaultGccVersionForNdk(Optional<String> ndkVersion) { if (ndkVersion.isPresent() && getNdkMajorVersion(ndkVersion.get()) < 11) { return "4.8"; } return "4.9"; } public static String getDefaultClangVersionForNdk(Optional<String> ndkVersion) { if (ndkVersion.isPresent() && getNdkMajorVersion(ndkVersion.get()) < 11) { return "3.5"; } return "3.8"; } public static boolean isSupportedConfiguration(Path ndkRoot, CxxRuntime cxxRuntime) { // TODO(12846101): With ndk r12, Android has started to use libc++abi. Buck // needs to figure out how to support that. String ndkVersion = readVersion(ndkRoot); return !(cxxRuntime == NdkCxxPlatforms.CxxRuntime.LIBCXX && getNdkMajorVersion(ndkVersion) >= 12); } public static ImmutableMap<TargetCpuType, NdkCxxPlatform> getPlatforms(CxxBuckConfig config, ProjectFilesystem filesystem, Path ndkRoot, NdkCxxPlatformCompiler compiler, CxxRuntime cxxRuntime, String androidPlatform, Set<String> cpuAbis, Platform platform) { return getPlatforms(config, filesystem, ndkRoot, compiler, cxxRuntime, androidPlatform, cpuAbis, platform, new ExecutableFinder(), /* strictToolchainPaths */ true); } /** * @return the map holding the available {@link NdkCxxPlatform}s. */ public static ImmutableMap<TargetCpuType, NdkCxxPlatform> getPlatforms(CxxBuckConfig config, ProjectFilesystem filesystem, Path ndkRoot, NdkCxxPlatformCompiler compiler, CxxRuntime cxxRuntime, String androidPlatform, Set<String> cpuAbis, Platform platform, ExecutableFinder executableFinder, boolean strictToolchainPaths) { ImmutableMap.Builder<TargetCpuType, NdkCxxPlatform> ndkCxxPlatformBuilder = ImmutableMap.builder(); // ARM Platform if (cpuAbis.contains("arm")) { NdkCxxPlatformTargetConfiguration targetConfiguration = getTargetConfiguration(TargetCpuType.ARM, compiler, androidPlatform); NdkCxxPlatform armeabi = build(config, filesystem, ImmutableFlavor.of("android-arm"), platform, ndkRoot, targetConfiguration, cxxRuntime, executableFinder, strictToolchainPaths); ndkCxxPlatformBuilder.put(TargetCpuType.ARM, armeabi); } // ARMv7 Platform if (cpuAbis.contains("armv7")) { NdkCxxPlatformTargetConfiguration targetConfiguration = getTargetConfiguration(TargetCpuType.ARMV7, compiler, androidPlatform); NdkCxxPlatform armeabiv7 = build(config, filesystem, ImmutableFlavor.of("android-armv7"), platform, ndkRoot, targetConfiguration, cxxRuntime, executableFinder, strictToolchainPaths); ndkCxxPlatformBuilder.put(TargetCpuType.ARMV7, armeabiv7); } // ARM64 Platform if (cpuAbis.contains("arm64")) { NdkCxxPlatformTargetConfiguration targetConfiguration = getTargetConfiguration(TargetCpuType.ARM64, compiler, androidPlatform); NdkCxxPlatform arm64 = build(config, filesystem, ImmutableFlavor.of("android-arm64"), platform, ndkRoot, targetConfiguration, cxxRuntime, executableFinder, strictToolchainPaths); ndkCxxPlatformBuilder.put(TargetCpuType.ARM64, arm64); } // x86 Platform if (cpuAbis.contains("x86")) { NdkCxxPlatformTargetConfiguration targetConfiguration = getTargetConfiguration(TargetCpuType.X86, compiler, androidPlatform); NdkCxxPlatform x86 = build(config, filesystem, ImmutableFlavor.of("android-x86"), platform, ndkRoot, targetConfiguration, cxxRuntime, executableFinder, strictToolchainPaths); ndkCxxPlatformBuilder.put(TargetCpuType.X86, x86); } // x86_64 Platform if (cpuAbis.contains("x86_64")) { NdkCxxPlatformTargetConfiguration targetConfiguration = getTargetConfiguration(TargetCpuType.X86_64, compiler, androidPlatform); // CHECKSTYLE.OFF: LocalVariableName NdkCxxPlatform x86_64 = // CHECKSTYLE.ON build(config, filesystem, ImmutableFlavor.of("android-x86_64"), platform, ndkRoot, targetConfiguration, cxxRuntime, executableFinder, strictToolchainPaths); ndkCxxPlatformBuilder.put(TargetCpuType.X86_64, x86_64); } return ndkCxxPlatformBuilder.build(); } private static NdkCxxPlatformTargetConfiguration getTargetConfiguration(TargetCpuType targetCpuType, NdkCxxPlatformCompiler compiler, String androidPlatform) { switch (targetCpuType) { case ARM: ImmutableList<String> armeabiArchFlags = ImmutableList.of("-march=armv5te", "-mtune=xscale", "-msoft-float", "-mthumb"); return NdkCxxPlatformTargetConfiguration.builder().setToolchain(Toolchain.ARM_LINUX_ANDROIDEABI) .setTargetArch(TargetArch.ARM).setTargetArchAbi(TargetArchAbi.ARMEABI) .setTargetAppPlatform(androidPlatform).setCompiler(compiler) .setToolchainTarget(ToolchainTarget.ARM_LINUX_ANDROIDEABI) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.GCC, armeabiArchFlags) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "armv5te-none-linux-androideabi") .addAll(armeabiArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>builder().add("-Os").addAll(armeabiArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "armv5te-none-linux-androideabi", "-Os") .addAll(armeabiArchFlags).build()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.of("-march=armv5te", "-Wl,--fix-cortex-a8")) .putLinkerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "armv5te-none-linux-androideabi", "-march=armv5te", "-Wl,--fix-cortex-a8")) .build(); case ARMV7: ImmutableList<String> armeabiv7ArchFlags = ImmutableList.of("-march=armv7-a", "-mfpu=vfpv3-d16", "-mfloat-abi=softfp", "-mthumb"); return NdkCxxPlatformTargetConfiguration.builder().setToolchain(Toolchain.ARM_LINUX_ANDROIDEABI) .setTargetArch(TargetArch.ARM).setTargetArchAbi(TargetArchAbi.ARMEABI_V7A) .setTargetAppPlatform(androidPlatform).setCompiler(compiler) .setToolchainTarget(ToolchainTarget.ARM_LINUX_ANDROIDEABI) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.GCC, armeabiv7ArchFlags) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "armv7-none-linux-androideabi") .addAll(armeabiv7ArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>builder().add("-finline-limit=64", "-Os") .addAll(armeabiv7ArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "armv7-none-linux-androideabi", "-Os") .addAll(armeabiv7ArchFlags).build()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "armv7-none-linux-androideabi")) .build(); case ARM64: ImmutableList<String> arm64ArchFlags = ImmutableList.of("-march=armv8-a"); return NdkCxxPlatformTargetConfiguration.builder().setToolchain(Toolchain.AARCH64_LINUX_ANDROID) .setTargetArch(TargetArch.ARM64).setTargetArchAbi(TargetArchAbi.ARM64_V8A) .setTargetAppPlatform(androidPlatform).setCompiler(compiler) .setToolchainTarget(ToolchainTarget.AARCH64_LINUX_ANDROID) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.GCC, arm64ArchFlags) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "aarch64-none-linux-android") .addAll(arm64ArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>builder().add("-O2").add("-fomit-frame-pointer") .add("-fstrict-aliasing").add("-funswitch-loops").add("-finline-limit=300") .addAll(arm64ArchFlags).build()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>builder().add("-target", "aarch64-none-linux-android").add("-O2") .add("-fomit-frame-pointer").add("-fstrict-aliasing").addAll(arm64ArchFlags) .build()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "aarch64-none-linux-android")) .build(); case X86: return NdkCxxPlatformTargetConfiguration.builder().setToolchain(Toolchain.X86) .setTargetArch(TargetArch.X86).setTargetArchAbi(TargetArchAbi.X86) .setTargetAppPlatform(androidPlatform).setCompiler(compiler) .setToolchainTarget(ToolchainTarget.I686_LINUX_ANDROID) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "i686-none-linux-android")) .putCompilerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.of("-funswitch-loops", "-finline-limit=300", "-O2")) .putCompilerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "i686-none-linux-android", "-O2")) .putLinkerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "i686-none-linux-android")) .build(); case X86_64: return NdkCxxPlatformTargetConfiguration.builder().setToolchain(Toolchain.X86_64) .setTargetArch(TargetArch.X86_64).setTargetArchAbi(TargetArchAbi.X86_64) .setTargetAppPlatform(androidPlatform).setCompiler(compiler) .setToolchainTarget(ToolchainTarget.X86_64_LINUX_ANDROID) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putAssemblerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.<String>of()) .putCompilerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.of("-funswitch-loops", "-finline-limit=300", "-O2")) .putCompilerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "i686-none-linux-android", "-O2")) .putLinkerFlags(NdkCxxPlatformCompiler.Type.GCC, ImmutableList.<String>of()) .putLinkerFlags(NdkCxxPlatformCompiler.Type.CLANG, ImmutableList.of("-target", "i686-none-linux-android")) .build(); case MIPS: break; } throw new AssertionError(); } @VisibleForTesting static NdkCxxPlatform build(CxxBuckConfig config, ProjectFilesystem filesystem, Flavor flavor, Platform platform, Path ndkRoot, NdkCxxPlatformTargetConfiguration targetConfiguration, CxxRuntime cxxRuntime, ExecutableFinder executableFinder, boolean strictToolchainPaths) { // Create a version string to use when generating rule keys via the NDK tools we'll generate // below. This will be used in lieu of hashing the contents of the tools, so that builds from // different host platforms (which produce identical output) will share the cache with one // another. NdkCxxPlatformCompiler.Type compilerType = targetConfiguration.getCompiler().getType(); String version = Joiner.on('-') .join(ImmutableList.of(readVersion(ndkRoot), targetConfiguration.getToolchain(), targetConfiguration.getTargetAppPlatform(), compilerType, targetConfiguration.getCompiler().getVersion(), targetConfiguration.getCompiler().getGccVersion(), cxxRuntime)); Host host = Preconditions.checkNotNull(BUILD_PLATFORMS.get(platform)); NdkCxxToolchainPaths toolchainPaths = new NdkCxxToolchainPaths(filesystem, ndkRoot, targetConfiguration, host.toString(), cxxRuntime, strictToolchainPaths); // Sanitized paths will have magic placeholders for parts of the paths that // are machine/host-specific. See comments on ANDROID_NDK_ROOT and // BUILD_HOST_SUBST above. NdkCxxToolchainPaths sanitizedPaths = toolchainPaths.getSanitizedPaths(); // Build up the map of paths that must be sanitized. ImmutableBiMap.Builder<Path, Path> sanitizePathsBuilder = ImmutableBiMap.builder(); sanitizePathsBuilder.put(toolchainPaths.getNdkToolRoot(), sanitizedPaths.getNdkToolRoot()); if (compilerType != NdkCxxPlatformCompiler.Type.GCC) { sanitizePathsBuilder.put(toolchainPaths.getNdkGccToolRoot(), sanitizedPaths.getNdkGccToolRoot()); } sanitizePathsBuilder.put(ndkRoot, Paths.get(ANDROID_NDK_ROOT)); CxxToolProvider.Type type = compilerType == NdkCxxPlatformCompiler.Type.CLANG ? CxxToolProvider.Type.CLANG : CxxToolProvider.Type.GCC; ToolProvider ccTool = new ConstantToolProvider( getCTool(toolchainPaths, compilerType.getCc(), version, executableFinder)); ToolProvider cxxTool = new ConstantToolProvider( getCTool(toolchainPaths, compilerType.getCxx(), version, executableFinder)); CompilerProvider cc = new CompilerProvider(ccTool, type); PreprocessorProvider cpp = new PreprocessorProvider(ccTool, type); CompilerProvider cxx = new CompilerProvider(cxxTool, type); PreprocessorProvider cxxpp = new PreprocessorProvider(cxxTool, type); CxxPlatform.Builder cxxPlatformBuilder = CxxPlatform.builder(); ImmutableBiMap<Path, Path> sanitizePaths = sanitizePathsBuilder.build(); PrefixMapDebugPathSanitizer compilerDebugPathSanitizer = new PrefixMapDebugPathSanitizer( config.getDebugPathSanitizerLimit(), File.separatorChar, Paths.get("."), sanitizePaths, filesystem.getRootPath().toAbsolutePath(), type); MungingDebugPathSanitizer assemblerDebugPathSanitizer = new MungingDebugPathSanitizer( config.getDebugPathSanitizerLimit(), File.separatorChar, Paths.get("."), sanitizePaths); cxxPlatformBuilder.setFlavor(flavor).setAs(cc) .addAllAsflags(getAsflags(targetConfiguration, toolchainPaths)).setAspp(cpp).setCc(cc) .addAllCflags(getCflagsInternal(targetConfiguration, toolchainPaths)).setCpp(cpp) .addAllCppflags(getCppflags(targetConfiguration, toolchainPaths)).setCxx(cxx) .addAllCxxflags(getCxxflagsInternal(targetConfiguration, toolchainPaths)).setCxxpp(cxxpp) .addAllCxxppflags(getCxxppflags(targetConfiguration, toolchainPaths)) .setLd(new DefaultLinkerProvider(LinkerProvider.Type.GNU, new ConstantToolProvider(getCcLinkTool(targetConfiguration, toolchainPaths, compilerType.getCxx(), version, executableFinder)))) .addAllLdflags(targetConfiguration.getLinkerFlags(compilerType)) // Default linker flags added by the NDK .addLdflags( // Add a deterministic build ID to Android builds. // We use it to find symbols from arbitrary binaries. "-Wl,--build-id", // Enforce the NX (no execute) security feature "-Wl,-z,noexecstack", // Strip unused code "-Wl,--gc-sections", // Refuse to produce dynamic objects with undefined symbols "-Wl,-z,defs", // Forbid dangerous copy "relocations" "-Wl,-z,nocopyreloc", // We always pass the runtime library on the command line, so setting this flag // means the resulting link will only use it if it was actually needed it. "-Wl,--as-needed") .setStrip(getGccTool(toolchainPaths, "strip", version, executableFinder)) .setSymbolNameTool( new PosixNmSymbolNameTool(getGccTool(toolchainPaths, "nm", version, executableFinder))) .setAr(new GnuArchiver(getGccTool(toolchainPaths, "ar", version, executableFinder))) .setRanlib(getGccTool(toolchainPaths, "ranlib", version, executableFinder)) // NDK builds are cross compiled, so the header is the same regardless of the host platform. .setCompilerDebugPathSanitizer(compilerDebugPathSanitizer) .setAssemblerDebugPathSanitizer(assemblerDebugPathSanitizer).setSharedLibraryExtension("so") .setSharedLibraryVersionedExtensionFormat("so.%s").setStaticLibraryExtension("a") .setObjectFileExtension("o") .setSharedLibraryInterfaceFactory(config.shouldUseSharedLibraryInterfaces() ? Optional.of(ElfSharedLibraryInterfaceFactory.of(new ConstantToolProvider( getGccTool(toolchainPaths, "objcopy", version, executableFinder)))) : Optional.empty()) .setHeaderVerification(config.getHeaderVerification()); if (cxxRuntime != CxxRuntime.SYSTEM) { cxxPlatformBuilder.putRuntimeLdflags(Linker.LinkableDepType.SHARED, "-l" + cxxRuntime.getSharedName()); cxxPlatformBuilder.putRuntimeLdflags(Linker.LinkableDepType.STATIC, "-l" + cxxRuntime.getStaticName()); } CxxPlatform cxxPlatform = cxxPlatformBuilder.build(); return NdkCxxPlatform.builder().setCxxPlatform(cxxPlatform).setCxxRuntime(cxxRuntime) .setCxxSharedRuntimePath( toolchainPaths.getCxxRuntimeLibsDirectory().resolve(cxxRuntime.getSoname())) .setObjdump(getGccTool(toolchainPaths, "objdump", version, executableFinder)).build(); } /** * It returns the version of the Android NDK located at the {@code ndkRoot} or throws the * exception. * * @param ndkRoot the path where Android NDK is located. * @return the version of the Android NDK located in {@code ndkRoot}. */ private static String readVersion(Path ndkRoot) { return DefaultAndroidDirectoryResolver.findNdkVersionFromDirectory(ndkRoot).get(); } private static Path getToolPath(NdkCxxToolchainPaths toolchainPaths, String tool, ExecutableFinder executableFinder) { Path expected = toolchainPaths.getToolPath(tool); Optional<Path> path = executableFinder.getOptionalExecutable(expected, ImmutableMap.of()); Preconditions.checkState(path.isPresent(), expected.toString()); return path.get(); } private static Path getGccToolPath(NdkCxxToolchainPaths toolchainPaths, String tool, ExecutableFinder executableFinder) { Path expected = toolchainPaths.getGccToolchainBinPath().resolve(tool); Optional<Path> path = executableFinder.getOptionalExecutable(expected, ImmutableMap.of()); Preconditions.checkState(path.isPresent(), expected.toString()); return path.get(); } private static Tool getGccTool(NdkCxxToolchainPaths toolchainPaths, String tool, String version, ExecutableFinder executableFinder) { return VersionedTool.of(getGccToolPath(toolchainPaths, tool, executableFinder), tool, version); } private static Tool getCTool(NdkCxxToolchainPaths toolchainPaths, String tool, String version, ExecutableFinder executableFinder) { return VersionedTool.of(getToolPath(toolchainPaths, tool, executableFinder), tool, version); } private static ImmutableList<String> getCxxRuntimeIncludeFlags( NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { ImmutableList.Builder<String> flags = ImmutableList.builder(); switch (toolchainPaths.getCxxRuntime()) { case GNUSTL: flags.add("-isystem", toolchainPaths.getCxxRuntimeDirectory().resolve("include").toString()); flags.add("-isystem", toolchainPaths.getCxxRuntimeDirectory().resolve("libs") .resolve(targetConfiguration.getTargetArchAbi().toString()).resolve("include").toString()); break; case LIBCXX: flags.add("-isystem", toolchainPaths.getCxxRuntimeDirectory().resolve("libcxx").resolve("include").toString()); flags.add("-isystem", toolchainPaths.getCxxRuntimeDirectory().getParent().resolve("llvm-libc++abi") .resolve("libcxxabi").resolve("include").toString()); flags.add("-isystem", toolchainPaths.getNdkRoot().resolve("sources").resolve("android") .resolve("support").resolve("include").toString()); break; // $CASES-OMITTED$ default: flags.add("-isystem", toolchainPaths.getCxxRuntimeDirectory().resolve("include").toString()); } return flags.build(); } private static Linker getCcLinkTool(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths, String tool, String version, ExecutableFinder executableFinder) { ImmutableList.Builder<String> flags = ImmutableList.builder(); // Clang still needs to find GCC tools. if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.CLANG) { flags.add("-gcc-toolchain", toolchainPaths.getNdkGccToolRoot().toString()); } // Set the sysroot to the platform-specific path. flags.add("--sysroot=" + toolchainPaths.getSysroot()); // TODO(#7264008): This was added for windows support but it's not clear why it's needed. if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.GCC) { flags.add("-B" + toolchainPaths.getLibexecGccToolPath(), "-B" + toolchainPaths.getLibPath()); } // Add the path to the C/C++ runtime libraries. flags.add("-L" + toolchainPaths.getCxxRuntimeLibsDirectory().toString()); return new GnuLinker(VersionedTool.builder().setPath(getToolPath(toolchainPaths, tool, executableFinder)) .setName(tool).setVersion(version).setExtraArgs(flags.build()).build()); } /** * Flags to be used when either preprocessing or compiling C or C++ sources. */ private static ImmutableList<String> getCommonFlags(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { ImmutableList.Builder<String> flags = ImmutableList.builder(); // Clang still needs to find the GCC tools. if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.CLANG) { flags.add("-gcc-toolchain", toolchainPaths.getNdkGccToolRoot().toString()); } // TODO(#7264008): This was added for windows support but it's not clear why it's needed. if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.GCC) { flags.add("-B" + toolchainPaths.getLibexecGccToolPath(), "-B" + toolchainPaths.getToolchainBinPath()); } // Enable default warnings and turn them into errors. flags.add("-Wall", "-Werror"); // NOTE: We pass all compiler flags to the preprocessor to make sure any necessary internal // macros get defined and we also pass the include paths to the to the compiler since we're // not whether we're doing combined preprocessing/compiling or not. if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.CLANG) { flags.add("-Wno-unused-command-line-argument"); } // NDK builds enable stack protector and debug symbols by default. flags.add("-fstack-protector", "-g3"); return flags.build(); } /** * Flags to be used when either preprocessing or compiling C sources. */ private static ImmutableList<String> getCommonCFlags() { return ImmutableList.of( // Default to the newer C11 standard. This is *not* a default set in the NDK. // Since this flag can be used multiple times, and because the compiler just uses // whichever standard was specified last, cxx_library rules can override this from // their BUCK-file definitions. "-std=gnu11"); } /** * Flags to be used when either preprocessing or compiling C++ sources. */ private static ImmutableList<String> getCommonCxxFlags() { return ImmutableList.of( // Default to the newer C++11 standard. This is *not* a default set in the NDK. // Since this flag can be used multiple times, and because the compiler just uses // whichever standard was specified last, cxx_library rules can override this from // their BUCK-file definitions. "-std=gnu++11", // By default, Android builds disable exceptions and runtime type identification. "-fno-exceptions", "-fno-rtti"); } /** * Flags to be used when preprocessing C or C++ sources. */ private static ImmutableList<String> getCommonPreprocessorFlags() { return ImmutableList.of( // Disable searching for headers provided by the system. This limits headers to just // those provided by the NDK and any library dependencies. "-nostdinc", // Default macro definitions applied to all builds. "-DNDEBUG", "-DANDROID"); } private static ImmutableList<String> getCommonIncludes(NdkCxxToolchainPaths toolchainPaths) { return ImmutableList.of("-isystem", toolchainPaths.getNdkToolRoot().resolve("include").toString(), "-isystem", toolchainPaths.getLibPath().resolve("include").toString(), "-isystem", toolchainPaths.getSysroot().resolve("usr").resolve("include").toString(), "-isystem", toolchainPaths.getSysroot().resolve("usr").resolve("include").resolve("linux").toString()); } private static ImmutableList<String> getAsflags(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { return ImmutableList.<String>builder().addAll(getCommonFlags(targetConfiguration, toolchainPaths)) // Default assembler flags added by the NDK to enforce the NX (no execute) security feature. .add("-Xassembler", "--noexecstack") .addAll(targetConfiguration.getAssemblerFlags(targetConfiguration.getCompiler().getType())).build(); } private static ImmutableList<String> getCppflags(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { return ImmutableList.<String>builder().addAll(getCommonIncludes(toolchainPaths)) .addAll(getCommonPreprocessorFlags()).addAll(getCommonFlags(targetConfiguration, toolchainPaths)) .addAll(getCommonCFlags()) .addAll(targetConfiguration.getCompilerFlags(targetConfiguration.getCompiler().getType())).build(); } private static ImmutableList<String> getCxxppflags(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { ImmutableList.Builder<String> flags = ImmutableList.builder(); flags.addAll(getCxxRuntimeIncludeFlags(targetConfiguration, toolchainPaths)); flags.addAll(getCommonIncludes(toolchainPaths)); flags.addAll(getCommonPreprocessorFlags()); flags.addAll(getCommonFlags(targetConfiguration, toolchainPaths)); flags.addAll(getCommonCxxFlags()); if (targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.GCC) { flags.add("-Wno-literal-suffix"); } flags.addAll(targetConfiguration.getCompilerFlags(targetConfiguration.getCompiler().getType())); return flags.build(); } /** * Flags used when compiling either C or C++ sources. */ private static ImmutableList<String> getCommonNdkCxxPlatformCompilerFlags() { return ImmutableList.of( // Default compiler flags provided by the NDK build makefiles. "-ffunction-sections", "-funwind-tables", "-fomit-frame-pointer", "-fno-strict-aliasing"); } private static ImmutableList<String> getCflagsInternal(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { return ImmutableList.<String>builder() .addAll(targetConfiguration.getCompilerFlags(targetConfiguration.getCompiler().getType())) .addAll(getCommonCFlags()).addAll(getCommonFlags(targetConfiguration, toolchainPaths)) .addAll(getCommonNdkCxxPlatformCompilerFlags()).build(); } private static ImmutableList<String> getCxxflagsInternal(NdkCxxPlatformTargetConfiguration targetConfiguration, NdkCxxToolchainPaths toolchainPaths) { return ImmutableList.<String>builder() .addAll(targetConfiguration.getCompilerFlags(targetConfiguration.getCompiler().getType())) .addAll(getCommonCxxFlags()).addAll(getCommonFlags(targetConfiguration, toolchainPaths)) .addAll(getCommonNdkCxxPlatformCompilerFlags()).build(); } /** * The CPU architectures to target. */ public enum TargetCpuType { ARM, ARMV7, ARM64, X86, X86_64, MIPS, } /** * The build toolchain, named (including compiler version) after the target platform/arch. */ public enum Toolchain { X86("x86"), X86_64("x86_64"), ARM_LINUX_ANDROIDEABI("arm-linux-androideabi"), AARCH64_LINUX_ANDROID( "aarch64-linux-android"),; private final String value; Toolchain(String value) { this.value = Preconditions.checkNotNull(value); } @Override public String toString() { return value; } } /** * Name of the target CPU architecture. */ public enum TargetArch { X86("x86"), X86_64("x86_64"), ARM("arm"), ARM64("arm64"),; private final String value; TargetArch(String value) { this.value = Preconditions.checkNotNull(value); } @Override public String toString() { return value; } } /** * Name of the target CPU + ABI. */ public enum TargetArchAbi { X86("x86"), X86_64("x86_64"), ARMEABI("armeabi"), ARMEABI_V7A("armeabi-v7a"), ARM64_V8A("arm64-v8a"),; private final String value; TargetArchAbi(String value) { this.value = Preconditions.checkNotNull(value); } @Override public String toString() { return value; } } /** * The OS and Architecture that we're building on. */ public enum Host { DARWIN_X86_64("darwin-x86_64"), LINUX_X86_64("linux-x86_64"), WINDOWS_X86_64("windows-x86_64"),; private final String value; Host(String value) { this.value = Preconditions.checkNotNull(value); } @Override public String toString() { return value; } } /** * The C/C++ runtime library to link against. */ public enum CxxRuntime { SYSTEM("system", "system", "system"), GABIXX("gabi++", "gabi++_shared", "gabi++_static"), STLPORT("stlport", "stlport_shared", "stlport_static"), GNUSTL("gnu-libstdc++", "gnustl_shared", "gnustl_static"), LIBCXX("llvm-libc++", "c++_shared", "c++_static"),; private final String name; private final String sharedName; private final String staticName; /** * @param name the runtimes directory name in the NDK. * @param sharedName the shared library name used for this runtime. * @param staticName the the static library used for this runtime. */ CxxRuntime(String name, String sharedName, String staticName) { this.name = name; this.sharedName = sharedName; this.staticName = staticName; } public String getName() { return name; } public String getStaticName() { return staticName; } public String getSharedName() { return sharedName; } public String getSoname() { return "lib" + sharedName + ".so"; } } /** * The toolchains name for the platform being targeted. */ public enum ToolchainTarget { I686_LINUX_ANDROID("i686-linux-android"), X86_64_LINUX_ANDROID( "x86_64-linux-android"), ARM_LINUX_ANDROIDEABI( "arm-linux-androideabi"), AARCH64_LINUX_ANDROID("aarch64-linux-android"),; private final String value; ToolchainTarget(String value) { this.value = Preconditions.checkNotNull(value); } @Override public String toString() { return value; } } static class NdkCxxToolchainPaths { private Path ndkRoot; private String ndkVersion; private NdkCxxPlatformTargetConfiguration targetConfiguration; private String hostName; private CxxRuntime cxxRuntime; private Map<String, Path> cachedPaths; private boolean strict; private int ndkMajorVersion; private ProjectFilesystem filesystem; NdkCxxToolchainPaths(ProjectFilesystem filesystem, Path ndkRoot, NdkCxxPlatformTargetConfiguration targetConfiguration, String hostName, CxxRuntime cxxRuntime, boolean strict) { this(filesystem, ndkRoot, readVersion(ndkRoot), targetConfiguration, hostName, cxxRuntime, strict); } private NdkCxxToolchainPaths(ProjectFilesystem filesystem, Path ndkRoot, String ndkVersion, NdkCxxPlatformTargetConfiguration targetConfiguration, String hostName, CxxRuntime cxxRuntime, boolean strict) { this.filesystem = filesystem; this.cachedPaths = new HashMap<>(); this.strict = strict; this.targetConfiguration = targetConfiguration; this.hostName = hostName; this.cxxRuntime = cxxRuntime; this.ndkRoot = ndkRoot; this.ndkVersion = ndkVersion; this.ndkMajorVersion = getNdkMajorVersion(ndkVersion); Assertions.assertCondition(ndkMajorVersion > 0, "Unknown ndk version: " + ndkVersion); } NdkCxxToolchainPaths getSanitizedPaths() { return new NdkCxxToolchainPaths(filesystem, Paths.get(ANDROID_NDK_ROOT), ndkVersion, targetConfiguration, BUILD_HOST_SUBST, cxxRuntime, false); } Path processPathPattern(Path root, String pattern) { String key = root.toString() + "/" + pattern; Path result = cachedPaths.get(key); if (result == null) { String[] segments = pattern.split("/"); result = root; for (String s : segments) { if (s.contains("{")) { s = s.replace("{toolchain}", targetConfiguration.getToolchain().toString()); s = s.replace("{toolchain_target}", targetConfiguration.getToolchainTarget().toString()); s = s.replace("{compiler_version}", targetConfiguration.getCompiler().getVersion()); s = s.replace("{compiler_type}", targetConfiguration.getCompiler().getType().getName()); s = s.replace("{gcc_compiler_version}", targetConfiguration.getCompiler().getGccVersion()); s = s.replace("{hostname}", hostName); s = s.replace("{target_platform}", targetConfiguration.getTargetAppPlatform()); s = s.replace("{target_arch}", targetConfiguration.getTargetArch().toString()); s = s.replace("{target_arch_abi}", targetConfiguration.getTargetArchAbi().toString()); } result = result.resolve(s); } if (strict) { Assertions.assertCondition(result.toFile().exists(), result.toString() + " doesn't exist."); } cachedPaths.put(key, result); } return result; } private boolean isGcc() { return targetConfiguration.getCompiler().getType() == NdkCxxPlatformCompiler.Type.GCC; } Path processPathPattern(String s) { return processPathPattern(ndkRoot, s); } Path getNdkToolRoot() { if (isGcc()) { return processPathPattern("toolchains/{toolchain}-{compiler_version}/prebuilt/{hostname}"); } else { if (ndkMajorVersion < 11) { return processPathPattern("toolchains/llvm-{compiler_version}/prebuilt/{hostname}"); } else { return processPathPattern("toolchains/llvm/prebuilt/{hostname}"); } } } /** * @return the path to use as the system root, targeted to the given target platform and * architecture. */ Path getSysroot() { return processPathPattern("platforms/{target_platform}/arch-{target_arch}"); } Path getLibexecGccToolPath() { Assertions.assertCondition(isGcc()); if (ndkMajorVersion < 12) { return processPathPattern(getNdkToolRoot(), "libexec/gcc/{toolchain_target}/{compiler_version}"); } else { return processPathPattern(getNdkToolRoot(), "libexec/gcc/{toolchain_target}/{compiler_version}.x"); } } Path getLibPath() { String pattern; if (isGcc()) { if (ndkMajorVersion < 12) { pattern = "lib/{compiler_type}/{toolchain_target}/{compiler_version}"; } else { pattern = "lib/{compiler_type}/{toolchain_target}/{compiler_version}.x"; } } else { if (ndkMajorVersion < 11) { pattern = "lib/{compiler_type}/{compiler_version}"; } else { pattern = "lib64/{compiler_type}/{compiler_version}"; } } return processPathPattern(getNdkToolRoot(), pattern); } Path getNdkGccToolRoot() { return processPathPattern("toolchains/{toolchain}-{gcc_compiler_version}/prebuilt/{hostname}"); } Path getToolchainBinPath() { if (isGcc()) { return processPathPattern(getNdkToolRoot(), "{toolchain_target}/bin"); } else { return processPathPattern(getNdkToolRoot(), "bin"); } } private Path getGccToolchainBinPath() { return processPathPattern(getNdkGccToolRoot(), "{toolchain_target}/bin"); } private Path getCxxRuntimeDirectory() { if (cxxRuntime == CxxRuntime.GNUSTL) { return processPathPattern("sources/cxx-stl/" + cxxRuntime.getName() + "/{gcc_compiler_version}"); } else { return processPathPattern("sources/cxx-stl/" + cxxRuntime.getName()); } } private Path getCxxRuntimeLibsDirectory() { return processPathPattern(getCxxRuntimeDirectory(), "libs/{target_arch_abi}"); } Path getToolPath(String tool) { if (isGcc()) { return processPathPattern(getNdkToolRoot(), "bin/{toolchain_target}-" + tool); } else { return processPathPattern(getNdkToolRoot(), "bin/" + tool); } } public Path getNdkRoot() { return ndkRoot; } public CxxRuntime getCxxRuntime() { return cxxRuntime; } } }