public static AppleCxxPlatform buildWithXcodeToolFinder()

in src/com/facebook/buck/apple/toolchain/impl/AppleCxxPlatforms.java [158:516]


  public static AppleCxxPlatform buildWithXcodeToolFinder(
      ProjectFilesystem filesystem,
      AppleSdk targetSdk,
      String minVersion,
      String targetArchitecture,
      AppleSdkPaths sdkPaths,
      BuckConfig buckConfig,
      XcodeToolFinder xcodeToolFinder,
      XcodeBuildVersionCache xcodeBuildVersionCache) {
    AppleCxxPlatform.Builder platformBuilder = AppleCxxPlatform.builder();

    ImmutableList.Builder<Path> toolSearchPathsBuilder = ImmutableList.builder();
    // Search for tools from most specific to least specific.
    toolSearchPathsBuilder
        .add(sdkPaths.getSdkPath().resolve(USR_BIN))
        .add(sdkPaths.getSdkPath().resolve("Developer").resolve(USR_BIN))
        .add(sdkPaths.getPlatformPath().resolve("Developer").resolve(USR_BIN));
    for (Path toolchainPath : sdkPaths.getToolchainPaths()) {
      toolSearchPathsBuilder.add(toolchainPath.resolve(USR_BIN));
    }
    if (sdkPaths.getDeveloperPath().isPresent()) {
      toolSearchPathsBuilder.add(sdkPaths.getDeveloperPath().get().resolve(USR_BIN));
      toolSearchPathsBuilder.add(sdkPaths.getDeveloperPath().get().resolve("Tools"));
    }

    // TODO(beng): Add more and better cflags.
    ImmutableList.Builder<String> cflagsBuilder = ImmutableList.builder();
    cflagsBuilder.add("-isysroot", sdkPaths.getSdkPath().toString());
    cflagsBuilder.add("-arch", targetArchitecture);
    cflagsBuilder.add(targetSdk.getApplePlatform().getMinVersionFlagPrefix() + minVersion);

    if (targetSdk.getApplePlatform().equals(ApplePlatform.WATCHOS)) {
      cflagsBuilder.add("-fembed-bitcode");
    }

    AppleConfig appleConfig = buckConfig.getView(AppleConfig.class);

    // Populate Xcode version keys from Xcode's own Info.plist if available.
    Optional<String> xcodeBuildVersion = Optional.empty();
    Optional<Path> developerPath = sdkPaths.getDeveloperPath();
    if (developerPath.isPresent()) {
      Path xcodeBundlePath = developerPath.get().getParent();
      if (xcodeBundlePath != null) {
        Path xcodeInfoPlistPath = xcodeBundlePath.resolve("Info.plist");
        try (InputStream stream = Files.newInputStream(xcodeInfoPlistPath)) {
          NSDictionary parsedXcodeInfoPlist = (NSDictionary) PropertyListParser.parse(stream);

          NSObject xcodeVersionObject = parsedXcodeInfoPlist.objectForKey("DTXcode");
          if (xcodeVersionObject != null) {
            Optional<String> xcodeVersion = Optional.of(xcodeVersionObject.toString());
            platformBuilder.setXcodeVersion(xcodeVersion);
          }
        } catch (IOException e) {
          LOG.warn(
              "Error reading Xcode's info plist %s; ignoring Xcode versions", xcodeInfoPlistPath);
        } catch (PropertyListFormatException
            | ParseException
            | ParserConfigurationException
            | SAXException e) {
          LOG.warn("Error in parsing %s; ignoring Xcode versions", xcodeInfoPlistPath);
        }
      }

      xcodeBuildVersion = xcodeBuildVersionCache.lookup(developerPath.get());
      platformBuilder.setXcodeBuildVersion(xcodeBuildVersion);
      LOG.debug("Xcode build version is: " + xcodeBuildVersion.orElse("<absent>"));
    }

    ImmutableList.Builder<String> versions = ImmutableList.builder();
    versions.add(targetSdk.getVersion());

    ImmutableList<String> toolchainVersions =
        targetSdk.getToolchains().stream()
            .map(AppleToolchain::getVersion)
            .flatMap(RichStream::from)
            .collect(ImmutableList.toImmutableList());
    if (toolchainVersions.isEmpty()) {
      if (!xcodeBuildVersion.isPresent()) {
        throw new HumanReadableException("Failed to read toolchain versions and Xcode version.");
      }
      versions.add(xcodeBuildVersion.get());
    } else {
      versions.addAll(toolchainVersions);
    }

    String version = Joiner.on(':').join(versions.build());

    ImmutableList<Path> toolSearchPaths = toolSearchPathsBuilder.build();

    Tool clangPath =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "clang", version);

    Tool clangXxPath =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "clang++", version);

    Tool ar =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "ar", version);

    Tool ranlib =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "ranlib", version);

    Tool strip =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "strip", version);

    Tool nm =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "nm", version);

    Tool actool =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "actool", version);

    Tool ibtool =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "ibtool", version);

    Tool libtool =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "libtool", version);

    Tool momc =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "momc", version);

    Tool xctest =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "xctest", version);

    Tool dsymutil =
        getXcodeTool(
            filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "dsymutil", version);

    // We are seeing a stack overflow in dsymutil during (fat) LTO
    // builds. Upstream dsymutil was patched to avoid recursion in the
    // offending path in https://reviews.llvm.org/D48899, and
    // https://reviews.llvm.org/D45172 mentioned that there is much
    // more stack space available when single threaded.
    if (appleConfig.shouldWorkAroundDsymutilLTOStackOverflowBug()) {
      dsymutil = new CommandTool.Builder(dsymutil).addArg("-num-threads=1").build();
    }

    Tool lipo =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "lipo", version);

    Tool lldb =
        getXcodeTool(filesystem, toolSearchPaths, xcodeToolFinder, appleConfig, "lldb", version);

    Optional<Path> stubBinaryPath =
        targetSdk
            .getApplePlatform()
            .getStubBinaryPath()
            .map(input -> sdkPaths.getSdkPath().resolve(input));

    UserFlavor targetFlavor =
        UserFlavor.of(
            Flavor.replaceInvalidCharacters(targetSdk.getName() + "-" + targetArchitecture),
            String.format("SDK: %s, architecture: %s", targetSdk.getName(), targetArchitecture));
    CxxBuckConfig config =
        appleConfig.useFlavoredCxxSections()
            ? new CxxBuckConfig(buckConfig, targetFlavor)
            : new CxxBuckConfig(buckConfig);

    ImmutableBiMap.Builder<Path, String> sanitizerPaths = ImmutableBiMap.builder();
    sanitizerPaths.put(sdkPaths.getSdkPath(), "APPLE_SDKROOT");
    sanitizerPaths.put(sdkPaths.getPlatformPath(), "APPLE_PLATFORM_DIR");
    if (sdkPaths.getDeveloperPath().isPresent()) {
      sanitizerPaths.put(sdkPaths.getDeveloperPath().get(), "APPLE_DEVELOPER_DIR");
    }

    // https://github.com/facebook/buck/pull/1168: add the root cell's absolute path to the quote
    // include path, and also force it to be sanitized by all user rule keys.
    if (appleConfig.addCellPathToIquotePath()) {
      sanitizerPaths.put(filesystem.getRootPath().getPath(), ".");
      cflagsBuilder.add("-iquote", filesystem.getRootPath().toString());
    }

    DebugPathSanitizer compilerDebugPathSanitizer =
        new PrefixMapDebugPathSanitizer(
            DebugPathSanitizer.getPaddedDir(
                ".", config.getDebugPathSanitizerLimit(), File.separatorChar),
            sanitizerPaths.build());

    ImmutableList<String> cflags = cflagsBuilder.build();

    ImmutableMap.Builder<String, String> macrosBuilder = ImmutableMap.builder();
    macrosBuilder.put("SDKROOT", sdkPaths.getSdkPath().toString());
    macrosBuilder.put("PLATFORM_DIR", sdkPaths.getPlatformPath().toString());
    macrosBuilder.put("CURRENT_ARCH", targetArchitecture);
    if (sdkPaths.getDeveloperPath().isPresent()) {
      macrosBuilder.put("DEVELOPER_DIR", sdkPaths.getDeveloperPath().get().toString());
    }
    ImmutableMap<String, String> macros = macrosBuilder.build();

    Optional<String> buildVersion = Optional.empty();
    Path platformVersionPlistPath = sdkPaths.getPlatformPath().resolve("version.plist");
    try (InputStream versionPlist = Files.newInputStream(platformVersionPlistPath)) {
      NSDictionary versionInfo = (NSDictionary) PropertyListParser.parse(versionPlist);
      if (versionInfo != null) {
        NSObject productBuildVersion = versionInfo.objectForKey("ProductBuildVersion");
        if (productBuildVersion != null) {
          buildVersion = Optional.of(productBuildVersion.toString());
        } else {
          LOG.warn(
              "In %s, missing ProductBuildVersion. Build version will be unset for this platform.",
              platformVersionPlistPath);
        }
      } else {
        LOG.warn(
            "Empty version plist in %s. Build version will be unset for this platform.",
            platformVersionPlistPath);
      }
    } catch (NoSuchFileException e) {
      LOG.warn(
          "%s does not exist. Build version will be unset for this platform.",
          platformVersionPlistPath);
    } catch (PropertyListFormatException
        | SAXException
        | ParserConfigurationException
        | ParseException
        | IOException e) {
      // Some other error occurred, print the exception since it may contain error details.
      LOG.warn(
          e,
          "Failed to parse %s. Build version will be unset for this platform.",
          platformVersionPlistPath);
    }

    PreprocessorProvider aspp =
        new PreprocessorProvider(
            new ConstantToolProvider(clangPath), CxxToolProvider.Type.CLANG, ToolType.ASPP);
    CompilerProvider as =
        new CompilerProvider(
            new ConstantToolProvider(clangPath),
            CxxToolProvider.Type.CLANG,
            ToolType.AS,
            config.getUseDetailedUntrackedHeaderMessages());
    PreprocessorProvider cpp =
        new PreprocessorProvider(
            new ConstantToolProvider(clangPath), CxxToolProvider.Type.CLANG, ToolType.CPP);
    CompilerProvider cc =
        new CompilerProvider(
            new ConstantToolProvider(clangPath),
            CxxToolProvider.Type.CLANG,
            ToolType.CC,
            config.getUseDetailedUntrackedHeaderMessages());
    PreprocessorProvider cxxpp =
        new PreprocessorProvider(
            new ConstantToolProvider(clangXxPath), CxxToolProvider.Type.CLANG, ToolType.CXXPP);
    CompilerProvider cxx =
        new CompilerProvider(
            new ConstantToolProvider(clangXxPath),
            CxxToolProvider.Type.CLANG,
            ToolType.CXX,
            config.getUseDetailedUntrackedHeaderMessages());
    ImmutableList.Builder<String> whitelistBuilder = ImmutableList.builder();
    whitelistBuilder.add("^" + Pattern.quote(sdkPaths.getSdkPath().toString()) + "\\/.*");
    whitelistBuilder.add(
        "^"
            + Pattern.quote(sdkPaths.getPlatformPath() + "/Developer/Library/Frameworks")
            + "\\/.*");
    for (Path toolchainPath : sdkPaths.getToolchainPaths()) {
      LOG.debug("Apple toolchain path: %s", toolchainPath);
      try {
        whitelistBuilder.add("^" + Pattern.quote(toolchainPath.toRealPath().toString()) + "\\/.*");
      } catch (IOException e) {
        LOG.warn(e, "Apple toolchain path could not be resolved: %s", toolchainPath);
      }
    }
    HeaderVerification headerVerification =
        config.getHeaderVerificationOrIgnore().withPlatformWhitelist(whitelistBuilder.build());
    LOG.debug(
        "Headers verification platform whitelist: %s", headerVerification.getPlatformWhitelist());
    ImmutableList<String> ldFlags =
        getLdFlags(targetSdk, filesystem, xcodeToolFinder, toolSearchPaths, appleConfig, version);
    ImmutableList<String> combinedLdFlags =
        ImmutableList.<String>builder().addAll(cflags).addAll(ldFlags).build();
    ImmutableList<Arg> cflagsArgs = ImmutableList.copyOf(StringArg.from(cflags));

    CxxPlatform cxxPlatform =
        CxxPlatforms.build(
            targetFlavor,
            Platform.MACOS,
            config,
            as,
            aspp,
            cc,
            cxx,
            cpp,
            cxxpp,
            new DefaultLinkerProvider(
                LinkerProvider.Type.DARWIN,
                new ConstantToolProvider(clangXxPath),
                config.shouldCacheLinks(),
                appleConfig.shouldLinkScrubConcurrently()),
            StringArg.from(combinedLdFlags),
            ImmutableMultimap.of(),
            strip,
            ArchiverProvider.from(new BsdArchiver(ar)),
            ArchiveContents.NORMAL,
            Optional.of(new ConstantToolProvider(ranlib)),
            new PosixNmSymbolNameTool(new ConstantToolProvider(nm)),
            cflagsArgs,
            ImmutableList.of(),
            cflagsArgs,
            ImmutableList.of(),
            cflagsArgs,
            ImmutableList.of(),
            "dylib",
            "%s.dylib",
            "a",
            "o",
            Optional.empty(),
            compilerDebugPathSanitizer,
            macros,
            Optional.empty(),
            headerVerification,
            config.getPublicHeadersSymlinksEnabled(),
            config.getPrivateHeadersSymlinksEnabled(),
            PicType.PIC);

    ApplePlatform applePlatform = targetSdk.getApplePlatform();
    ImmutableList.Builder<Path> swiftOverrideSearchPathBuilder = ImmutableList.builder();
    AppleSdkPaths.Builder swiftSdkPathsBuilder = AppleSdkPaths.builder().from(sdkPaths);
    Optional<SwiftPlatform> swiftPlatform =
        getSwiftPlatform(
            SwiftTargetTriple.of(
                targetArchitecture,
                "apple",
                applePlatform.getSwiftName().orElse(applePlatform.getName()),
                minVersion,
                applePlatform.getIsSimulator()),
            version,
            targetSdk,
            swiftSdkPathsBuilder.build(),
            appleConfig.shouldLinkSystemSwift(),
            swiftOverrideSearchPathBuilder.addAll(toolSearchPaths).build(),
            xcodeToolFinder,
            filesystem);

    platformBuilder
        .setCxxPlatform(cxxPlatform)
        .setSwiftPlatform(swiftPlatform)
        .setAppleSdk(targetSdk)
        .setAppleSdkPaths(sdkPaths)
        .setMinVersion(minVersion)
        .setBuildVersion(buildVersion)
        .setActool(actool)
        .setLibtool(libtool)
        .setIbtool(ibtool)
        .setMomc(momc)
        .setCopySceneKitAssets(
            getOptionalTool(
                "copySceneKitAssets", toolSearchPaths, xcodeToolFinder, version, filesystem))
        .setXctest(xctest)
        .setDsymutil(dsymutil)
        .setLipo(lipo)
        .setStubBinary(stubBinaryPath)
        .setLldb(lldb)
        .setCodesignAllocate(
            getOptionalTool(
                "codesign_allocate", toolSearchPaths, xcodeToolFinder, version, filesystem))
        .setCodesignProvider(appleConfig.getCodesignProvider());

    return platformBuilder.build();
  }