aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java')
-rw-r--r--src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java535
1 files changed, 535 insertions, 0 deletions
diff --git a/src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java b/src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java
new file mode 100644
index 0000000000..8a108f5548
--- /dev/null
+++ b/src/objc_tools/xcodegen/java/com/google/devtools/build/xcode/xcodegen/XcodeprojGeneration.java
@@ -0,0 +1,535 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// 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.google.devtools.build.xcode.xcodegen;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.devtools.build.xcode.common.BuildOptionsUtil.DEFAULT_OPTIONS_NAME;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.devtools.build.xcode.common.XcodeprojPath;
+import com.google.devtools.build.xcode.util.Containing;
+import com.google.devtools.build.xcode.util.Equaling;
+import com.google.devtools.build.xcode.util.Interspersing;
+import com.google.devtools.build.xcode.util.Mapping;
+import com.google.devtools.build.xcode.xcodegen.LibraryObjects.BuildPhaseBuilder;
+import com.google.devtools.build.xcode.xcodegen.SourceFile.BuildType;
+import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.Control;
+import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.DependencyControl;
+import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl;
+import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.XcodeprojBuildSetting;
+
+import com.dd.plist.NSArray;
+import com.dd.plist.NSDictionary;
+import com.dd.plist.NSObject;
+import com.dd.plist.NSString;
+import com.facebook.buck.apple.xcode.GidGenerator;
+import com.facebook.buck.apple.xcode.XcodeprojSerializer;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXBuildFile;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXContainerItemProxy.ProxyType;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXCopyFilesBuildPhase;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXFileReference;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXFrameworksBuildPhase;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXNativeTarget;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXProject;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXReference;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXReference.SourceTree;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXResourcesBuildPhase;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXSourcesBuildPhase;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXTarget.ProductType;
+import com.facebook.buck.apple.xcode.xcodeproj.PBXTargetDependency;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystem;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Utility code for generating Xcode project files.
+ */
+public class XcodeprojGeneration {
+ public static final String FILE_TYPE_ARCHIVE_LIBRARY = "archive.ar";
+ public static final String FILE_TYPE_WRAPPER_APPLICATION = "wrapper.application";
+ public static final String FILE_TYPE_WRAPPER_BUNDLE = "wrapper.cfbundle";
+ public static final String FILE_TYPE_APP_EXTENSION = "wrapper.app-extension";
+
+ @VisibleForTesting
+ static final String APP_NEEDS_SOURCE_ERROR =
+ "Due to limitations in Xcode, application projects must have at least one source file.";
+
+ private XcodeprojGeneration() {
+ throw new UnsupportedOperationException("static-only");
+ }
+
+ /**
+ * Determines the relative path to the workspace root from the path of the project.pbxproj output
+ * file. An absolute path is preferred if available.
+ */
+ static Path relativeWorkspaceRoot(Path pbxproj) {
+ int levelsToExecRoot = pbxproj.getParent().getParent().getNameCount();
+ return pbxproj.getFileSystem().getPath(Joiner
+ .on('/')
+ .join(Collections.nCopies(levelsToExecRoot, "..")));
+ }
+
+ /**
+ * Writes a project to an {@code OutputStream} in the correct encoding.
+ */
+ public static void write(OutputStream out, PBXProject project) throws IOException {
+ XcodeprojSerializer ser = new XcodeprojSerializer(
+ new GidGenerator(ImmutableSet.<String>of()), project);
+ Writer outWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8);
+ // toXMLPropertyList includes an XML encoding specification (UTF-8), which we specify above.
+ // Standard Xcodeproj files use the toASCIIPropertyList format, but Xcode will rewrite
+ // XML-encoded project files automatically when first opening them. We use XML to prevent
+ // encoding issues, since toASCIIPropertyList does not include the UTF-8 encoding comment, and
+ // Xcode by default apparently uses MacRoman.
+ // This encoding concern is probably why Buck also generates XML project files as well.
+ outWriter.write(ser.toPlist().toXMLPropertyList());
+ outWriter.flush();
+ }
+
+ private static final EnumSet<ProductType> SUPPORTED_PRODUCT_TYPES = EnumSet.of(
+ ProductType.STATIC_LIBRARY,
+ ProductType.APPLICATION,
+ ProductType.BUNDLE,
+ ProductType.UNIT_TEST,
+ ProductType.APP_EXTENSION);
+
+ private static final EnumSet<ProductType> PRODUCT_TYPES_THAT_HAVE_A_BINARY = EnumSet.of(
+ ProductType.APPLICATION,
+ ProductType.BUNDLE,
+ ProductType.UNIT_TEST,
+ ProductType.APP_EXTENSION);
+
+ /**
+ * Detects the product type of the given target based on multiple fields in {@code targetControl}.
+ * {@code productType} is set as a field on {@code PBXNativeTarget} objects in Xcode project
+ * files, and we support three values: {@link ProductType#APPLICATION},
+ * {@link ProductType#STATIC_LIBRARY}, and {@link ProductType#BUNDLE}. The product type is not
+ * only what xcodegen sets the {@code productType} field to - it also dictates what can be built
+ * with this target (e.g. a library cannot be built with resources), what build phase it should be
+ * added to of its dependers, and the name and shape of its build output.
+ */
+ public static ProductType productType(TargetControl targetControl) {
+ if (targetControl.hasProductType()) {
+ for (ProductType supportedType : SUPPORTED_PRODUCT_TYPES) {
+ if (targetControl.getProductType().equals(supportedType.identifier)) {
+ return supportedType;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unsupported product type: " + targetControl.getProductType());
+ }
+
+ return targetControl.hasInfoplist() ? ProductType.APPLICATION : ProductType.STATIC_LIBRARY;
+ }
+
+ private static String productName(TargetControl targetControl) {
+ if (Equaling.of(ProductType.STATIC_LIBRARY, productType(targetControl))) {
+ // The product names for static libraries must be unique since the final
+ // binary is linked with "clang -l${LIBRARY_PRODUCT_NAME}" for each static library.
+ // Unlike other product types, a full application may have dozens of static libraries,
+ // so rather than just use the target name, we use the full label to generate the product
+ // name.
+ return labelToXcodeTargetName(targetControl.getLabel());
+ } else {
+ return targetControl.getName();
+ }
+ }
+
+ /**
+ * Returns the file reference corresponding to the {@code productReference} of the given target.
+ * The {@code productReference} is the build output of a target, and its name and file type
+ * (stored in the {@link FileReference}) change based on the product type.
+ */
+ private static FileReference productReference(TargetControl targetControl) {
+ ProductType type = productType(targetControl);
+ String productName = productName(targetControl);
+
+ switch (type) {
+ case APPLICATION:
+ return FileReference.of(String.format("%s.app", productName), SourceTree.BUILT_PRODUCTS_DIR)
+ .withExplicitFileType(FILE_TYPE_WRAPPER_APPLICATION);
+ case STATIC_LIBRARY:
+ return FileReference.of(
+ String.format("lib%s.a", productName), SourceTree.BUILT_PRODUCTS_DIR)
+ .withExplicitFileType(FILE_TYPE_ARCHIVE_LIBRARY);
+ case BUNDLE:
+ return FileReference.of(
+ String.format("%s.bundle", productName), SourceTree.BUILT_PRODUCTS_DIR)
+ .withExplicitFileType(FILE_TYPE_WRAPPER_BUNDLE);
+ case UNIT_TEST:
+ return FileReference.of(
+ String.format("%s.xctest", productName), SourceTree.BUILT_PRODUCTS_DIR)
+ .withExplicitFileType(FILE_TYPE_WRAPPER_BUNDLE);
+ case APP_EXTENSION:
+ return FileReference.of(
+ String.format("%s.appex", productName), SourceTree.BUILT_PRODUCTS_DIR)
+ .withExplicitFileType(FILE_TYPE_APP_EXTENSION);
+ default:
+ throw new IllegalArgumentException("unknown: " + type);
+ }
+ }
+
+ private static class TargetInfo {
+ final TargetControl control;
+ final PBXNativeTarget nativeTarget;
+ final PBXFrameworksBuildPhase frameworksPhase;
+ final PBXResourcesBuildPhase resourcesPhase;
+ final PBXBuildFile productBuildFile;
+ final PBXTargetDependency targetDependency;
+ final NSDictionary buildConfig;
+
+ TargetInfo(TargetControl control,
+ PBXNativeTarget nativeTarget,
+ PBXFrameworksBuildPhase frameworksPhase,
+ PBXResourcesBuildPhase resourcesPhase,
+ PBXBuildFile productBuildFile,
+ PBXTargetDependency targetDependency,
+ NSDictionary buildConfig) {
+ this.control = control;
+ this.nativeTarget = nativeTarget;
+ this.frameworksPhase = frameworksPhase;
+ this.resourcesPhase = resourcesPhase;
+ this.productBuildFile = productBuildFile;
+ this.targetDependency = targetDependency;
+ this.buildConfig = buildConfig;
+ }
+
+ /**
+ * Returns the path to the built, statically-linked binary for this target. The path contains
+ * build-setting variables and may be used in a build setting such as {@code TEST_HOST}.
+ *
+ * <p>One example return value is {@code $(BUILT_PRODUCTS_DIR)/Foo.app/Foo}.
+ */
+ String staticallyLinkedBinary() {
+ ProductType type = productType(control);
+ Preconditions.checkArgument(
+ Containing.item(PRODUCT_TYPES_THAT_HAVE_A_BINARY, type),
+ "This product type (%s) is not known to have a binary.", type);
+ FileReference productReference = productReference(control);
+ return String.format("$(%s)/%s/%s",
+ productReference.sourceTree().name(),
+ productReference.path().or(productReference.name()),
+ control.getName());
+ }
+
+ /**
+ * Adds the given dependency to the list of dependencies, the
+ * appropriate build phase if applicable, and the appropriate build setting values if
+ * applicable, of this target.
+ */
+ void addDependencyInfo(
+ DependencyControl dependencyControl, Map<String, TargetInfo> targetInfoByLabel) {
+ TargetInfo dependencyInfo =
+ Mapping.of(targetInfoByLabel, dependencyControl.getTargetLabel()).get();
+ if (dependencyControl.getTestHost()) {
+ buildConfig.put("TEST_HOST", dependencyInfo.staticallyLinkedBinary());
+ buildConfig.put("BUNDLE_LOADER", dependencyInfo.staticallyLinkedBinary());
+ } else if (productType(dependencyInfo.control) == ProductType.BUNDLE) {
+ resourcesPhase.getFiles().add(dependencyInfo.productBuildFile);
+ } else if (productType(dependencyInfo.control) == ProductType.APP_EXTENSION) {
+ PBXCopyFilesBuildPhase copyFilesPhase = new PBXCopyFilesBuildPhase(
+ PBXCopyFilesBuildPhase.Destination.PLUGINS, /*path=*/"");
+ copyFilesPhase.getFiles().add(dependencyInfo.productBuildFile);
+ nativeTarget.getBuildPhases().add(copyFilesPhase);
+ } else {
+ frameworksPhase.getFiles().add(dependencyInfo.productBuildFile);
+ }
+ nativeTarget.getDependencies().add(dependencyInfo.targetDependency);
+ }
+ }
+
+ private static String labelToXcodeTargetName(String label) {
+ String pathFromWorkspaceRoot = label.replace("//", "").replace(':', '/');
+ List<String> components = Splitter.on('/').splitToList(pathFromWorkspaceRoot);
+ return Joiner.on('_').join(Lists.reverse(components));
+ }
+
+ private static NSDictionary nonArcCompileSettings() {
+ NSDictionary result = new NSDictionary();
+ result.put("COMPILER_FLAGS", "-fno-objc-arc");
+ return result;
+ }
+
+ private static boolean hasAtLeastOneCompilableSource(TargetControl control) {
+ return (control.getSourceFileCount() != 0) || (control.getNonArcSourceFileCount() != 0);
+ }
+
+ private static <E> Iterable<E> plus(Iterable<E> before, E... rest) {
+ return Iterables.concat(before, ImmutableList.copyOf(rest));
+ }
+
+ /**
+ * Returns the final header search paths to be placed in a build configuration.
+ */
+ private static NSArray headerSearchPaths(Iterable<String> paths) {
+ ImmutableList.Builder<String> result = new ImmutableList.Builder<>();
+ for (String path : paths) {
+ // TODO(bazel-team): Remove this hack once the released version of Bazel is prepending
+ // "$(WORKSPACE_ROOT)/" to every "source rooted" path.
+ if (!path.startsWith("$")) {
+ path = "$(WORKSPACE_ROOT)/" + path;
+ }
+ result.add(path);
+ }
+ return (NSArray) NSObject.wrap(result.build());
+ }
+
+ /**
+ * Returns the {@code FRAMEWORK_SEARCH_PATHS} array for a target's build config given the list of
+ * {@code .framework} directory paths.
+ */
+ private static NSArray frameworkSearchPaths(Iterable<String> frameworks) {
+ ImmutableSet.Builder<NSString> result = new ImmutableSet.Builder<>();
+ for (String framework : frameworks) {
+ result.add(new NSString("$(WORKSPACE_ROOT)/" + Paths.get(framework).getParent()));
+ }
+ // This is needed by XcTest targets (and others, just in case) for SenTestingKit.framework.
+ result.add(new NSString("$(SDKROOT)/Developer/Library/Frameworks"));
+ // This is needed by non-XcTest targets that use XcTest.framework, for instance for test
+ // utility libraries packaged as an objc_library.
+ result.add(new NSString("$(PLATFORM_DIR)/Developer/Library/Frameworks"));
+
+ return (NSArray) NSObject.wrap(result.build().asList());
+ }
+
+ private static PBXFrameworksBuildPhase buildLibraryInfo(
+ LibraryObjects libraryObjects, TargetControl target) {
+ BuildPhaseBuilder builder = libraryObjects.newBuildPhase();
+ for (String dylib : target.getSdkDylibList()) {
+ builder.addDylib(dylib);
+ }
+ for (String sdkFramework : target.getSdkFrameworkList()) {
+ builder.addSdkFramework(sdkFramework);
+ }
+ for (String framework : target.getFrameworkList()) {
+ builder.addFramework(framework);
+ }
+ return builder.build();
+ }
+
+ private static ImmutableList<String> otherLdflags(TargetControl targetControl) {
+ Iterable<String> givenFlags = targetControl.getLinkoptList();
+ ImmutableList.Builder<String> flags = new ImmutableList.Builder<>();
+ flags.addAll(givenFlags);
+ if (!Equaling.of(ProductType.STATIC_LIBRARY, productType(targetControl))) {
+ flags.addAll(Interspersing.prependEach(
+ "$(WORKSPACE_ROOT)/", targetControl.getImportedLibraryList()));
+ }
+ return flags.build();
+ }
+
+ /** Generates a project file. */
+ public static PBXProject xcodeproj(Path workspaceRoot, Control control,
+ Iterable<PbxReferencesProcessor> postProcessors) {
+ checkArgument(control.hasPbxproj(), "Must set pbxproj field on control proto.");
+ FileSystem fileSystem = workspaceRoot.getFileSystem();
+
+ XcodeprojPath<Path> outputPath = XcodeprojPath.converter().fromPath(
+ RelativePaths.fromString(fileSystem, control.getPbxproj()));
+
+ NSDictionary projBuildConfigMap = new NSDictionary();
+ projBuildConfigMap.put("ARCHS", new NSArray(
+ new NSString("arm7"), new NSString("arm7s"), new NSString("arm64")));
+ projBuildConfigMap.put("CLANG_ENABLE_OBJC_ARC", "YES");
+ projBuildConfigMap.put("SDKROOT", "iphoneos");
+ projBuildConfigMap.put("IPHONEOS_DEPLOYMENT_TARGET", "7.0");
+ projBuildConfigMap.put("GCC_VERSION", "com.apple.compilers.llvm.clang.1_0");
+ projBuildConfigMap.put("CODE_SIGN_IDENTITY[sdk=iphoneos*]", "iPhone Developer");
+
+ for (XcodeprojBuildSetting projectSetting : control.getBuildSettingList()) {
+ projBuildConfigMap.put(projectSetting.getName(), projectSetting.getValue());
+ }
+
+ PBXProject project = new PBXProject(outputPath.getProjectName());
+ project.getMainGroup().setPath(workspaceRoot.toString());
+ try {
+ project
+ .getBuildConfigurationList()
+ .getBuildConfigurationsByName()
+ .get(DEFAULT_OPTIONS_NAME)
+ .setBuildSettings(projBuildConfigMap);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+
+ Map<String, TargetInfo> targetInfoByLabel = new HashMap<>();
+
+ PBXFileReferences fileReferences = new PBXFileReferences();
+ LibraryObjects libraryObjects = new LibraryObjects(fileReferences);
+ PBXBuildFiles pbxBuildFiles = new PBXBuildFiles(fileReferences);
+ Resources resources =
+ Resources.fromTargetControls(fileSystem, pbxBuildFiles, control.getTargetList());
+ Xcdatamodels xcdatamodels =
+ Xcdatamodels.fromTargetControls(fileSystem, pbxBuildFiles, control.getTargetList());
+ // We use a hash set for the Project Navigator files so that the same PBXFileReference does not
+ // get added twice. Because PBXFileReference uses equality-by-identity semantics, this requires
+ // the PBXFileReferences cache to properly return the same reference for functionally-equivalent
+ // files.
+ Set<PBXReference> projectNavigatorFiles = new LinkedHashSet<>();
+ for (TargetControl targetControl : control.getTargetList()) {
+ checkArgument(targetControl.hasName(), "TargetControl requires a name: %s", targetControl);
+ checkArgument(targetControl.hasLabel(), "TargetControl requires a label: %s", targetControl);
+
+ ProductType productType = productType(targetControl);
+ Preconditions.checkArgument(
+ (productType != ProductType.APPLICATION) || hasAtLeastOneCompilableSource(targetControl),
+ APP_NEEDS_SOURCE_ERROR);
+ PBXSourcesBuildPhase sourcesBuildPhase = new PBXSourcesBuildPhase();
+
+ for (SourceFile source : SourceFile.allSourceFiles(fileSystem, targetControl)) {
+ PBXFileReference fileRef =
+ fileReferences.get(FileReference.of(source.path().toString(), SourceTree.GROUP));
+ projectNavigatorFiles.add(fileRef);
+ if (Equaling.of(source.buildType(), BuildType.NO_BUILD)) {
+ continue;
+ }
+ PBXBuildFile buildFile = new PBXBuildFile(fileRef);
+ if (Equaling.of(source.buildType(), BuildType.NON_ARC_BUILD)) {
+ buildFile.setSettings(Optional.of(nonArcCompileSettings()));
+ }
+ sourcesBuildPhase.getFiles().add(buildFile);
+ }
+ sourcesBuildPhase.getFiles().addAll(xcdatamodels.buildFiles().get(targetControl));
+
+ PBXFileReference productReference = fileReferences.get(productReference(targetControl));
+ projectNavigatorFiles.add(productReference);
+
+ NSDictionary targetBuildConfigMap = new NSDictionary();
+ // TODO(bazel-team): Stop adding the workspace root automatically once the
+ // released version of Bazel starts passing it.
+ targetBuildConfigMap.put("USER_HEADER_SEARCH_PATHS",
+ headerSearchPaths(
+ plus(targetControl.getUserHeaderSearchPathList(), "$(WORKSPACE_ROOT)")));
+ targetBuildConfigMap.put("HEADER_SEARCH_PATHS",
+ headerSearchPaths(
+ plus(targetControl.getHeaderSearchPathList(), "$(inherited)")));
+ targetBuildConfigMap.put("FRAMEWORK_SEARCH_PATHS",
+ frameworkSearchPaths(targetControl.getFrameworkList()));
+
+ targetBuildConfigMap.put("WORKSPACE_ROOT", workspaceRoot.toString());
+
+ if (targetControl.hasPchPath()) {
+ targetBuildConfigMap.put(
+ "GCC_PREFIX_HEADER", "$(WORKSPACE_ROOT)/" + targetControl.getPchPath());
+ }
+
+ targetBuildConfigMap.put("PRODUCT_NAME", productName(targetControl));
+ if (targetControl.hasInfoplist()) {
+ targetBuildConfigMap.put(
+ "INFOPLIST_FILE", "$(WORKSPACE_ROOT)/" + targetControl.getInfoplist());
+ }
+
+ if (targetControl.getCoptCount() > 0) {
+ targetBuildConfigMap.put("OTHER_CFLAGS", NSObject.wrap(targetControl.getCoptList()));
+ }
+ targetBuildConfigMap.put("OTHER_LDFLAGS", NSObject.wrap(otherLdflags(targetControl)));
+ for (XcodeprojBuildSetting setting : targetControl.getBuildSettingList()) {
+ String name = setting.getName();
+ String value = setting.getValue();
+ // TODO(bazel-team): Remove this hack after next Bazel release.
+ if (name.equals("CODE_SIGN_ENTITLEMENTS") && !value.startsWith("$")) {
+ value = "$(WORKSPACE_ROOT)/" + value;
+ }
+ targetBuildConfigMap.put(name, value);
+ }
+
+ PBXNativeTarget target = new PBXNativeTarget(
+ labelToXcodeTargetName(targetControl.getLabel()), productType);
+ try {
+ target
+ .getBuildConfigurationList()
+ .getBuildConfigurationsByName()
+ .get(DEFAULT_OPTIONS_NAME)
+ .setBuildSettings(targetBuildConfigMap);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ target.setProductReference(productReference);
+
+ PBXFrameworksBuildPhase frameworksPhase = buildLibraryInfo(libraryObjects, targetControl);
+ PBXResourcesBuildPhase resourcesPhase = resources.resourcesBuildPhase(targetControl);
+
+ for (String importedArchive : targetControl.getImportedLibraryList()) {
+ PBXFileReference fileReference = fileReferences.get(
+ FileReference.of(importedArchive, SourceTree.GROUP)
+ .withExplicitFileType(FILE_TYPE_ARCHIVE_LIBRARY));
+ projectNavigatorFiles.add(fileReference);
+ }
+
+ project.getTargets().add(target);
+
+ target.getBuildPhases().add(frameworksPhase);
+ target.getBuildPhases().add(sourcesBuildPhase);
+ target.getBuildPhases().add(resourcesPhase);
+
+ checkState(!Mapping.of(targetInfoByLabel, targetControl.getLabel()).isPresent(),
+ "Mapping already exists for target with label %s in map: %s",
+ targetControl.getLabel(), targetInfoByLabel);
+ targetInfoByLabel.put(
+ targetControl.getLabel(),
+ new TargetInfo(
+ targetControl,
+ target,
+ frameworksPhase,
+ resourcesPhase,
+ new PBXBuildFile(productReference),
+ new LocalPBXTargetDependency(
+ new LocalPBXContainerItemProxy(
+ project, target, ProxyType.TARGET_REFERENCE)),
+ targetBuildConfigMap));
+ }
+
+ for (HasProjectNavigatorFiles references : ImmutableList.of(pbxBuildFiles, libraryObjects)) {
+ Iterables.addAll(projectNavigatorFiles, references.mainGroupReferences());
+ }
+
+ Iterable<PBXReference> processedProjectFiles = projectNavigatorFiles;
+ for (PbxReferencesProcessor postProcessor : postProcessors) {
+ processedProjectFiles = postProcessor.process(processedProjectFiles);
+ }
+
+ Iterables.addAll(project.getMainGroup().getChildren(), processedProjectFiles);
+ for (TargetInfo targetInfo : targetInfoByLabel.values()) {
+ for (DependencyControl dependency : targetInfo.control.getDependencyList()) {
+ targetInfo.addDependencyInfo(dependency, targetInfoByLabel);
+ }
+ }
+
+ return project;
+ }
+}