/* * Copyright 2014-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 org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import com.facebook.buck.apple.AppleBinaryBuilder; import com.facebook.buck.apple.AppleBundleBuilder; import com.facebook.buck.apple.AppleBundleExtension; import com.facebook.buck.apple.AppleLibraryBuilder; import com.facebook.buck.apple.AppleLibraryDescriptionArg; import com.facebook.buck.apple.AppleTestBuilder; import com.facebook.buck.apple.XcodeWorkspaceConfigBuilder; import com.facebook.buck.apple.XcodeWorkspaceConfigDescription; import com.facebook.buck.apple.XcodeWorkspaceConfigDescriptionArg; import com.facebook.buck.apple.xcode.XCScheme; import com.facebook.buck.cli.BuckConfig; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.CxxPlatformUtils; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.event.BuckEventBusFactory; import com.facebook.buck.halide.HalideBuckConfig; import com.facebook.buck.halide.HalideLibraryBuilder; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Either; import com.facebook.buck.rules.ActionGraphCache; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.Cell; import com.facebook.buck.rules.DefaultBuildTargetSourcePath; import com.facebook.buck.rules.FakeSourcePath; 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.shell.GenruleBuilder; import com.facebook.buck.shell.GenruleDescription; import com.facebook.buck.shell.GenruleDescriptionArg; import com.facebook.buck.swift.SwiftBuckConfig; import com.facebook.buck.testutil.TargetGraphFactory; import com.facebook.buck.timing.IncrementingFakeClock; import com.google.common.base.Function; 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.util.concurrent.MoreExecutors; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.hamcrest.core.AllOf; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class WorkspaceAndProjectGeneratorTest { private static final CxxPlatform DEFAULT_PLATFORM = CxxPlatformUtils.DEFAULT_PLATFORM; private Cell rootCell; private HalideBuckConfig halideBuckConfig; private CxxBuckConfig cxxBuckConfig; private SwiftBuckConfig swiftBuckConfig; private TargetGraph targetGraph; private TargetNode<XcodeWorkspaceConfigDescriptionArg, XcodeWorkspaceConfigDescription> workspaceNode; private TargetNode<XcodeWorkspaceConfigDescriptionArg, XcodeWorkspaceConfigDescription> workspaceWithExtraSchemeNode; @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void setUp() throws InterruptedException, IOException { rootCell = (new TestCellBuilder()).build(); ProjectFilesystem projectFilesystem = rootCell.getFilesystem(); halideBuckConfig = HalideLibraryBuilder.createDefaultHalideConfig(projectFilesystem); cxxBuckConfig = new CxxBuckConfig(FakeBuckConfig.builder().build()); BuckConfig fakeBuckConfig = FakeBuckConfig.builder().build(); swiftBuckConfig = new SwiftBuckConfig(fakeBuckConfig); setUpWorkspaceAndProjects(); } private void setUpWorkspaceAndProjects() { // Create the following dep tree: // FooBin -has-test-> FooBinTest // | // V // FooLib -has-test-> FooLibTest // | | // V V // BarLib BazLib -has-test-> BazLibTest // ^ // | // QuxBin // // // FooBin and BazLib and FooLibTest use "tests" to specify their tests. // // Calling generate on FooBin should pull in everything except BazLibTest and QuxBin. BuildTarget bazTestTarget = BuildTarget.builder(rootCell.getRoot(), "//baz", "xctest").build(); BuildTarget fooBinTestTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "bin-xctest").build(); BuildTarget fooTestTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "lib-xctest").build(); BuildTarget barLibTarget = BuildTarget.builder(rootCell.getRoot(), "//bar", "lib").build(); TargetNode<?, ?> barLibNode = AppleLibraryBuilder.createBuilder(barLibTarget).build(); BuildTarget fooLibTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "lib").build(); TargetNode<?, ?> fooLibNode = AppleLibraryBuilder.createBuilder(fooLibTarget) .setDeps(ImmutableSortedSet.of(barLibTarget)) .setTests(ImmutableSortedSet.of(fooTestTarget)) .build(); BuildTarget fooBinBinaryTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "binbinary").build(); TargetNode<?, ?> fooBinBinaryNode = AppleBinaryBuilder.createBuilder(fooBinBinaryTarget) .setDeps(ImmutableSortedSet.of(fooLibTarget)) .build(); BuildTarget fooBinTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "bin").build(); TargetNode<?, ?> fooBinNode = AppleBundleBuilder.createBuilder(fooBinTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(fooBinBinaryTarget) .setTests(ImmutableSortedSet.of(fooBinTestTarget)) .build(); BuildTarget bazLibTarget = BuildTarget.builder(rootCell.getRoot(), "//baz", "lib").build(); TargetNode<?, ?> bazLibNode = AppleLibraryBuilder.createBuilder(bazLibTarget) .setDeps(ImmutableSortedSet.of(fooLibTarget)) .setTests(ImmutableSortedSet.of(bazTestTarget)) .build(); TargetNode<?, ?> bazTestNode = AppleTestBuilder.createBuilder(bazTestTarget) .setDeps(ImmutableSortedSet.of(bazLibTarget)) .setInfoPlist(new FakeSourcePath("Info.plist")) .build(); TargetNode<?, ?> fooTestNode = AppleTestBuilder.createBuilder(fooTestTarget) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(bazLibTarget)) .build(); TargetNode<?, ?> fooBinTestNode = AppleTestBuilder.createBuilder(fooBinTestTarget) .setDeps(ImmutableSortedSet.of(fooBinTarget)) .setInfoPlist(new FakeSourcePath("Info.plist")) .build(); BuildTarget quxBinTarget = BuildTarget.builder(rootCell.getRoot(), "//qux", "bin").build(); TargetNode<?, ?> quxBinNode = AppleBinaryBuilder.createBuilder(quxBinTarget) .setDeps(ImmutableSortedSet.of(barLibTarget)) .build(); BuildTarget workspaceTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build(); workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder(workspaceTarget) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(fooBinTarget)) .build(); targetGraph = TargetGraphFactory.newInstance( barLibNode, fooLibNode, fooBinBinaryNode, fooBinNode, bazLibNode, bazTestNode, fooTestNode, fooBinTestNode, quxBinNode, workspaceNode); } private BuckEventBus getFakeBuckEventBus() { return BuckEventBusFactory.newInstance(new IncrementingFakeClock(TimeUnit.SECONDS.toNanos(1))); } @Test public void workspaceAndProjectsShouldDiscoverDependenciesAndTests() throws IOException, InterruptedException { WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); ProjectGenerator fooProjectGenerator = projectGenerators.get(Paths.get("foo")); ProjectGenerator barProjectGenerator = projectGenerators.get(Paths.get("bar")); ProjectGenerator bazProjectGenerator = projectGenerators.get(Paths.get("baz")); ProjectGenerator quxProjectGenerator = projectGenerators.get(Paths.get("qux")); assertNull("The Qux project should not be generated at all", quxProjectGenerator); assertNotNull("The Foo project should have been generated", fooProjectGenerator); assertNotNull("The Bar project should have been generated", barProjectGenerator); assertNotNull("The Baz project should have been generated", bazProjectGenerator); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:bin"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:lib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:bin-xctest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:lib-xctest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( barProjectGenerator.getGeneratedProject(), "//bar:lib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( bazProjectGenerator.getGeneratedProject(), "//baz:lib"); } @Test public void combinedProjectShouldDiscoverDependenciesAndTests() throws IOException, InterruptedException { WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), true /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); assertTrue( "Combined project generation should not populate the project generators map", projectGenerators.isEmpty()); Optional<ProjectGenerator> projectGeneratorOptional = generator.getCombinedProjectGenerator(); assertTrue( "Combined project generator should be present", projectGeneratorOptional.isPresent()); ProjectGenerator projectGenerator = projectGeneratorOptional.get(); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//foo:bin"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//foo:lib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//foo:bin-xctest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//foo:lib-xctest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//bar:lib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( projectGenerator.getGeneratedProject(), "//baz:lib"); } @Test public void workspaceAndProjectsWithoutTests() throws IOException, InterruptedException { WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of(), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); ProjectGenerator fooProjectGenerator = projectGenerators.get(Paths.get("foo")); ProjectGenerator barProjectGenerator = projectGenerators.get(Paths.get("bar")); ProjectGenerator bazProjectGenerator = projectGenerators.get(Paths.get("baz")); ProjectGenerator quxProjectGenerator = projectGenerators.get(Paths.get("qux")); assertNull("The Qux project should not be generated at all", quxProjectGenerator); assertNull("The Baz project should not be generated at all", bazProjectGenerator); assertNotNull("The Foo project should have been generated", fooProjectGenerator); assertNotNull("The Bar project should have been generated", barProjectGenerator); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:bin"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:lib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( barProjectGenerator.getGeneratedProject(), "//bar:lib"); } @Test public void workspaceAndProjectsWithoutDependenciesTests() throws IOException, InterruptedException { WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of(ProjectGenerator.Option.INCLUDE_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); Optional<XCScheme> scheme = Iterables.getOnlyElement(generator.getSchemeGenerators().values()).getOutputScheme(); assertThat(scheme.isPresent(), is(true)); assertThat( "Test for project FooBin should have been generated", scheme.get().getBuildAction().get().getBuildActionEntries(), hasItem( withNameAndBuildingFor( "bin-xctest", equalTo(XCScheme.BuildActionEntry.BuildFor.TEST_ONLY)))); assertThat( "Test for project FooLib should not be generated at all", scheme.get().getBuildAction().get().getBuildActionEntries(), not( hasItem( withNameAndBuildingFor( "lib-xctest", equalTo(XCScheme.BuildActionEntry.BuildFor.TEST_ONLY))))); } @Test public void requiredBuildTargets() throws IOException, InterruptedException { BuildTarget genruleTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "gen").build(); TargetNode<GenruleDescriptionArg, GenruleDescription> genrule = GenruleBuilder.newGenruleBuilder(genruleTarget).setOut("source.m").build(); BuildTarget libraryTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "lib").build(); TargetNode<AppleLibraryDescriptionArg, ?> library = AppleLibraryBuilder.createBuilder(libraryTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new DefaultBuildTargetSourcePath(genruleTarget)))) .build(); TargetNode<XcodeWorkspaceConfigDescriptionArg, ?> workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder( BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build()) .setSrcTarget(Optional.of(libraryTarget)) .build(); TargetGraph targetGraph = TargetGraphFactory.newInstance(genrule, library, workspaceNode); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of(), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); assertEquals(generator.getRequiredBuildTargets(), ImmutableSet.of(genruleTarget)); } @Test public void requiredBuildTargetsForCombinedProject() throws IOException, InterruptedException { BuildTarget genruleTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "gen").build(); TargetNode<GenruleDescriptionArg, GenruleDescription> genrule = GenruleBuilder.newGenruleBuilder(genruleTarget).setOut("source.m").build(); BuildTarget libraryTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "lib").build(); TargetNode<AppleLibraryDescriptionArg, ?> library = AppleLibraryBuilder.createBuilder(libraryTarget) .setSrcs( ImmutableSortedSet.of( SourceWithFlags.of(new DefaultBuildTargetSourcePath(genruleTarget)))) .build(); TargetNode<XcodeWorkspaceConfigDescriptionArg, ?> workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder( BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build()) .setSrcTarget(Optional.of(libraryTarget)) .build(); TargetGraph targetGraph = TargetGraphFactory.newInstance(genrule, library, workspaceNode); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of(), true /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); assertEquals(generator.getRequiredBuildTargets(), ImmutableSet.of(genruleTarget)); } private void setUpWorkspaceWithSchemeAndProjects() { // Create the following dep tree: // FooBin -has-test-> FooBinTest // | // V // FooLib -has-test-> FooLibTest // | | // V V // BarLib BazLib -has-test-> BazLibTest // ^ // | // QuxBin // // // FooBin and BazLib and FooLibTest use "tests" to specify their tests. // // Calling generate on FooBin should pull in everything except BazLibTest and QuxBin. BuildTarget bazTestTarget = BuildTarget.builder(rootCell.getRoot(), "//baz", "BazTest").build(); BuildTarget fooBinTestTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooBinTest").build(); BuildTarget fooTestTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooLibTest").build(); BuildTarget barLibTarget = BuildTarget.builder(rootCell.getRoot(), "//bar", "BarLib").build(); TargetNode<?, ?> barLibNode = AppleLibraryBuilder.createBuilder(barLibTarget).build(); BuildTarget fooLibTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooLib").build(); TargetNode<?, ?> fooLibNode = AppleLibraryBuilder.createBuilder(fooLibTarget) .setDeps(ImmutableSortedSet.of(barLibTarget)) .setTests(ImmutableSortedSet.of(fooTestTarget)) .build(); BuildTarget fooBinBinaryTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooBinBinary").build(); TargetNode<?, ?> fooBinBinaryNode = AppleBinaryBuilder.createBuilder(fooBinBinaryTarget) .setDeps(ImmutableSortedSet.of(fooLibTarget)) .build(); BuildTarget fooBinTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooBin").build(); TargetNode<?, ?> fooBinNode = AppleBundleBuilder.createBuilder(fooBinTarget) .setExtension(Either.ofLeft(AppleBundleExtension.APP)) .setInfoPlist(new FakeSourcePath("Info.plist")) .setBinary(fooBinBinaryTarget) .setTests(ImmutableSortedSet.of(fooBinTestTarget)) .build(); BuildTarget bazLibTarget = BuildTarget.builder(rootCell.getRoot(), "//baz", "BazLib").build(); TargetNode<?, ?> bazLibNode = AppleLibraryBuilder.createBuilder(bazLibTarget) .setDeps(ImmutableSortedSet.of(fooLibTarget)) .setTests(ImmutableSortedSet.of(bazTestTarget)) .build(); TargetNode<?, ?> bazTestNode = AppleTestBuilder.createBuilder(bazTestTarget) .setDeps(ImmutableSortedSet.of(bazLibTarget)) .setInfoPlist(new FakeSourcePath("Info.plist")) .build(); TargetNode<?, ?> fooTestNode = AppleTestBuilder.createBuilder(fooTestTarget) .setInfoPlist(new FakeSourcePath("Info.plist")) .setDeps(ImmutableSortedSet.of(bazLibTarget)) .build(); TargetNode<?, ?> fooBinTestNode = AppleTestBuilder.createBuilder(fooBinTestTarget) .setDeps(ImmutableSortedSet.of(fooBinTarget)) .setInfoPlist(new FakeSourcePath("Info.plist")) .build(); BuildTarget quxBinTarget = BuildTarget.builder(rootCell.getRoot(), "//qux", "QuxBin").build(); TargetNode<?, ?> quxBinNode = AppleBinaryBuilder.createBuilder(quxBinTarget) .setDeps(ImmutableSortedSet.of(barLibTarget)) .build(); BuildTarget workspaceTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build(); workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder(workspaceTarget) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(fooBinTarget)) .build(); BuildTarget workspaceWithExtraSchemeTarget = BuildTarget.builder(rootCell.getRoot(), "//qux", "workspace").build(); workspaceWithExtraSchemeNode = XcodeWorkspaceConfigBuilder.createBuilder(workspaceWithExtraSchemeTarget) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(quxBinTarget)) .setExtraSchemes(ImmutableSortedMap.of("FooScheme", workspaceTarget)) .build(); targetGraph = TargetGraphFactory.newInstance( barLibNode, fooLibNode, fooBinBinaryNode, fooBinNode, bazLibNode, bazTestNode, fooTestNode, fooBinTestNode, quxBinNode, workspaceNode, workspaceWithExtraSchemeNode); } @Test public void targetsForWorkspaceWithExtraSchemes() throws IOException, InterruptedException { setUpWorkspaceWithSchemeAndProjects(); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceWithExtraSchemeNode.getConstructorArg(), workspaceWithExtraSchemeNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); ProjectGenerator fooProjectGenerator = projectGenerators.get(Paths.get("foo")); ProjectGenerator barProjectGenerator = projectGenerators.get(Paths.get("bar")); ProjectGenerator bazProjectGenerator = projectGenerators.get(Paths.get("baz")); ProjectGenerator quxProjectGenerator = projectGenerators.get(Paths.get("qux")); assertNotNull("The Qux project should have been generated", quxProjectGenerator); assertNotNull("The Foo project should have been generated", fooProjectGenerator); assertNotNull("The Bar project should have been generated", barProjectGenerator); assertNotNull("The Baz project should have been generated", bazProjectGenerator); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:FooBin"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:FooLib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:FooBinTest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:FooLibTest"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( barProjectGenerator.getGeneratedProject(), "//bar:BarLib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( bazProjectGenerator.getGeneratedProject(), "//baz:BazLib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( quxProjectGenerator.getGeneratedProject(), "//qux:QuxBin"); XCScheme mainScheme = generator.getSchemeGenerators().get("workspace").getOutputScheme().get(); XCScheme.BuildAction mainSchemeBuildAction = mainScheme.getBuildAction().get(); // I wish we could use Hamcrest contains() here, but we hit // https://code.google.com/p/hamcrest/issues/detail?id=190 if we do that. assertThat(mainSchemeBuildAction.getBuildActionEntries(), hasSize(2)); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(0), withNameAndBuildingFor("BarLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(1), withNameAndBuildingFor("QuxBin", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); XCScheme fooScheme = generator.getSchemeGenerators().get("FooScheme").getOutputScheme().get(); XCScheme.BuildAction fooSchemeBuildAction = fooScheme.getBuildAction().get(); assertThat(fooSchemeBuildAction.getBuildActionEntries(), hasSize(6)); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(0), withNameAndBuildingFor("BarLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(1), withNameAndBuildingFor("FooLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(2), withNameAndBuildingFor("FooBin", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(3), withNameAndBuildingFor("BazLib", equalTo(XCScheme.BuildActionEntry.BuildFor.TEST_ONLY))); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(4), withNameAndBuildingFor( "FooLibTest", equalTo(XCScheme.BuildActionEntry.BuildFor.TEST_ONLY))); assertThat( fooSchemeBuildAction.getBuildActionEntries().get(5), withNameAndBuildingFor( "FooBinTest", equalTo(XCScheme.BuildActionEntry.BuildFor.TEST_ONLY))); } @Test public void targetsForWorkspaceWithExtraTargets() throws IOException, InterruptedException { BuildTarget fooLibTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooLib").build(); TargetNode<AppleLibraryDescriptionArg, ?> fooLib = AppleLibraryBuilder.createBuilder(fooLibTarget).build(); BuildTarget barLibTarget = BuildTarget.builder(rootCell.getRoot(), "//bar", "BarLib").build(); TargetNode<AppleLibraryDescriptionArg, ?> barLib = AppleLibraryBuilder.createBuilder(barLibTarget).build(); BuildTarget bazLibTarget = BuildTarget.builder(rootCell.getRoot(), "//baz", "BazLib").build(); TargetNode<AppleLibraryDescriptionArg, ?> bazLib = AppleLibraryBuilder.createBuilder(bazLibTarget) .setDeps(ImmutableSortedSet.of(barLibTarget)) .build(); TargetNode<XcodeWorkspaceConfigDescriptionArg, ?> workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder( BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build()) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(fooLibTarget)) .setExtraTargets(ImmutableSortedSet.of(bazLibTarget)) .build(); TargetGraph targetGraph = TargetGraphFactory.newInstance(fooLib, barLib, bazLib, workspaceNode); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), false /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); ProjectGenerator fooProjectGenerator = projectGenerators.get(Paths.get("foo")); ProjectGenerator barProjectGenerator = projectGenerators.get(Paths.get("bar")); ProjectGenerator bazProjectGenerator = projectGenerators.get(Paths.get("baz")); assertNotNull("The Foo project should have been generated", fooProjectGenerator); assertNotNull("The Bar project should have been generated", barProjectGenerator); assertNotNull("The Baz project should have been generated", bazProjectGenerator); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( fooProjectGenerator.getGeneratedProject(), "//foo:FooLib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( barProjectGenerator.getGeneratedProject(), "//bar:BarLib"); ProjectGeneratorTestUtils.assertTargetExistsAndReturnTarget( bazProjectGenerator.getGeneratedProject(), "//baz:BazLib"); XCScheme mainScheme = generator.getSchemeGenerators().get("workspace").getOutputScheme().get(); XCScheme.BuildAction mainSchemeBuildAction = mainScheme.getBuildAction().get(); // I wish we could use Hamcrest contains() here, but we hit // https://code.google.com/p/hamcrest/issues/detail?id=190 if we do that. assertThat(mainSchemeBuildAction.getBuildActionEntries(), hasSize(3)); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(0), withNameAndBuildingFor("FooLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(1), withNameAndBuildingFor("BarLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(2), withNameAndBuildingFor("BazLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); } @Test public void enablingParallelizeBuild() throws IOException, InterruptedException { BuildTarget fooLibTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooLib").build(); TargetNode<AppleLibraryDescriptionArg, ?> fooLib = AppleLibraryBuilder.createBuilder(fooLibTarget).build(); TargetNode<XcodeWorkspaceConfigDescriptionArg, ?> workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder( BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build()) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(fooLibTarget)) .build(); TargetGraph targetGraph = TargetGraphFactory.newInstance(fooLib, workspaceNode); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), true /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); XCScheme mainScheme = generator.getSchemeGenerators().get("workspace").getOutputScheme().get(); XCScheme.BuildAction mainSchemeBuildAction = mainScheme.getBuildAction().get(); // I wish we could use Hamcrest contains() here, but we hit // https://code.google.com/p/hamcrest/issues/detail?id=190 if we do that. assertThat(mainSchemeBuildAction.getBuildActionEntries(), hasSize(1)); assertThat( mainSchemeBuildAction.getBuildActionEntries().get(0), withNameAndBuildingFor("FooLib", equalTo(XCScheme.BuildActionEntry.BuildFor.DEFAULT))); assertThat(mainSchemeBuildAction.getParallelizeBuild(), is(true)); } @Test public void customRunnableSettings() throws IOException, InterruptedException { BuildTarget fooLibTarget = BuildTarget.builder(rootCell.getRoot(), "//foo", "FooLib").build(); TargetNode<AppleLibraryDescriptionArg, ?> fooLib = AppleLibraryBuilder.createBuilder(fooLibTarget).build(); TargetNode<XcodeWorkspaceConfigDescriptionArg, ?> workspaceNode = XcodeWorkspaceConfigBuilder.createBuilder( BuildTarget.builder(rootCell.getRoot(), "//foo", "workspace").build()) .setWorkspaceName(Optional.of("workspace")) .setSrcTarget(Optional.of(fooLibTarget)) .setExplicitRunnablePath(Optional.of("/some.app")) .setLaunchStyle(Optional.of(XCScheme.LaunchAction.LaunchStyle.WAIT)) .build(); TargetGraph targetGraph = TargetGraphFactory.newInstance(fooLib, workspaceNode); WorkspaceAndProjectGenerator generator = new WorkspaceAndProjectGenerator( rootCell, targetGraph, workspaceNode.getConstructorArg(), workspaceNode.getBuildTarget(), ImmutableSet.of( ProjectGenerator.Option.INCLUDE_TESTS, ProjectGenerator.Option.INCLUDE_DEPENDENCIES_TESTS), false /* combinedProject */, FocusedModuleTargetMatcher.noFocus(), true /* parallelizeBuild */, DEFAULT_PLATFORM, "BUCK", getBuildRuleResolverForNodeFunction(targetGraph), getFakeBuckEventBus(), halideBuckConfig, cxxBuckConfig, swiftBuckConfig); Map<Path, ProjectGenerator> projectGenerators = new HashMap<>(); generator.generateWorkspaceAndDependentProjects( projectGenerators, MoreExecutors.newDirectExecutorService()); XCScheme mainScheme = generator.getSchemeGenerators().get("workspace").getOutputScheme().get(); XCScheme.LaunchAction launchAction = mainScheme.getLaunchAction().get(); assertThat(launchAction.getRunnablePath().get(), Matchers.equalTo("/some.app")); assertThat( launchAction.getLaunchStyle(), Matchers.equalTo(XCScheme.LaunchAction.LaunchStyle.WAIT)); } private Matcher<XCScheme.BuildActionEntry> buildActionEntryWithName(String name) { return new FeatureMatcher<XCScheme.BuildActionEntry, String>( equalTo(name), "BuildActionEntry named", "name") { @Override protected String featureValueOf(XCScheme.BuildActionEntry buildActionEntry) { return buildActionEntry.getBuildableReference().blueprintName; } }; } private Matcher<XCScheme.BuildActionEntry> withNameAndBuildingFor( String name, Matcher<? super EnumSet<XCScheme.BuildActionEntry.BuildFor>> buildFor) { return AllOf.allOf( buildActionEntryWithName(name), new FeatureMatcher<XCScheme.BuildActionEntry, EnumSet<XCScheme.BuildActionEntry.BuildFor>>( buildFor, "Building for", "BuildFor") { @Override protected EnumSet<XCScheme.BuildActionEntry.BuildFor> featureValueOf( XCScheme.BuildActionEntry entry) { return entry.getBuildFor(); } }); } private Function<TargetNode<?, ?>, BuildRuleResolver> getBuildRuleResolverForNodeFunction( final TargetGraph targetGraph) { return input -> ActionGraphCache.getFreshActionGraph( BuckEventBusFactory.newInstance(), targetGraph.getSubgraph(ImmutableSet.of(input))) .getResolver(); } }