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;
}