blob: 9cdb75a9b1641e6a74ad56e3eb99492394418ff2 [file] [log] [blame]
/*
* 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;
import static com.facebook.buck.apple.ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries;
import static com.facebook.buck.apple.ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.instanceOf;
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 com.dd.plist.NSDictionary;
import com.dd.plist.NSString;
import com.facebook.buck.apple.clang.HeaderMap;
import com.facebook.buck.apple.xcode.xcodeproj.ImmutableProductType;
import com.facebook.buck.apple.xcode.xcodeproj.PBXBuildFile;
import com.facebook.buck.apple.xcode.xcodeproj.PBXBuildPhase;
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.XCBuildConfiguration;
import com.facebook.buck.cxx.CxxDescriptionEnhancer;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.HasBuildTarget;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.TargetNode;
import com.facebook.buck.rules.TestSourcePath;
import com.facebook.buck.rules.coercer.SourceWithFlags;
import com.facebook.buck.rules.coercer.Either;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.TargetGraphFactory;
import com.facebook.buck.timing.SettableFakeClock;
import com.facebook.buck.util.BuckConstant;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
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.io.ByteStreams;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.List;
import java.util.Map;
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 SettableFakeClock clock;
private ProjectFilesystem projectFilesystem;
private FakeProjectFilesystem fakeProjectFilesystem;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws IOException {
clock = new SettableFakeClock(0, 0);
fakeProjectFilesystem = new FakeProjectFilesystem(clock);
projectFilesystem = fakeProjectFilesystem;
// Add support files needed by project generation to fake filesystem.
projectFilesystem.writeContentsToPath(
"",
Paths.get(ProjectGenerator.PATH_TO_ASSET_CATALOG_BUILD_PHASE_SCRIPT));
projectFilesystem.writeContentsToPath(
"",
Paths.get(ProjectGenerator.PATH_TO_ASSET_CATALOG_COMPILER));
// 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"));
}
@Test
public void testProjectStructureForEmptyProject() throws IOException {
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of());
projectGenerator.createXcodeProjects();
Optional<String> pbxproj = projectFilesystem.readFileIfItExists(OUTPUT_PROJECT_FILE_PATH);
assertTrue(pbxproj.isPresent());
}
@Test
public void testCreateDirectoryStructure() throws IOException {
BuildTarget buildTarget1 = BuildTarget.builder("//foo/bar", "target1").build();
TargetNode<?> node1 = AppleLibraryBuilder.createBuilder(buildTarget1).build();
BuildTarget buildTarget2 = BuildTarget.builder("//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 shouldNotCreateHeaderMapsWhenHeaderMapsAreDisabled() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(new TestSourcePath("HeaderGroup1/foo.h")),
SourceWithFlags.of(
new TestSourcePath("HeaderGroup1/bar.h"), ImmutableList.of("public")))))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
projectGenerator.createXcodeProjects();
// No header map should be generated
List<Path> headerMaps = projectGenerator.getGeneratedHeaderMaps();
assertThat(headerMaps, hasSize(0));
}
@Test
public void testLibraryHeaderGroupsWithHeaderMaps() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setSrcs(Optional.of(ImmutableList.<SourceWithFlags>of()))
.setHeaders(
Optional.of(
ImmutableSortedSet.<SourcePath>of(
new TestSourcePath("HeaderGroup1/foo.h"),
new TestSourcePath("HeaderGroup2/baz.h"))))
.setExportedHeaders(
Optional.of(
ImmutableSortedSet.<SourcePath>of(
new TestSourcePath("HeaderGroup1/bar.h"))))
.setUseBuckHeaderMaps(Optional.of(true))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>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(Optional.<PBXBuildPhase>absent(),
Iterables.tryFind(target.getBuildPhases(), new Predicate<PBXBuildPhase>() {
@Override
public boolean apply(PBXBuildPhase input) {
return input instanceof PBXHeadersBuildPhase;
}
}));
List<Path> headerMaps = projectGenerator.getGeneratedHeaderMaps();
assertThat(headerMaps, hasSize(3));
assertEquals("buck-out/gen/foo/lib-public-headers.hmap", headerMaps.get(0).toString());
assertThatHeaderMapFileContains(
Paths.get("buck-out/gen/foo/lib-public-headers.hmap"),
ImmutableMap.of("lib/bar.h", "HeaderGroup1/bar.h")
);
assertEquals("buck-out/gen/foo/lib-target-headers.hmap", headerMaps.get(1).toString());
assertThatHeaderMapFileContains(
Paths.get("buck-out/gen/foo/lib-target-headers.hmap"),
ImmutableMap.of(
"lib/foo.h", "HeaderGroup1/foo.h",
"lib/bar.h", "HeaderGroup1/bar.h",
"lib/baz.h", "HeaderGroup2/baz.h"
)
);
assertEquals("buck-out/gen/foo/lib-target-user-headers.hmap", headerMaps.get(2).toString());
assertThatHeaderMapFileContains(
Paths.get("buck-out/gen/foo/lib-target-user-headers.hmap"),
ImmutableMap.of(
"foo.h", "HeaderGroup1/foo.h",
"bar.h", "HeaderGroup1/bar.h",
"baz.h", "HeaderGroup2/baz.h"
)
);
}
@Test
public void testHeaderMapsWithHeadersVisibleForTesting() throws IOException {
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
BuildTarget testTarget = BuildTarget.builder("//foo", "test").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.h"),
ImmutableList.of("public")),
SourceWithFlags.of(
new TestSourcePath("bar.h")))))
.setTests(Optional.of(ImmutableSortedSet.of(testTarget)))
.setUseBuckHeaderMaps(Optional.of(true))
.build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Default",
ImmutableMap.<String, String>of())))
.setUseBuckHeaderMaps(Optional.of(true))
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setDeps(Optional.of(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 headermaps for both public and non-public headers of the tested " +
"library in HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-headers.hmap " +
"../buck-out/gen/foo/lib-public-headers.hmap " +
"../buck-out/gen/foo/lib-target-headers.hmap",
buildSettings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"test binary should use user headermaps of its own as well as of the tested library " +
"in USER_HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-user-headers.hmap " +
"../buck-out/gen/foo/lib-target-user-headers.hmap",
buildSettings.get("USER_HEADER_SEARCH_PATHS"));
}
@Test
public void testHeaderMapsWithTestsAndLibraryBundles() throws IOException {
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "bundle").build();
BuildTarget testTarget = BuildTarget.builder("//foo", "test").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.h"),
ImmutableList.of("public")),
SourceWithFlags.of(
new TestSourcePath("bar.h")))))
.setUseBuckHeaderMaps(Optional.of(true))
.build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setBinary(libraryTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setTests(Optional.of(ImmutableSortedSet.of(testTarget)))
.build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Default",
ImmutableMap.<String, String>of())))
.setUseBuckHeaderMaps(Optional.of(true))
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setDeps(Optional.of(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 headermaps for both public and non-public headers of the tested " +
"library in HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-headers.hmap " +
"../buck-out/gen/foo/lib-public-headers.hmap " +
"../buck-out/gen/foo/lib-target-headers.hmap",
buildSettings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"test binary should use user headermaps of its own as well as of the tested library " +
"in USER_HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-user-headers.hmap " +
"../buck-out/gen/foo/lib-target-user-headers.hmap",
buildSettings.get("USER_HEADER_SEARCH_PATHS"));
}
@Test
public void testHeaderMapsWithTestsAndBinaryBundles() throws IOException {
BuildTarget binaryTarget = BuildTarget.builder("//foo", "bin").build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "bundle").build();
BuildTarget testTarget = BuildTarget.builder("//foo", "test").build();
TargetNode<?> binaryNode = AppleBinaryBuilder
.createBuilder(binaryTarget)
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.h"),
ImmutableList.of("public")),
SourceWithFlags.of(
new TestSourcePath("bar.h")))))
.setUseBuckHeaderMaps(Optional.of(true))
.build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setBinary(binaryTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.APP))
.setTests(Optional.of(ImmutableSortedSet.of(testTarget)))
.build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Default",
ImmutableMap.<String, String>of())))
.setUseBuckHeaderMaps(Optional.of(true))
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setDeps(Optional.of(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 headermaps for both public and non-public headers of the tested " +
"binary in HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-headers.hmap " +
"../buck-out/gen/foo/bin-public-headers.hmap " +
"../buck-out/gen/foo/bin-target-headers.hmap",
buildSettings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"test binary should use user headermaps of its own as well as of the tested binary " +
"in USER_HEADER_SEARCH_PATHS",
"$(inherited) " +
"../buck-out/gen/foo/test-target-user-headers.hmap " +
"../buck-out/gen/foo/bin-target-user-headers.hmap",
buildSettings.get("USER_HEADER_SEARCH_PATHS"));
}
private void assertThatHeaderMapFileContains(Path file, ImmutableMap<String, String> content)
throws IOException {
byte[] headerMapBytes = ByteStreams.toByteArray(projectFilesystem.newFileInputStream(file));
HeaderMap map = HeaderMap.deserialize(headerMapBytes);
assertEquals(content.size(), map.getNumEntries());
for (String key : content.keySet()) {
assertEquals(
Paths.get(content.get(key)).toAbsolutePath().toString(),
map.lookup(key));
}
}
@Test
public void testAppleLibraryRule() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.m"), ImmutableList.of("-foo")),
SourceWithFlags.of(new TestSourcePath("bar.m")))))
.setHeaders(
Optional.of(
ImmutableSortedSet.<SourcePath>of(new TestSourcePath("foo.h"))))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.ProductType.STATIC_LIBRARY));
assertHasConfigurations(target, "Debug");
assertEquals("Should have exact number of build phases", 2, target.getBuildPhases().size());
assertHasSingletonSourcesPhaseWithSourcesAndFlags(
target, ImmutableMap.of(
"foo.m", Optional.of("-foo"),
"bar.m", Optional.<String>absent()));
// check headers
{
PBXBuildPhase headersBuildPhase =
Iterables.find(target.getBuildPhases(), new Predicate<PBXBuildPhase>() {
@Override
public boolean apply(PBXBuildPhase input) {
return input instanceof PBXHeadersBuildPhase;
}
});
PBXBuildFile headerBuildFile = Iterables.getOnlyElement(headersBuildPhase.getFiles());
String headerBuildFilePath = assertFileRefIsRelativeAndResolvePath(
headerBuildFile.getFileRef());
assertEquals(
projectFilesystem.getRootPath().resolve("foo.h").toAbsolutePath().normalize().toString(),
headerBuildFilePath);
}
// this target should not have an asset catalog build phase
assertFalse(hasShellScriptPhaseToCompileAssetCatalogs(target));
}
@Test
public void testAppleLibraryConfiguresOutputPaths() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.setHeaderPathPrefix(Optional.of("MyHeaderPathPrefix"))
.setPrefixHeader(Optional.<SourcePath>of(new TestSourcePath("Foo/Foo-Prefix.pch")))
.setUseBuckHeaderMaps(Optional.of(false))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.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/F4XWM33PHJWGSYQ",
settings.get("CONFIGURATION_BUILD_DIR"));
assertEquals(
"Headers/MyHeaderPathPrefix",
settings.get("PUBLIC_HEADERS_FOLDER_PATH"));
}
@Test
public void testAppleLibraryConfiguresSharedLibraryOutputPaths() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//hi", "lib")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.setHeaderPathPrefix(Optional.of("MyHeaderPathPrefix"))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//hi:lib#shared");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.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/F4XWQ2J2NRUWEI3TNBQXEZLE",
settings.get("CONFIGURATION_BUILD_DIR"));
assertEquals(
"Headers/MyHeaderPathPrefix",
settings.get("PUBLIC_HEADERS_FOLDER_PATH"));
}
@Test
public void testAppleLibraryDoesntOverrideHeaderOutputPath() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.of("PUBLIC_HEADERS_FOLDER_PATH", "FooHeaders"))))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.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/F4XWM33PHJWGSYQ",
settings.get("CONFIGURATION_BUILD_DIR"));
assertEquals(
"FooHeaders",
settings.get("PUBLIC_HEADERS_FOLDER_PATH"));
}
@Test
public void testAppleLibraryCompilerAndPreprocessorFlags() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.of("PUBLIC_HEADERS_FOLDER_PATH", "FooHeaders"))))
.setCompilerFlags(Optional.of(ImmutableList.of("-fhello")))
.setPreprocessorFlags(Optional.of(ImmutableList.of("-fworld")))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.ProductType.STATIC_LIBRARY));
ImmutableMap<String, String> settings = getBuildSettings(buildTarget, target, "Debug");
assertEquals("$(inherited) -fhello -fworld", settings.get("OTHER_CFLAGS"));
}
@Test
public void testConfigurationSerializationWithoutExistingXcconfig() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.of("CUSTOM_SETTING", "VALUE"))))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
assertThat(target.isa(), equalTo("PBXNativeTarget"));
assertThat(target.getProductType(), equalTo(PBXTarget.ProductType.STATIC_LIBRARY));
assertHasConfigurations(target, "Debug");
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/F4XWM33PHJWGSYQ",
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("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(ImmutableList.of(SourceWithFlags.of(new TestSourcePath("foo.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Library.framework")))
.build();
BuildTarget testTarget = BuildTarget.builder("//foo", "xctest").build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(
ImmutableList.of(SourceWithFlags.of(new TestSourcePath("fooTest.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Test.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(libraryTarget)))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(libraryNode, testNode),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:xctest");
ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug");
assertEquals(
"$(inherited) " + "$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ/Headers",
settings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"$(inherited) ",
settings.get("USER_HEADER_SEARCH_PATHS"));
assertEquals(
"$(inherited) " + "$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ",
settings.get("LIBRARY_SEARCH_PATHS"));
assertEquals(
"$(inherited) ",
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("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(ImmutableList.of(SourceWithFlags.of(new TestSourcePath("foo.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Library.framework")))
.build();
BuildTarget testTarget = BuildTarget.builder("//foo", "xctest").build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(
ImmutableList.of(SourceWithFlags.of(new TestSourcePath("fooTest.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Test.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(libraryTarget)))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(libraryNode, testNode),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:xctest");
ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug");
assertEquals(
"headers " + "$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ/Headers",
settings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"user_headers ",
settings.get("USER_HEADER_SEARCH_PATHS"));
assertEquals(
"libraries " + "$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ",
settings.get("LIBRARY_SEARCH_PATHS"));
assertEquals(
"frameworks ",
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("//bar", "lib").build();
TargetNode<?> libraryDepNode = AppleLibraryBuilder
.createBuilder(libraryDepTarget)
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(ImmutableList.of(SourceWithFlags.of(new TestSourcePath("foo.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Library.framework")))
.build();
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(ImmutableList.of(SourceWithFlags.of(new TestSourcePath("foo.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Library.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(libraryDepTarget)))
.build();
BuildTarget testTarget = BuildTarget.builder("//foo", "xctest").build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setConfigs(Optional.of(configs))
.setSrcs(
Optional.of(
ImmutableList.of(SourceWithFlags.of(new TestSourcePath("fooTest.m")))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Test.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(libraryTarget)))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(libraryDepNode, libraryNode, testNode),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:xctest");
ImmutableMap<String, String> settings = getBuildSettings(testTarget, target, "Debug");
assertEquals(
"$(inherited) " +
"$BUILT_PRODUCTS_DIR/F4XWEYLSHJWGSYQ/Headers " +
"$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ/Headers",
settings.get("HEADER_SEARCH_PATHS"));
assertEquals(
"$(inherited) ",
settings.get("USER_HEADER_SEARCH_PATHS"));
assertEquals(
"$(inherited) " +
"$BUILT_PRODUCTS_DIR/F4XWEYLSHJWGSYQ " +
"$BUILT_PRODUCTS_DIR/F4XWM33PHJWGSYQ",
settings.get("LIBRARY_SEARCH_PATHS"));
assertEquals(
"$(inherited) ",
settings.get("FRAMEWORK_SEARCH_PATHS"));
}
@Test
public void testAppleTestRule() throws IOException {
BuildTarget testTarget = BuildTarget.builder("//foo", "xctest").build();
TargetNode<?> testNode = AppleTestBuilder
.createBuilder(testTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(testNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:xctest");
assertEquals(target.getProductType(), PBXTarget.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("//dep", "dep").build();
TargetNode<?> depNode = AppleLibraryBuilder
.createBuilder(depTarget)
.build();
BuildTarget binaryTarget = BuildTarget.builder("//foo", "binary").build();
TargetNode<?> binaryNode = AppleBinaryBuilder
.createBuilder(binaryTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.m"), ImmutableList.of("-foo")))))
.setHeaders(
Optional.of(
ImmutableSortedSet.<SourcePath>of(
new TestSourcePath("foo.h"))))
.setFrameworks(Optional.of(ImmutableSortedSet.of("$SDKROOT/Foo.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(depTarget)))
.setGid(Optional.<String>absent())
.setHeaderPathPrefix(Optional.<String>absent())
.setUseBuckHeaderMaps(Optional.<Boolean>absent())
.setPrefixHeader(Optional.<SourcePath>absent())
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(depNode, binaryNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:binary");
assertHasConfigurations(target, "Debug");
assertEquals(target.getProductType(), PBXTarget.ProductType.TOOL);
assertEquals("Should have exact number of build phases", 3, target.getBuildPhases().size());
assertHasSingletonSourcesPhaseWithSourcesAndFlags(
target,
ImmutableMap.of(
"foo.m", Optional.of("-foo")));
ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries(
target,
ImmutableList.of(
"$SDKROOT/Foo.framework",
// Propagated library from deps.
"$BUILT_PRODUCTS_DIR/F4XWIZLQHJSGK4A/libdep.a"));
// this test does not have a dependency on any asset catalogs, so verify no build phase for them
// exists.
assertFalse(hasShellScriptPhaseToCompileAssetCatalogs(target));
}
@Test
public void testAppleBundleRuleWithPostBuildScriptDependency() throws IOException {
BuildTarget scriptTarget = BuildTarget.builder("//foo", "post_build_script").build();
TargetNode<?> scriptNode = IosPostprocessResourcesBuilder
.createBuilder(scriptTarget)
.setCmd(Optional.of("script.sh"))
.build();
BuildTarget resourceTarget = BuildTarget.builder("//foo", "resource").build();
TargetNode<?> resourceNode = AppleResourceBuilder
.createBuilder(resourceTarget)
.setFiles(ImmutableSet.<SourcePath>of(new TestSourcePath("bar.png")))
.setDirs(ImmutableSet.<Path>of())
.build();
BuildTarget sharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> sharedLibraryNode = AppleLibraryBuilder
.createBuilder(sharedLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(resourceTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "bundle").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.setBinary(sharedLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(scriptTarget)))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(scriptNode, resourceNode, sharedLibraryNode, bundleNode));
projectGenerator.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = ProjectGeneratorTestUtils.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 testAppleBundleRuleForSharedLibaryFramework() throws IOException {
BuildTarget sharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> sharedLibraryNode = AppleLibraryBuilder
.createBuilder(sharedLibraryTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.build();
BuildTarget buildTarget = BuildTarget.builder("//foo", "bundle").build();
TargetNode<?> node = AppleBundleBuilder
.createBuilder(buildTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setBinary(sharedLibraryTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(sharedLibraryNode, node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:bundle");
assertEquals(target.getProductType(), PBXTarget.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 testAppleBundleRuleWithCustomXcodeProductType() throws IOException {
BuildTarget sharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> sharedLibraryNode = AppleLibraryBuilder
.createBuilder(sharedLibraryTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.build();
BuildTarget buildTarget = BuildTarget.builder("//foo", "custombundle").build();
TargetNode<?> node = AppleBundleBuilder
.createBuilder(buildTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setBinary(sharedLibraryTarget)
.setXcodeProductType(Optional.of("com.facebook.buck.niftyProductType"))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(sharedLibraryNode, node),
ImmutableSet.<ProjectGenerator.Option>of());
projectGenerator.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(project, "//foo:custombundle");
assertEquals(
target.getProductType(),
ImmutableProductType.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 testCoreDataModelRuleAddsReference() throws IOException {
BuildTarget modelTarget = BuildTarget.builder("//foo", "model").build();
TargetNode<?> modelNode = CoreDataModelBuilder
.createBuilder(modelTarget)
.setPath(new TestSourcePath("foo.xcdatamodel").getRelativePath())
.build();
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(modelTarget)))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(modelNode, 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 modelReference = (PBXFileReference) Iterables.get(
resourcesGroup.getChildren(),
0);
assertEquals("foo.xcdatamodel", modelReference.getName());
}
@Test
public void ruleToTargetMapContainsPBXTarget() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setSrcs(
Optional.of(
ImmutableList.of(
SourceWithFlags.of(
new TestSourcePath("foo.m"), ImmutableList.of("-foo")),
SourceWithFlags.of(new TestSourcePath("bar.m")))))
.setHeaders(
Optional.of(
ImmutableSortedSet.<SourcePath>of(
new TestSourcePath("foo.h"))))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>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.<String>absent()));
}
@Test
public void generatedGidsForTargetsAreStable() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "foo").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>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("//dep", "static").build();
TargetNode<?> dependentStaticLibraryNode = AppleLibraryBuilder
.createBuilder(dependentStaticLibraryTarget)
.build();
BuildTarget dependentSharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> dependentSharedLibraryNode = AppleLibraryBuilder
.createBuilder(dependentSharedLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentStaticLibraryTarget)))
.build();
BuildTarget libraryTarget = BuildTarget
.builder("//foo", "library")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentSharedLibraryTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "final").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.setBinary(libraryTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(
dependentStaticLibraryNode,
dependentSharedLibraryNode,
libraryNode,
bundleNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:final");
assertEquals(target.getProductType(), PBXTarget.ProductType.BUNDLE);
assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size());
ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries(
target,
ImmutableList.of(
"$BUILT_PRODUCTS_DIR/F4XWIZLQHJZWQYLSMVSCG43IMFZGKZA/libshared.dylib"));
}
@Test
public void stopsLinkingRecursiveDependenciesAtBundles() throws IOException {
BuildTarget dependentStaticLibraryTarget = BuildTarget.builder("//dep", "static").build();
TargetNode<?> dependentStaticLibraryNode = AppleLibraryBuilder
.createBuilder(dependentStaticLibraryTarget)
.build();
BuildTarget dependentSharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> dependentSharedLibraryNode = AppleLibraryBuilder
.createBuilder(dependentSharedLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentStaticLibraryTarget)))
.build();
BuildTarget dependentFrameworkTarget = BuildTarget.builder("//dep", "framework").build();
TargetNode<?> dependentFrameworkNode = AppleBundleBuilder
.createBuilder(dependentFrameworkTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setBinary(dependentSharedLibraryTarget)
.build();
BuildTarget libraryTarget = BuildTarget
.builder("//foo", "library")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentFrameworkTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "final").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.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(), PBXTarget.ProductType.BUNDLE);
assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size());
ProjectGeneratorTestUtils.assertHasSingletonFrameworksPhaseWithFrameworkEntries(
target,
ImmutableList.of("$BUILT_PRODUCTS_DIR/F4XWIZLQHJTHEYLNMV3W64TL/framework.framework"));
}
@Test
public void stopsCopyingRecursiveDependenciesAtBundles() throws IOException {
BuildTarget dependentStaticLibraryTarget = BuildTarget.builder("//dep", "static").build();
TargetNode<?> dependentStaticLibraryNode = AppleLibraryBuilder
.createBuilder(dependentStaticLibraryTarget)
.build();
BuildTarget dependentStaticFrameworkTarget = BuildTarget
.builder("//dep", "static-framework")
.build();
TargetNode<?> dependentStaticFrameworkNode = AppleBundleBuilder
.createBuilder(dependentStaticFrameworkTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setBinary(dependentStaticLibraryTarget)
.build();
BuildTarget dependentSharedLibraryTarget = BuildTarget
.builder("//dep", "shared")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> dependentSharedLibraryNode = AppleLibraryBuilder
.createBuilder(dependentSharedLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentStaticFrameworkTarget)))
.build();
BuildTarget dependentFrameworkTarget = BuildTarget.builder("//dep", "framework").build();
TargetNode<?> dependentFrameworkNode = AppleBundleBuilder
.createBuilder(dependentFrameworkTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.FRAMEWORK))
.setBinary(dependentSharedLibraryTarget)
.build();
BuildTarget libraryTarget = BuildTarget
.builder("//foo", "library")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(dependentFrameworkTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "final").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.setBinary(libraryTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
// ant needs this to be explicit
ImmutableSet.<TargetNode<?>>of(
dependentStaticLibraryNode,
dependentStaticFrameworkNode,
dependentSharedLibraryNode,
dependentFrameworkNode,
libraryNode,
bundleNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:final");
assertEquals(target.getProductType(), PBXTarget.ProductType.BUNDLE);
assertEquals("Should have exact number of build phases ", 2, target.getBuildPhases().size());
ProjectGeneratorTestUtils.assertHasSingletonCopyFilesPhaseWithFileEntries(
target,
ImmutableList.of("$BUILT_PRODUCTS_DIR/F4XWIZLQHJTHEYLNMV3W64TL/framework.framework"));
}
@Test
public void bundlesDontLinkTheirOwnBinary() throws IOException {
BuildTarget libraryTarget = BuildTarget
.builder("//foo", "library")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "final").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.setBinary(libraryTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(libraryNode, bundleNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:final");
assertEquals(target.getProductType(), PBXTarget.ProductType.BUNDLE);
assertEquals("Should have exact number of build phases ", 0, target.getBuildPhases().size());
}
@Test
public void resourcesInDependenciesPropagatesToBundles() throws IOException {
BuildTarget resourceTarget = BuildTarget.builder("//foo", "res").build();
TargetNode<?> resourceNode = AppleResourceBuilder
.createBuilder(resourceTarget)
.setFiles(ImmutableSet.<SourcePath>of(new TestSourcePath("bar.png")))
.setDirs(ImmutableSet.of(Paths.get("foodir")))
.build();
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(resourceTarget)))
.build();
BuildTarget bundleLibraryTarget = BuildTarget.builder("//foo", "bundlelib").build();
TargetNode<?> bundleLibraryNode = AppleLibraryBuilder
.createBuilder(bundleLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(libraryTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "bundle").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.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("//foo", "asset_catalog").build();
TargetNode<?> assetCatalogNode = AppleAssetCatalogBuilder
.createBuilder(assetCatalogTarget)
.setDirs(ImmutableSet.of(Paths.get("AssetCatalog.xcassets")))
.build();
BuildTarget libraryTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(assetCatalogTarget)))
.build();
BuildTarget bundleLibraryTarget = BuildTarget.builder("//foo", "bundlelib").build();
TargetNode<?> bundleLibraryNode = AppleLibraryBuilder
.createBuilder(bundleLibraryTarget)
.setDeps(Optional.of(ImmutableSortedSet.of(libraryTarget)))
.build();
BuildTarget bundleTarget = BuildTarget.builder("//foo", "bundle").build();
TargetNode<?> bundleNode = AppleBundleBuilder
.createBuilder(bundleTarget)
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.BUNDLE))
.setBinary(bundleLibraryTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(assetCatalogNode, libraryNode, bundleLibraryNode, bundleNode));
projectGenerator.createXcodeProjects();
PBXProject generatedProject = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(
generatedProject,
"//foo:bundle");
assertTrue(hasShellScriptPhaseToCompileAssetCatalogs(target));
}
@Test
public void generatedTargetConfigurationHasRepoRootSet() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "rule").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Debug",
ImmutableMap.<String, String>of())))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node),
ImmutableSet.<ProjectGenerator.Option>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("//foo", "rule1").build();
TargetNode<?> node1 = AppleLibraryBuilder
.createBuilder(buildTarget1)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Conf1", ImmutableMap.<String, String>of(),
"Conf2", ImmutableMap.<String, String>of())))
.build();
BuildTarget buildTarget2 = BuildTarget.builder("//foo", "rule2").build();
TargetNode<?> node2 = AppleLibraryBuilder
.createBuilder(buildTarget2)
.setConfigs(
Optional.of(
ImmutableSortedMap.of(
"Conf2", ImmutableMap.<String, String>of(),
"Conf3", ImmutableMap.<String, String>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("//foo", "rule")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setFrameworks(
Optional.of(
ImmutableSortedSet.of(
"$BUILT_PRODUCTS_DIR/libfoo.a",
"$SDKROOT/libfoo.a",
"$SOURCE_ROOT/libfoo.a")))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
projectGenerator.createXcodeProjects();
PBXProject generatedProject = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(generatedProject, "//foo:rule#shared");
assertHasSingletonFrameworksPhaseWithFrameworkEntries(
target,
ImmutableList.of(
"$BUILT_PRODUCTS_DIR/libfoo.a",
"$SDKROOT/libfoo.a",
"$SOURCE_ROOT/libfoo.a"));
}
@Test(expected = HumanReadableException.class)
public void shouldRejectUnknownBuildSettingsInFrameworkEntries() throws IOException {
BuildTarget buildTarget = BuildTarget
.builder("//foo", "rule")
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setFrameworks(Optional.of(ImmutableSortedSet.of("$FOOBAR/libfoo.a")))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
projectGenerator.createXcodeProjects();
}
@Test
public void testGeneratedProjectIsNotReadOnlyIfOptionNotSpecified() throws IOException {
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of());
projectGenerator.createXcodeProjects();
assertTrue(fakeProjectFilesystem.getFileAttributesAtPath(OUTPUT_PROJECT_FILE_PATH).isEmpty());
}
@Test
public void testGeneratedProjectIsReadOnlyIfOptionSpecified() throws IOException {
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>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 targetGidInDescriptionSetsTargetGidInGeneratedProject() throws IOException {
BuildTarget buildTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.setGid(Optional.of("D00D64738"))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//foo:lib");
// Ensure the GID for the target uses the gid value in the description.
assertThat(target.getGlobalID(), equalTo("D00D64738"));
}
@Test
public void targetGidInDescriptionReservesGidFromUseByAnotherTarget() throws IOException {
BuildTarget fooTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> fooNode = AppleLibraryBuilder
.createBuilder(fooTarget)
.setGid(Optional.of("E66DC04E36F2D8BE00000000"))
.build();
BuildTarget barTarget = BuildTarget.builder("//bar", "lib").build();
TargetNode<?> barNode = AppleLibraryBuilder
.createBuilder(barTarget)
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(fooNode, barNode));
projectGenerator.createXcodeProjects();
PBXTarget target = assertTargetExistsAndReturnTarget(
projectGenerator.getGeneratedProject(),
"//bar:lib");
// Note the '1': normally //bar:lib's GID would be
// E66DC04E36F2D8BE00000000 but we hard-coded that in //foo:lib, so //bar:lib
// will try and fail to use GID, as it'll already have been reserved.
String expectedGID = String.format(
"%08X%08X%08X", target.isa().hashCode(), target.getName().hashCode(), 1);
assertEquals(
"expected GID has correct value",
"E66DC04E36F2D8BE00000001", expectedGID);
assertEquals("generated GID is same as expected", expectedGID, target.getGlobalID());
}
@Test
public void conflictingHardcodedGidsThrow() throws IOException {
BuildTarget fooTarget = BuildTarget.builder("//foo", "lib").build();
TargetNode<?> fooNode = AppleLibraryBuilder
.createBuilder(fooTarget)
.setGid(Optional.of("E66DC04E36F2D8BE00000000"))
.build();
BuildTarget barTarget = BuildTarget.builder("//bar", "lib").build();
TargetNode<?> barNode = AppleLibraryBuilder
.createBuilder(barTarget)
.setGid(Optional.of("E66DC04E36F2D8BE00000000"))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.of(fooNode, barNode));
thrown.expect(HumanReadableException.class);
thrown.expectMessage(
"Targets [//bar:lib, //foo:lib] have the same hardcoded GID (E66DC04E36F2D8BE00000000)");
projectGenerator.createXcodeProjects();
}
@Test
public void projectIsRewrittenIfContentsHaveChanged() throws IOException {
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of());
clock.setCurrentTimeMillis(49152);
projectGenerator.createXcodeProjects();
assertThat(
projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH),
equalTo(49152L));
BuildTarget buildTarget = BuildTarget.builder("//foo", "foo").build();
TargetNode<?> node = AppleLibraryBuilder
.createBuilder(buildTarget)
.build();
ProjectGenerator projectGenerator2 = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of(node));
clock.setCurrentTimeMillis(64738);
projectGenerator2.createXcodeProjects();
assertThat(
projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH),
equalTo(64738L));
}
@Test
public void projectIsNotRewrittenIfContentsHaveNotChanged() throws IOException {
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of());
clock.setCurrentTimeMillis(49152);
projectGenerator.createXcodeProjects();
assertThat(
projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH),
equalTo(49152L));
ProjectGenerator projectGenerator2 = createProjectGeneratorForCombinedProject(
ImmutableSet.<TargetNode<?>>of());
clock.setCurrentTimeMillis(64738);
projectGenerator2.createXcodeProjects();
assertThat(
projectFilesystem.getLastModifiedTime(OUTPUT_PROJECT_FILE_PATH),
equalTo(49152L));
}
@Test
public void nonexistentResourceDirectoryShouldThrow() throws IOException {
ImmutableSet<TargetNode<?>> nodes = setupSimpleLibraryWithResources(
ImmutableSet.<SourcePath>of(),
ImmutableSet.of(Paths.get("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.<SourcePath>of(new TestSourcePath("nonexistent-file.png")),
ImmutableSet.<Path>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.<SourcePath>of(),
ImmutableSet.of(Paths.get("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.<SourcePath>of(new TestSourcePath("foodir")),
ImmutableSet.<Path>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 testGeneratingTestsAsStaticLibraries() throws IOException {
TargetNode<AppleTestDescription.Arg> libraryTestStatic =
AppleTestBuilder.createBuilder(BuildTarget.builder("//foo", "libraryTestStatic").build())
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.build();
TargetNode<AppleTestDescription.Arg> libraryTestNotStatic =
AppleTestBuilder.createBuilder(BuildTarget.builder("//foo", "libraryTestNotStatic").build())
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.build();
ProjectGenerator projectGenerator = createProjectGeneratorForCombinedProject(
ImmutableList.<TargetNode<?>>of(libraryTestStatic, libraryTestNotStatic));
projectGenerator
.setTestsToGenerateAsStaticLibraries(ImmutableSet.of(libraryTestStatic))
.createXcodeProjects();
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget libraryTestStaticTarget =
assertTargetExistsAndReturnTarget(project, "//foo:libraryTestStatic");
PBXTarget libraryTestNotStaticTarget =
assertTargetExistsAndReturnTarget(project, "//foo:libraryTestNotStatic");
assertThat(
libraryTestStaticTarget.getProductType(),
equalTo(PBXTarget.ProductType.STATIC_LIBRARY));
assertThat(
libraryTestNotStaticTarget.getProductType(),
equalTo(PBXTarget.ProductType.UNIT_TEST));
}
@Test
public void testGeneratingCombinedTests() throws IOException {
TargetNode<AppleResourceDescription.Arg> testLibDepResource =
AppleResourceBuilder.createBuilder(BuildTarget.builder("//lib", "deplibresource").build())
.setFiles(ImmutableSet.<SourcePath>of(new TestSourcePath("bar.png")))
.setDirs(ImmutableSet.<Path>of())
.build();
TargetNode<AppleNativeTargetDescriptionArg> testLibDepLib =
AppleLibraryBuilder.createBuilder(BuildTarget.builder("//libs", "deplib").build())
.setFrameworks(
Optional.of(ImmutableSortedSet.of("$SDKROOT/DeclaredInTestLibDep.framework")))
.setDeps(Optional.of(ImmutableSortedSet.of(testLibDepResource.getBuildTarget())))
.build();
TargetNode<AppleNativeTargetDescriptionArg> dep1 =
AppleLibraryBuilder.createBuilder(BuildTarget.builder("//foo", "dep1").build())
.setDeps(Optional.of(ImmutableSortedSet.of(testLibDepLib.getBuildTarget())))
.setFrameworks(
Optional.of(ImmutableSortedSet.of("$SDKROOT/DeclaredInTestLib.framework")))
.build();
TargetNode<AppleNativeTargetDescriptionArg> dep2 =
AppleLibraryBuilder.createBuilder(BuildTarget.builder("//foo", "dep2").build())
.build();
TargetNode<AppleTestDescription.Arg> xctest1 =
AppleTestBuilder.createBuilder(BuildTarget.builder("//foo", "xctest1").build())
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setDeps(Optional.of(ImmutableSortedSet.of(dep1.getBuildTarget())))
.setFrameworks(
Optional.of(ImmutableSortedSet.of("$SDKROOT/DeclaredInTest.framework")))
.build();
TargetNode<AppleTestDescription.Arg> xctest2 =
AppleTestBuilder.createBuilder(BuildTarget.builder("//foo", "xctest2").build())
.setExtension(Either.<AppleBundleExtension, String>ofLeft(AppleBundleExtension.XCTEST))
.setDeps(Optional.of(ImmutableSortedSet.of(dep2.getBuildTarget())))
.build();
ProjectGenerator projectGenerator = new ProjectGenerator(
TargetGraphFactory.newInstance(
testLibDepResource,
testLibDepLib,
dep1,
dep2,
xctest1,
xctest2),
ImmutableSet.<BuildTarget>of(),
projectFilesystem,
OUTPUT_DIRECTORY,
PROJECT_NAME,
"BUCK",
ProjectGenerator.SEPARATED_PROJECT_OPTIONS)
.setTestsToGenerateAsStaticLibraries(ImmutableSet.of(xctest1, xctest2))
.setAdditionalCombinedTestTargets(
ImmutableMultimap.of(
AppleTestBundleParamsKey.fromAppleTestDescriptionArg(xctest1.getConstructorArg()),
xctest1,
AppleTestBundleParamsKey.fromAppleTestDescriptionArg(xctest2.getConstructorArg()),
xctest2));
projectGenerator.createXcodeProjects();
ImmutableSet<PBXTarget> combinedTestTargets =
projectGenerator.getBuildableCombinedTestTargets();
assertThat(combinedTestTargets, hasSize(1));
assertThat(combinedTestTargets, hasItem(targetWithName("_BuckCombinedTest-xctest-0")));
PBXProject project = projectGenerator.getGeneratedProject();
PBXTarget target = assertTargetExistsAndReturnTarget(project, "_BuckCombinedTest-xctest-0");
assertHasSingletonSourcesPhaseWithSourcesAndFlags(
target,
ImmutableMap.of(
BuckConstant.GEN_PATH.resolve("xcode-scripts/emptyFile.c").toString(),
Optional.<String>absent()));
assertHasSingletonFrameworksPhaseWithFrameworkEntries(
target,
ImmutableList.of(
"$BUILT_PRODUCTS_DIR/libxctest1.a",
"$BUILT_PRODUCTS_DIR/libxctest2.a",
"$BUILT_PRODUCTS_DIR/F4XWY2LCOM5GIZLQNRUWE/libdeplib.a",
"$BUILT_PRODUCTS_DIR/F4XWM33PHJSGK4BR/libdep1.a",
"$BUILT_PRODUCTS_DIR/F4XWM33PHJSGK4BS/libdep2.a",
"$SDKROOT/DeclaredInTestLib.framework",
"$SDKROOT/DeclaredInTestLibDep.framework",
"$SDKROOT/DeclaredInTest.framework"));
assertHasSingletonResourcesPhaseWithEntries(
target,
"bar.png");
}
private ProjectGenerator createProjectGeneratorForCombinedProject(
Iterable<TargetNode<?>> nodes) {
return createProjectGeneratorForCombinedProject(
nodes,
ImmutableSet.<ProjectGenerator.Option>of());
}
private ProjectGenerator createProjectGeneratorForCombinedProject(
Iterable<TargetNode<?>> nodes,
ImmutableSet<ProjectGenerator.Option> projectGeneratorOptions) {
ImmutableSet<BuildTarget> initialBuildTargets = FluentIterable
.from(nodes)
.transform(HasBuildTarget.TO_TARGET)
.toSet();
return new ProjectGenerator(
TargetGraphFactory.newInstance(ImmutableSet.copyOf(nodes)),
initialBuildTargets,
projectFilesystem,
OUTPUT_DIRECTORY,
PROJECT_NAME,
"BUCK",
projectGeneratorOptions);
}
private ImmutableSet<TargetNode<?>> setupSimpleLibraryWithResources(
ImmutableSet<SourcePath> resourceFiles,
ImmutableSet<Path> resourceDirectories) {
BuildTarget resourceTarget = BuildTarget.builder("//foo", "res").build();
TargetNode<?> resourceNode = AppleResourceBuilder
.createBuilder(resourceTarget)
.setFiles(resourceFiles)
.setDirs(resourceDirectories)
.build();
BuildTarget libraryTarget = BuildTarget.builder("//foo", "foo").build();
TargetNode<?> libraryNode = AppleLibraryBuilder
.createBuilder(libraryTarget)
.setDeps(Optional.of(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 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("Source file is expected", 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 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);
}
private boolean hasShellScriptPhaseToCompileAssetCatalogs(PBXTarget target) {
boolean found = false;
for (PBXBuildPhase phase : target.getBuildPhases()) {
if (phase.getClass().equals(PBXShellScriptBuildPhase.class)) {
PBXShellScriptBuildPhase shellScriptBuildPhase = (PBXShellScriptBuildPhase) phase;
if (shellScriptBuildPhase.getShellScript().contains("compile_asset_catalogs")) {
found = true;
}
}
}
return found;
}
private Matcher<PBXTarget> targetWithName(String name) {
return new FeatureMatcher<PBXTarget, String>(
org.hamcrest.Matchers.equalTo(name),
"target with name",
"name") {
@Override
protected String featureValueOf(PBXTarget pbxTarget) {
return pbxTarget.getName();
}
};
}
}