static UnresolvedNdkCxxPlatform build()

in src/com/facebook/buck/android/toolchain/ndk/impl/NdkCxxPlatforms.java [446:681]


  static UnresolvedNdkCxxPlatform build(
      CxxBuckConfig config,
      AndroidBuckConfig androidConfig,
      ProjectFilesystem filesystem,
      Flavor flavor,
      Platform platform,
      Path ndkRoot,
      NdkCxxPlatformTargetConfiguration targetConfiguration,
      NdkCxxRuntime cxxRuntime,
      NdkCxxRuntimeType runtimeType,
      ExecutableFinder executableFinder,
      boolean strictToolchainPaths) {
    String ndkVersion = readVersion(ndkRoot);
    if (getNdkMajorVersion(ndkVersion) > 17
        && cxxRuntime != NdkCxxRuntime.LIBCXX
        && cxxRuntime != NdkCxxRuntime.SYSTEM) {
      throw new HumanReadableException(
          "C++ runtime %s was removed in Android NDK 18.\n"
              + "Detected Android NDK version is %s.\n"
              + "Configuration needs to be changed in order to build with the current Android NDK",
          cxxRuntime.toString(), ndkVersion);
    }
    // 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.
    NdkCompilerType 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 = getHost(platform);

    NdkCxxToolchainPaths toolchainPaths =
        new NdkCxxToolchainPaths(
            filesystem,
            ndkRoot,
            targetConfiguration,
            host.toString(),
            cxxRuntime,
            strictToolchainPaths,
            getUseUnifiedHeaders(androidConfig, ndkVersion));
    // 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, String> sanitizePathsBuilder = ImmutableBiMap.builder();
    sanitizePathsBuilder.put(
        toolchainPaths.getNdkToolRoot(),
        PathFormatter.pathWithUnixSeparators(sanitizedPaths.getNdkToolRoot()));
    if (compilerType != NdkCompilerType.GCC) {
      sanitizePathsBuilder.put(
          toolchainPaths.getNdkGccToolRoot(),
          PathFormatter.pathWithUnixSeparators(sanitizedPaths.getNdkGccToolRoot()));
    }
    sanitizePathsBuilder.put(ndkRoot, AndroidNdkConstants.ANDROID_NDK_ROOT);

    CxxToolProvider.Type type =
        compilerType == NdkCompilerType.CLANG
            ? CxxToolProvider.Type.CLANG
            : CxxToolProvider.Type.GCC;
    ToolProvider ccTool =
        new ConstantToolProvider(
            getCTool(toolchainPaths, compilerType.cc, version, executableFinder));
    ToolProvider cxxTool =
        new ConstantToolProvider(
            getCTool(toolchainPaths, compilerType.cxx, version, executableFinder));
    CompilerProvider cc =
        new CompilerProvider(
            ccTool, () -> type, ToolType.CC, config.getUseDetailedUntrackedHeaderMessages(), true);
    PreprocessorProvider cpp = new PreprocessorProvider(ccTool, type, ToolType.CPP, true);
    CompilerProvider cxx =
        new CompilerProvider(
            cxxTool,
            () -> type,
            ToolType.CXX,
            config.getUseDetailedUntrackedHeaderMessages(),
            true);
    PreprocessorProvider cxxpp = new PreprocessorProvider(cxxTool, type, ToolType.CXXPP, true);

    Optional<SharedLibraryInterfaceParams.Type> sharedLibType = config.getSharedLibraryInterfaces();
    Optional<SharedLibraryInterfaceParams> sharedLibParams = Optional.empty();
    if (sharedLibType.isPresent()
        && sharedLibType.get() != SharedLibraryInterfaceParams.Type.DISABLED) {
      sharedLibParams =
          Optional.of(
              ElfSharedLibraryInterfaceParams.of(
                  new ConstantToolProvider(
                      getGccTool(toolchainPaths, "objcopy", version, executableFinder)),
                  ImmutableList.of(),
                  sharedLibType.get() == SharedLibraryInterfaceParams.Type.DEFINED_ONLY));
    }

    CxxPlatform.Builder cxxPlatformBuilder = CxxPlatform.builder();
    ImmutableBiMap<Path, String> sanitizePaths = sanitizePathsBuilder.build();
    PrefixMapDebugPathSanitizer compilerDebugPathSanitizer =
        new PrefixMapDebugPathSanitizer(".", sanitizePaths, true);
    cxxPlatformBuilder
        .setFlavor(flavor)
        .setAs(cc)
        .addAllAsflags(StringArg.from(getAsflags(targetConfiguration, toolchainPaths)))
        .setAspp(cpp)
        .setCc(cc)
        .addAllCflags(
            StringArg.from(
                getCCompilationFlags(targetConfiguration, toolchainPaths, androidConfig)))
        .setCpp(cpp)
        .addAllCppflags(
            StringArg.from(
                getCPreprocessorFlags(targetConfiguration, toolchainPaths, androidConfig)))
        .setCxx(cxx)
        .addAllCxxflags(
            StringArg.from(
                getCxxCompilationFlags(targetConfiguration, toolchainPaths, androidConfig)))
        .setCxxpp(cxxpp)
        .addAllCxxppflags(
            StringArg.from(
                getCxxPreprocessorFlags(targetConfiguration, toolchainPaths, androidConfig)))
        .setLd(
            new DefaultLinkerProvider(
                LinkerProvider.Type.GNU,
                new ConstantToolProvider(
                    getCcLinkTool(
                        targetConfiguration,
                        toolchainPaths,
                        compilerType.cxx,
                        version,
                        cxxRuntime,
                        executableFinder)),
                config.shouldCacheLinks()))
        .addAllLdflags(StringArg.from(getLdFlags(targetConfiguration, androidConfig)))
        .setStrip(getGccTool(toolchainPaths, "strip", version, executableFinder))
        .setSymbolNameTool(
            new PosixNmSymbolNameTool(
                new ConstantToolProvider(
                    getGccTool(toolchainPaths, "nm", version, executableFinder))))
        .setAr(
            ArchiverProvider.from(
                new GnuArchiver(getGccTool(toolchainPaths, "ar", version, executableFinder))))
        .setArchiveContents(config.getArchiveContents().orElse(ArchiveContents.NORMAL))
        .setRanlib(
            new ConstantToolProvider(
                getGccTool(toolchainPaths, "ranlib", version, executableFinder)))
        // NDK builds are cross compiled, so the header is the same regardless of the host platform.
        .setCompilerDebugPathSanitizer(compilerDebugPathSanitizer)
        .setSharedLibraryExtension("so")
        .setSharedLibraryVersionedExtensionFormat("so.%s")
        .setStaticLibraryExtension("a")
        .setObjectFileExtension("o")
        .setSharedLibraryInterfaceParams(sharedLibParams)
        .setPublicHeadersSymlinksEnabled(config.getPublicHeadersSymlinksEnabled())
        .setPrivateHeadersSymlinksEnabled(config.getPrivateHeadersSymlinksEnabled())
        .setFilepathLengthLimited(config.getFilepathLengthLimited());

    // Add the NDK root path to the white-list so that headers from the NDK won't trigger the
    // verification warnings.  Ideally, long-term, we'd model NDK libs/headers via automatically
    // generated nodes/descriptions so that they wouldn't need to special case it here.
    HeaderVerification headerVerification = config.getHeaderVerificationOrIgnore();
    try {
      headerVerification =
          headerVerification.withPlatformWhitelist(
              ImmutableList.of(
                  "^"
                      + Pattern.quote(ndkRoot.toRealPath().toString() + File.separatorChar)
                      + ".*"));
    } catch (IOException e) {
      LOG.warn(e, "NDK path could not be resolved: %s", ndkRoot);
    }
    cxxPlatformBuilder.setHeaderVerification(headerVerification);
    LOG.debug("NDK root: %s", ndkRoot.toString());
    LOG.debug(
        "Headers verification platform whitelist: %s", headerVerification.getPlatformWhitelist());

    if (cxxRuntime != NdkCxxRuntime.SYSTEM) {
      cxxPlatformBuilder.putRuntimeLdflags(
          Linker.LinkableDepType.SHARED, StringArg.of("-l" + cxxRuntime.sharedName));
      cxxPlatformBuilder.putRuntimeLdflags(
          Linker.LinkableDepType.STATIC, StringArg.of("-l" + cxxRuntime.staticName));

      if (getNdkMajorVersion(ndkVersion) >= 12 && cxxRuntime == NdkCxxRuntime.LIBCXX) {
        if (getNdkMajorVersion(ndkVersion) < 17
            || targetConfiguration.getTargetArchAbi() == NdkTargetArchAbi.ARMEABI_V7A
            || targetConfiguration.getTargetArchAbi() == NdkTargetArchAbi.X86) {
          cxxPlatformBuilder.putRuntimeLdflags(
              Linker.LinkableDepType.STATIC, StringArg.of("-landroid_support"));
        }
        cxxPlatformBuilder.putRuntimeLdflags(
            Linker.LinkableDepType.STATIC, StringArg.of("-lc++abi"));

        if (targetConfiguration.getTargetArchAbi() == NdkTargetArchAbi.ARMEABI_V7A) {
          // libc++abi on 32-bit ARM depends on the LLVM unwinder; if not explicitly
          // included here, clang++ would resolve references to _Unwind_RaiseException
          // and related symbols with implementations provided by libgcc.a, which is
          // not ABI-compatible with libc++ (and would most likely result in crashes
          // when throwing exceptions).
          cxxPlatformBuilder.putRuntimeLdflags(
              Linker.LinkableDepType.STATIC, StringArg.of("-lunwind"));
          // Don't export symbols from libunwind and libgcc in the linked binary.
          cxxPlatformBuilder.putRuntimeLdflags(
              Linker.LinkableDepType.STATIC, StringArg.of("-Wl,--exclude-libs,libunwind.a"));
          cxxPlatformBuilder.putRuntimeLdflags(
              Linker.LinkableDepType.STATIC, StringArg.of("-Wl,--exclude-libs,libgcc.a"));
        }

        if (targetConfiguration.getTargetArchAbi() == NdkTargetArchAbi.ARMEABI) {
          cxxPlatformBuilder.putRuntimeLdflags(
              Linker.LinkableDepType.STATIC, StringArg.of("-latomic"));
        }
      }
    }

    CxxPlatform cxxPlatform = cxxPlatformBuilder.build();

    NdkCxxPlatform.Builder builder = NdkCxxPlatform.builder();
    builder
        .setCxxPlatform(cxxPlatform)
        .setCxxRuntime(cxxRuntime)
        .setObjdump(getGccTool(toolchainPaths, "objdump", version, executableFinder));
    if ((cxxRuntime != NdkCxxRuntime.SYSTEM) && (runtimeType != NdkCxxRuntimeType.STATIC)) {
      builder.setCxxSharedRuntimePath(
          PathSourcePath.of(
              filesystem,
              toolchainPaths.getCxxRuntimeLibsDirectory().resolve(cxxRuntime.getSoname())));
    }
    return StaticUnresolvedNdkCxxPlatform.of(builder.build());
  }