/*
 * Copyright 2016-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.swift.toolchain.impl;

import com.facebook.buck.apple.platform_type.ApplePlatformType;
import com.facebook.buck.core.toolchain.tool.Tool;
import com.facebook.buck.core.util.log.Logger;
import com.facebook.buck.swift.toolchain.SwiftPlatform;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;

public class SwiftPlatformFactory {

  private static final Logger LOG = Logger.get(SwiftPlatformFactory.class);

  // Utility class, do not instantiate.
  private SwiftPlatformFactory() {}

  public static SwiftPlatform build(
      String platformName,
      Set<Path> toolchainPaths,
      Tool swiftc,
      Optional<Tool> swiftStdLibTool,
      boolean shouldLinkSystemSwift) {
    SwiftPlatform.Builder builder =
        SwiftPlatform.builder()
            .setSwiftc(swiftc)
            .setSwiftStdlibTool(swiftStdLibTool)
            .setSwiftSharedLibraryRunPaths(
                buildSharedRunPaths(platformName, shouldLinkSystemSwift));

    for (Path toolchainPath : toolchainPaths) {
      Optional<Path> foundSwiftRuntimePath = findSwiftRuntimePath(toolchainPath, platformName);
      if (foundSwiftRuntimePath.isPresent()) {
        builder.addSwiftRuntimePaths(foundSwiftRuntimePath.get());
      }

      Path swiftStaticRuntimePath =
          toolchainPath.resolve("usr/lib/swift_static").resolve(platformName);
      if (Files.isDirectory(swiftStaticRuntimePath)) {
        builder.addSwiftStaticRuntimePaths(swiftStaticRuntimePath);
      }
    }
    return builder.build();
  }

  public static Optional<Path> findSwiftRuntimePath(Path toolchainPath, String platformName) {
    if (platformName == "driverkit") {
      return Optional.empty();
    }

    // The location of the Swift stdlib changed in Xcode 11, and swift-stdlib-tool wasn't updated to
    // accommodate, so we need to manually find and supply the new path.
    Path swiftRuntimePath = toolchainPath.resolve("usr/lib/swift").resolve(platformName);
    String libSwiftCoreDylibName = "libswiftCore.dylib";
    LOG.debug("Searching for swift toolchain in: %s", toolchainPath.toString());
    if (Files.exists(swiftRuntimePath.resolve(libSwiftCoreDylibName))) {
      LOG.debug("Found swift toolchain at %s", swiftRuntimePath.toString());
      return Optional.of(swiftRuntimePath);
    } else {
      Path toolchainUsrLib = toolchainPath.resolve("usr/lib");
      try (DirectoryStream<Path> toolchainUsrLibDirectoryStream =
          Files.newDirectoryStream(toolchainUsrLib, "swift*")) {
        // Go through all swift* folders inside toolchain/usr/lib and find the first that contains
        // libSwiftCore.dylib.
        Path swiftStdLibRoot = null;
        for (Path swiftFolder : toolchainUsrLibDirectoryStream) {
          if (Files.exists(swiftFolder.resolve(platformName).resolve(libSwiftCoreDylibName))) {
            swiftStdLibRoot = swiftFolder.resolve(platformName);
          }

          if (swiftStdLibRoot != null) {
            break;
          }
        }
        if (swiftStdLibRoot != null) {
          return Optional.of(swiftStdLibRoot);
        }
      } catch (IOException x) {
        LOG.debug(
            "Unable to find swift libraries in toolchain: %s for platform: %s. Exception: %s.",
            toolchainPath, platformName, x.getLocalizedMessage());
        return Optional.empty();
      }
      LOG.debug(
          "Unable to find swift libraries in toolchain: %s for platform: %s",
          toolchainPath, platformName);
      return Optional.empty();
    }
  }

  private static ImmutableList<Path> buildSharedRunPaths(
      String platformName, boolean shouldLinkSystemSwift) {
    ApplePlatformType platformType = ApplePlatformType.of(platformName);

    ArrayList<Path> paths = new ArrayList<Path>();
    if (shouldLinkSystemSwift) {
      paths.add(Paths.get("/usr/lib/swift"));
    }
    if (platformType == ApplePlatformType.MAC) {
      paths.add(Paths.get("@executable_path", "..", "Frameworks"));
      paths.add(Paths.get("@loader_path", "..", "Frameworks"));
    } else {
      paths.add(Paths.get("@executable_path", "Frameworks"));
      paths.add(Paths.get("@loader_path", "Frameworks"));
    }

    return ImmutableList.copyOf(paths);
  }
}

@@ -2056,24 +2056,305 @@
 );%0A      %7D%0A%0A
+      Optional%3CPath%3E foundSwiftCompatibilityRuntimePath =%0A          findSwiftCompatibilityRuntimePath(toolchainPath, platformName);%0A      if (foundSwiftCompatibilityRuntimePath.isPresent()) %7B%0A        builder.addSwiftRuntimePaths(foundSwiftCompatibilityRuntimePath.get());%0A      %7D%0A%0A
       Path s
@@ -4618,16 +4618,990 @@
  %7D%0A  %7D%0A%0A
+  public static Optional%3CPath%3E findSwiftCompatibilityRuntimePath(%0A      Path toolchainPath, String platformName) %7B%0A    if (platformName == %22driverkit%22) %7B%0A      return Optional.empty();%0A    %7D%0A%0A    // Currently Xcode includes swift and swift-5.0 directories, and each contains different%0A    // contents. Specifically, swift/platform contains the libswiftCompatibility50 and%0A    // libswiftCompatibilityDynamicReplacements libraries which are *also* necessary.%0A    Path swiftRuntimePath = toolchainPath.resolve(%22usr/lib/swift%22).resolve(platformName);%0A    String libSwiftCompatibilityLibraryName = %22libswiftCompatibility50.a%22;%0A    LOG.debug(%22Searching for swift compatibility toolchain in %25s%22, toolchainPath.toString());%0A    if (Files.exists(swiftRuntimePath.resolve(libSwiftCompatibilityLibraryName))) %7B%0A      LOG.debug(%22Found swift compatibility toolchain at %25s%22, toolchainPath.toString());%0A      return Optional.of(swiftRuntimePath);%0A    %7D%0A%0A    return Optional.empty();%0A  %7D%0A%0A
   privat

Include toolchain/usr/lib/swift in linker paths