private PBXNativeTarget generateBinaryTarget()

in src/com/facebook/buck/features/apple/project/ProjectGenerator.java [1280:2082]


  private PBXNativeTarget generateBinaryTarget(
      PBXProject project,
      Optional<? extends TargetNode<? extends HasAppleBundleFields>> bundle,
      TargetNode<? extends CxxLibraryDescription.CommonArg> targetNode,
      ProductType productType,
      String productOutputFormat,
      Optional<Path> infoPlistOptional,
      boolean includeFrameworks,
      ImmutableSet<AppleResourceDescriptionArg> recursiveResources,
      ImmutableSet<AppleResourceDescriptionArg> directResources,
      ImmutableSet<AppleAssetCatalogDescriptionArg> recursiveAssetCatalogs,
      ImmutableSet<AppleAssetCatalogDescriptionArg> directAssetCatalogs,
      ImmutableSet<AppleWrapperResourceArg> wrapperResources,
      Optional<Iterable<PBXBuildPhase>> copyFilesPhases,
      Optional<TargetNode<AppleBundleDescriptionArg>> bundleLoaderNode)
      throws IOException {

    LOG.debug("Generating binary target for node %s", targetNode);

    TargetNode<?> buildTargetNode = bundle.isPresent() ? bundle.get() : targetNode;
    BuildTarget buildTarget = buildTargetNode.getBuildTarget();
    boolean containsSwiftCode = projGenerationStateCache.targetContainsSwiftSourceCode(targetNode);

    String buildTargetName = getProductNameForBuildTargetNode(buildTargetNode);
    CxxLibraryDescription.CommonArg arg = targetNode.getConstructorArg();
    NewNativeTargetProjectMutator mutator =
        new NewNativeTargetProjectMutator(
            pathRelativizer, sourcePath -> resolveSourcePath(sourcePath).getPath());

    // Both exported headers and exported platform headers will be put into the symlink tree
    // exported platform headers will be excluded and then included by platform
    ImmutableSet.Builder<SourcePath> exportedHeadersBuilder = ImmutableSet.builder();
    exportedHeadersBuilder.addAll(getHeaderSourcePaths(arg.getExportedHeaders()));
    PatternMatchedCollection<SourceSortedSet> exportedPlatformHeaders =
        arg.getExportedPlatformHeaders();
    for (SourceSortedSet headersSet : exportedPlatformHeaders.getValues()) {
      exportedHeadersBuilder.addAll(getHeaderSourcePaths(headersSet));
    }

    ImmutableSet<SourcePath> exportedHeaders = exportedHeadersBuilder.build();
    ImmutableSet.Builder<SourcePath> headersBuilder = ImmutableSet.builder();
    headersBuilder.addAll(getHeaderSourcePaths(arg.getHeaders()));
    for (SourceSortedSet headersSet : arg.getPlatformHeaders().getValues()) {
      headersBuilder.addAll(getHeaderSourcePaths(headersSet));
    }
    ImmutableSet<SourcePath> headers = headersBuilder.build();
    ImmutableMap<CxxSource.Type, ImmutableList<StringWithMacros>> langPreprocessorFlags =
        targetNode.getConstructorArg().getLangPreprocessorFlags();
    ImmutableMap<CxxSource.Type, ImmutableList<StringWithMacros>> langCompilerFlags =
        targetNode.getConstructorArg().getLangCompilerFlags();
    boolean isFocusedOnTarget = focusModules.isFocusedOn(buildTarget);

    Optional<String> swiftVersion = getSwiftVersionForTargetNode(targetNode);
    boolean hasSwiftVersionArg = swiftVersion.isPresent();
    if (!swiftVersion.isPresent()) {
      swiftVersion = swiftBuckConfig.getVersion();
    }

    mutator
        .setTargetName(getXcodeTargetName(buildTarget))
        .setProduct(
            productType,
            buildTargetName,
            Paths.get(String.format(productOutputFormat, buildTargetName)));

    boolean isModularAppleLibrary = isModularAppleLibrary(targetNode);
    mutator.setFrameworkHeadersEnabled(isModularAppleLibrary);

    ImmutableMap.Builder<String, String> swiftDepsSettingsBuilder = ImmutableMap.builder();
    ImmutableList.Builder<String> swiftDebugLinkerFlagsBuilder = ImmutableList.builder();

    ImmutableMap.Builder<String, String> extraSettingsBuilder = ImmutableMap.builder();
    ImmutableMap.Builder<String, String> defaultSettingsBuilder = ImmutableMap.builder();

    ImmutableList<Pair<Pattern, SourceSortedSet>> platformHeaders =
        arg.getPlatformHeaders().getPatternsAndValues();
    ImmutableList.Builder<Pair<Pattern, Iterable<SourcePath>>> platformHeadersIterableBuilder =
        ImmutableList.builder();
    for (Pair<Pattern, SourceSortedSet> platformHeader : platformHeaders) {
      platformHeadersIterableBuilder.add(
          new Pair<>(platformHeader.getFirst(), getHeaderSourcePaths(platformHeader.getSecond())));
    }

    ImmutableList<Pair<Pattern, SourceSortedSet>> exportedPlatformHeadersPatternsAndValues =
        exportedPlatformHeaders.getPatternsAndValues();
    for (Pair<Pattern, SourceSortedSet> exportedPlatformHeader :
        exportedPlatformHeadersPatternsAndValues) {
      platformHeadersIterableBuilder.add(
          new Pair<>(
              exportedPlatformHeader.getFirst(),
              getHeaderSourcePaths(exportedPlatformHeader.getSecond())));
    }

    ImmutableList<Pair<Pattern, Iterable<SourcePath>>> platformHeadersIterable =
        platformHeadersIterableBuilder.build();

    ImmutableList<Pair<Pattern, ImmutableSortedSet<SourceWithFlags>>> platformSources =
        arg.getPlatformSrcs().getPatternsAndValues();
    ImmutableMap<String, ImmutableSortedSet<String>> platformExcludedSourcesMapping =
        ProjectGenerator.gatherExcludedSources(
            appleCxxFlavors,
            platformSources,
            platformHeadersIterable,
            outputDirectory,
            defaultPathResolver);
    for (Map.Entry<String, ImmutableSortedSet<String>> platformExcludedSources :
        platformExcludedSourcesMapping.entrySet()) {
      if (platformExcludedSources.getValue().size() > 0) {
        extraSettingsBuilder.put(
            platformExcludedSources.getKey(), String.join(" ", platformExcludedSources.getValue()));
      }
    }

    ImmutableSortedSet<SourceWithFlags> nonPlatformSrcs = arg.getSrcs();
    ImmutableSortedSet.Builder<SourceWithFlags> allSrcsBuilder = ImmutableSortedSet.naturalOrder();
    allSrcsBuilder.addAll(nonPlatformSrcs);
    for (Pair<Pattern, ImmutableSortedSet<SourceWithFlags>> platformSource : platformSources) {
      allSrcsBuilder.addAll(platformSource.getSecond());
    }

    ImmutableSortedSet<SourceWithFlags> allSrcs = allSrcsBuilder.build();

    if (!options.shouldGenerateHeaderSymlinkTreesOnly()) {
      if (isFocusedOnTarget) {
        filesAddedBuilder.addAll(
            allSrcs.stream().map(s -> s.getSourcePath()).collect(ImmutableList.toImmutableList()));
        mutator
            .setLangPreprocessorFlags(
                ImmutableMap.copyOf(
                    Maps.transformValues(
                        langPreprocessorFlags, f -> convertStringWithMacros(targetNode, f))))
            .setLangCompilerFlags(
                ImmutableMap.copyOf(
                    Maps.transformValues(
                        langCompilerFlags, f -> convertStringWithMacros(targetNode, f))))
            .setPublicHeaders(exportedHeaders)
            .setPrefixHeader(getPrefixHeaderSourcePath(arg))
            .setSourcesWithFlags(ImmutableSet.copyOf(allSrcs))
            .setPrivateHeaders(headers)
            .setRecursiveResources(recursiveResources)
            .setDirectResources(directResources)
            .setWrapperResources(wrapperResources)
            .setExtraXcodeSources(ImmutableSet.copyOf(arg.getExtraXcodeSources()))
            .setExtraXcodeFiles(ImmutableSet.copyOf(arg.getExtraXcodeFiles()));
      }

      if (bundle.isPresent() && isFocusedOnTarget) {
        HasAppleBundleFields bundleArg = bundle.get().getConstructorArg();
        mutator.setInfoPlist(Optional.of(bundleArg.getInfoPlist()));
      }

      mutator.setBridgingHeader(arg.getBridgingHeader());

      if (options.shouldCreateDirectoryStructure() && isFocusedOnTarget) {
        mutator.setTargetGroupPath(
            RichStream.from(
                    buildTarget
                        .getCellRelativeBasePath()
                        .getPath()
                        .toPath(projectFilesystem.getFileSystem()))
                .map(Object::toString)
                .toImmutableList());
      }

      if (!recursiveAssetCatalogs.isEmpty() && isFocusedOnTarget) {
        mutator.setRecursiveAssetCatalogs(recursiveAssetCatalogs);
      }

      if (!directAssetCatalogs.isEmpty() && isFocusedOnTarget) {
        mutator.setDirectAssetCatalogs(directAssetCatalogs);
      }

      FluentIterable<TargetNode<?>> depTargetNodes = collectRecursiveLibraryDepTargets(targetNode);

      if (includeFrameworks && isFocusedOnTarget) {

        if (!options.shouldAddLinkedLibrariesAsFlags()) {
          mutator.setFrameworks(getSytemFrameworksLibsForTargetNode(targetNode));
        }

        if (sharedLibraryToBundle.isPresent()) {
          // Replace target nodes of libraries which are actually constituents of embedded
          // frameworks to the bundle representing the embedded framework.
          // This will be converted to a reference to the xcode build product for the embedded
          // framework rather than the dylib
          depTargetNodes =
              swapSharedLibrariesForBundles(depTargetNodes, sharedLibraryToBundle.get());
        }

        ImmutableSet<PBXFileReference> targetNodeDeps =
            filterRecursiveLibraryDependenciesForLinkerPhase(depTargetNodes);

        if (isTargetNodeApplicationTestTarget(targetNode, bundleLoaderNode)) {
          ImmutableSet<PBXFileReference> bundleLoaderDeps =
              bundleLoaderNode.isPresent()
                  ? collectRecursiveLibraryDependencies(bundleLoaderNode.get())
                  : ImmutableSet.of();
          mutator.setArchives(Sets.difference(targetNodeDeps, bundleLoaderDeps));
        } else {
          mutator.setArchives(targetNodeDeps);
        }
      }

      if (isFocusedOnTarget) {
        ImmutableSet<TargetNode<?>> swiftDepTargets =
            filterRecursiveLibraryDepTargetsWithSwiftSources(depTargetNodes);

        if (!includeFrameworks && !swiftDepTargets.isEmpty()) {
          // If the current target, which is non-shared (e.g., static lib), depends on other focused
          // targets which include Swift code, we must ensure those are treated as dependencies so
          // that Xcode builds the targets in the correct order. Unfortunately, those deps can be
          // part of other projects which would require cross-project references.
          //
          // Thankfully, there's an easy workaround because we can just create a phony copy phase
          // which depends on the outputs of the deps (i.e., the static libs). The copy phase
          // will effectively say "Copy libX.a from Products Dir into Products Dir" which is a nop.
          // To be on the safe side, we're explicitly marking the copy phase as only running for
          // deployment postprocessing (i.e., "Copy only when installing") and disabling
          // deployment postprocessing (it's enabled by default for release builds).
          PBXCopyFilesBuildPhase copyFiles =
              new PBXCopyFilesBuildPhase(
                  CopyFilePhaseDestinationSpec.of(PBXCopyFilesBuildPhase.Destination.PRODUCTS));
          copyFiles.setRunOnlyForDeploymentPostprocessing(Optional.of(Boolean.TRUE));
          copyFiles.setName(Optional.of("Fake Swift Dependencies (Copy Files Phase)"));

          ImmutableSet<PBXFileReference> swiftDepsFileRefs =
              targetNodesSetToPBXFileReference(swiftDepTargets);
          for (PBXFileReference fileRef : swiftDepsFileRefs) {
            PBXBuildFile buildFile = new PBXBuildFile(fileRef);
            copyFiles.getFiles().add(buildFile);
          }

          swiftDepsSettingsBuilder.put("DEPLOYMENT_POSTPROCESSING", "NO");

          mutator.setSwiftDependenciesBuildPhase(copyFiles);
        }

        if (includeFrameworks
            && !swiftDepTargets.isEmpty()
            && shouldEmbedSwiftRuntimeInBundleTarget(bundle)
            && swiftBuckConfig.getProjectEmbedRuntime()) {
          // This is a binary that transitively depends on a library that uses Swift. We must ensure
          // that the Swift runtime is bundled.
          swiftDepsSettingsBuilder.put("ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
        }

        if (includeFrameworks
            && !swiftDepTargets.isEmpty()
            && swiftBuckConfig.getProjectAddASTPaths()) {
          for (TargetNode<?> swiftNode : swiftDepTargets) {
            String swiftModulePath =
                String.format(
                    "${BUILT_PRODUCTS_DIR}/%s.swiftmodule/${CURRENT_ARCH}.swiftmodule",
                    getModuleName(swiftNode));
            swiftDebugLinkerFlagsBuilder.add("-Xlinker");
            swiftDebugLinkerFlagsBuilder.add("-add_ast_path");
            swiftDebugLinkerFlagsBuilder.add("-Xlinker");
            swiftDebugLinkerFlagsBuilder.add(swiftModulePath);
          }
        }
      }

      // TODO(Task #3772930): Go through all dependencies of the rule
      // and add any shell script rules here
      ImmutableList.Builder<TargetNode<?>> preScriptPhasesBuilder = ImmutableList.builder();
      ImmutableList.Builder<TargetNode<?>> postScriptPhasesBuilder = ImmutableList.builder();
      if (bundle.isPresent() && targetNode != bundle.get() && isFocusedOnTarget) {
        collectBuildScriptDependencies(
            targetGraph.getAll(bundle.get().getDeclaredDeps()),
            preScriptPhasesBuilder,
            postScriptPhasesBuilder);
      }
      collectBuildScriptDependencies(
          targetGraph.getAll(targetNode.getDeclaredDeps()),
          preScriptPhasesBuilder,
          postScriptPhasesBuilder);
      if (isFocusedOnTarget) {
        ImmutableList<TargetNode<?>> preScriptPhases = preScriptPhasesBuilder.build();
        ImmutableList<TargetNode<?>> postScriptPhases = postScriptPhasesBuilder.build();

        mutator.setPreBuildRunScriptPhasesFromTargetNodes(
            preScriptPhases, actionGraphBuilderForNode::apply);
        if (copyFilesPhases.isPresent()) {
          mutator.setCopyFilesPhases(copyFilesPhases.get());
        }
        mutator.setPostBuildRunScriptPhasesFromTargetNodes(
            postScriptPhases, actionGraphBuilderForNode::apply);

        ImmutableList<TargetNode<?>> scriptPhases =
            Stream.concat(preScriptPhases.stream(), postScriptPhases.stream())
                .collect(ImmutableList.toImmutableList());
        mutator.collectFilesToCopyInXcode(
            filesToCopyInXcodeBuilder, scriptPhases, projectCell, actionGraphBuilderForNode::apply);
      }
    }

    NewNativeTargetProjectMutator.Result targetBuilderResult =
        mutator.buildTargetAndAddToProject(project, isFocusedOnTarget);
    PBXNativeTarget target = targetBuilderResult.target;
    Optional<PBXGroup> targetGroup = targetBuilderResult.targetGroup;

    extraSettingsBuilder.putAll(swiftDepsSettingsBuilder.build());

    setAppIconSettings(
        recursiveAssetCatalogs, directAssetCatalogs, buildTarget, defaultSettingsBuilder);
    setLaunchImageSettings(
        recursiveAssetCatalogs, directAssetCatalogs, buildTarget, defaultSettingsBuilder);

    ImmutableSortedMap<Path, SourcePath> publicCxxHeaders = getPublicCxxHeaders(targetNode);
    if (isModularAppleLibrary(targetNode) && isFrameworkProductType(productType)) {
      // Modular frameworks should not include Buck-generated hmaps as they break the VFS overlay
      // that's generated by Xcode and consequently, all headers part of a framework's umbrella
      // header fail the modularity test, as they're expected to be mapped by the VFS layer under
      // $BUILT_PRODUCTS_DIR/Module.framework/Versions/A/Headers.
      publicCxxHeaders = ImmutableSortedMap.of();
    }

    if (!options.shouldGenerateHeaderSymlinkTreesOnly()) {
      if (isFocusedOnTarget) {
        SourceTreePath buckFilePath =
            new SourceTreePath(
                PBXReference.SourceTree.SOURCE_ROOT,
                pathRelativizer.outputPathToBuildTargetPath(buildTarget).resolve(buildFileName),
                Optional.empty());
        PBXFileReference buckReference =
            targetGroup.get().getOrCreateFileReferenceBySourceTreePath(buckFilePath);
        buckReference.setExplicitFileType(Optional.of("text.script.python"));
      }

      // Watch dependencies need to have explicit target dependencies setup in order for Xcode to
      // build them properly within the IDE.  It is unable to match the implicit dependency because
      // of the different in flavor between the targets (iphoneos vs watchos).
      if (bundle.isPresent() && isFocusedOnTarget) {
        collectProjectTargetWatchDependencies(
            targetNode.getBuildTarget().getFlavorPostfix(),
            target,
            targetGraph.getAll(bundle.get().getExtraDeps()));
      }

      // -- configurations
      extraSettingsBuilder
          .put("TARGET_NAME", buildTargetName)
          .put("SRCROOT", pathRelativizer.outputPathToBuildTargetPath(buildTarget).toString());
      if (productType == ProductTypes.UI_TEST && isFocusedOnTarget) {
        if (bundleLoaderNode.isPresent()) {
          BuildTarget testTarget = bundleLoaderNode.get().getBuildTarget();
          extraSettingsBuilder.put("TEST_TARGET_NAME", getXcodeTargetName(testTarget));
          addPBXTargetDependency(target, testTarget);
          for (BuildTarget depTarget : buildTargetNode.getDeclaredDeps()) {
            Object depArg = targetGraph.get(depTarget).getConstructorArg();
            if (depArg instanceof HasAppleBundleFields && isApp((HasAppleBundleFields) depArg)) {
              addPBXTargetDependency(target, depTarget);
            }
          }
        } else {
          throw new HumanReadableException(
              "The test rule '%s' is configured with 'is_ui_test' but has no test_host_app",
              buildTargetName);
        }
      } else if (bundleLoaderNode.isPresent() && isFocusedOnTarget) {
        TargetNode<AppleBundleDescriptionArg> bundleLoader = bundleLoaderNode.get();
        String bundleLoaderProductName = getProductName(bundleLoader);
        String bundleLoaderBundleName =
            bundleLoaderProductName
                + "."
                + getExtensionString(bundleLoader.getConstructorArg().getExtension());
        // NOTE(grp): This is a hack. We need to support both deep (OS X) and flat (iOS)
        // style bundles for the bundle loader, but at this point we don't know what platform
        // the bundle loader (or current target) is going to be built for. However, we can be
        // sure that it's the same as the target (presumably a test) we're building right now.
        //
        // Using that knowledge, we can do build setting tricks to defer choosing the bundle
        // loader path until Xcode build time, when the platform is known. There's no build
        // setting that conclusively says whether the current platform uses deep bundles:
        // that would be too easy. But in the cases we care about (unit test bundles), the
        // current bundle will have a style matching the style of the bundle loader app, so
        // we can take advantage of that to do the determination.
        //
        // Unfortunately, the build setting for the bundle structure (CONTENTS_FOLDER_PATH)
        // includes the WRAPPER_NAME, so we can't just interpolate that in. Instead, we have
        // to use another trick with build setting operations and evaluation. By using the
        // $(:file) operation, we can extract the last component of the contents path: either
        // "Contents" or the current bundle name. Then, we can interpolate with that expected
        // result in the build setting name to conditionally choose a different loader path.

        // The conditional that decides which path is used. This is a complex Xcode build setting
        // expression that expands to one of two values, depending on the last path component of
        // the CONTENTS_FOLDER_PATH variable. As described above, this will be either "Contents"
        // for deep bundles or the bundle file name itself for flat bundles. Finally, to santiize
        // the potentially invalid build setting names from the bundle file name, it converts that
        // to an identifier. We rely on BUNDLE_LOADER_BUNDLE_STYLE_CONDITIONAL_<bundle file name>
        // being undefined (and thus expanding to nothing) for the path resolution to work.
        //
        // The operations on the CONTENTS_FOLDER_PATH are documented here:
        // http://codeworkshop.net/posts/xcode-build-setting-transformations
        String bundleLoaderOutputPathConditional =
            "$(BUNDLE_LOADER_BUNDLE_STYLE_CONDITIONAL_$(CONTENTS_FOLDER_PATH:file:identifier))";

        // If the $(CONTENTS_FOLDER_PATH:file:identifier) expands to this, we add the deep bundle
        // path into the bundle loader. See above for the case when it will expand to this value.
        extraSettingsBuilder.put(
            "BUNDLE_LOADER_BUNDLE_STYLE_CONDITIONAL_Contents",
            Joiner.on('/')
                .join(
                    getTargetOutputPath(bundleLoader),
                    bundleLoaderBundleName,
                    "Contents/MacOS",
                    bundleLoaderProductName));

        extraSettingsBuilder.put(
            "BUNDLE_LOADER_BUNDLE_STYLE_CONDITIONAL_"
                + getProductName(bundle.get())
                + "_"
                + getExtensionString(bundle.get().getConstructorArg().getExtension()),
            Joiner.on('/')
                .join(
                    getTargetOutputPath(bundleLoader),
                    bundleLoaderBundleName,
                    bundleLoaderProductName));

        extraSettingsBuilder
            .put("BUNDLE_LOADER", bundleLoaderOutputPathConditional)
            .put("TEST_HOST", "$(BUNDLE_LOADER)");

        addPBXTargetDependency(target, bundleLoader.getBuildTarget());
      }
      if (infoPlistOptional.isPresent()) {
        Path infoPlistPath = pathRelativizer.outputDirToRootRelative(infoPlistOptional.get());
        extraSettingsBuilder.put("INFOPLIST_FILE", infoPlistPath.toString());
      }
      if (arg.getBridgingHeader().isPresent()) {
        Path bridgingHeaderPath =
            pathRelativizer.outputDirToRootRelative(
                resolveSourcePath(arg.getBridgingHeader().get()).getPath());
        extraSettingsBuilder.put(
            "SWIFT_OBJC_BRIDGING_HEADER",
            Joiner.on('/').join("$(SRCROOT)", bridgingHeaderPath.toString()));
      }

      swiftVersion.ifPresent(s -> extraSettingsBuilder.put("SWIFT_VERSION", s));
      swiftVersion.ifPresent(
          s -> extraSettingsBuilder.put("PRODUCT_MODULE_NAME", getModuleName(targetNode)));

      if (hasSwiftVersionArg && containsSwiftCode && isFocusedOnTarget) {
        extraSettingsBuilder.put(
            "SWIFT_OBJC_INTERFACE_HEADER_NAME", getSwiftObjCGeneratedHeaderName(buildTargetNode));

        if (swiftBuckConfig.getProjectWMO()) {
          // We must disable "Index While Building" as there's a bug in the LLVM infra which
          // makes the compilation fail.
          extraSettingsBuilder.put("COMPILER_INDEX_STORE_ENABLE", "NO");

          // This is a hidden Xcode setting which is needed for two reasons:
          // - Stops Xcode adding .o files for each Swift compilation unit to dependency db
          //   which is used during linking (which will fail with WMO).
          // - Turns on WMO itself.
          //
          // Note that setting SWIFT_OPTIMIZATION_LEVEL (which is public) to '-Owholemodule'
          // ends up crashing the Swift compiler for some reason while this doesn't.
          extraSettingsBuilder.put("SWIFT_WHOLE_MODULE_OPTIMIZATION", "YES");
        }
      }

      Optional<SourcePath> prefixHeaderOptional =
          getPrefixHeaderSourcePath(targetNode.getConstructorArg());
      if (prefixHeaderOptional.isPresent()) {
        RelPath prefixHeaderRelative = resolveSourcePath(prefixHeaderOptional.get());
        Path prefixHeaderPath =
            pathRelativizer.outputDirToRootRelative(prefixHeaderRelative.getPath());
        extraSettingsBuilder.put("GCC_PREFIX_HEADER", prefixHeaderPath.toString());
        extraSettingsBuilder.put("GCC_PRECOMPILE_PREFIX_HEADER", "YES");
      }

      boolean shouldSetUseHeadermap = false;
      if (isModularAppleLibrary) {
        extraSettingsBuilder.put("CLANG_ENABLE_MODULES", "YES");
        extraSettingsBuilder.put("DEFINES_MODULE", "YES");

        if (isFrameworkProductType(productType)) {
          // Modular frameworks need to have both USE_HEADERMAP enabled so that Xcode generates
          // .framework VFS overlays, in modular libraries we handle this in buck
          shouldSetUseHeadermap = true;
        }
      }
      extraSettingsBuilder.put("USE_HEADERMAP", shouldSetUseHeadermap ? "YES" : "NO");

      AbsPath repoRoot = projectFilesystem.getRootPath().normalize();
      defaultSettingsBuilder.put("REPO_ROOT", repoRoot.toString());
      if (hasSwiftVersionArg && containsSwiftCode && isFocusedOnTarget) {
        // We need to be able to control the directory where Xcode places the derived sources, so
        // that the Obj-C Generated Header can be included in the header map and imported through
        // a framework-style import like <Module/Module-Swift.h>
        Path derivedSourcesDir =
            getDerivedSourcesDirectoryForBuildTarget(buildTarget, projectFilesystem);
        defaultSettingsBuilder.put(
            "DERIVED_FILE_DIR", repoRoot.resolve(derivedSourcesDir).toString());
      }

      defaultSettingsBuilder.put(PRODUCT_NAME, getProductName(buildTargetNode));
      bundle.ifPresent(
          bundleNode ->
              defaultSettingsBuilder.put(
                  "WRAPPER_EXTENSION",
                  getExtensionString(bundleNode.getConstructorArg().getExtension())));

      // We use BUILT_PRODUCTS_DIR as the root for the everything being built. Target-
      // specific output is placed within CONFIGURATION_BUILD_DIR, inside BUILT_PRODUCTS_DIR.
      // That allows Copy Files build phases to reference files in the CONFIGURATION_BUILD_DIR
      // of other targets by using paths relative to the target-independent BUILT_PRODUCTS_DIR.
      defaultSettingsBuilder.put(
          "BUILT_PRODUCTS_DIR",
          // $EFFECTIVE_PLATFORM_NAME starts with a dash, so this expands to something like:
          // $SYMROOT/Debug-iphonesimulator
          Joiner.on('/').join("$SYMROOT", "$CONFIGURATION$EFFECTIVE_PLATFORM_NAME"));
      defaultSettingsBuilder.put("CONFIGURATION_BUILD_DIR", "$BUILT_PRODUCTS_DIR");
      boolean nodeIsAppleLibrary = targetNode.getDescription() instanceof AppleLibraryDescription;
      boolean nodeIsCxxLibrary = targetNode.getDescription() instanceof CxxLibraryDescription;
      if (!bundle.isPresent() && (nodeIsAppleLibrary || nodeIsCxxLibrary)) {
        defaultSettingsBuilder.put("EXECUTABLE_PREFIX", "lib");
      }

      if (isFocusedOnTarget) {
        Set<Path> recursivePublicSystemIncludeDirectories =
            collectRecursivePublicSystemIncludeDirectories(targetNode);
        Set<Path> recursivePublicIncludeDirectories =
            collectRecursivePublicIncludeDirectories(targetNode);
        Set<Path> includeDirectories = extractIncludeDirectories(targetNode);

        // Explicitly add system include directories to compile flags to mute warnings,
        // XCode seems to not support system include directories directly.
        // But even if headers dirs are passed as flags, we still need to add
        // them to `HEADER_SEARCH_PATH` otherwise header generation for Swift interop
        // won't work (it doesn't use `OTHER_XXX_FLAGS`).
        Iterable<String> systemIncludeDirectoryFlags =
            StreamSupport.stream(recursivePublicSystemIncludeDirectories.spliterator(), false)
                .map(path -> "-isystem" + path)
                .collect(Collectors.toList());

        ImmutableSet<Path> recursiveHeaderSearchPaths =
            collectRecursiveHeaderSearchPaths(targetNode);
        ImmutableSet<Path> headerMapBases = collectRecursiveHeaderMapBases(targetNode);

        ImmutableMap.Builder<String, String> appendConfigsBuilder = ImmutableMap.builder();
        appendConfigsBuilder.putAll(
            getFrameworkAndLibrarySearchPathConfigs(targetNode, includeFrameworks));
        appendConfigsBuilder.put(
            "HEADER_SEARCH_PATHS",
            Joiner.on(' ')
                .join(
                    Iterables.concat(
                        recursiveHeaderSearchPaths,
                        headerMapBases,
                        recursivePublicSystemIncludeDirectories,
                        recursivePublicIncludeDirectories,
                        includeDirectories)));
        if (hasSwiftVersionArg && isFocusedOnTarget) {
          ImmutableSet<Path> swiftIncludePaths = collectRecursiveSwiftIncludePaths(targetNode);
          Stream<String> allValues =
              Streams.concat(
                  Stream.of("$BUILT_PRODUCTS_DIR"),
                  Streams.stream(swiftIncludePaths)
                      .map((path) -> path.toString())
                      .map(Escaper.BASH_ESCAPER));
          appendConfigsBuilder.put(
              "SWIFT_INCLUDE_PATHS", allValues.collect(Collectors.joining(" ")));
        }

        ImmutableList.Builder<String> targetSpecificSwiftFlags = ImmutableList.builder();
        Optional<TargetNode<SwiftCommonArg>> swiftTargetNode =
            TargetNodes.castArg(targetNode, SwiftCommonArg.class);
        targetSpecificSwiftFlags.addAll(
            swiftTargetNode
                .map(
                    x ->
                        convertStringWithMacros(
                            targetNode, x.getConstructorArg().getSwiftCompilerFlags()))
                .orElse(ImmutableList.of()));

        if (containsSwiftCode && isModularAppleLibrary && publicCxxHeaders.size() > 0) {
          targetSpecificSwiftFlags.addAll(collectModularTargetSpecificSwiftFlags(targetNode));
        }

        ImmutableList<String> testingOverlay = getFlagsForExcludesForModulesUnderTests(targetNode);
        Iterable<String> otherSwiftFlags =
            Utils.distinctUntilChanged(
                Iterables.concat(
                    swiftBuckConfig.getCompilerFlags().orElse(DEFAULT_SWIFTFLAGS),
                    targetSpecificSwiftFlags.build()));

        Iterable<String> targetCFlags =
            Utils.distinctUntilChanged(
                ImmutableList.<String>builder()
                    .addAll(
                        convertStringWithMacros(
                            targetNode, collectRecursiveExportedPreprocessorFlags(targetNode)))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, targetNode.getConstructorArg().getCompilerFlags()))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, targetNode.getConstructorArg().getPreprocessorFlags()))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, collectRecursiveSystemPreprocessorFlags(targetNode)))
                    .addAll(systemIncludeDirectoryFlags)
                    .addAll(testingOverlay)
                    .build());
        Iterable<String> targetCxxFlags =
            Utils.distinctUntilChanged(
                ImmutableList.<String>builder()
                    .addAll(
                        convertStringWithMacros(
                            targetNode, collectRecursiveExportedPreprocessorFlags(targetNode)))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, targetNode.getConstructorArg().getCompilerFlags()))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, targetNode.getConstructorArg().getPreprocessorFlags()))
                    .addAll(
                        convertStringWithMacros(
                            targetNode, collectRecursiveSystemPreprocessorFlags(targetNode)))
                    .addAll(systemIncludeDirectoryFlags)
                    .addAll(testingOverlay)
                    .build());

        appendConfigsBuilder
            .put(
                "OTHER_SWIFT_FLAGS",
                Streams.stream(otherSwiftFlags)
                    .map(Escaper.BASH_ESCAPER)
                    .collect(Collectors.joining(" ")))
            .put(
                "OTHER_CFLAGS",
                Streams.stream(
                        Iterables.concat(
                            cxxBuckConfig.getCflags().orElse(DEFAULT_CFLAGS),
                            cxxBuckConfig.getCppflags().orElse(DEFAULT_CPPFLAGS),
                            targetCFlags))
                    .map(Escaper.BASH_ESCAPER)
                    .collect(Collectors.joining(" ")))
            .put(
                "OTHER_CPLUSPLUSFLAGS",
                Streams.stream(
                        Iterables.concat(
                            cxxBuckConfig.getCxxflags().orElse(DEFAULT_CXXFLAGS),
                            cxxBuckConfig.getCxxppflags().orElse(DEFAULT_CXXPPFLAGS),
                            targetCxxFlags))
                    .map(Escaper.BASH_ESCAPER)
                    .collect(Collectors.joining(" ")));

        Iterable<String> otherLdFlags =
            ImmutableList.<String>builder()
                .addAll(cxxBuckConfig.getLdflags().orElse(DEFAULT_LDFLAGS))
                .addAll(appleConfig.linkAllObjC() ? ImmutableList.of("-ObjC") : ImmutableList.of())
                .addAll(
                    convertStringWithMacros(
                        targetNode,
                        Iterables.concat(
                            targetNode.getConstructorArg().getLinkerFlags(),
                            collectRecursiveExportedLinkerFlags(targetNode))))
                .addAll(swiftDebugLinkerFlagsBuilder.build())
                .build();

        updateOtherLinkerFlagsForOptions(
            targetNode, bundleLoaderNode, appendConfigsBuilder, otherLdFlags);

        ImmutableMultimap<String, ImmutableList<String>> platformFlags =
            convertPlatformFlags(
                targetNode,
                Iterables.concat(
                    ImmutableList.of(targetNode.getConstructorArg().getPlatformCompilerFlags()),
                    ImmutableList.of(targetNode.getConstructorArg().getPlatformPreprocessorFlags()),
                    collectRecursiveExportedPlatformPreprocessorFlags(targetNode)));
        for (Flavor platformFlavor : appleCxxFlavors) {
          Optional<CxxBuckConfig> platformConfig =
              Optional.ofNullable(platformCxxBuckConfigs.get(platformFlavor));
          String platform = platformFlavor.getName();

          // The behavior below matches the CxxPlatform behavior where it adds the cxx flags,
          // then the cxx#platform flags, then the flags for the target
          appendConfigsBuilder
              .put(
                  generateConfigKey("OTHER_CFLAGS", platform),
                  Streams.stream(
                          Utils.distinctUntilChanged(
                              Iterables.transform(
                                  Iterables.concat(
                                      cxxBuckConfig.getCflags().orElse(DEFAULT_CFLAGS),
                                      platformConfig
                                          .flatMap(CxxBuckConfig::getCflags)
                                          .orElse(DEFAULT_CFLAGS),
                                      cxxBuckConfig.getCppflags().orElse(DEFAULT_CPPFLAGS),
                                      platformConfig
                                          .flatMap(CxxBuckConfig::getCppflags)
                                          .orElse(DEFAULT_CPPFLAGS),
                                      targetCFlags,
                                      Iterables.concat(platformFlags.get(platform))),
                                  Escaper.BASH_ESCAPER::apply)))
                      .collect(Collectors.joining(" ")))
              .put(
                  generateConfigKey("OTHER_CPLUSPLUSFLAGS", platform),
                  Streams.stream(
                          Utils.distinctUntilChanged(
                              Iterables.transform(
                                  Iterables.concat(
                                      cxxBuckConfig.getCxxflags().orElse(DEFAULT_CPPFLAGS),
                                      platformConfig
                                          .flatMap(CxxBuckConfig::getCxxflags)
                                          .orElse(DEFAULT_CXXFLAGS),
                                      cxxBuckConfig.getCxxppflags().orElse(DEFAULT_CXXPPFLAGS),
                                      platformConfig
                                          .flatMap(CxxBuckConfig::getCxxppflags)
                                          .orElse(DEFAULT_CXXPPFLAGS),
                                      targetCxxFlags,
                                      Iterables.concat(platformFlags.get(platform))),
                                  Escaper.BASH_ESCAPER::apply)))
                      .collect(Collectors.joining(" ")));
        }

        ImmutableMultimap<String, ImmutableList<String>> platformLinkerFlags =
            convertPlatformFlags(
                targetNode,
                Iterables.concat(
                    ImmutableList.of(targetNode.getConstructorArg().getPlatformLinkerFlags()),
                    collectRecursiveExportedPlatformLinkerFlags(targetNode)));
        for (String platform : platformLinkerFlags.keySet()) {
          appendConfigsBuilder.put(
              generateConfigKey("OTHER_LDFLAGS", platform),
              Streams.stream(
                      Iterables.transform(
                          Iterables.concat(
                              otherLdFlags, Iterables.concat(platformLinkerFlags.get(platform))),
                          Escaper.BASH_ESCAPER::apply))
                  .collect(Collectors.joining(" ")));
        }

        ImmutableMap<String, String> appendedConfig = appendConfigsBuilder.build();

        Optional<ImmutableSortedMap<String, ImmutableMap<String, String>>> configs =
            getXcodeBuildConfigurationsForTargetNode(targetNode);
        setTargetBuildConfigurations(
            buildTarget,
            target,
            project.getMainGroup(),
            configs.get(),
            getTargetCxxBuildConfigurationForTargetNode(targetNode, appendedConfig),
            extraSettingsBuilder.build(),
            defaultSettingsBuilder.build(),
            appendedConfig);
      }
    }

    Optional<String> moduleName =
        isModularAppleLibrary ? Optional.of(getModuleName(targetNode)) : Optional.empty();
    ModuleMapMode moduleMapMode = getModuleMapMode(targetNode);
    boolean shouldGenerateMissingUmbrellaHeader =
        options.shouldGenerateMissingUmbrellaHeader()
            && moduleMapMode.shouldGenerateMissingUmbrellaHeader();
    // -- phases
    createHeaderSymlinkTree(
        publicCxxHeaders,
        getSwiftPublicHeaderMapEntriesForTarget(targetNode),
        moduleName,
        moduleMapMode,
        getPathToHeaderSymlinkTree(targetNode, HeaderVisibility.PUBLIC),
        arg.getXcodePublicHeadersSymlinks().orElse(cxxBuckConfig.getPublicHeadersSymlinksEnabled())
            || !options.shouldUseHeaderMaps()
            || isModularAppleLibrary,
        !shouldMergeHeaderMaps(),
        shouldGenerateMissingUmbrellaHeader);
    if (isFocusedOnTarget) {
      createHeaderSymlinkTree(
          getPrivateCxxHeaders(targetNode),
          ImmutableMap.of(), // private interfaces never have a modulemap
          Optional.empty(),
          moduleMapMode,
          getPathToHeaderSymlinkTree(targetNode, HeaderVisibility.PRIVATE),
          arg.getXcodePrivateHeadersSymlinks()
                  .orElse(cxxBuckConfig.getPrivateHeadersSymlinksEnabled())
              || !options.shouldUseHeaderMaps(),
          options.shouldUseHeaderMaps(),
          shouldGenerateMissingUmbrellaHeader);
    }

    Optional<TargetNode<AppleNativeTargetDescriptionArg>> appleTargetNode =
        TargetNodes.castArg(targetNode, AppleNativeTargetDescriptionArg.class);
    if (appleTargetNode.isPresent()
        && isFocusedOnTarget
        && !options.shouldGenerateHeaderSymlinkTreesOnly()) {
      // Use Core Data models from immediate dependencies only.
      addCoreDataModelsIntoTarget(appleTargetNode.get(), targetGroup.get());
      addSceneKitAssetsIntoTarget(appleTargetNode.get(), targetGroup.get());
    }

    if (bundle.isPresent()
        && isFocusedOnTarget
        && !options.shouldGenerateHeaderSymlinkTreesOnly()) {
      addEntitlementsPlistIntoTarget(bundle.get(), targetGroup.get());
    }

    return target;
  }