| /* |
| * 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.CxxPlatform; |
| import com.facebook.buck.cxx.DebugPathSanitizer; |
| import com.facebook.buck.cxx.GnuLinker; |
| import com.facebook.buck.cxx.ImmutableCxxPlatform; |
| import com.facebook.buck.cxx.Linker; |
| import com.facebook.buck.cxx.Tool; |
| import com.facebook.buck.cxx.VersionedTool; |
| import com.facebook.buck.model.Flavor; |
| import com.facebook.buck.util.HumanReadableException; |
| import com.facebook.buck.util.environment.Platform; |
| import com.google.common.base.Optional; |
| 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.io.Files; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.charset.Charset; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| |
| public class NdkCxxPlatforms { |
| // Utility class, do not instantiate. |
| private NdkCxxPlatforms() { } |
| |
| /** |
| * The build toolchain, named (including compiler version) after the target platform/arch. |
| */ |
| public static enum Toolchain { |
| |
| X86_4_8("x86-4.8"), |
| ARM_LINUX_ADNROIDEABI_4_8("arm-linux-androideabi-4.8"), |
| ; |
| |
| private final String value; |
| |
| private Toolchain(String value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| @Override |
| public String toString() { |
| return value; |
| } |
| |
| } |
| |
| /** |
| * The prefix used for tools built for the above toolchain. |
| */ |
| public static enum ToolchainPrefix { |
| |
| I686_LINUX_ANDROID("i686-linux-android"), |
| ARM_LINUX_ANDROIDEABI("arm-linux-androideabi"), |
| ; |
| |
| private final String value; |
| |
| private ToolchainPrefix(String value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| @Override |
| public String toString() { |
| return value; |
| } |
| |
| } |
| |
| /** |
| * Name of the target CPU architecture. |
| */ |
| public static enum TargetArch { |
| |
| X86("x86"), |
| ARM("arm"), |
| ; |
| |
| private final String value; |
| |
| private TargetArch(String value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| @Override |
| public String toString() { |
| return value; |
| } |
| |
| } |
| |
| /** |
| * Name of the target CPU + ABI. |
| */ |
| public static enum TargetArchAbi { |
| |
| X86("x86"), |
| ARMEABI("armeabi"), |
| ARMEABI_V7A("armeabi-v7a"), |
| ; |
| |
| private final String value; |
| |
| private TargetArchAbi(String value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| @Override |
| public String toString() { |
| return value; |
| } |
| |
| } |
| |
| /** |
| * The OS and Architecture that we're building on. |
| */ |
| public static enum Host { |
| |
| DARWIN_X86_64("darwin-x86_64"), |
| LINUX_X86_64("linux-x86_64"), |
| WINDOWS_X86_64("windows-x86_64"), |
| ; |
| |
| private final String value; |
| |
| private Host(String value) { |
| this.value = Preconditions.checkNotNull(value); |
| } |
| |
| @Override |
| public String toString() { |
| return value; |
| } |
| |
| } |
| |
| /** |
| * The C/C++ runtime library to link against. |
| */ |
| public static enum CxxRuntime { |
| |
| SYSTEM("system", "system"), |
| GABIXX("gabi++_shared", "gabi++_static"), |
| STLPORT("stlport_shared", "stlport_static"), |
| GNUSTL("gnustl_shared", "gnustl_static"), |
| ; |
| |
| private final String sharedName; |
| private final String staticName; |
| |
| private CxxRuntime(String sharedName, String staticName) { |
| this.sharedName = sharedName; |
| this.staticName = staticName; |
| } |
| |
| public String getStaticName() { |
| return staticName; |
| } |
| |
| public String getSharedName() { |
| return sharedName; |
| } |
| |
| public String getSoname() { |
| return "lib" + sharedName + ".so"; |
| } |
| |
| } |
| |
| /** |
| * A container for all configuration settings needed to define a build target. |
| */ |
| public static class TargetConfiguration { |
| |
| public final Toolchain toolchain; |
| public final ToolchainPrefix toolchainPrefix; |
| public final TargetArch targetArch; |
| public final TargetArchAbi targetArchAbi; |
| public final String targetPlatform; |
| public final String compilerVersion; |
| public final ImmutableList<String> compilerFlags; |
| public final ImmutableList<String> linkerFlags; |
| |
| public TargetConfiguration( |
| Toolchain toolchain, |
| ToolchainPrefix toolchainPrefix, |
| TargetArch targetArch, |
| TargetArchAbi targetArchAbi, |
| String targetPlatform, |
| String compilerVersion, |
| ImmutableList<String> compilerFlags, |
| ImmutableList<String> linkerFlags) { |
| this.toolchain = Preconditions.checkNotNull(toolchain); |
| this.toolchainPrefix = Preconditions.checkNotNull(toolchainPrefix); |
| this.targetArch = Preconditions.checkNotNull(targetArch); |
| this.targetArchAbi = Preconditions.checkNotNull(targetArchAbi); |
| this.targetPlatform = Preconditions.checkNotNull(targetPlatform); |
| this.compilerVersion = Preconditions.checkNotNull(compilerVersion); |
| this.compilerFlags = Preconditions.checkNotNull(compilerFlags); |
| this.linkerFlags = Preconditions.checkNotNull(linkerFlags); |
| } |
| |
| } |
| |
| 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); |
| |
| public static NdkCxxPlatform build( |
| Flavor flavor, |
| Platform platform, |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| CxxRuntime cxxRuntime) { |
| |
| String version = readVersion(ndkRoot); |
| |
| Host host = Preconditions.checkNotNull(BUILD_PLATFORMS.get(platform)); |
| |
| ImmutableCxxPlatform.Builder cxxPlatformBuilder = ImmutableCxxPlatform.builder(); |
| cxxPlatformBuilder |
| .setFlavor(flavor) |
| .setAs(getTool(ndkRoot, targetConfiguration, host, "as", version)) |
| // Default assembler flags added by the NDK to enforce the NX (no execute) security feature. |
| .addAsflags("--noexecstack") |
| .setAspp(getTool(ndkRoot, targetConfiguration, host, "gcc", version)) |
| .setCc(getTool(ndkRoot, targetConfiguration, host, "gcc", version)) |
| .addAllCflags(getCflagsInternal(targetConfiguration)) |
| .setCpp(getCppTool(ndkRoot, targetConfiguration, host, "gcc", version)) |
| .addAllCppflags(getCppflags(ndkRoot, targetConfiguration)) |
| .setCxx(getTool(ndkRoot, targetConfiguration, host, "g++", version)) |
| .addAllCxxflags(getCxxflagsInternal(targetConfiguration)) |
| .setCxxpp(getCppTool(ndkRoot, targetConfiguration, host, "g++", version)) |
| .addAllCxxppflags(getCxxppflags(ndkRoot, targetConfiguration)) |
| .setCxxld( |
| getCcLinkTool( |
| ndkRoot, |
| targetConfiguration, |
| host, |
| cxxRuntime, |
| "g++", |
| version)) |
| .addAllCxxldflags(targetConfiguration.linkerFlags) |
| .setLd( |
| new GnuLinker( |
| getTool( |
| ndkRoot, |
| targetConfiguration, |
| host, |
| "ld.gold", |
| version))) |
| // Default linker flags added by the NDK |
| .addLdflags( |
| // Enforce the NX (no execute) security feature |
| "-z", "noexecstack", |
| // Strip unused code |
| "--gc-sections", |
| // Forbid dangerous copy "relocations" |
| "-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. |
| "--as-needed") |
| .setAr(getTool(ndkRoot, targetConfiguration, host, "ar", version)) |
| .setDebugPathSanitizer( |
| Optional.of( |
| new DebugPathSanitizer( |
| 250, |
| File.separatorChar, |
| Paths.get("."), |
| ImmutableBiMap.of( |
| getNdkToolRoot(ndkRoot, targetConfiguration, host), |
| Paths.get("ANDROID_TOOLS_ROOT"), |
| ndkRoot, |
| Paths.get("ANDROID_NDK_ROOT"))))) |
| .setSharedLibraryExtension("so"); |
| |
| 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 ImmutableNdkCxxPlatform.builder() |
| .setCxxPlatform(cxxPlatform) |
| .setObjcopy(getToolPath(ndkRoot, targetConfiguration, host, "objcopy")) |
| .setCxxRuntime(cxxRuntime) |
| .setCxxSharedRuntimePath( |
| getCxxRuntimeDirectory(ndkRoot, targetConfiguration) |
| .resolve(cxxRuntime.getSoname())) |
| .build(); |
| } |
| |
| // Read the NDK version from the "RELEASE.TXT" at the NDK root. |
| private static String readVersion(Path ndkRoot) { |
| try { |
| return Files.readFirstLine( |
| ndkRoot.resolve("RELEASE.TXT").toFile(), |
| Charset.defaultCharset()).trim(); |
| } catch (IOException e) { |
| throw new HumanReadableException( |
| e, |
| "could not extract version from NDK repository at %s: %s", |
| ndkRoot, |
| e.getMessage()); |
| } |
| } |
| |
| private static Path getNdkToolRoot( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| Host host) { |
| return ndkRoot |
| .resolve("toolchains") |
| .resolve(targetConfiguration.toolchain.toString()) |
| .resolve("prebuilt") |
| .resolve(host.toString()); |
| } |
| |
| private static Path getToolPath( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| Host host, |
| String tool) { |
| return getNdkToolRoot(ndkRoot, targetConfiguration, host) |
| .resolve(targetConfiguration.toolchainPrefix.toString()) |
| .resolve("bin") |
| .resolve(tool); |
| } |
| |
| private static Tool getTool( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| Host host, |
| String tool, |
| String version) { |
| return new VersionedTool( |
| getToolPath(ndkRoot, targetConfiguration, host, tool), |
| ImmutableList.<String>of(), |
| tool, |
| targetConfiguration.toolchain.toString() + " " + version); |
| } |
| |
| private static Tool getCppTool( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| Host host, |
| String tool, |
| String version) { |
| return new VersionedTool( |
| getToolPath(ndkRoot, targetConfiguration, host, tool), |
| ImmutableList.of( |
| "-isystem", ndkRoot |
| .resolve("toolchains") |
| .resolve(targetConfiguration.toolchain.toString()) |
| .resolve("prebuilt") |
| .resolve(host.toString()) |
| .resolve("include") |
| .toString(), |
| "-isystem", ndkRoot |
| .resolve("toolchains") |
| .resolve(targetConfiguration.toolchain.toString()) |
| .resolve("prebuilt") |
| .resolve(host.toString()) |
| .resolve("lib") |
| .resolve("gcc") |
| .resolve(targetConfiguration.toolchainPrefix.toString()) |
| .resolve(targetConfiguration.compilerVersion) |
| .resolve("include") |
| .toString()), |
| tool, |
| targetConfiguration.toolchain.toString() + " " + version); |
| } |
| |
| private static Path getCxxRuntimeDirectory( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration) { |
| return ndkRoot |
| .resolve("sources") |
| .resolve("cxx-stl") |
| .resolve("gnu-libstdc++") |
| .resolve(targetConfiguration.compilerVersion) |
| .resolve("libs") |
| .resolve(targetConfiguration.targetArchAbi.toString()); |
| } |
| |
| private static Tool getCcLinkTool( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration, |
| Host host, |
| CxxRuntime cxxRuntime, |
| String tool, |
| String version) { |
| return new VersionedTool( |
| getToolPath(ndkRoot, targetConfiguration, host, tool), |
| ImmutableList.of( |
| "-B" + ndkRoot |
| .resolve("platforms") |
| .resolve(targetConfiguration.targetPlatform) |
| .resolve("arch-" + targetConfiguration.targetArch) |
| .resolve("usr") |
| .resolve("lib") |
| .toString(), |
| "-L" + getCxxRuntimeDirectory(ndkRoot, targetConfiguration).toString()), |
| tool, |
| targetConfiguration.toolchain.toString() + " " + version + " " + cxxRuntime.toString()); |
| } |
| |
| /** |
| * Flags to be used when either preprocessing or compiling C or C++ sources. |
| */ |
| private static ImmutableList<String> getCommonFlags() { |
| return ImmutableList.of( |
| // Enable default warnings and turn them into errors. |
| "-Wall", |
| "-Werror", |
| // NDK builds enable stack protector and debug symbols by default. |
| "-fstack-protector", |
| "-g3"); |
| } |
| |
| /** |
| * 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( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration) { |
| return ImmutableList.of( |
| "-isystem", ndkRoot |
| .resolve("platforms") |
| .resolve(targetConfiguration.targetPlatform) |
| .resolve("arch-" + targetConfiguration.targetArch) |
| .resolve("usr") |
| .resolve("include") |
| .toString(), |
| "-isystem", ndkRoot |
| .resolve("platforms") |
| .resolve(targetConfiguration.targetPlatform) |
| .resolve("arch-" + targetConfiguration.targetArch) |
| .resolve("usr") |
| .resolve("include") |
| .resolve("linux") |
| .toString()); |
| } |
| |
| private static ImmutableList<String> getCppflags( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration) { |
| return ImmutableList.<String>builder() |
| .addAll(targetConfiguration.compilerFlags) |
| .addAll(getCommonPreprocessorFlags()) |
| .addAll(getCommonCFlags()) |
| .addAll(getCommonFlags()) |
| .addAll(getCommonIncludes(ndkRoot, targetConfiguration)) |
| .build(); |
| } |
| |
| private static ImmutableList<String> getCxxppflags( |
| Path ndkRoot, |
| TargetConfiguration targetConfiguration) { |
| return ImmutableList.<String>builder() |
| .addAll(targetConfiguration.compilerFlags) |
| .addAll(getCommonPreprocessorFlags()) |
| .addAll(getCommonCxxFlags()) |
| .addAll(getCommonFlags()) |
| .add("-Wno-literal-suffix") |
| .add( |
| "-isystem", |
| ndkRoot |
| .resolve("sources") |
| .resolve("cxx-stl") |
| .resolve("gnu-libstdc++") |
| .resolve(targetConfiguration.compilerVersion) |
| .resolve("include") |
| .toString()) |
| .add( |
| "-isystem", |
| ndkRoot |
| .resolve("sources") |
| .resolve("cxx-stl") |
| .resolve("gnu-libstdc++") |
| .resolve(targetConfiguration.compilerVersion) |
| .resolve("libs") |
| .resolve(targetConfiguration.targetArchAbi.toString()) |
| .resolve("include") |
| .toString()) |
| .addAll(getCommonIncludes(ndkRoot, targetConfiguration)) |
| .build(); |
| } |
| |
| /** |
| * Flags used when compiling either C or C++ sources. |
| */ |
| private static ImmutableList<String> getCommonCompilerFlags() { |
| 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( |
| TargetConfiguration targetConfiguration) { |
| return ImmutableList.<String>builder() |
| .addAll(targetConfiguration.compilerFlags) |
| .addAll(getCommonCFlags()) |
| .addAll(getCommonFlags()) |
| .addAll(getCommonCompilerFlags()) |
| .build(); |
| } |
| |
| private static ImmutableList<String> getCxxflagsInternal( |
| TargetConfiguration targetConfiguration) { |
| return ImmutableList.<String>builder() |
| .addAll(targetConfiguration.compilerFlags) |
| .addAll(getCommonCxxFlags()) |
| .addAll(getCommonFlags()) |
| .addAll(getCommonCompilerFlags()) |
| .build(); |
| } |
| } |