/* * Copyright 2013-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.apple.project_generator; import static com.facebook.buck.apple.project_generator.ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.dd.plist.NSDictionary; import com.dd.plist.NSString; import com.facebook.buck.apple.AppleAssetCatalogBuilder; import com.facebook.buck.apple.AppleBinaryBuilder; import com.facebook.buck.apple.AppleBundleBuilder; import com.facebook.buck.apple.AppleBundleExtension; import com.facebook.buck.apple.AppleDependenciesCache; import com.facebook.buck.apple.AppleLibraryBuilder; import com.facebook.buck.apple.AppleLibraryDescriptionArg; import com.facebook.buck.apple.AppleResourceBuilder; import com.facebook.buck.apple.AppleTestBuilder; import com.facebook.buck.apple.CoreDataModelBuilder; import com.facebook.buck.apple.FakeAppleRuleDescriptions; import com.facebook.buck.apple.SceneKitAssetsBuilder; import com.facebook.buck.apple.XcodePostbuildScriptBuilder; import com.facebook.buck.apple.XcodePrebuildScriptBuilder; import com.facebook.buck.apple.clang.HeaderMap; import com.facebook.buck.apple.xcode.xcodeproj.CopyFilePhaseDestinationSpec; import com.facebook.buck.apple.xcode.xcodeproj.PBXBuildFile; import com.facebook.buck.apple.xcode.xcodeproj.PBXBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXCopyFilesBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXFileReference; import com.facebook.buck.apple.xcode.xcodeproj.PBXGroup; import com.facebook.buck.apple.xcode.xcodeproj.PBXHeadersBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXProject; import com.facebook.buck.apple.xcode.xcodeproj.PBXReference; import com.facebook.buck.apple.xcode.xcodeproj.PBXResourcesBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXShellScriptBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXSourcesBuildPhase; import com.facebook.buck.apple.xcode.xcodeproj.PBXTarget; import com.facebook.buck.apple.xcode.xcodeproj.PBXVariantGroup; import com.facebook.buck.apple.xcode.xcodeproj.ProductType; import com.facebook.buck.apple.xcode.xcodeproj.SourceTreePath; import com.facebook.buck.apple.xcode.xcodeproj.XCBuildConfiguration; import com.facebook.buck.cli.BuckConfig; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxDescriptionEnhancer; import com.facebook.buck.cxx.CxxLibraryBuilder; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.CxxPlatformUtils; import com.facebook.buck.cxx.CxxSource; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.event.BuckEventBusFactory; import com.facebook.buck.graph.AbstractBottomUpTraversal; import com.facebook.buck.halide.HalideBuckConfig; import com.facebook.buck.halide.HalideLibraryBuilder; import com.facebook.buck.halide.HalideLibraryDescription; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.js.IosReactNativeLibraryBuilder; import com.facebook.buck.js.ReactNativeBuckConfig; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.model.Either; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.Cell; import com.facebook.buck.rules.DefaultBuildTargetSourcePath; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.FakeSourcePath; import com.facebook.buck.rules.PathSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourceWithFlags; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.TargetNode; import com.facebook.buck.rules.TestCellBuilder; import com.facebook.buck.rules.coercer.FrameworkPath; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.rules.macros.StringWithMacrosUtils; import com.facebook.buck.shell.ExportFileBuilder; import com.facebook.buck.shell.ExportFileDescriptionArg; import com.facebook.buck.shell.GenruleBuilder; import com.facebook.buck.swift.SwiftBuckConfig; import com.facebook.buck.testutil.AllExistingProjectFilesystem; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.testutil.TargetGraphFactory; import com.facebook.buck.timing.IncrementingFakeClock; import com.facebook.buck.timing.SettableFakeClock; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreCollectors; import com.facebook.buck.util.environment.Platform; import com.google.common.base.Function; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.google.common.io.ByteStreams; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class ProjectGeneratorTest { private static final Path OUTPUT_DIRECTORY = Paths.get("_gen"); private static final String PROJECT_NAME = "GeneratedProject"; private static final String PROJECT_CONTAINER = PROJECT_NAME + ".xcodeproj"; private static final Path OUTPUT_PROJECT_BUNDLE_PATH = OUTPUT_DIRECTORY.resolve(PROJECT_CONTAINER); private static final Path OUTPUT_PROJECT_FILE_PATH = OUTPUT_PROJECT_BUNDLE_PATH.resolve("project.pbxproj"); private static final CxxPlatform DEFAULT_PLATFORM = CxxPlatformUtils.DEFAULT_PLATFORM; private static final Flavor DEFAULT_FLAVOR = InternalFlavor.of("default"); private SettableFakeClock clock; private ProjectFilesystem projectFilesystem; private Cell projectCell; private FakeProjectFilesystem fakeProjectFilesystem; private HalideBuckConfig halideBuckConfig; private CxxBuckConfig cxxBuckConfig; private SwiftBuckConfig swiftBuckConfig; @Rule public ExpectedException thrown = ExpectedException.none(); private Path rootPath; @Before public void setUp() throws InterruptedException, IOException { assumeTrue(Platform.detect() == Platform.MACOS || Platform.detect() == Platform.LINUX); clock = new SettableFakeClock(0, 0); fakeProjectFilesystem = new FakeProjectFilesystem(clock); projectCell = (new TestCellBuilder()).setFilesystem(fakeProjectFilesystem).build(); projectFilesystem = projectCell.getFilesystem(); rootPath = projectFilesystem.getRootPath(); // Add files and directories used to test resources. projectFilesystem.createParentDirs(Paths.get("foodir", "foo.png")); projectFilesystem.writeContentsToPath("", Paths.get("foodir", "foo.png")); projectFilesystem.writeContentsToPath("", Paths.get("bar.png")); fakeProjectFilesystem.touch(Paths.get("Base.lproj", "Bar.storyboard")); halideBuckConfig = HalideLibraryBuilder.createDefaultHalideConfig(fakeProjectFilesystem); ImmutableMap<String, ImmutableMap<String, String>> sections = ImmutableMap.of( "cxx", ImmutableMap.of( "cflags", "-Wno-deprecated -Wno-conversion", "cxxflags", "-Wundeclared-selector -Wno-objc-designated-initializers"), "apple", ImmutableMap.of("force_dsym_mode_in_build_with_buck", "false"), "swift", ImmutableMap.of("version", "1.23")); BuckConfig config = FakeBuckConfig.builder().setSections(sections).build(); cxxBuckConfig = new CxxBuckConfig(config); swiftBuckConfig = new SwiftBuckConfig(config); } @Test public void testProjectStructureForEmptyProject() throws IOException { ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of()); projectGenerator.createXcodeProjects(); Optional<String> pbxproj = projectFilesystem.readFileIfItExists(OUTPUT_PROJECT_FILE_PATH); assertTrue(pbxproj.isPresent()); } @Test public void testProjectStructureWithInfoPlist() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath(("Info.plist"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup bundleGroup = project.getMainGroup().getOrCreateChildGroupByName(bundleTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = bundleGroup.getOrCreateChildGroupByName("Sources"); assertThat(bundleGroup.getChildren(), hasSize(2)); Iterable<String> childNames = Iterables.transform(sourcesGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("Info.plist")); } @Test public void testProjectStructureWithGenruleSources() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); BuildTarget genruleTarget = BuildTarget.builder(rootPath, "//foo", "genrule").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath(("Info.plist"))) .build(); TargetNode<?, ?> genruleNode = GenruleBuilder.newGenruleBuilder(genruleTarget) .setSrcs( ImmutableList.of( new FakeSourcePath("foo/foo.json"), new FakeSourcePath("bar.json"))) .setOut("out") .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, bundleNode, genruleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup bundleGroup = project.getMainGroup().getOrCreateChildGroupByName(bundleTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = bundleGroup.getOrCreateChildGroupByName("Sources"); assertThat(bundleGroup.getChildren(), hasSize(2)); Iterable<String> childNames = Iterables.transform(sourcesGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("Info.plist")); PBXGroup otherGroup = project .getMainGroup() .getOrCreateChildGroupByName("Other") .getOrCreateChildGroupByName(".."); assertThat(otherGroup.getChildren(), hasSize(2)); childNames = Iterables.transform(otherGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("bar.json")); PBXGroup otherFooGroup = otherGroup.getOrCreateChildGroupByName("foo"); assertThat(otherFooGroup.getChildren(), hasSize(1)); childNames = Iterables.transform(otherFooGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("foo.json")); } @Test public void testProjectStructureWithExtraXcodeFiles() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .setExtraXcodeFiles( ImmutableList.of( new FakeSourcePath("foo/foo.json"), new FakeSourcePath("bar.json"))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath(("Info.plist"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup bundleGroup = project.getMainGroup().getOrCreateChildGroupByName(libraryTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = bundleGroup.getOrCreateChildGroupByName("Sources"); Iterable<String> childNames = Iterables.transform(sourcesGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("bar.json")); PBXGroup fooGroup = sourcesGroup.getOrCreateChildGroupByName("foo"); childNames = Iterables.transform(fooGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("foo.json")); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertSourcesNotInSourcesPhase(target, ImmutableSet.of("bar.json")); } @Test public void testProjectStructureWithExtraXcodeSources() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .setExtraXcodeSources( ImmutableList.of(new FakeSourcePath("foo/foo.m"), new FakeSourcePath("bar.m"))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath(("Info.plist"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup bundleGroup = project.getMainGroup().getOrCreateChildGroupByName(libraryTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = bundleGroup.getOrCreateChildGroupByName("Sources"); Iterable<String> childNames = Iterables.transform(sourcesGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("bar.m")); PBXGroup fooGroup = sourcesGroup.getOrCreateChildGroupByName("foo"); childNames = Iterables.transform(fooGroup.getChildren(), PBXReference::getName); assertThat(childNames, hasItem("foo.m")); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "foo/foo.m", Optional.empty(), "bar.m", Optional.empty())); } @Test public void testCreateDirectoryStructure() throws IOException { BuildTarget buildTarget1 = BuildTarget.builder(rootPath, "//foo/bar", "target1").build(); TargetNode<?, ?> node1 = AppleLibraryBuilder.createBuilder(buildTarget1).build(); BuildTarget buildTarget2 = BuildTarget.builder(rootPath, "//foo/foo", "target2").build(); TargetNode<?, ?> node2 = AppleLibraryBuilder.createBuilder(buildTarget2).build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node1, node2), ImmutableSet.of( ProjectGenerator.Option.CREATE_DIRECTORY_STRUCTURE, ProjectGenerator.Option.USE_SHORT_NAMES_FOR_TARGETS)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup mainGroup = project.getMainGroup(); PBXGroup groupFoo = null; for (PBXReference reference : mainGroup.getChildren()) { if (reference instanceof PBXGroup && "foo".equals(reference.getName())) { groupFoo = (PBXGroup) reference; } } assertNotNull("Project should have a group called foo", groupFoo); assertEquals("foo", groupFoo.getName()); assertThat(groupFoo.getChildren(), hasSize(2)); PBXGroup groupFooBar = (PBXGroup) Iterables.get(groupFoo.getChildren(), 0); assertEquals("bar", groupFooBar.getName()); assertThat(groupFooBar.getChildren(), hasSize(1)); PBXGroup groupFooFoo = (PBXGroup) Iterables.get(groupFoo.getChildren(), 1); assertEquals("foo", groupFooFoo.getName()); assertThat(groupFooFoo.getChildren(), hasSize(1)); PBXGroup groupFooBarTarget1 = (PBXGroup) Iterables.get(groupFooBar.getChildren(), 0); assertEquals("target1", groupFooBarTarget1.getName()); PBXGroup groupFooFooTarget2 = (PBXGroup) Iterables.get(groupFooFoo.getChildren(), 0); assertEquals("target2", groupFooFooTarget2.getName()); } @Test public void testModularFrameworkHeaderMapInclusionAsDependency() throws IOException { BuildTarget frameworkBundleTarget = BuildTarget.builder(rootPath, "//foo", "framework").build(); BuildTarget frameworkLibTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget appBundleTarget = BuildTarget.builder(rootPath, "//product", "app").build(); BuildTarget appBinaryTarget = BuildTarget.builder(rootPath, "//product", "binary").build(); String configName = "Default"; TargetNode<?, ?> frameworkLibNode = AppleLibraryBuilder.createBuilder(frameworkLibTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("HeaderGroup1/foo.h"), new FakeSourcePath("HeaderGroup2/baz.h"))) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/bar.h"))) .setConfigs(ImmutableSortedMap.of(configName, ImmutableMap.of())) .setModular(true) .build(); TargetNode<?, ?> frameworkBundleNode = AppleBundleBuilder.createBuilder(frameworkBundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(frameworkLibTarget) .build(); TargetNode<?, ?> appBinaryNode = AppleLibraryBuilder.createBuilder(appBinaryTarget) .setSrcs(ImmutableSortedSet.of()) .setDeps(ImmutableSortedSet.of(frameworkBundleTarget)) .setConfigs(ImmutableSortedMap.of(configName, ImmutableMap.of())) .build(); TargetNode<?, ?> appBundleNode = AppleBundleBuilder.createBuilder(appBundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(appBinaryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(frameworkLibNode, frameworkBundleNode, appBinaryNode, appBundleNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); assertNotNull(project); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(8)); assertTrue(headerSymlinkTrees.contains(Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderMapWithoutSymLinksIsEmpty(Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub")); } @Test public void testModularFrameworkHeaderMapInclusionInTargetItself() throws IOException { BuildTarget libTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libNode = AppleLibraryBuilder.createBuilder(libTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("HeaderGroup1/foo.h"), new FakeSourcePath("HeaderGroup2/baz.h"))) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/bar.h"))) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setModular(true) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); assertNotNull(project); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderMapWithoutSymLinksIsEmpty(Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub")); } @Test public void testModularFrameworkBuildSettings() throws IOException { BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "framework").build(); BuildTarget libTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); String configName = "Default"; TargetNode<?, ?> libNode = AppleLibraryBuilder.createBuilder(libTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("HeaderGroup1/foo.h"), new FakeSourcePath("HeaderGroup2/baz.h"))) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/bar.h"))) .setConfigs(ImmutableSortedMap.of(configName, ImmutableMap.of())) .setModular(true) .build(); TargetNode<?, ?> frameworkNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libNode, frameworkNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget libPBXTarget = assertTargetExistsAndReturnTarget(project, "//foo:lib"); ImmutableMap<String, String> buildSettings = getBuildSettings(bundleTarget, libPBXTarget, configName); assertEquals( "USE_HEADERMAP must be turned on for modular framework targets " + "so that Xcode generates VFS overlays", "YES", buildSettings.get("USE_HEADERMAP")); assertEquals( "CLANG_ENABLE_MODULES must be turned on for modular framework targets" + "so that Xcode generates VFS overlays", "YES", buildSettings.get("CLANG_ENABLE_MODULES")); assertEquals( "DEFINES_MODULE must be turned on for modular framework targets", "YES", buildSettings.get("DEFINES_MODULE")); } @Test public void testModularFrameworkHeadersInHeadersBuildPhase() throws IOException { BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "framework").build(); BuildTarget libTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); String exportedHeaderName = "bar.h"; TargetNode<?, ?> libNode = AppleLibraryBuilder.createBuilder(libTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("HeaderGroup1/foo.h"), new FakeSourcePath("HeaderGroup2/baz.h"))) .setExportedHeaders( ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/" + exportedHeaderName))) .setModular(true) .build(); TargetNode<?, ?> frameworkNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libNode, frameworkNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:framework"); List<PBXBuildPhase> headersPhases = target.getBuildPhases(); headersPhases.removeIf(input -> !(input instanceof PBXHeadersBuildPhase)); assertEquals(1, headersPhases.size()); PBXHeadersBuildPhase headersPhase = (PBXHeadersBuildPhase) headersPhases.get(0); List<PBXBuildFile> headers = headersPhase.getFiles(); assertEquals(1, headers.size()); PBXFileReference headerReference = (PBXFileReference) headers.get(0).getFileRef(); assertNotNull(headerReference); assertEquals(headerReference.getName(), exportedHeaderName); } @Test public void testAppleLibraryHeaderGroupsWithHeaderSymlinkTrees() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("HeaderGroup1/foo.h"), new FakeSourcePath("HeaderGroup2/baz.h"))) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/bar.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(buildTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = targetGroup.getOrCreateChildGroupByName("Sources"); assertThat(sourcesGroup.getChildren(), hasSize(2)); PBXGroup group1 = (PBXGroup) Iterables.get(sourcesGroup.getChildren(), 0); assertEquals("HeaderGroup1", group1.getName()); assertThat(group1.getChildren(), hasSize(2)); PBXFileReference fileRefFoo = (PBXFileReference) Iterables.get(group1.getChildren(), 0); assertEquals("bar.h", fileRefFoo.getName()); PBXFileReference fileRefBar = (PBXFileReference) Iterables.get(group1.getChildren(), 1); assertEquals("foo.h", fileRefBar.getName()); PBXGroup group2 = (PBXGroup) Iterables.get(sourcesGroup.getChildren(), 1); assertEquals("HeaderGroup2", group2.getName()); assertThat(group2.getChildren(), hasSize(1)); PBXFileReference fileRefBaz = (PBXFileReference) Iterables.get(group2.getChildren(), 0); assertEquals("baz.h", fileRefBaz.getName()); // There should be no PBXHeadersBuildPhase in the 'Buck header map mode'. PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:lib"); assertEquals( 0, target .getBuildPhases() .stream() .filter(input -> input instanceof PBXHeadersBuildPhase) .count()); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-pub", headerSymlinkTrees.get(0).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of("lib/bar.h", "HeaderGroup1/bar.h")); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.<String, String>builder() .put("lib/foo.h", "HeaderGroup1/foo.h") .put("lib/baz.h", "HeaderGroup2/baz.h") .put("foo.h", "HeaderGroup1/foo.h") .put("bar.h", "HeaderGroup1/bar.h") .put("baz.h", "HeaderGroup2/baz.h") .build()); } @Test public void testAppleLibraryHeaderGroupsWithMappedHeaders() throws IOException { BuildTarget privateGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated1.h").build(); BuildTarget publicGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated2.h").build(); TargetNode<?, ?> privateGeneratedNode = ExportFileBuilder.newExportFileBuilder(privateGeneratedTarget).build(); TargetNode<?, ?> publicGeneratedNode = ExportFileBuilder.newExportFileBuilder(publicGeneratedTarget).build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders( ImmutableSortedMap.of( "any/name.h", new FakeSourcePath("HeaderGroup1/foo.h"), "different/name.h", new FakeSourcePath("HeaderGroup2/baz.h"), "one/more/name.h", new DefaultBuildTargetSourcePath(privateGeneratedTarget))) .setExportedHeaders( ImmutableSortedMap.of( "yet/another/name.h", new FakeSourcePath("HeaderGroup1/bar.h"), "and/one/more.h", new DefaultBuildTargetSourcePath(publicGeneratedTarget))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, privateGeneratedNode, publicGeneratedNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(buildTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = targetGroup.getOrCreateChildGroupByName("Sources"); assertThat(sourcesGroup.getChildren(), hasSize(3)); PBXGroup group1 = (PBXGroup) Iterables.get(sourcesGroup.getChildren(), 0); assertEquals("HeaderGroup1", group1.getName()); assertThat(group1.getChildren(), hasSize(2)); PBXFileReference fileRefFoo = (PBXFileReference) Iterables.get(group1.getChildren(), 0); assertEquals("bar.h", fileRefFoo.getName()); PBXFileReference fileRefBar = (PBXFileReference) Iterables.get(group1.getChildren(), 1); assertEquals("foo.h", fileRefBar.getName()); PBXGroup group2 = (PBXGroup) Iterables.get(sourcesGroup.getChildren(), 1); assertEquals("HeaderGroup2", group2.getName()); assertThat(group2.getChildren(), hasSize(1)); PBXFileReference fileRefBaz = (PBXFileReference) Iterables.get(group2.getChildren(), 0); assertEquals("baz.h", fileRefBaz.getName()); PBXGroup group3 = (PBXGroup) Iterables.get(sourcesGroup.getChildren(), 2); assertEquals("foo", group3.getName()); assertThat(group3.getChildren(), hasSize(2)); PBXFileReference fileRefGenerated1 = (PBXFileReference) Iterables.get(group3.getChildren(), 0); assertEquals("generated1.h", fileRefGenerated1.getName()); PBXFileReference fileRefGenerated2 = (PBXFileReference) Iterables.get(group3.getChildren(), 1); assertEquals("generated2.h", fileRefGenerated2.getName()); // There should be no PBXHeadersBuildPhase in the 'Buck header map mode'. PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:lib"); assertEquals( 0, target .getBuildPhases() .stream() .filter(input -> input instanceof PBXHeadersBuildPhase) .count()); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-pub", headerSymlinkTrees.get(0).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of( "yet/another/name.h", "HeaderGroup1/bar.h", "and/one/more.h", "foo/generated2.h")); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of( "any/name.h", "HeaderGroup1/foo.h", "different/name.h", "HeaderGroup2/baz.h", "one/more/name.h", "foo/generated1.h")); } @Test public void testCxxLibraryWithListsOfHeaders() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo/dir1/bar.h"))) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("foo/dir1/foo.h"), new FakeSourcePath("foo/dir2/baz.h"))) .setSrcs(ImmutableSortedSet.of()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of("foo/dir1/bar.h", "foo/dir1/bar.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.<String, String>builder() .put("foo/dir1/foo.h", "foo/dir1/foo.h") .put("foo/dir2/baz.h", "foo/dir2/baz.h") .build()); } @Test public void testCxxLibraryWithoutHeadersSymLinks() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo/dir1/bar.h"))) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("foo/dir1/foo.h"), new FakeSourcePath("foo/dir2/baz.h"))) .setSrcs(ImmutableSortedSet.of()) .setXcodePublicHeadersSymlinks(false) .setXcodePrivateHeadersSymlinks(false) .build(); ImmutableSet.Builder<ProjectGenerator.Option> optionsBuilder = ImmutableSet.builder(); ImmutableSet<ProjectGenerator.Option> projectGeneratorOptions = optionsBuilder.build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), projectGeneratorOptions); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderMapWithoutSymLinksContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of("foo/dir1/bar.h", "foo/dir1/bar.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderMapWithoutSymLinksContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.<String, String>builder() .put("foo/dir1/foo.h", "foo/dir1/foo.h") .put("foo/dir2/baz.h", "foo/dir2/baz.h") .build()); } @Test public void testCxxLibraryWithListsOfHeadersAndCustomNamespace() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo/dir1/bar.h"))) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("foo/dir1/foo.h"), new FakeSourcePath("foo/dir2/baz.h"))) .setSrcs(ImmutableSortedSet.of()) .setHeaderNamespace("name/space") .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of("name/space/dir1/bar.h", "foo/dir1/bar.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.<String, String>builder() .put("name/space/dir1/foo.h", "foo/dir1/foo.h") .put("name/space/dir2/baz.h", "foo/dir2/baz.h") .build()); } @Test public void testCxxLibraryHeaderGroupsWithMapsOfHeaders() throws IOException { BuildTarget privateGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated1.h").build(); BuildTarget publicGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated2.h").build(); TargetNode<?, ?> privateGeneratedNode = ExportFileBuilder.newExportFileBuilder(privateGeneratedTarget).build(); TargetNode<?, ?> publicGeneratedNode = ExportFileBuilder.newExportFileBuilder(publicGeneratedTarget).build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders( ImmutableSortedMap.of( "yet/another/name.h", new FakeSourcePath("foo/dir1/bar.h"), "and/one/more.h", new DefaultBuildTargetSourcePath(publicGeneratedTarget))) .setHeaders( ImmutableSortedMap.of( "any/name.h", new FakeSourcePath("foo/dir1/foo.h"), "different/name.h", new FakeSourcePath("foo/dir2/baz.h"), "one/more/name.h", new DefaultBuildTargetSourcePath(privateGeneratedTarget))) .setSrcs(ImmutableSortedSet.of()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, privateGeneratedNode, publicGeneratedNode)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of( "foo/yet/another/name.h", "foo/dir1/bar.h", "foo/and/one/more.h", "foo/generated2.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of( "foo/any/name.h", "foo/dir1/foo.h", "foo/different/name.h", "foo/dir2/baz.h", "foo/one/more/name.h", "foo/generated1.h")); } @Test public void testCxxLibraryHeaderGroupsWithMapsOfHeadersAndCustomNamespace() throws IOException { BuildTarget privateGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated1.h").build(); BuildTarget publicGeneratedTarget = BuildTarget.builder(rootPath, "//foo", "generated2.h").build(); TargetNode<?, ?> privateGeneratedNode = ExportFileBuilder.newExportFileBuilder(privateGeneratedTarget).build(); TargetNode<?, ?> publicGeneratedNode = ExportFileBuilder.newExportFileBuilder(publicGeneratedTarget).build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders( ImmutableSortedMap.of( "yet/another/name.h", new FakeSourcePath("foo/dir1/bar.h"), "and/one/more.h", new DefaultBuildTargetSourcePath(publicGeneratedTarget))) .setHeaders( ImmutableSortedMap.of( "any/name.h", new FakeSourcePath("foo/dir1/foo.h"), "different/name.h", new FakeSourcePath("foo/dir2/baz.h"), "one/more/name.h", new DefaultBuildTargetSourcePath(privateGeneratedTarget))) .setSrcs(ImmutableSortedSet.of()) .setHeaderNamespace("name/space") .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, privateGeneratedNode, publicGeneratedNode)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of( "name/space/yet/another/name.h", "foo/dir1/bar.h", "name/space/and/one/more.h", "foo/generated2.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of( "name/space/any/name.h", "foo/dir1/foo.h", "name/space/different/name.h", "foo/dir2/baz.h", "name/space/one/more/name.h", "foo/generated1.h")); } @Test public void testHeaderSymlinkTreesAreRegeneratedWhenKeyChanges() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders(ImmutableSortedMap.of("key.h", new FakeSourcePath("value.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of("key.h", "value.h")); node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders(ImmutableSortedMap.of("new-key.h", new FakeSourcePath("value.h"))) .build(); projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertFalse( projectFilesystem.isSymLink( Paths.get("buck-out/gen/foo/lib-private-header-symlink-tree/key.h"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of("new-key.h", "value.h")); } @Test public void testHeaderSymlinkTreesAreRegeneratedWhenValueChanges() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders(ImmutableSortedMap.of("key.h", new FakeSourcePath("value.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of("key.h", "value.h")); node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of()) .setHeaders(ImmutableSortedMap.of("key.h", new FakeSourcePath("new-value.h"))) .build(); projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertEquals("buck-out/gen/_p/CwkbTNOBmb-priv", headerSymlinkTrees.get(1).toString()); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.of("key.h", "new-value.h")); } @Test public void testHeaderSymlinkTreesWithHeadersVisibleForTesting() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "test").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.h"), ImmutableList.of("public")), SourceWithFlags.of(new FakeSourcePath("bar.h")))) .setTests(ImmutableSortedSet.of(testTarget)) .build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, testNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(project, "//foo:test"); ImmutableMap<String, String> buildSettings = getBuildSettings(testTarget, testPBXTarget, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested library in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/LpygK8zq5F-priv/.hmap " + "../buck-out/gen/_p/LpygK8zq5F-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-priv/.hmap " + "../buck-out", buildSettings.get("HEADER_SEARCH_PATHS")); assertEquals( "USER_HEADER_SEARCH_PATHS should not be set", null, buildSettings.get("USER_HEADER_SEARCH_PATHS")); } @Test public void testHeaderSymlinkTreesWithTestsAndLibraryBundles() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "test").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.h"), ImmutableList.of("public")), SourceWithFlags.of(new FakeSourcePath("bar.h")))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setTests(ImmutableSortedSet.of(testTarget)) .build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(bundleTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, bundleNode, testNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(project, "//foo:test"); ImmutableMap<String, String> buildSettings = getBuildSettings(testTarget, testPBXTarget, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested library in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/LpygK8zq5F-priv/.hmap " + "../buck-out/gen/_p/LpygK8zq5F-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-priv/.hmap " + "../buck-out", buildSettings.get("HEADER_SEARCH_PATHS")); assertEquals( "USER_HEADER_SEARCH_PATHS should not be set", null, buildSettings.get("USER_HEADER_SEARCH_PATHS")); } @Test public void testHeaderSymlinkTreesWithTestsAndBinaryBundles() throws IOException { BuildTarget binaryTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "test").build(); TargetNode<?, ?> binaryNode = AppleBinaryBuilder.createBuilder(binaryTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.h"), ImmutableList.of("public")), SourceWithFlags.of(new FakeSourcePath("bar.h")))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(binaryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setTests(ImmutableSortedSet.of(testTarget)) .build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(bundleTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(binaryNode, bundleNode, testNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(project, "//foo:test"); ImmutableMap<String, String> buildSettings = getBuildSettings(testTarget, testPBXTarget, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested binary in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/LpygK8zq5F-priv/.hmap " + "../buck-out/gen/_p/LpygK8zq5F-pub/.hmap " + "../buck-out/gen/_p/4UdYl649ee-pub/.hmap " + "../buck-out/gen/_p/4UdYl649ee-priv/.hmap " + "../buck-out", buildSettings.get("HEADER_SEARCH_PATHS")); assertEquals( "USER_HEADER_SEARCH_PATHS should not be set", null, buildSettings.get("USER_HEADER_SEARCH_PATHS")); } @Test public void testGenerateOnlyHeaderSymlinkTrees() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo/dir1/bar.h"))) .setHeaders( ImmutableSortedSet.of( new FakeSourcePath("foo/dir1/foo.h"), new FakeSourcePath("foo/dir2/baz.h"))) .setSrcs(ImmutableSortedSet.of()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node), ImmutableSet.of(ProjectGenerator.Option.GENERATE_HEADERS_SYMLINK_TREES_ONLY)); projectGenerator.createXcodeProjects(); // The project should not generated since we're generating only header symlink trees. assertFalse( projectGenerator.getProjectPath() + " should not be generated.", projectFilesystem.isDirectory(projectGenerator.getProjectPath())); List<Path> headerSymlinkTrees = projectGenerator.getGeneratedHeaderSymlinkTrees(); assertThat(headerSymlinkTrees, hasSize(2)); assertThat(headerSymlinkTrees.get(0).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-pub"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-pub"), ImmutableMap.of("foo/dir1/bar.h", "foo/dir1/bar.h")); assertThat( headerSymlinkTrees.get(1).toString(), is(equalTo("buck-out/gen/_p/CwkbTNOBmb-priv"))); assertThatHeaderSymlinkTreeContains( Paths.get("buck-out/gen/_p/CwkbTNOBmb-priv"), ImmutableMap.<String, String>builder() .put("foo/dir1/foo.h", "foo/dir1/foo.h") .put("foo/dir2/baz.h", "foo/dir2/baz.h") .build()); } private void assertThatHeaderSymlinkTreeContains(Path root, ImmutableMap<String, String> content) throws IOException { // Read the tree's header map. byte[] headerMapBytes; try (InputStream headerMapInputStream = projectFilesystem.newFileInputStream(root.resolve(".hmap"))) { headerMapBytes = ByteStreams.toByteArray(headerMapInputStream); } HeaderMap headerMap = HeaderMap.deserialize(headerMapBytes); assertNotNull(headerMap); assertThat(headerMap.getNumEntries(), equalTo(content.size())); for (Map.Entry<String, String> entry : content.entrySet()) { String key = entry.getKey(); Path link = root.resolve(Paths.get(key)); Path target = Paths.get(entry.getValue()).toAbsolutePath(); // Check the filesystem symlink assertTrue(projectFilesystem.isSymLink(link)); assertEquals(target, projectFilesystem.readSymLink(link)); // Check the header map assertThat( headerMap.lookup(key), equalTo( Paths.get("../../") .resolve(projectCell.getRoot().getFileName()) .resolve(link) .toString())); } } private void assertThatHeaderMapWithoutSymLinksIsEmpty(Path root) throws IOException { // Read the tree's header map. byte[] headerMapBytes; try (InputStream headerMapInputStream = projectFilesystem.newFileInputStream(root.resolve(".hmap"))) { headerMapBytes = ByteStreams.toByteArray(headerMapInputStream); } HeaderMap headerMap = HeaderMap.deserialize(headerMapBytes); assertNotNull(headerMap); assertEquals(headerMap.getNumEntries(), 0); } private void assertThatHeaderMapWithoutSymLinksContains( Path root, ImmutableMap<String, String> content) throws IOException { // Read the tree's header map. byte[] headerMapBytes; try (InputStream headerMapInputStream = projectFilesystem.newFileInputStream(root.resolve(".hmap"))) { headerMapBytes = ByteStreams.toByteArray(headerMapInputStream); } HeaderMap headerMap = HeaderMap.deserialize(headerMapBytes); assertNotNull(headerMap); assertThat(headerMap.getNumEntries(), equalTo(content.size())); for (Map.Entry<String, String> entry : content.entrySet()) { String key = entry.getKey(); Path target = Paths.get(entry.getValue()).toAbsolutePath(); // Check the header map assertThat(headerMap.lookup(key), equalTo(target.toString())); } } @Test public void testAppleLibraryRule() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.m"), ImmutableList.of("-foo")), SourceWithFlags.of(new FakeSourcePath("bar.m")))) .setExtraXcodeSources(ImmutableList.of(new FakeSourcePath("libsomething.a"))) .setHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.STATIC_LIBRARY)); assertHasConfigurations(target, "Debug"); assertEquals("Should have exact number of build phases", 1, target.getBuildPhases().size()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "foo.m", Optional.of("-foo"), "bar.m", Optional.empty(), "libsomething.a", Optional.empty())); // this target should not have an asset catalog build phase assertTrue( FluentIterable.from(target.getBuildPhases()) .filter(PBXResourcesBuildPhase.class) .isEmpty()); } @Test public void testHalideLibraryRule() throws IOException { BuildTarget compilerTarget = BuildTarget.builder(rootPath, "//foo", "lib") .addFlavors(HalideLibraryDescription.HALIDE_COMPILER_FLAVOR) .build(); TargetNode<?, ?> compiler = new HalideLibraryBuilder(compilerTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("main.cpp")), SourceWithFlags.of(new FakeSourcePath("filter.cpp")))) .build(); BuildTarget libTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> lib = new HalideLibraryBuilder(libTarget).build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(compiler, lib)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertHasConfigurations(target, "Debug", "Release", "Profile"); assertEquals(1, target.getBuildPhases().size()); PBXShellScriptBuildPhase scriptPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class); assertEquals(0, scriptPhase.getInputPaths().size()); assertEquals(0, scriptPhase.getOutputPaths().size()); // Note that we require that both the Halide "compiler" and the unflavored // library target are present in the requiredBuildTargets, so that both the // compiler and the generated header for the pipeline will be available for // use by the Xcode compilation step. ImmutableSet<BuildTarget> requiredBuildTargets = projectGenerator.getRequiredBuildTargets(); assertTrue(requiredBuildTargets.contains(compilerTarget)); assertThat( requiredBuildTargets, hasItem( libTarget.withFlavors( HalideLibraryDescription.HALIDE_COMPILE_FLAVOR, DEFAULT_PLATFORM.getFlavor()))); } @Test public void testCxxLibraryRule() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> cxxNode = new CxxLibraryBuilder(buildTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.cpp"), ImmutableList.of("-foo")), SourceWithFlags.of(new FakeSourcePath("bar.cpp")))) .setHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(cxxNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.STATIC_LIBRARY)); assertHasConfigurations(target, "Debug", "Release", "Profile"); assertEquals("Should have exact number of build phases", 1, target.getBuildPhases().size()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "foo.cpp", Optional.of("-foo"), "bar.cpp", Optional.empty())); } @Test public void testAppleLibraryConfiguresOutputPaths() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setHeaderPathPrefix(Optional.of("MyHeaderPathPrefix")) .setPrefixHeader(Optional.of(new FakeSourcePath("Foo/Foo-Prefix.pch"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.STATIC_LIBRARY)); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("../Foo/Foo-Prefix.pch", settings.get("GCC_PREFIX_HEADER")); assertEquals( "$SYMROOT/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME", settings.get("BUILT_PRODUCTS_DIR")); assertEquals("$BUILT_PRODUCTS_DIR", settings.get("CONFIGURATION_BUILD_DIR")); } @Test public void testAppleLibraryConfiguresSharedLibraryOutputPaths() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//hi", "lib") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setHeaderPathPrefix(Optional.of("MyHeaderPathPrefix")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//hi:lib#shared"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.DYNAMIC_LIBRARY)); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$SYMROOT/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME", settings.get("BUILT_PRODUCTS_DIR")); assertEquals("$BUILT_PRODUCTS_DIR", settings.get("CONFIGURATION_BUILD_DIR")); } @Test public void testAppleLibraryDoesntOverrideHeaderOutputPath() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs( ImmutableSortedMap.of( "Debug", ImmutableMap.of("PUBLIC_HEADERS_FOLDER_PATH", "FooHeaders"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.STATIC_LIBRARY)); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$SYMROOT/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME", settings.get("BUILT_PRODUCTS_DIR")); assertEquals("$BUILT_PRODUCTS_DIR", settings.get("CONFIGURATION_BUILD_DIR")); assertEquals("FooHeaders", settings.get("PUBLIC_HEADERS_FOLDER_PATH")); } @Test public void testAppleLibraryCxxCFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) -Wno-deprecated -Wno-conversion", settings.get("OTHER_CFLAGS")); assertEquals( "$(inherited) -Wundeclared-selector -Wno-objc-designated-initializers", settings.get("OTHER_CPLUSPLUSFLAGS")); } @Test public void testAppleLibraryCompilerAndPreprocessorFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setCompilerFlags(ImmutableList.of("-fhello")) .setPreprocessorFlags(ImmutableList.of("-fworld")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$(inherited) -Wno-deprecated -Wno-conversion -fhello -fworld", settings.get("OTHER_CFLAGS")); } @Test public void testAppleLibraryCompilerAndPreprocessorFlagsDontPropagate() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setCompilerFlags(ImmutableList.of("-fhello")) .setPreprocessorFlags(ImmutableList.of("-fworld")) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) -Wno-deprecated -Wno-conversion", settings.get("OTHER_CFLAGS")); } @Test public void testAppleLibraryExportedPreprocessorFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setExportedPreprocessorFlags(ImmutableList.of("-DHELLO")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$(inherited) -Wno-deprecated -Wno-conversion -DHELLO", settings.get("OTHER_CFLAGS")); } @Test public void testAppleLibraryExportedPreprocessorFlagsPropagate() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setExportedPreprocessorFlags(ImmutableList.of("-DHELLO")) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$(inherited) -Wno-deprecated -Wno-conversion -DHELLO", settings.get("OTHER_CFLAGS")); } @Test public void testAppleLibraryLinkerFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setLinkerFlags( ImmutableList.of( StringWithMacrosUtils.format("-Xlinker"), StringWithMacrosUtils.format("-lhello"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) -Xlinker -lhello", settings.get("OTHER_LDFLAGS")); } @Test public void testAppleLibraryLinkerFlagsDontPropagate() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setLinkerFlags(ImmutableList.of(StringWithMacrosUtils.format("-lhello"))) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) ", settings.get("OTHER_LDFLAGS")); } @Test public void testAppleLibraryExportedLinkerFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setExportedLinkerFlags( ImmutableList.of( StringWithMacrosUtils.format("-Xlinker"), StringWithMacrosUtils.format("-lhello"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) -Xlinker -lhello", settings.get("OTHER_LDFLAGS")); } @Test public void testAppleLibraryExportedLinkerFlagsPropagate() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setExportedLinkerFlags( ImmutableList.of( StringWithMacrosUtils.format("-Xlinker"), StringWithMacrosUtils.format("-lhello"))) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("$(inherited) -Xlinker -lhello", settings.get("OTHER_LDFLAGS")); } @Test public void testCxxLibraryCompilerAndPreprocessorFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setCompilerFlags(ImmutableList.of("-ffoo")) .setPreprocessorFlags(ImmutableList.of("-fbar")) .setLinkerFlags(ImmutableList.of(StringWithMacrosUtils.format("-lbaz"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> settings = ProjectGeneratorTestUtils.getBuildSettings(projectFilesystem, buildTarget, target, "Debug"); assertEquals( "-Wno-deprecated -Wno-conversion -ffoo -fbar " + "-Wno-deprecated -Wno-conversion -ffoo -fbar", settings.get("OTHER_CFLAGS")); assertEquals( "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo -fbar " + "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo -fbar", settings.get("OTHER_CPLUSPLUSFLAGS")); assertEquals("-lbaz -lbaz", settings.get("OTHER_LDFLAGS")); } @Test public void testCxxLibraryPlatformFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setPlatformCompilerFlags( PatternMatchedCollection.<ImmutableList<String>>builder() .add(Pattern.compile("android.*"), ImmutableList.of("-ffoo-android")) .add(Pattern.compile("iphone.*"), ImmutableList.of("-ffoo-iphone")) .add(Pattern.compile("macosx.*"), ImmutableList.of("-ffoo-macosx")) .build()) .setPlatformPreprocessorFlags( PatternMatchedCollection.<ImmutableList<String>>builder() .add(Pattern.compile("android.*"), ImmutableList.of("-fbar-android")) .add(Pattern.compile("iphone.*"), ImmutableList.of("-fbar-iphone")) .add(Pattern.compile("macosx.*"), ImmutableList.of("-fbar-macosx")) .build()) .setPlatformLinkerFlags( PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder() .add( Pattern.compile("android.*"), ImmutableList.of(StringWithMacrosUtils.format("-lbaz-android"))) .add( Pattern.compile("iphone.*"), ImmutableList.of(StringWithMacrosUtils.format("-lbaz-iphone"))) .add( Pattern.compile("macosx.*"), ImmutableList.of(StringWithMacrosUtils.format("-lbaz-macosx"))) .build()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> settings = ProjectGeneratorTestUtils.getBuildSettings(projectFilesystem, buildTarget, target, "Debug"); assertEquals( "-Wno-deprecated -Wno-conversion -Wno-deprecated -Wno-conversion", settings.get("OTHER_CFLAGS")); assertEquals( "-Wundeclared-selector -Wno-objc-designated-initializers " + "-Wundeclared-selector -Wno-objc-designated-initializers", settings.get("OTHER_CPLUSPLUSFLAGS")); assertEquals("$(inherited) ", settings.get("OTHER_LDFLAGS")); assertEquals( "-Wno-deprecated -Wno-conversion -ffoo-iphone -fbar-iphone " + "-Wno-deprecated -Wno-conversion -ffoo-iphone -fbar-iphone", settings.get("OTHER_CFLAGS[sdk=*iphone*]")); assertEquals( "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo-iphone -fbar-iphone " + "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo-iphone -fbar-iphone", settings.get("OTHER_CPLUSPLUSFLAGS[sdk=*iphone*]")); assertEquals("-lbaz-iphone -lbaz-iphone", settings.get("OTHER_LDFLAGS[sdk=*iphone*]")); } @Test public void testCxxLibraryExportedPreprocessorFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedPreprocessorFlags(ImmutableList.of("-DHELLO")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> settings = ProjectGeneratorTestUtils.getBuildSettings(projectFilesystem, buildTarget, target, "Debug"); assertEquals( "-Wno-deprecated -Wno-conversion -DHELLO -Wno-deprecated -Wno-conversion -DHELLO", settings.get("OTHER_CFLAGS")); } @Test public void testCxxLibraryExportedPreprocessorFlagsPropagate() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedPreprocessorFlags(ImmutableList.of("-DHELLO")) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setPreprocessorFlags(ImmutableList.of("-D__APPLE__")) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$(inherited) -Wno-deprecated -Wno-conversion -DHELLO -D__APPLE__", settings.get("OTHER_CFLAGS")); } @Test public void testCxxLibraryExportedPlatformFlags() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = new CxxLibraryBuilder(buildTarget) .setExportedPlatformPreprocessorFlags( PatternMatchedCollection.<ImmutableList<String>>builder() .add(Pattern.compile("iphone.*"), ImmutableList.of("-fbar-iphone")) .build()) .setExportedPlatformLinkerFlags( PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder() .add( Pattern.compile("macosx.*"), ImmutableList.of(StringWithMacrosUtils.format("-lbaz-macosx"))) .build()) .build(); BuildTarget dependentBuildTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> dependentNode = AppleBinaryBuilder.createBuilder(dependentBuildTarget) .setPlatformCompilerFlags( PatternMatchedCollection.<ImmutableList<String>>builder() .add(Pattern.compile("iphone.*"), ImmutableList.of("-ffoo-iphone")) .build()) .setDeps(ImmutableSortedSet.of(buildTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(node, dependentNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> settings = ProjectGeneratorTestUtils.getBuildSettings(projectFilesystem, buildTarget, target, "Debug"); assertEquals( "-Wno-deprecated -Wno-conversion -fbar-iphone -Wno-deprecated -Wno-conversion -fbar-iphone", settings.get("OTHER_CFLAGS[sdk=*iphone*]")); assertEquals( "-Wundeclared-selector -Wno-objc-designated-initializers -fbar-iphone " + "-Wundeclared-selector -Wno-objc-designated-initializers -fbar-iphone", settings.get("OTHER_CPLUSPLUSFLAGS[sdk=*iphone*]")); assertEquals(null, settings.get("OTHER_LDFLAGS[sdk=*iphone*]")); PBXTarget dependentTarget = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bin"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> dependentSettings = ProjectGeneratorTestUtils.getBuildSettings( projectFilesystem, dependentBuildTarget, dependentTarget, "Debug"); assertEquals( "-Wno-deprecated -Wno-conversion -ffoo-iphone -fbar-iphone " + "-Wno-deprecated -Wno-conversion -ffoo-iphone -fbar-iphone", dependentSettings.get("OTHER_CFLAGS[sdk=*iphone*]")); assertEquals( "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo-iphone -fbar-iphone " + "-Wundeclared-selector -Wno-objc-designated-initializers -ffoo-iphone -fbar-iphone", dependentSettings.get("OTHER_CPLUSPLUSFLAGS[sdk=*iphone*]")); assertEquals(null, dependentSettings.get("OTHER_LDFLAGS[sdk=*iphone*]")); } @Test public void testConfigurationSerializationWithoutExistingXcconfig() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of("CUSTOM_SETTING", "VALUE"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertThat(target.isa(), equalTo("PBXNativeTarget")); assertThat(target.getProductType(), equalTo(ProductType.STATIC_LIBRARY)); assertHasConfigurations(target, "Debug"); assertKeepsConfigurationsInMainGroup(projectGenerator.getGeneratedProject(), target); XCBuildConfiguration configuration = target.getBuildConfigurationList().getBuildConfigurationsByName().asMap().get("Debug"); assertEquals(configuration.getBuildSettings().count(), 0); PBXFileReference xcconfigReference = configuration.getBaseConfigurationReference(); assertEquals(xcconfigReference.getPath(), "../buck-out/gen/foo/lib-Debug.xcconfig"); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$SYMROOT/$CONFIGURATION$EFFECTIVE_PLATFORM_NAME", settings.get("BUILT_PRODUCTS_DIR")); assertEquals("$BUILT_PRODUCTS_DIR", settings.get("CONFIGURATION_BUILD_DIR")); assertEquals("VALUE", settings.get("CUSTOM_SETTING")); } @Test public void testAppleLibraryDependentsSearchHeadersAndLibraries() throws IOException { ImmutableSortedMap<String, ImmutableMap<String, String>> configs = ImmutableSortedMap.of("Debug", ImmutableMap.<String, String>of()); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setConfigs(configs) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(configs) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Test.framework"), Optional.empty())))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug"); assertEquals( "$(inherited) " + "../buck-out/gen/_p/ptQfVNNRRE-priv/.hmap " + "../buck-out/gen/_p/ptQfVNNRRE-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out", settings.get("HEADER_SEARCH_PATHS")); assertEquals(null, settings.get("USER_HEADER_SEARCH_PATHS")); assertEquals("$(inherited) $BUILT_PRODUCTS_DIR", settings.get("LIBRARY_SEARCH_PATHS")); assertEquals( "$(inherited) $BUILT_PRODUCTS_DIR $SDKROOT", settings.get("FRAMEWORK_SEARCH_PATHS")); } @Test public void testAppleLibraryDependentsInheritSearchPaths() throws IOException { ImmutableSortedMap<String, ImmutableMap<String, String>> configs = ImmutableSortedMap.of( "Debug", ImmutableMap.of( "HEADER_SEARCH_PATHS", "headers", "USER_HEADER_SEARCH_PATHS", "user_headers", "LIBRARY_SEARCH_PATHS", "libraries", "FRAMEWORK_SEARCH_PATHS", "frameworks")); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setConfigs(configs) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(configs) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Test.framework"), Optional.empty())))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug"); assertEquals( "headers " + "../buck-out/gen/_p/ptQfVNNRRE-priv/.hmap " + "../buck-out/gen/_p/ptQfVNNRRE-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out", settings.get("HEADER_SEARCH_PATHS")); assertEquals("user_headers", settings.get("USER_HEADER_SEARCH_PATHS")); assertEquals("libraries $BUILT_PRODUCTS_DIR", settings.get("LIBRARY_SEARCH_PATHS")); assertEquals("frameworks $BUILT_PRODUCTS_DIR $SDKROOT", settings.get("FRAMEWORK_SEARCH_PATHS")); } @Test public void testAppleLibraryTransitiveDependentsSearchHeadersAndLibraries() throws IOException { ImmutableSortedMap<String, ImmutableMap<String, String>> configs = ImmutableSortedMap.of("Debug", ImmutableMap.<String, String>of()); BuildTarget libraryDepTarget = BuildTarget.builder(rootPath, "//bar", "lib").build(); TargetNode<?, ?> libraryDepNode = AppleLibraryBuilder.createBuilder(libraryDepTarget) .setConfigs(configs) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setConfigs(configs) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .setDeps(ImmutableSortedSet.of(libraryDepTarget)) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(configs) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Test.framework"), Optional.empty())))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryDepNode, libraryNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug"); assertEquals( "$(inherited) " + "../buck-out/gen/_p/ptQfVNNRRE-priv/.hmap " + "../buck-out/gen/_p/ptQfVNNRRE-pub/.hmap " + "../buck-out/gen/_p/zAW4E7kxsV-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out", settings.get("HEADER_SEARCH_PATHS")); assertEquals(null, settings.get("USER_HEADER_SEARCH_PATHS")); assertEquals("$(inherited) " + "$BUILT_PRODUCTS_DIR", settings.get("LIBRARY_SEARCH_PATHS")); assertEquals( "$(inherited) " + "$BUILT_PRODUCTS_DIR $SDKROOT", settings.get("FRAMEWORK_SEARCH_PATHS")); } @Test public void testAppleLibraryWithoutSources() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); assertEquals("Should have exact number of build phases", 2, target.getBuildPhases().size()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of("fooTest.m", Optional.empty())); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of("$SDKROOT/Library.framework")); } @Test public void testAppleLibraryWithoutSourcesWithHeaders() throws IOException { ImmutableSortedMap<String, ImmutableMap<String, String>> configs = ImmutableSortedMap.of( "Debug", ImmutableMap.of( "HEADER_SEARCH_PATHS", "headers", "LIBRARY_SEARCH_PATHS", "libraries")); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setConfigs(configs) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("HeaderGroup1/bar.h"))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Library.framework"), Optional.empty())))) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(configs) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug"); assertEquals( "headers " + "../buck-out/gen/_p/ptQfVNNRRE-priv/.hmap " + "../buck-out/gen/_p/ptQfVNNRRE-pub/.hmap " + "../buck-out/gen/_p/CwkbTNOBmb-pub/.hmap " + "../buck-out", settings.get("HEADER_SEARCH_PATHS")); assertEquals("libraries $BUILT_PRODUCTS_DIR", settings.get("LIBRARY_SEARCH_PATHS")); assertEquals("Should have exact number of build phases", 2, target.getBuildPhases().size()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of("fooTest.m", Optional.empty())); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of("$SDKROOT/Library.framework")); } @Test public void testAppleTestRule() throws IOException { BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setInfoPlist(new FakeSourcePath("Info.plist")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(testNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); assertEquals(target.getProductType(), ProductType.UNIT_TEST); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXFileReference productReference = target.getProductReference(); assertEquals("xctest.xctest", productReference.getName()); } @Test public void testAppleBinaryRule() throws IOException { BuildTarget depTarget = BuildTarget.builder(rootPath, "//dep", "dep").build(); TargetNode<?, ?> depNode = AppleLibraryBuilder.createBuilder(depTarget) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("e.m")))) .build(); BuildTarget binaryTarget = BuildTarget.builder(rootPath, "//foo", "binary").build(); TargetNode<?, ?> binaryNode = AppleBinaryBuilder.createBuilder(binaryTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.m"), ImmutableList.of("-foo")))) .setExtraXcodeSources(ImmutableList.of(new FakeSourcePath("libsomething.a"))) .setHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .setFrameworks( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("Foo.framework"), Optional.empty())))) .setDeps(ImmutableSortedSet.of(depTarget)) .setHeaderPathPrefix(Optional.empty()) .setPrefixHeader(Optional.empty()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(depNode, binaryNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:binary"); assertHasConfigurations(target, "Debug"); assertEquals(target.getProductType(), ProductType.TOOL); assertEquals("Should have exact number of build phases", 2, target.getBuildPhases().size()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "foo.m", Optional.of("-foo"), "libsomething.a", Optional.empty())); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of( "$SDKROOT/Foo.framework", // Propagated library from deps. "$BUILT_PRODUCTS_DIR/libdep.a")); // this test does not have a dependency on any asset catalogs, so verify no build phase for them // exists. assertTrue( FluentIterable.from(target.getBuildPhases()) .filter(PBXResourcesBuildPhase.class) .isEmpty()); } @Test public void testAppleBundleRuleWithPreBuildScriptDependency() throws IOException { BuildTarget scriptTarget = BuildTarget.builder(rootPath, "//foo", "pre_build_script") .addFlavors(DEFAULT_FLAVOR) .build(); TargetNode<?, ?> scriptNode = XcodePrebuildScriptBuilder.createBuilder(scriptTarget).setCmd("script.sh").build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "resource").build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(ImmutableSet.of(new FakeSourcePath("bar.png"))) .setDirs(ImmutableSet.of()) .build(); BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(scriptTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(scriptNode, resourceNode, sharedLibraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle"); assertThat(target.getName(), equalTo("//foo:bundle")); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXShellScriptBuildPhase shellScriptBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class); assertThat(shellScriptBuildPhase.getShellScript(), equalTo("script.sh")); // Assert that the pre-build script phase comes before resources are copied. assertThat(target.getBuildPhases().get(0), instanceOf(PBXShellScriptBuildPhase.class)); assertThat(target.getBuildPhases().get(1), instanceOf(PBXResourcesBuildPhase.class)); } @Test public void testAppleBundleRuleWithPostBuildScriptDependency() throws IOException { BuildTarget scriptTarget = BuildTarget.builder(rootPath, "//foo", "post_build_script") .addFlavors(DEFAULT_FLAVOR) .build(); TargetNode<?, ?> scriptNode = XcodePostbuildScriptBuilder.createBuilder(scriptTarget).setCmd("script.sh").build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "resource").build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(ImmutableSet.of(new FakeSourcePath("bar.png"))) .setDirs(ImmutableSet.of()) .build(); BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(scriptTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(scriptNode, resourceNode, sharedLibraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle"); assertThat(target.getName(), equalTo("//foo:bundle")); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXShellScriptBuildPhase shellScriptBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class); assertThat(shellScriptBuildPhase.getShellScript(), equalTo("script.sh")); // Assert that the post-build script phase comes after resources are copied. assertThat(target.getBuildPhases().get(0), instanceOf(PBXResourcesBuildPhase.class)); assertThat(target.getBuildPhases().get(1), instanceOf(PBXShellScriptBuildPhase.class)); } @Test public void testAppleBundleRuleWithRNLibraryDependency() throws IOException { BuildTarget rnLibraryTarget = BuildTarget.builder(rootPath, "//foo", "rn_library").addFlavors(DEFAULT_FLAVOR).build(); ProjectFilesystem filesystem = new AllExistingProjectFilesystem(); ReactNativeBuckConfig buckConfig = new ReactNativeBuckConfig( FakeBuckConfig.builder() .setSections( ImmutableMap.of( "react-native", ImmutableMap.of("packager_worker", "react-native/packager.sh"))) .setFilesystem(filesystem) .build()); TargetNode<?, ?> rnLibraryNode = IosReactNativeLibraryBuilder.builder(rnLibraryTarget, buckConfig) .setBundleName("Apps/Foo/FooBundle.js") .setEntryPath(new PathSourcePath(filesystem, Paths.get("js/FooApp.js"))) .build(); BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget).build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(rnLibraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(rnLibraryNode, sharedLibraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle"); assertThat(target.getName(), equalTo("//foo:bundle")); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXShellScriptBuildPhase shellScriptBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXShellScriptBuildPhase.class); assertThat(shellScriptBuildPhase.getShellScript(), startsWith("BASE_DIR=")); } @Test public void testAppleBundleRuleForSharedLibraryFramework() throws IOException { BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> node = AppleBundleBuilder.createBuilder(buildTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(sharedLibraryNode, node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle"); assertEquals(target.getProductType(), ProductType.FRAMEWORK); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXFileReference productReference = target.getProductReference(); assertEquals("bundle.framework", productReference.getName()); assertEquals(Optional.of("wrapper.framework"), productReference.getExplicitFileType()); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("framework", settings.get("WRAPPER_EXTENSION")); } @Test public void testAppleResourceWithVariantGroupSetsFileTypeBasedOnPath() throws IOException { BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "resource").addFlavors(DEFAULT_FLAVOR).build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(ImmutableSet.of()) .setDirs(ImmutableSet.of()) .setVariants(ImmutableSet.of(new FakeSourcePath("Base.lproj/Bar.storyboard"))) .build(); BuildTarget fooLibraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> fooLibraryNode = AppleLibraryBuilder.createBuilder(fooLibraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(fooLibraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(fooLibraryNode, bundleNode, resourceNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(bundleTarget.getFullyQualifiedName()); PBXGroup resourcesGroup = targetGroup.getOrCreateChildGroupByName("Resources"); PBXVariantGroup storyboardGroup = (PBXVariantGroup) Iterables.get(resourcesGroup.getChildren(), 0); List<PBXReference> storyboardGroupChildren = storyboardGroup.getChildren(); assertEquals(1, storyboardGroupChildren.size()); assertTrue(storyboardGroupChildren.get(0) instanceof PBXFileReference); PBXFileReference baseStoryboardReference = (PBXFileReference) storyboardGroupChildren.get(0); assertEquals("Base", baseStoryboardReference.getName()); // Make sure the file type is set from the path. assertEquals(Optional.of("file.storyboard"), baseStoryboardReference.getLastKnownFileType()); assertEquals(Optional.empty(), baseStoryboardReference.getExplicitFileType()); } @Test public void testAppleBundleRuleWithCustomXcodeProductType() throws IOException { BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "custombundle").build(); TargetNode<?, ?> node = AppleBundleBuilder.createBuilder(buildTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setXcodeProductType(Optional.of("com.facebook.buck.niftyProductType")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(sharedLibraryNode, node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:custombundle"); assertEquals(target.getProductType(), ProductType.of("com.facebook.buck.niftyProductType")); assertThat(target.isa(), equalTo("PBXNativeTarget")); PBXFileReference productReference = target.getProductReference(); assertEquals("custombundle.framework", productReference.getName()); assertEquals(Optional.of("wrapper.framework"), productReference.getExplicitFileType()); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug"); assertEquals("framework", settings.get("WRAPPER_EXTENSION")); } @Test public void testAppleBundleRuleWithCustomXcodeProductNameFromConfigs() throws IOException { BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget) .setConfigs( ImmutableSortedMap.of("Debug", ImmutableMap.of("PRODUCT_NAME", "FancyFramework"))) .build(); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "custombundle").build(); TargetNode<?, ?> node = AppleBundleBuilder.createBuilder(buildTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(sharedLibraryNode, node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:custombundle"); ImmutableMap<String, String> buildSettings = getBuildSettings(buildTarget, target, "Debug"); assertThat(buildSettings.get("PRODUCT_NAME"), Matchers.equalTo("FancyFramework")); } private void testRuleAddsReference(BuildTarget ruleTarget, TargetNode<?, ?> ruleNode, String path) throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(ruleTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(ruleNode, libraryNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(libraryTarget.getFullyQualifiedName()); PBXGroup resourcesGroup = targetGroup.getOrCreateChildGroupByName("Resources"); assertThat(resourcesGroup.getChildren(), hasSize(1)); PBXFileReference ruleReference = (PBXFileReference) Iterables.get(resourcesGroup.getChildren(), 0); assertEquals(path, ruleReference.getName()); } @Test public void testCoreDataModelRuleAddsReference() throws IOException { BuildTarget modelTarget = BuildTarget.builder(rootPath, "//foo", "model").build(); TargetNode<?, ?> modelNode = CoreDataModelBuilder.createBuilder(modelTarget) .setPath(new FakeSourcePath("foo.xcdatamodel").getRelativePath()) .build(); testRuleAddsReference(modelTarget, modelNode, "foo.xcdatamodel"); } @Test public void testSceneKitAssetsRuleAddsReference() throws IOException { BuildTarget target = BuildTarget.builder(rootPath, "//foo", "scenekitasset").build(); TargetNode<?, ?> node = SceneKitAssetsBuilder.createBuilder(target) .setPath(new FakeSourcePath("foo.scnassets").getRelativePath()) .build(); testRuleAddsReference(target, node, "foo.scnassets"); } @Test public void testCodeSignEntitlementsAddsReference() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setBinary(libraryTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath(("Info.plist"))) .setInfoPlistSubstitutions(ImmutableMap.of("CODE_SIGN_ENTITLEMENTS", "foo.plist")) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(bundleTarget.getFullyQualifiedName()); assertThat(targetGroup.getChildren(), hasSize(3)); PBXFileReference ruleReference = (PBXFileReference) Iterables.get(targetGroup.getChildren(), 2); assertEquals("foo.plist", ruleReference.getName()); } @Test public void testAppleWatchTarget() throws IOException { BuildTarget watchAppBinaryTarget = BuildTarget.builder(rootPath, "//foo", "WatchAppBinary").build(); TargetNode<?, ?> watchAppBinaryNode = AppleBinaryBuilder.createBuilder(watchAppBinaryTarget).build(); BuildTarget watchAppTarget = BuildTarget.builder(rootPath, "//foo", "WatchApp").addFlavors(DEFAULT_FLAVOR).build(); TargetNode<?, ?> watchAppNode = AppleBundleBuilder.createBuilder(watchAppTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setXcodeProductType(Optional.of("com.apple.product-type.application.watchapp2")) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(watchAppBinaryTarget) .build(); BuildTarget hostAppBinaryTarget = BuildTarget.builder(rootPath, "//foo", "HostAppBinary").build(); TargetNode<?, ?> hostAppBinaryNode = AppleBinaryBuilder.createBuilder(hostAppBinaryTarget).build(); BuildTarget hostAppTarget = BuildTarget.builder(rootPath, "//foo", "HostApp").build(); TargetNode<?, ?> hostAppNode = AppleBundleBuilder.createBuilder(hostAppTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(hostAppBinaryTarget) .setDeps(ImmutableSortedSet.of(watchAppTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(watchAppNode, watchAppBinaryNode, hostAppNode, hostAppBinaryNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:HostApp"); assertEquals(target.getProductType(), ProductType.APPLICATION); ProjectGeneratorTestUtils.assertHasSingletonCopyFilesPhaseWithFileEntries( target, ImmutableList.of("$BUILT_PRODUCTS_DIR/WatchApp.app")); PBXCopyFilesBuildPhase copyBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXCopyFilesBuildPhase.class); assertEquals( copyBuildPhase.getDstSubfolderSpec(), CopyFilePhaseDestinationSpec.builder() .setDestination(PBXCopyFilesBuildPhase.Destination.PRODUCTS) .setPath("$(CONTENTS_FOLDER_PATH)/Watch") .build()); } @Test public void ruleToTargetMapContainsPBXTarget() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo.m"), ImmutableList.of("-foo")), SourceWithFlags.of(new FakeSourcePath("bar.m")))) .setHeaders(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); assertEquals( buildTarget, Iterables.getOnlyElement(projectGenerator.getBuildTargetToGeneratedTargetMap().keySet())); PBXTarget target = Iterables.getOnlyElement(projectGenerator.getBuildTargetToGeneratedTargetMap().values()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "foo.m", Optional.of("-foo"), "bar.m", Optional.empty())); } @Test public void generatedGidsForTargetsAreStable() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "foo").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget).build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:foo"); String expectedGID = String.format("%08X%08X%08X", target.isa().hashCode(), target.getName().hashCode(), 0); assertEquals( "expected GID has correct value (value from which it's derived have not changed)", "E66DC04E2245423200000000", expectedGID); assertEquals("generated GID is same as expected", expectedGID, target.getGlobalID()); } @Test public void stopsLinkingRecursiveDependenciesAtSharedLibraries() throws IOException { BuildTarget dependentStaticLibraryTarget = BuildTarget.builder(rootPath, "//dep", "static").build(); TargetNode<?, ?> dependentStaticLibraryNode = AppleLibraryBuilder.createBuilder(dependentStaticLibraryTarget).build(); BuildTarget dependentSharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> dependentSharedLibraryNode = AppleLibraryBuilder.createBuilder(dependentSharedLibraryTarget) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("empty.m")))) .setDeps(ImmutableSortedSet.of(dependentStaticLibraryTarget)) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "library") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(dependentSharedLibraryTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "final").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of( dependentStaticLibraryNode, dependentSharedLibraryNode, libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:final"); assertEquals(target.getProductType(), ProductType.BUNDLE); assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size()); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of("$BUILT_PRODUCTS_DIR/libshared.dylib")); } @Test public void stopsLinkingRecursiveDependenciesAtBundles() throws IOException { BuildTarget dependentStaticLibraryTarget = BuildTarget.builder(rootPath, "//dep", "static").build(); TargetNode<?, ?> dependentStaticLibraryNode = AppleLibraryBuilder.createBuilder(dependentStaticLibraryTarget).build(); BuildTarget dependentSharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> dependentSharedLibraryNode = AppleLibraryBuilder.createBuilder(dependentSharedLibraryTarget) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("e.m")))) .setDeps(ImmutableSortedSet.of(dependentStaticLibraryTarget)) .build(); BuildTarget dependentFrameworkTarget = BuildTarget.builder(rootPath, "//dep", "framework").build(); TargetNode<?, ?> dependentFrameworkNode = AppleBundleBuilder.createBuilder(dependentFrameworkTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(dependentSharedLibraryTarget) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "library") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(dependentFrameworkTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "final").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of( dependentStaticLibraryNode, dependentSharedLibraryNode, dependentFrameworkNode, libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:final"); assertEquals(target.getProductType(), ProductType.BUNDLE); assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size()); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of("$BUILT_PRODUCTS_DIR/framework.framework")); } @Test public void stopsCopyingRecursiveDependenciesAtBundles() throws IOException { BuildTarget dependentStaticLibraryTarget = BuildTarget.builder(rootPath, "//dep", "static").build(); TargetNode<?, ?> dependentStaticLibraryNode = AppleLibraryBuilder.createBuilder(dependentStaticLibraryTarget).build(); BuildTarget dependentStaticFrameworkTarget = BuildTarget.builder(rootPath, "//dep", "static-framework").build(); TargetNode<?, ?> dependentStaticFrameworkNode = AppleBundleBuilder.createBuilder(dependentStaticFrameworkTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(dependentStaticLibraryTarget) .build(); BuildTarget dependentSharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> dependentSharedLibraryNode = AppleLibraryBuilder.createBuilder(dependentSharedLibraryTarget) .setDeps(ImmutableSortedSet.of(dependentStaticFrameworkTarget)) .build(); BuildTarget dependentFrameworkTarget = BuildTarget.builder(rootPath, "//dep", "framework").build(); TargetNode<?, ?> dependentFrameworkNode = AppleBundleBuilder.createBuilder(dependentFrameworkTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(dependentSharedLibraryTarget) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "library") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("e.m")))) .setDeps(ImmutableSortedSet.of(dependentFrameworkTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "final").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( // ant needs this to be explicit ImmutableSet.of( dependentStaticLibraryNode, dependentStaticFrameworkNode, dependentSharedLibraryNode, dependentFrameworkNode, libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:final"); assertEquals(target.getProductType(), ProductType.BUNDLE); assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size()); ProjectGeneratorTestUtils.assertHasSingletonCopyFilesPhaseWithFileEntries( target, ImmutableList.of("$BUILT_PRODUCTS_DIR/framework.framework")); } @Test public void bundlesDontLinkTheirOwnBinary() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "library") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget).build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "final").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(libraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(libraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:final"); assertEquals(target.getProductType(), ProductType.BUNDLE); assertEquals("Should have exact number of build phases ", 0, target.getBuildPhases().size()); } @Test public void resourcesInDependenciesPropagatesToBundles() throws IOException { BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(ImmutableSet.of(new FakeSourcePath("bar.png"))) .setDirs(ImmutableSet.of(new FakeSourcePath("foodir"))) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); BuildTarget bundleLibraryTarget = BuildTarget.builder(rootPath, "//foo", "bundlelib").build(); TargetNode<?, ?> bundleLibraryNode = AppleLibraryBuilder.createBuilder(bundleLibraryTarget) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(bundleLibraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(resourceNode, libraryNode, bundleLibraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject generatedProject = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(generatedProject, "//foo:bundle"); assertHasSingletonResourcesPhaseWithEntries(target, "bar.png", "foodir"); } @Test public void assetCatalogsInDependenciesPropogatesToBundles() throws IOException { BuildTarget assetCatalogTarget = BuildTarget.builder(rootPath, "//foo", "asset_catalog").build(); TargetNode<?, ?> assetCatalogNode = AppleAssetCatalogBuilder.createBuilder(assetCatalogTarget) .setDirs(ImmutableSortedSet.of(new FakeSourcePath("AssetCatalog.xcassets"))) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(assetCatalogTarget)) .build(); BuildTarget bundleLibraryTarget = BuildTarget.builder(rootPath, "//foo", "bundlelib").build(); TargetNode<?, ?> bundleLibraryNode = AppleLibraryBuilder.createBuilder(bundleLibraryTarget) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(bundleLibraryTarget) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(assetCatalogNode, libraryNode, bundleLibraryNode, bundleNode)); projectGenerator.createXcodeProjects(); PBXProject generatedProject = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(generatedProject, "//foo:bundle"); assertHasSingletonResourcesPhaseWithEntries(target, "AssetCatalog.xcassets"); } @Test public void generatedTargetConfigurationHasRepoRootSet() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "rule").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXProject generatedProject = projectGenerator.getGeneratedProject(); ImmutableMap<String, String> settings = getBuildSettings(buildTarget, generatedProject.getTargets().get(0), "Debug"); assertThat(settings, hasKey("REPO_ROOT")); assertEquals( projectFilesystem.getRootPath().toAbsolutePath().normalize().toString(), settings.get("REPO_ROOT")); } /** * The project configurations should have named entries corresponding to every existing target * configuration for targets in the project. */ @Test public void generatedProjectConfigurationListIsUnionOfAllTargetConfigurations() throws IOException { BuildTarget buildTarget1 = BuildTarget.builder(rootPath, "//foo", "rule1").build(); TargetNode<?, ?> node1 = AppleLibraryBuilder.createBuilder(buildTarget1) .setConfigs( ImmutableSortedMap.of( "Conf1", ImmutableMap.of(), "Conf2", ImmutableMap.of())) .build(); BuildTarget buildTarget2 = BuildTarget.builder(rootPath, "//foo", "rule2").build(); TargetNode<?, ?> node2 = AppleLibraryBuilder.createBuilder(buildTarget2) .setConfigs( ImmutableSortedMap.of( "Conf2", ImmutableMap.of(), "Conf3", ImmutableMap.of())) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node1, node2)); projectGenerator.createXcodeProjects(); PBXProject generatedProject = projectGenerator.getGeneratedProject(); Map<String, XCBuildConfiguration> configurations = generatedProject.getBuildConfigurationList().getBuildConfigurationsByName().asMap(); assertThat(configurations, hasKey("Conf1")); assertThat(configurations, hasKey("Conf2")); assertThat(configurations, hasKey("Conf3")); } @Test public void shouldEmitFilesForBuildSettingPrefixedFrameworks() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "rule") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setLibraries( ImmutableSortedSet.of( FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.BUILT_PRODUCTS_DIR, Paths.get("libfoo.a"), Optional.empty())), FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SDKROOT, Paths.get("libfoo.a"), Optional.empty())), FrameworkPath.ofSourceTreePath( new SourceTreePath( PBXReference.SourceTree.SOURCE_ROOT, Paths.get("libfoo.a"), Optional.empty())))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXProject generatedProject = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(generatedProject, "//foo:rule#shared"); ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries( target, ImmutableList.of( "$BUILT_PRODUCTS_DIR/libfoo.a", "$SDKROOT/libfoo.a", "$SOURCE_ROOT/libfoo.a")); } @Test public void testGeneratedProjectIsNotReadOnlyIfOptionNotSpecified() throws IOException { ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of()); projectGenerator.createXcodeProjects(); assertTrue(fakeProjectFilesystem.getFileAttributesAtPath(OUTPUT_PROJECT_FILE_PATH).isEmpty()); } @Test public void testGeneratedProjectIsReadOnlyIfOptionSpecified() throws IOException { ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(), ImmutableSet.of(ProjectGenerator.Option.GENERATE_READ_ONLY_FILES)); projectGenerator.createXcodeProjects(); ImmutableSet<PosixFilePermission> permissions = ImmutableSet.of( PosixFilePermission.OWNER_READ, PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ); FileAttribute<?> expectedAttribute = PosixFilePermissions.asFileAttribute(permissions); // This is lame; Java's PosixFilePermissions class doesn't // implement equals() or hashCode() in its FileAttribute anonymous // class (http://tinyurl.com/nznhfhy). So instead of comparing // the sets, we have to pull out the attribute and check its value // for equality. FileAttribute<?> actualAttribute = Iterables.getOnlyElement( fakeProjectFilesystem.getFileAttributesAtPath(OUTPUT_PROJECT_FILE_PATH)); assertEquals(expectedAttribute.value(), actualAttribute.value()); } @Test public void projectIsRewrittenIfContentsHaveChanged() throws IOException { ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of()); clock.setCurrentTimeMillis(49152); projectGenerator.createXcodeProjects(); assertThat( projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH), equalTo(FileTime.fromMillis(49152L))); BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "foo").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget).build(); ProjectGenerator projectGenerator2 = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); clock.setCurrentTimeMillis(64738); projectGenerator2.createXcodeProjects(); assertThat( projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH), equalTo(FileTime.fromMillis(64738L))); } @Test public void projectIsNotRewrittenIfContentsHaveNotChanged() throws IOException { ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of()); clock.setCurrentTimeMillis(49152); projectGenerator.createXcodeProjects(); assertThat( projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH), equalTo(FileTime.fromMillis(49152L))); ProjectGenerator projectGenerator2 = createProjectGeneratorForCombinedProject(ImmutableSet.of()); clock.setCurrentTimeMillis(64738); projectGenerator2.createXcodeProjects(); assertThat( projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH), equalTo(FileTime.fromMillis(49152L))); } @Test public void nonexistentResourceDirectoryShouldThrow() throws IOException { ImmutableSet<TargetNode<?, ?>> nodes = setupSimpleLibraryWithResources( ImmutableSet.of(), ImmutableSet.of(new FakeSourcePath("nonexistent-directory"))); thrown.expect(HumanReadableException.class); thrown.expectMessage( "nonexistent-directory specified in the dirs parameter of //foo:res is not a directory"); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(nodes); projectGenerator.createXcodeProjects(); } @Test public void nonexistentResourceFileShouldThrow() throws IOException { ImmutableSet<TargetNode<?, ?>> nodes = setupSimpleLibraryWithResources( ImmutableSet.of(new FakeSourcePath("nonexistent-file.png")), ImmutableSet.of()); thrown.expect(HumanReadableException.class); thrown.expectMessage( "nonexistent-file.png specified in the files parameter of //foo:res is not a regular file"); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(nodes); projectGenerator.createXcodeProjects(); } @Test public void usingFileAsResourceDirectoryShouldThrow() throws IOException { ImmutableSet<TargetNode<?, ?>> nodes = setupSimpleLibraryWithResources( ImmutableSet.of(), ImmutableSet.of(new FakeSourcePath("bar.png"))); thrown.expect(HumanReadableException.class); thrown.expectMessage("bar.png specified in the dirs parameter of //foo:res is not a directory"); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(nodes); projectGenerator.createXcodeProjects(); } @Test public void usingDirectoryAsResourceFileShouldThrow() throws IOException { ImmutableSet<TargetNode<?, ?>> nodes = setupSimpleLibraryWithResources( ImmutableSet.of(new FakeSourcePath("foodir")), ImmutableSet.of()); thrown.expect(HumanReadableException.class); thrown.expectMessage( "foodir specified in the files parameter of //foo:res is not a regular file"); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(nodes); projectGenerator.createXcodeProjects(); } @Test public void usingBuildTargetSourcePathInResourceDirsOrFilesDoesNotThrow() throws IOException { BuildTarget buildTarget = BuildTargetFactory.newInstance("//some:rule"); SourcePath sourcePath = new DefaultBuildTargetSourcePath(buildTarget); TargetNode<?, ?> generatingTarget = ExportFileBuilder.newExportFileBuilder(buildTarget).build(); ImmutableSet<TargetNode<?, ?>> nodes = FluentIterable.from( setupSimpleLibraryWithResources( ImmutableSet.of(sourcePath), ImmutableSet.of(sourcePath))) .append(generatingTarget) .toSet(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(nodes); projectGenerator.createXcodeProjects(); } private BuckEventBus getFakeBuckEventBus() { return BuckEventBusFactory.newInstance(new IncrementingFakeClock(TimeUnit.SECONDS.toNanos(1))); } @Test public void testResolvingExportFile() throws IOException { BuildTarget source1Target = BuildTarget.builder(rootPath, "//Vendor", "source1").build(); BuildTarget source2Target = BuildTarget.builder(rootPath, "//Vendor", "source2").build(); BuildTarget source2RefTarget = BuildTarget.builder(rootPath, "//Vendor", "source2ref").build(); BuildTarget source3Target = BuildTarget.builder(rootPath, "//Vendor", "source3").build(); BuildTarget headerTarget = BuildTarget.builder(rootPath, "//Vendor", "header").build(); BuildTarget libTarget = BuildTarget.builder(rootPath, "//Libraries", "foo").build(); TargetNode<ExportFileDescriptionArg, ?> source1 = ExportFileBuilder.newExportFileBuilder(source1Target) .setSrc(new PathSourcePath(projectFilesystem, Paths.get("Vendor/sources/source1"))) .build(); TargetNode<ExportFileDescriptionArg, ?> source2 = ExportFileBuilder.newExportFileBuilder(source2Target) .setSrc(new PathSourcePath(projectFilesystem, Paths.get("Vendor/source2"))) .build(); TargetNode<ExportFileDescriptionArg, ?> source2Ref = ExportFileBuilder.newExportFileBuilder(source2RefTarget) .setSrc(new DefaultBuildTargetSourcePath(source2Target)) .build(); TargetNode<ExportFileDescriptionArg, ?> source3 = ExportFileBuilder.newExportFileBuilder(source3Target).build(); TargetNode<ExportFileDescriptionArg, ?> header = ExportFileBuilder.newExportFileBuilder(headerTarget).build(); TargetNode<AppleLibraryDescriptionArg, ?> library = AppleLibraryBuilder.createBuilder(libTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new DefaultBuildTargetSourcePath(source1Target)), SourceWithFlags.of(new DefaultBuildTargetSourcePath(source2RefTarget)), SourceWithFlags.of(new DefaultBuildTargetSourcePath(source3Target)))) .setPrefixHeader(Optional.of(new DefaultBuildTargetSourcePath(headerTarget))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(source1, source2, source2Ref, source3, header, library)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), libTarget.toString()); assertHasSingletonSourcesPhaseWithSourcesAndFlags( target, ImmutableMap.of( "Vendor/sources/source1", Optional.empty(), "Vendor/source2", Optional.empty(), "Vendor/source3", Optional.empty())); ImmutableMap<String, String> settings = getBuildSettings(libTarget, target, "Debug"); assertEquals("../Vendor/header", settings.get("GCC_PREFIX_HEADER")); } @Test public void applicationTestUsesHostAppAsTestHostAndBundleLoader() throws IOException { BuildTarget hostAppBinaryTarget = BuildTarget.builder(rootPath, "//foo", "HostAppBinary").build(); TargetNode<?, ?> hostAppBinaryNode = AppleBinaryBuilder.createBuilder(hostAppBinaryTarget).build(); BuildTarget hostAppTarget = BuildTarget.builder(rootPath, "//foo", "HostApp").build(); TargetNode<?, ?> hostAppNode = AppleBundleBuilder.createBuilder(hostAppTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(hostAppBinaryTarget) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "AppTest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setTestHostApp(Optional.of(hostAppTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(hostAppBinaryNode, hostAppNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:AppTest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, testPBXTarget, "Debug"); // Check starts with as the remainder depends on the bundle style at build time. assertTrue(settings.get("BUNDLE_LOADER").startsWith("$BUILT_PRODUCTS_DIR/./HostApp.app/")); assertEquals("$(BUNDLE_LOADER)", settings.get("TEST_HOST")); } @Test public void uiTestUsesHostAppAsTarget() throws IOException { BuildTarget hostAppBinaryTarget = BuildTarget.builder(rootPath, "//foo", "HostAppBinary").build(); TargetNode<?, ?> hostAppBinaryNode = AppleBinaryBuilder.createBuilder(hostAppBinaryTarget).build(); BuildTarget hostAppTarget = BuildTarget.builder(rootPath, "//foo", "HostApp").build(); TargetNode<?, ?> hostAppNode = AppleBundleBuilder.createBuilder(hostAppTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(hostAppBinaryTarget) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "AppTest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setTestHostApp(Optional.of(hostAppTarget)) .isUiTest(true) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(hostAppBinaryNode, hostAppNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:AppTest"); assertEquals(testPBXTarget.getProductType(), ProductType.UI_TEST); ImmutableMap<String, String> settings = getBuildSettings(testTarget, testPBXTarget, "Debug"); // Check starts with as the remainder depends on the bundle style at build time. assertEquals(settings.get("TEST_TARGET_NAME"), "//foo:HostApp"); } @Test public void applicationTestDoesNotCopyHostAppBundleIntoTestBundle() throws IOException { BuildTarget hostAppBinaryTarget = BuildTarget.builder(rootPath, "//foo", "HostAppBinary").build(); TargetNode<?, ?> hostAppBinaryNode = AppleBinaryBuilder.createBuilder(hostAppBinaryTarget).build(); BuildTarget hostAppTarget = BuildTarget.builder(rootPath, "//foo", "HostApp").build(); TargetNode<?, ?> hostAppNode = AppleBundleBuilder.createBuilder(hostAppTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(hostAppBinaryTarget) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "AppTest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setTestHostApp(Optional.of(hostAppTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(hostAppBinaryNode, hostAppNode, testNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget testPBXTarget = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:AppTest"); // for this test phases should be empty - there should be no copy phases in particular assertThat(testPBXTarget.getBuildPhases().size(), Matchers.equalTo(0)); } @Test public void cxxFlagsPropagatedToConfig() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setLangPreprocessorFlags( ImmutableMap.of( CxxSource.Type.C, ImmutableList.of("-std=gnu11"), CxxSource.Type.OBJC, ImmutableList.of("-std=gnu11", "-fobjc-arc"), CxxSource.Type.CXX, ImmutableList.of("-std=c++11", "-stdlib=libc++"), CxxSource.Type.OBJCXX, ImmutableList.of("-std=c++11", "-stdlib=libc++", "-fobjc-arc"))) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new FakeSourcePath("foo1.m")), SourceWithFlags.of(new FakeSourcePath("foo2.mm")), SourceWithFlags.of(new FakeSourcePath("foo3.c")), SourceWithFlags.of(new FakeSourcePath("foo4.cc")))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); PBXSourcesBuildPhase sourcesBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXSourcesBuildPhase.class); ImmutableMap<String, String> expected = ImmutableMap.of( "foo1.m", "-std=gnu11 -fobjc-arc", "foo2.mm", "-std=c++11 -stdlib=libc++ -fobjc-arc", "foo3.c", "-std=gnu11", "foo4.cc", "-std=c++11 -stdlib=libc++"); for (PBXBuildFile file : sourcesBuildPhase.getFiles()) { String fileName = file.getFileRef().getName(); NSDictionary buildFileSettings = file.getSettings().get(); NSString compilerFlags = (NSString) buildFileSettings.get("COMPILER_FLAGS"); assertNotNull("Build file settings should have COMPILER_FLAGS entry", compilerFlags); assertEquals(compilerFlags.toString(), expected.get(fileName)); } } @Test public void testConfiglessAppleTargetGetsDefaultBuildConfigurations() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.mm")))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:lib"); assertHasConfigurations(target, "Debug", "Release", "Profile"); ImmutableMap<String, String> debugSettings = ProjectGeneratorTestUtils.getBuildSettings(projectFilesystem, buildTarget, target, "Debug"); assertThat(debugSettings.size(), Matchers.greaterThan(0)); ImmutableMap<String, String> profileSettings = ProjectGeneratorTestUtils.getBuildSettings( projectFilesystem, buildTarget, target, "Profile"); assertThat(debugSettings, Matchers.equalTo(profileSettings)); ImmutableMap<String, String> releaseSettings = ProjectGeneratorTestUtils.getBuildSettings( projectFilesystem, buildTarget, target, "Release"); assertThat(debugSettings, Matchers.equalTo(releaseSettings)); } @Test public void testAssetCatalogsUnderLibraryNotTest() throws IOException { BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "test").build(); BuildTarget assetCatalogTarget = BuildTarget.builder(rootPath, "//foo", "asset_catalog").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setTests(ImmutableSortedSet.of(testTarget)) .setDeps(ImmutableSortedSet.of(assetCatalogTarget)) .build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); TargetNode<?, ?> assetCatalogNode = AppleAssetCatalogBuilder.createBuilder(assetCatalogTarget) .setDirs(ImmutableSortedSet.of(new FakeSourcePath("AssetCatalog.xcassets"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode, assetCatalogNode), ImmutableSet.of(ProjectGenerator.Option.USE_SHORT_NAMES_FOR_TARGETS)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup mainGroup = project.getMainGroup(); PBXTarget fooLibTarget = assertTargetExistsAndReturnTarget(project, "lib"); assertTrue( FluentIterable.from(fooLibTarget.getBuildPhases()) .filter(PBXResourcesBuildPhase.class) .isEmpty()); PBXGroup libResourcesGroup = mainGroup.getOrCreateChildGroupByName("lib").getOrCreateChildGroupByName("Resources"); PBXFileReference assetCatalogFile = (PBXFileReference) libResourcesGroup.getChildren().get(0); assertEquals("AssetCatalog.xcassets", assetCatalogFile.getName()); PBXTarget fooTestTarget = assertTargetExistsAndReturnTarget(project, "test"); PBXResourcesBuildPhase resourcesBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType( fooTestTarget, PBXResourcesBuildPhase.class); assertThat(resourcesBuildPhase.getFiles(), hasSize(1)); assertThat( assertFileRefIsRelativeAndResolvePath(resourcesBuildPhase.getFiles().get(0).getFileRef()), equalTo(projectFilesystem.resolve("AssetCatalog.xcassets").toString())); PBXGroup testResourcesGroup = mainGroup.getOrCreateChildGroupByName("test").getOrCreateChildGroupByName("Resources"); assetCatalogFile = (PBXFileReference) testResourcesGroup.getChildren().get(0); assertEquals("AssetCatalog.xcassets", assetCatalogFile.getName()); } @Test public void testResourcesUnderLibrary() throws IOException { BuildTarget fileTarget = BuildTarget.builder(rootPath, "//foo", "file").build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> fileNode = ExportFileBuilder.newExportFileBuilder(fileTarget).build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setDirs(ImmutableSet.of()) .setFiles(ImmutableSet.of(new DefaultBuildTargetSourcePath(fileTarget))) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(fileNode, resourceNode, libraryNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup mainGroup = project.getMainGroup(); PBXGroup resourcesGroup = mainGroup.getOrCreateDescendantGroupByPath(ImmutableList.of("//foo:lib", "Resources")); PBXFileReference resource = (PBXFileReference) Iterables.get(resourcesGroup.getChildren(), 0); assertThat(resource.getName(), equalTo("file")); } @Test public void resourceDirectoriesHaveFolderType() throws IOException { BuildTarget directoryTarget = BuildTarget.builder(rootPath, "//foo", "dir").build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> directoryNode = ExportFileBuilder.newExportFileBuilder(directoryTarget).build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setDirs(ImmutableSet.of(new DefaultBuildTargetSourcePath(directoryTarget))) .setFiles(ImmutableSet.of()) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(directoryNode, resourceNode, libraryNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup mainGroup = project.getMainGroup(); PBXGroup resourcesGroup = mainGroup.getOrCreateDescendantGroupByPath(ImmutableList.of("//foo:lib", "Resources")); PBXFileReference resource = (PBXFileReference) Iterables.get(resourcesGroup.getChildren(), 0); assertThat(resource.getName(), equalTo("dir")); assertThat(resource.getExplicitFileType(), equalTo(Optional.of("folder"))); } @Test public void resourceDirectoriesDontHaveFolderTypeIfTheyCanHaveAMoreSpecificType() throws IOException { BuildTarget directoryTarget = BuildTarget.builder(rootPath, "//foo", "dir.iconset").build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> directoryNode = ExportFileBuilder.newExportFileBuilder(directoryTarget).build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setDirs(ImmutableSet.of(new DefaultBuildTargetSourcePath(directoryTarget))) .setFiles(ImmutableSet.of()) .build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(directoryNode, resourceNode, libraryNode)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup mainGroup = project.getMainGroup(); PBXGroup resourcesGroup = mainGroup.getOrCreateDescendantGroupByPath(ImmutableList.of("//foo:lib", "Resources")); PBXFileReference resource = (PBXFileReference) Iterables.get(resourcesGroup.getChildren(), 0); assertThat(resource.getName(), equalTo("dir.iconset")); assertThat(resource.getExplicitFileType(), not(equalTo(Optional.of("folder")))); } @Test public void testAppleLibraryWithoutHeaderMaps() throws IOException { ImmutableSortedMap<String, ImmutableMap<String, String>> configs = ImmutableSortedMap.of("Debug", ImmutableMap.<String, String>of()); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setConfigs(configs) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.m")))) .build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "xctest").build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(configs) .setInfoPlist(new FakeSourcePath("Info.plist")) .setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("fooTest.m")))) .setDeps(ImmutableSortedSet.of(libraryTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of(libraryNode, testNode), ImmutableSet.of(ProjectGenerator.Option.DISABLE_HEADER_MAPS)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:xctest"); ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug"); assertEquals( "$(inherited) " + "../buck-out/gen/_p/ptQfVNNRRE-priv " + "../buck-out/gen/_p/ptQfVNNRRE-pub " + "../buck-out/gen/_p/CwkbTNOBmb-pub " + "../buck-out", settings.get("HEADER_SEARCH_PATHS")); } @Test public void testFrameworkBundleDepIsNotCopiedToFrameworkBundle() throws IOException { BuildTarget framework2Target = BuildTarget.builder(rootPath, "//foo", "framework_2") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); BuildTarget framework2BinaryTarget = BuildTarget.builder(rootPath, "//foo", "framework_2_bin") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> framework2BinaryNode = AppleLibraryBuilder.createBuilder(framework2BinaryTarget).build(); TargetNode<?, ?> framework2Node = AppleBundleBuilder.createBuilder(framework2Target) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(framework2BinaryTarget) .build(); BuildTarget framework1Target = BuildTarget.builder(rootPath, "//foo", "framework_1") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); BuildTarget framework1BinaryTarget = BuildTarget.builder(rootPath, "//foo", "framework_1_bin") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> framework1BinaryNode = AppleLibraryBuilder.createBuilder(framework1BinaryTarget).build(); TargetNode<?, ?> framework1Node = AppleBundleBuilder.createBuilder(framework1Target) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(framework1BinaryTarget) .setDeps(ImmutableSortedSet.of(framework2Target)) .build(); BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> sharedLibraryNode = AppleLibraryBuilder.createBuilder(sharedLibraryTarget).build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.BUNDLE)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(framework1Target)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of( framework1Node, framework2Node, framework1BinaryNode, framework2BinaryNode, sharedLibraryNode, bundleNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//foo:framework_1#default,shared"); assertEquals(target.getProductType(), ProductType.FRAMEWORK); for (PBXBuildPhase buildPhase : target.getBuildPhases()) { if (buildPhase instanceof PBXCopyFilesBuildPhase) { PBXCopyFilesBuildPhase copyFilesBuildPhase = (PBXCopyFilesBuildPhase) buildPhase; assertThat( copyFilesBuildPhase.getDstSubfolderSpec().getDestination(), Matchers.not(PBXCopyFilesBuildPhase.Destination.FRAMEWORKS)); } } } @Test public void testAppBundleContainsAllTransitiveFrameworkDeps() throws IOException { BuildTarget framework2Target = BuildTarget.builder(rootPath, "//foo", "framework_2") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); BuildTarget framework2BinaryTarget = BuildTarget.builder(rootPath, "//foo", "framework_2_bin") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> framework2BinaryNode = AppleLibraryBuilder.createBuilder(framework2BinaryTarget).build(); TargetNode<?, ?> framework2Node = AppleBundleBuilder.createBuilder(framework2Target) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(framework2BinaryTarget) .setProductName(Optional.of("framework_2_override")) .build(); BuildTarget framework1Target = BuildTarget.builder(rootPath, "//foo", "framework_1") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); BuildTarget framework1BinaryTarget = BuildTarget.builder(rootPath, "//foo", "framework_1_bin") .addFlavors(DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> framework1BinaryNode = AppleLibraryBuilder.createBuilder(framework1BinaryTarget).build(); TargetNode<?, ?> framework1Node = AppleBundleBuilder.createBuilder(framework1Target) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(framework1BinaryTarget) .setDeps(ImmutableSortedSet.of(framework2Target)) .build(); BuildTarget framework1FlavoredTarget = BuildTarget.builder(rootPath, "//foo", "framework_1") .addFlavors( DEFAULT_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR, InternalFlavor.of("iphoneos-arm64")) .build(); TargetNode<?, ?> framework1FlavoredNode = AppleBundleBuilder.createBuilder(framework1FlavoredTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(framework1BinaryTarget) .setDeps(ImmutableSortedSet.of(framework2Target)) .build(); BuildTarget sharedLibraryTarget = BuildTarget.builder(rootPath, "//dep", "shared") .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> binaryNode = AppleBinaryBuilder.createBuilder(sharedLibraryTarget).build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle").build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(sharedLibraryTarget) .setDeps(ImmutableSortedSet.of(framework1Target, framework1FlavoredTarget)) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( ImmutableSet.of( framework1Node, framework1FlavoredNode, framework2Node, framework1BinaryNode, framework2BinaryNode, binaryNode, bundleNode), ImmutableSet.of()); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget(projectGenerator.getGeneratedProject(), "//foo:bundle"); assertEquals(target.getProductType(), ProductType.APPLICATION); assertThat(target.getBuildPhases().size(), Matchers.equalTo(1)); PBXBuildPhase buildPhase = target.getBuildPhases().get(0); assertThat(buildPhase instanceof PBXCopyFilesBuildPhase, Matchers.equalTo(true)); PBXCopyFilesBuildPhase copyFilesBuildPhase = (PBXCopyFilesBuildPhase) buildPhase; assertThat(copyFilesBuildPhase.getFiles().size(), Matchers.equalTo(2)); ImmutableSet<String> frameworkNames = FluentIterable.from(copyFilesBuildPhase.getFiles()) .transform(input -> input.getFileRef().getName()) .toSortedSet(Ordering.natural()); assertThat( frameworkNames, Matchers.equalToObject( ImmutableSortedSet.of("framework_1.framework", "framework_2_override.framework"))); } @Test public void testAppBundleDoesntLinkFrameworkWrappedWithResource() throws Exception { BuildTarget frameworkTarget = BuildTarget.builder(rootPath, "//foo", "framework") .addFlavors( FakeAppleRuleDescriptions.DEFAULT_MACOSX_X86_64_PLATFORM.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); BuildTarget frameworkBinaryTarget = BuildTarget.builder(rootPath, "//foo", "framework_bin") .addFlavors( FakeAppleRuleDescriptions.DEFAULT_MACOSX_X86_64_PLATFORM.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR) .build(); TargetNode<?, ?> frameworkBinaryNode = AppleLibraryBuilder.createBuilder(frameworkBinaryTarget).build(); TargetNode<?, ?> frameworkNode = AppleBundleBuilder.createBuilder(frameworkTarget) .setExtension(Either.ofLeft(AppleBundleExtension.FRAMEWORK)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(frameworkBinaryTarget) .build(); BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); SourcePath sourcePath = new DefaultBuildTargetSourcePath(frameworkTarget); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(ImmutableSet.of()) .setDirs(ImmutableSet.of(sourcePath)) .build(); BuildTarget binaryTarget = BuildTarget.builder(rootPath, "//foo", "bin").build(); TargetNode<?, ?> binaryNode = AppleBinaryBuilder.createBuilder(binaryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); BuildTarget bundleTarget = BuildTarget.builder(rootPath, "//foo", "bundle") .addFlavors(FakeAppleRuleDescriptions.DEFAULT_MACOSX_X86_64_PLATFORM.getFlavor()) .build(); TargetNode<?, ?> bundleNode = AppleBundleBuilder.createBuilder(bundleTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(binaryTarget) .build(); ImmutableSet<TargetNode<?, ?>> nodes = ImmutableSet.of(frameworkBinaryNode, frameworkNode, resourceNode, binaryNode, bundleNode); final TargetGraph targetGraph = TargetGraphFactory.newInstance(ImmutableSet.copyOf(nodes)); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject( nodes, ImmutableSet.of(), getBuildRuleResolverWithRulesForNodeFunction(targetGraph)); projectGenerator.createXcodeProjects(); PBXTarget target = assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), bundleTarget.getFullyQualifiedName()); assertEquals(target.getProductType(), ProductType.APPLICATION); for (PBXBuildPhase buildPhase : target.getBuildPhases()) { assertFalse(buildPhase instanceof PBXCopyFilesBuildPhase); } assertThat(target.getBuildPhases().size(), Matchers.equalTo(1)); } @Test public void testGeneratedProjectStructureAndSettingsWithBridgingHeader() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs(ImmutableSortedSet.of()) .setBridgingHeader(Optional.of(new FakeSourcePath("BridgingHeader/header1.h"))) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); // check if bridging header file existing in the project structure PBXProject project = projectGenerator.getGeneratedProject(); PBXGroup targetGroup = project.getMainGroup().getOrCreateChildGroupByName(buildTarget.getFullyQualifiedName()); PBXGroup sourcesGroup = targetGroup.getOrCreateChildGroupByName("Sources"); assertThat(sourcesGroup.getChildren(), hasSize(1)); PBXFileReference fileRefBridgingHeader = (PBXFileReference) Iterables.get(sourcesGroup.getChildren(), 0); assertEquals("header1.h", fileRefBridgingHeader.getName()); // check for bridging header build setting PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:lib"); ImmutableMap<String, String> buildSettings = getBuildSettings(buildTarget, target, "Debug"); assertEquals( "$(SRCROOT)/../BridgingHeader/header1.h", buildSettings.get("SWIFT_OBJC_BRIDGING_HEADER")); } @Test public void testGeneratedProjectSettingForSwiftVersion() throws IOException { BuildTarget buildTarget = BuildTarget.builder(rootPath, "//foo", "lib").build(); TargetNode<?, ?> node = AppleLibraryBuilder.createBuilder(buildTarget) .setConfigs(ImmutableSortedMap.of("Debug", ImmutableMap.of())) .setSrcs(ImmutableSortedSet.of()) .build(); ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(ImmutableSet.of(node)); projectGenerator.createXcodeProjects(); PBXProject project = projectGenerator.getGeneratedProject(); PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:lib"); ImmutableMap<String, String> buildSettings = getBuildSettings(buildTarget, target, "Debug"); assertThat(buildSettings.get("SWIFT_VERSION"), equalTo("1.23")); } @Test public void testMergedHeaderMap() throws IOException { BuildTarget lib1Target = BuildTarget.builder(rootPath, "//foo", "lib1").build(); BuildTarget lib2Target = BuildTarget.builder(rootPath, "//bar", "lib2").build(); BuildTarget lib3Target = BuildTarget.builder(rootPath, "//foo", "lib3").build(); BuildTarget testTarget = BuildTarget.builder(rootPath, "//foo", "test").build(); BuildTarget lib4Target = BuildTarget.builder(rootPath, "//foo", "lib4").build(); TargetNode<?, ?> lib1Node = AppleLibraryBuilder.createBuilder(lib1Target) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("lib1.h"))) .setDeps(ImmutableSortedSet.of(lib2Target)) .setTests(ImmutableSortedSet.of(testTarget)) .build(); TargetNode<?, ?> lib2Node = AppleLibraryBuilder.createBuilder(lib2Target) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("lib2.h"))) .build(); TargetNode<?, ?> lib3Node = AppleLibraryBuilder.createBuilder(lib3Target) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("lib3.h"))) .build(); TargetNode<?, ?> testNode = AppleTestBuilder.createBuilder(testTarget) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(lib1Target, lib4Target)) .build(); TargetNode<?, ?> lib4Node = AppleLibraryBuilder.createBuilder(lib4Target) .setConfigs(ImmutableSortedMap.of("Default", ImmutableMap.of())) .setExportedHeaders(ImmutableSortedSet.of(new FakeSourcePath("lib4.h"))) .build(); final TargetGraph targetGraph = TargetGraphFactory.newInstance( ImmutableSet.of(lib1Node, lib2Node, lib3Node, testNode, lib4Node)); final AppleDependenciesCache cache = new AppleDependenciesCache(targetGraph); ProjectGenerator projectGeneratorLib2 = new ProjectGenerator( targetGraph, cache, ImmutableSet.of(lib2Target), projectCell, OUTPUT_DIRECTORY, PROJECT_NAME, "BUCK", ImmutableSet.of(ProjectGenerator.Option.MERGE_HEADER_MAPS), false, /* isMainProject */ Optional.of(lib1Target), ImmutableSet.of(lib1Target, lib4Target), FocusedModuleTargetMatcher.noFocus(), DEFAULT_PLATFORM, getBuildRuleResolverNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); projectGeneratorLib2.createXcodeProjects(); // The merged header map should not generated at this point. Path hmapPath = Paths.get("buck-out/gen/_p/pub-hmap/.hmap"); assertFalse(hmapPath.toString() + " should NOT exist.", projectFilesystem.isFile(hmapPath)); // Checks the content of the header search paths. PBXProject project2 = projectGeneratorLib2.getGeneratedProject(); PBXTarget testPBXTarget2 = assertTargetExistsAndReturnTarget(project2, "//bar:lib2"); ImmutableMap<String, String> buildSettings2 = getBuildSettings(lib2Target, testPBXTarget2, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested library in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/YAYFR3hXIb-priv/.hmap " + "../buck-out/gen/_p/pub-hmap/.hmap " + "../buck-out", buildSettings2.get("HEADER_SEARCH_PATHS")); ProjectGenerator projectGeneratorLib1 = new ProjectGenerator( targetGraph, cache, ImmutableSet.of(lib1Target, testTarget), /* lib3Target not included on purpose */ projectCell, OUTPUT_DIRECTORY, PROJECT_NAME, "BUCK", ImmutableSet.of(ProjectGenerator.Option.MERGE_HEADER_MAPS), true, /* isMainProject */ Optional.of(lib1Target), ImmutableSet.of(lib1Target, lib4Target), FocusedModuleTargetMatcher.noFocus(), DEFAULT_PLATFORM, getBuildRuleResolverNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); projectGeneratorLib1.createXcodeProjects(); // The merged header map should not generated at this point. assertTrue(hmapPath.toString() + " should exist.", projectFilesystem.isFile(hmapPath)); assertThatHeaderMapWithoutSymLinksContains( Paths.get("buck-out/gen/_p/pub-hmap"), ImmutableMap.of( "lib1/lib1.h", "buck-out/gen/_p/WNl0jZWMBk-pub/lib1/lib1.h", "lib2/lib2.h", "buck-out/gen/_p/YAYFR3hXIb-pub/lib2/lib2.h", "lib4/lib4.h", "buck-out/gen/_p/nmnbF8ID6C-pub/lib4/lib4.h")); // Checks the content of the header search paths. PBXProject project1 = projectGeneratorLib1.getGeneratedProject(); // For //foo:lib1 PBXTarget testPBXTarget1 = assertTargetExistsAndReturnTarget(project1, "//foo:lib1"); ImmutableMap<String, String> buildSettings1 = getBuildSettings(lib1Target, testPBXTarget1, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested library in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/WNl0jZWMBk-priv/.hmap " + "../buck-out/gen/_p/pub-hmap/.hmap " + "../buck-out", buildSettings1.get("HEADER_SEARCH_PATHS")); // For //foo:test PBXTarget testPBXTargetTest = assertTargetExistsAndReturnTarget(project1, "//foo:test"); ImmutableMap<String, String> buildSettingsTest = getBuildSettings(testTarget, testPBXTargetTest, "Default"); assertEquals( "test binary should use header symlink trees for both public and non-public headers " + "of the tested library in HEADER_SEARCH_PATHS", "$(inherited) " + "../buck-out/gen/_p/LpygK8zq5F-priv/.hmap " + "../buck-out/gen/_p/pub-hmap/.hmap " + "../buck-out/gen/_p/WNl0jZWMBk-priv/.hmap " + "../buck-out", buildSettingsTest.get("HEADER_SEARCH_PATHS")); } private ProjectGenerator createProjectGeneratorForCombinedProject( Collection<TargetNode<?, ?>> nodes) { return createProjectGeneratorForCombinedProject(nodes, ImmutableSet.of()); } private ProjectGenerator createProjectGeneratorForCombinedProject( Collection<TargetNode<?, ?>> nodes, ImmutableSet<ProjectGenerator.Option> projectGeneratorOptions) { final TargetGraph targetGraph = TargetGraphFactory.newInstance(ImmutableSet.copyOf(nodes)); return createProjectGeneratorForCombinedProject( nodes, projectGeneratorOptions, getBuildRuleResolverNodeFunction(targetGraph)); } private ProjectGenerator createProjectGeneratorForCombinedProject( Collection<TargetNode<?, ?>> nodes, ImmutableSet<ProjectGenerator.Option> projectGeneratorOptions, Function<? super TargetNode<?, ?>, BuildRuleResolver> buildRuleResolverForNode) { ImmutableSet<BuildTarget> initialBuildTargets = nodes.stream().map(TargetNode::getBuildTarget).collect(MoreCollectors.toImmutableSet()); final TargetGraph targetGraph = TargetGraphFactory.newInstance(ImmutableSet.copyOf(nodes)); final AppleDependenciesCache cache = new AppleDependenciesCache(targetGraph); return new ProjectGenerator( targetGraph, cache, initialBuildTargets, projectCell, OUTPUT_DIRECTORY, PROJECT_NAME, "BUCK", projectGeneratorOptions, false, Optional.empty(), ImmutableSet.of(), FocusedModuleTargetMatcher.noFocus(), DEFAULT_PLATFORM, buildRuleResolverForNode, getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); } private Function<TargetNode<?, ?>, BuildRuleResolver> getBuildRuleResolverNodeFunction( final TargetGraph targetGraph) { BuildRuleResolver resolver = new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer()); AbstractBottomUpTraversal<TargetNode<?, ?>, RuntimeException> bottomUpTraversal = new AbstractBottomUpTraversal<TargetNode<?, ?>, RuntimeException>(targetGraph) { @Override @SuppressWarnings("PMD.EmptyCatchBlock") public void visit(TargetNode<?, ?> node) { try { resolver.requireRule(node.getBuildTarget()); } catch (Exception e) { // NOTE(agallagher): A large number of the tests appear to setup their target nodes // incorrectly, causing action graph creation to fail with lots of missing expected // Apple C/C++ platform flavors. This is gross, but to support tests that need a // complete sub-action graph, just skip over the errors. } } }; bottomUpTraversal.traverse(); return input -> resolver; } private Function<TargetNode<?, ?>, BuildRuleResolver> getBuildRuleResolverWithRulesForNodeFunction(final TargetGraph targetGraph) throws NoSuchBuildTargetException { final BuildRuleResolver ruleResolver = new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer()); for (TargetNode<?, ?> node : targetGraph.getNodes()) { ruleResolver.requireRule(node.getBuildTarget()); ruleResolver.requireRule(node.getBuildTarget().withFlavors()); } return input -> ruleResolver; } private ImmutableSet<TargetNode<?, ?>> setupSimpleLibraryWithResources( ImmutableSet<SourcePath> resourceFiles, ImmutableSet<SourcePath> resourceDirectories) { BuildTarget resourceTarget = BuildTarget.builder(rootPath, "//foo", "res").build(); TargetNode<?, ?> resourceNode = AppleResourceBuilder.createBuilder(resourceTarget) .setFiles(resourceFiles) .setDirs(resourceDirectories) .build(); BuildTarget libraryTarget = BuildTarget.builder(rootPath, "//foo", "foo").build(); TargetNode<?, ?> libraryNode = AppleLibraryBuilder.createBuilder(libraryTarget) .setDeps(ImmutableSortedSet.of(resourceTarget)) .build(); return ImmutableSet.of(resourceNode, libraryNode); } private String assertFileRefIsRelativeAndResolvePath(PBXReference fileRef) { assert (!fileRef.getPath().startsWith("/")); assertEquals( "file path should be relative to project directory", PBXReference.SourceTree.SOURCE_ROOT, fileRef.getSourceTree()); return projectFilesystem .resolve(OUTPUT_DIRECTORY) .resolve(fileRef.getPath()) .normalize() .toString(); } private void assertHasConfigurations(PBXTarget target, String... names) { Map<String, XCBuildConfiguration> buildConfigurationMap = target.getBuildConfigurationList().getBuildConfigurationsByName().asMap(); assertEquals( "Configuration list has expected number of entries", names.length, buildConfigurationMap.size()); for (String name : names) { XCBuildConfiguration configuration = buildConfigurationMap.get(name); assertNotNull("Configuration entry exists", configuration); assertEquals("Configuration name is same as key", name, configuration.getName()); assertTrue( "Configuration has xcconfig file", configuration.getBaseConfigurationReference().getPath().endsWith(".xcconfig")); } } private void assertKeepsConfigurationsInMainGroup(PBXProject project, PBXTarget target) { Map<String, XCBuildConfiguration> buildConfigurationMap = target.getBuildConfigurationList().getBuildConfigurationsByName().asMap(); PBXGroup configsGroup = project .getMainGroup() .getOrCreateChildGroupByName("Configurations") .getOrCreateChildGroupByName("Buck (Do Not Modify)"); assertNotNull("Configuration group exists", configsGroup); List<PBXReference> configReferences = configsGroup.getChildren(); assertFalse("Configuration file references exist", configReferences.isEmpty()); for (XCBuildConfiguration configuration : buildConfigurationMap.values()) { String path = configuration.getBaseConfigurationReference().getPath(); PBXReference foundReference = null; for (PBXReference reference : configReferences) { assertTrue( "References in the configuration group should point to xcconfigs", reference.getPath().endsWith(".xcconfig")); if (reference.getPath().equals(path)) { foundReference = reference; break; } } assertNotNull( "File reference for configuration " + path + " should be in main group", foundReference); } } private void assertHasSingletonSourcesPhaseWithSourcesAndFlags( PBXTarget target, ImmutableMap<String, Optional<String>> sourcesAndFlags) { PBXSourcesBuildPhase sourcesBuildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXSourcesBuildPhase.class); assertEquals( "Sources build phase should have correct number of sources", sourcesAndFlags.size(), sourcesBuildPhase.getFiles().size()); // map keys to absolute paths ImmutableMap.Builder<String, Optional<String>> absolutePathFlagMapBuilder = ImmutableMap.builder(); for (Map.Entry<String, Optional<String>> name : sourcesAndFlags.entrySet()) { absolutePathFlagMapBuilder.put( projectFilesystem .getRootPath() .resolve(name.getKey()) .toAbsolutePath() .normalize() .toString(), name.getValue()); } ImmutableMap<String, Optional<String>> absolutePathFlagMap = absolutePathFlagMapBuilder.build(); for (PBXBuildFile file : sourcesBuildPhase.getFiles()) { String filePath = assertFileRefIsRelativeAndResolvePath(file.getFileRef()); Optional<String> flags = absolutePathFlagMap.get(filePath); assertNotNull(String.format("Unexpected file ref '%s' found", filePath), flags); if (flags.isPresent()) { assertTrue("Build file should have settings dictionary", file.getSettings().isPresent()); NSDictionary buildFileSettings = file.getSettings().get(); NSString compilerFlags = (NSString) buildFileSettings.get("COMPILER_FLAGS"); assertNotNull("Build file settings should have COMPILER_FLAGS entry", compilerFlags); assertEquals( "Build file settings should be expected value", flags.get(), compilerFlags.getContent()); } else { assertFalse( "Build file should not have settings dictionary", file.getSettings().isPresent()); } } } private void assertSourcesNotInSourcesPhase(PBXTarget target, ImmutableSet<String> sources) { ImmutableSet.Builder<String> absoluteSourcesBuilder = ImmutableSet.builder(); for (String name : sources) { absoluteSourcesBuilder.add( projectFilesystem.getRootPath().resolve(name).toAbsolutePath().normalize().toString()); } Iterable<PBXBuildPhase> buildPhases = Iterables.filter(target.getBuildPhases(), PBXSourcesBuildPhase.class::isInstance); if (Iterables.size(buildPhases) == 0) { return; } ImmutableSet<String> absoluteSources = absoluteSourcesBuilder.build(); PBXSourcesBuildPhase sourcesBuildPhase = (PBXSourcesBuildPhase) Iterables.getOnlyElement(buildPhases); for (PBXBuildFile file : sourcesBuildPhase.getFiles()) { String filePath = assertFileRefIsRelativeAndResolvePath(file.getFileRef()); assertFalse( "Build phase should not contain this file " + filePath, absoluteSources.contains(filePath)); } } private void assertHasSingletonResourcesPhaseWithEntries(PBXTarget target, String... resources) { PBXResourcesBuildPhase buildPhase = ProjectGeneratorTestUtils.getSingletonPhaseByType(target, PBXResourcesBuildPhase.class); assertEquals( "Resources phase should have right number of elements", resources.length, buildPhase.getFiles().size()); ImmutableSet.Builder<String> expectedResourceSetBuilder = ImmutableSet.builder(); for (String resource : resources) { expectedResourceSetBuilder.add( projectFilesystem .getRootPath() .resolve(resource) .toAbsolutePath() .normalize() .toString()); } ImmutableSet<String> expectedResourceSet = expectedResourceSetBuilder.build(); for (PBXBuildFile file : buildPhase.getFiles()) { String source = assertFileRefIsRelativeAndResolvePath(file.getFileRef()); assertTrue( "Resource should be in list of expected resources: " + source, expectedResourceSet.contains(source)); } } private ImmutableMap<String, String> getBuildSettings( BuildTarget buildTarget, PBXTarget target, String config) { assertHasConfigurations(target, config); return ProjectGeneratorTestUtils.getBuildSettings( projectFilesystem, buildTarget, target, config); } }