/* * Copyright 2015-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.crosscell; import static com.facebook.buck.android.AndroidNdkHelper.SymbolGetter; import static com.facebook.buck.android.AndroidNdkHelper.SymbolsAndDtNeeded; import static com.facebook.buck.util.environment.Platform.WINDOWS; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeThat; import com.facebook.buck.android.AndroidNdkHelper; import com.facebook.buck.android.AssumeAndroidPlatform; import com.facebook.buck.android.NdkCxxPlatform; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.event.BuckEventBusFactory; import com.facebook.buck.event.listener.BroadcastEventListener; import com.facebook.buck.io.MorePaths; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.json.BuildFileParseException; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetException; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.model.Pair; import com.facebook.buck.parser.Parser; import com.facebook.buck.parser.ParserConfig; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.Cell; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.coercer.ConstructorArgMarshaller; import com.facebook.buck.rules.coercer.DefaultTypeCoercerFactory; import com.facebook.buck.rules.coercer.TypeCoercerFactory; import com.facebook.buck.testutil.MoreAsserts; import com.facebook.buck.testutil.TestConsole; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.testutil.integration.ZipInspector; import com.facebook.buck.util.DefaultProcessExecutor; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.RichStream; import com.facebook.buck.util.environment.Platform; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import com.google.common.util.concurrent.MoreExecutors; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.Optional; import java.util.concurrent.Executors; import java.util.stream.Collectors; import org.hamcrest.Matchers; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; /** Cross-cell related integration tests that don't fit anywhere else. */ public class InterCellIntegrationTest { @Rule public TemporaryPaths tmp = new TemporaryPaths(); @Test public void ensureThatNormalBuildsWorkAsExpected() throws IOException { ProjectWorkspace secondary = TestDataHelper.createProjectWorkspaceForScenario( this, "inter-cell/export-file/secondary", tmp); secondary.setUp(); ProjectWorkspace.ProcessResult result = secondary.runBuckBuild("//:hello"); result.assertSuccess(); } @Test public void shouldBeAbleToUseAnExportFileXRepoTarget() throws IOException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); String expected = secondary.getFileContents("hello.txt"); Path path = primary.buildAndReturnOutput("//:exported-file"); String actual = new String(Files.readAllBytes(path), UTF_8); assertEquals(expected, actual); Path secondaryPath = primary.buildAndReturnRelativeOutput("secondary//:hello"); actual = new String(Files.readAllBytes(secondary.resolve(secondaryPath)), UTF_8); assertEquals(expected, actual); } @Test public void shouldBeAbleToUseTargetsCommandXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace.ProcessResult result = primary.runBuckCommand("targets", "--show-target-hash", "//:cxxbinary"); result.assertSuccess(); ProjectWorkspace.ProcessResult result2 = primary.runBuckCommand("targets", "secondary//:cxxlib"); result2.assertSuccess(); } @Test public void shouldBeAbleToUseQueryCommandXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); ProjectWorkspace primary = createWorkspace("inter-cell/multi-cell/primary"); primary.setUp(); ProjectWorkspace secondary = createWorkspace("inter-cell/multi-cell/secondary"); secondary.setUp(); ProjectWorkspace ternary = createWorkspace("inter-cell/multi-cell/ternary"); ternary.setUp(); registerCell(secondary, "ternary", ternary); registerCell(primary, "secondary", secondary); registerCell(primary, "ternary", ternary); primary.runBuckCommand("targets", "--show-target-hash", "//:cxxbinary"); secondary.runBuckCommand("targets", "--show-target-hash", "//:cxxlib"); ternary.runBuckCommand("targets", "--show-target-hash", "//:cxxlib2"); ProjectWorkspace.ProcessResult result = primary.runBuckCommand("query", "deps(%s)", "//:cxxbinary"); result.assertSuccess(); assertThat(result.getStdout(), is(primary.getFileContents("stdout-cross-cell-dep"))); } @Test public void shouldBeAbleToUseProjectCommandXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace.ProcessResult result = primary.runBuckCommand("project", "//:cxxbinary"); result.assertSuccess(); } @Test public void shouldBeAbleToUseACxxLibraryXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace.ProcessResult result = primary.runBuckBuild("//:cxxbinary"); result.assertSuccess(); } @Test public void shouldBeAbleToUseMultipleXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); ProjectWorkspace primary = createWorkspace("inter-cell/multi-cell/primary"); ProjectWorkspace secondary = createWorkspace("inter-cell/multi-cell/secondary"); ProjectWorkspace ternary = createWorkspace("inter-cell/multi-cell/ternary"); registerCell(secondary, "ternary", ternary); registerCell(primary, "secondary", secondary); registerCell(primary, "ternary", ternary); primary.runBuckCommand("targets", "--show-target-hash", "//:cxxbinary"); secondary.runBuckCommand("targets", "--show-target-hash", "//:cxxlib"); ternary.runBuckCommand("targets", "--show-target-hash", "//:cxxlib2"); ProjectWorkspace.ProcessResult result = primary.runBuckBuild("//:cxxbinary"); result.assertSuccess(); } @Test public void xCellCxxLibraryBuildsShouldBeHermetic() throws InterruptedException, IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); Path firstBinary = primary.buildAndReturnOutput("//:cxxbinary"); ImmutableMap<String, HashCode> firstPrimaryObjectFiles = findObjectFiles(primary); ImmutableMap<String, HashCode> firstObjectFiles = findObjectFiles(secondary); // Now recreate an identical checkout cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); primary = cells.getFirst(); secondary = cells.getSecond(); Path secondBinary = primary.buildAndReturnOutput("//:cxxbinary"); ImmutableMap<String, HashCode> secondPrimaryObjectFiles = findObjectFiles(primary); ImmutableMap<String, HashCode> secondObjectFiles = findObjectFiles(secondary); assertEquals(firstPrimaryObjectFiles, secondPrimaryObjectFiles); assertEquals(firstObjectFiles, secondObjectFiles); MoreAsserts.assertContentsEqual(firstBinary, secondBinary); } private ImmutableMap<String, HashCode> findObjectFiles(final ProjectWorkspace workspace) throws InterruptedException, IOException { ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath()); final Path buckOut = workspace.getPath(filesystem.getBuckPaths().getBuckOut()); final ImmutableMap.Builder<String, HashCode> objectHashCodes = ImmutableMap.builder(); Files.walkFileTree( buckOut, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (MorePaths.getFileExtension(file).equals("o")) { HashCode hash = MorePaths.asByteSource(file).hash(Hashing.sha1()); objectHashCodes.put(buckOut.relativize(file).toString(), hash); } return FileVisitResult.CONTINUE; } }); ImmutableMap<String, HashCode> toReturn = objectHashCodes.build(); Preconditions.checkState(!toReturn.isEmpty()); return toReturn; } @Test public void shouldBeAbleToUseAJavaLibraryTargetXCell() throws IOException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/java/primary", "inter-cell/java/secondary"); ProjectWorkspace primary = cells.getFirst(); primary.runBuckBuild("//:lib").assertSuccess(); primary.runBuckBuild("//:java-binary", "-v", "5").assertSuccess(); } @Test public void buildFileNamesCanBeDifferentCrossCell() throws IOException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/build-file-names/primary", "inter-cell/build-file-names/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); Path output = primary.buildAndReturnOutput("//:export"); String expected = secondary.getFileContents("hello-world.txt"); assertEquals(expected, new String(Files.readAllBytes(output), UTF_8)); } @SuppressWarnings("PMD.EmptyCatchBlock") @Test public void xCellVisibilityShouldWorkAsExpected() throws IOException, InterruptedException, BuildFileParseException, BuildTargetException { try { parseTargetForXCellVisibility("//:not-visible-target"); fail("Did not expect parsing to succeed"); } catch (HumanReadableException expected) { // Everything is as it should be. } } @Test public void xCellVisibilityPatternsBasedOnPublicBuildTargetsWork() throws InterruptedException, BuildFileParseException, IOException, BuildTargetException { parseTargetForXCellVisibility("//:public-target"); } @Test public void xCellVisibilityPatternsBasedOnExplicitBuildTargetsWork() throws InterruptedException, BuildFileParseException, IOException, BuildTargetException { parseTargetForXCellVisibility("//:visible-target"); } @Test public void xCellSingleDirectoryVisibilityPatternsWork() throws InterruptedException, BuildFileParseException, IOException, BuildTargetException { parseTargetForXCellVisibility("//sub2:directory"); } @Test public void xCellSubDirectoryVisibilityPatternsWork() throws InterruptedException, BuildFileParseException, IOException, BuildTargetException { parseTargetForXCellVisibility("//sub:wild-card"); } private void parseTargetForXCellVisibility(String targetName) throws IOException, InterruptedException, BuildFileParseException, BuildTargetException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/visibility/primary", "inter-cell/visibility/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); registerCell(primary, "primary", primary); registerCell(secondary, "primary", primary); // We could just do a build, but that's a little extreme since all we need is the target graph TypeCoercerFactory coercerFactory = new DefaultTypeCoercerFactory(); Parser parser = new Parser( new BroadcastEventListener(), primary.asCell().getBuckConfig().getView(ParserConfig.class), coercerFactory, new ConstructorArgMarshaller(coercerFactory)); BuckEventBus eventBus = BuckEventBusFactory.newInstance(); Cell primaryCell = primary.asCell(); BuildTarget namedTarget = BuildTargetFactory.newInstance(primaryCell.getFilesystem().getRootPath(), targetName); // It's enough that this parses cleanly. parser.buildTargetGraph( eventBus, primaryCell, false, MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()), ImmutableSet.of(namedTarget)); } @Test @Ignore public void allOutputsShouldBePlacedInTheSameRootOutputFolder() {} @Test public void circularCellReferencesAreAllowed() throws IOException { ProjectWorkspace mainRepo = TestDataHelper.createProjectWorkspaceForScenario(this, "inter-cell/circular", tmp); mainRepo.setUp(); Path primary = mainRepo.getPath("primary"); ProjectWorkspace.ProcessResult result = mainRepo.runBuckCommandWithEnvironmentOverridesAndContext( primary, Optional.empty(), ImmutableMap.of(), "build", "//:bin"); result.assertSuccess(); } @SuppressWarnings("PMD.EmptyCatchBlock") @Test public void shouldBeAbleToUseCommandLineConfigOverrides() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); TestDataHelper.overrideBuckconfig( secondary, ImmutableMap.of("cxx", ImmutableMap.of("cc", "/does/not/exist"))); try { primary.runBuckBuild("//:cxxbinary"); fail("Did not expect to finish building"); } catch (HumanReadableException expected) { assertEquals( expected.getMessage(), "Couldn't get dependency 'secondary//:cxxlib' of target '//:cxxbinary':\n" + "Overridden cxx:cc path not found: /does/not/exist"); } ProjectWorkspace.ProcessResult result = primary.runBuckBuild("--config", "secondary//cxx.cc=", "//:cxxbinary"); result.assertSuccess(); } @Test public void globalCommandLineConfigOverridesShouldWork() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); TestDataHelper.overrideBuckconfig( primary, ImmutableMap.of("cxx", ImmutableMap.of("cc", "/does/not/exist"))); TestDataHelper.overrideBuckconfig( secondary, ImmutableMap.of("cxx", ImmutableMap.of("cc", "/does/not/exist"))); try { primary.runBuckBuild("//:cxxbinary"); fail("Did not expect to finish building"); } catch (HumanReadableException expected) { assertEquals(expected.getMessage(), "Overridden cxx:cc path not found: /does/not/exist"); } ProjectWorkspace.ProcessResult result = primary.runBuckBuild("--config", "cxx.cc=", "//:cxxbinary"); result.assertSuccess(); } @Test public void buildFilesCanIncludeDefsFromOtherCells() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); ProjectWorkspace root = createWorkspace("inter-cell/include-defs/root"); ProjectWorkspace other = createWorkspace("inter-cell/include-defs/other"); registerCell(root, "other", other); registerCell(root, "root", root); registerCell(other, "root", root); root.runBuckBuild("//:rule", "other//:rule").assertSuccess(); } @Test public void shouldBeAbleToTestACxxLibrary() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); ProjectWorkspace workspace = createWorkspace("inter-cell/gtest/secondary"); TestDataHelper.overrideBuckconfig( workspace, ImmutableMap.of("cxx", ImmutableMap.of("gtest_dep", "//gtest:gtest"))); ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//test:cxxtest"); result.assertSuccess(); result = workspace.runBuckCommand("test", "//test:cxxtest"); result.assertSuccess(); } @Test public void shouldBeAbleToTestACxxLibraryXCell() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/gtest/primary", "inter-cell/gtest/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); TestDataHelper.overrideBuckconfig( secondary, ImmutableMap.of("cxx", ImmutableMap.of("gtest_dep", "//gtest:gtest"))); ProjectWorkspace.ProcessResult result = primary.runBuckBuild("secondary//test:cxxtest"); result.assertSuccess(); result = primary.runBuckCommand("test", "secondary//test:cxxtest"); result.assertSuccess(); result = primary.runBuckCommand("test", "//main:main"); result.assertSuccess(); } @Test public void shouldBeAbleToShareGtest() throws IOException { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/gtest/primary", "inter-cell/gtest/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); TestDataHelper.overrideBuckconfig( primary, ImmutableMap.of("cxx", ImmutableMap.of("gtest_dep", "secondary//gtest:gtest"))); // TODO(mzlee,dwh): secondary//gtest:gtest should be //gtest:gtest or we // should be able to use different cell names registerCell(secondary, "secondary", secondary); TestDataHelper.overrideBuckconfig( secondary, ImmutableMap.of("cxx", ImmutableMap.of("gtest_dep", "secondary//gtest:gtest"))); // TODO(mzlee,dwh): //test:cxxtest should be able to safely depend on // secondary//lib:cxxlib instead of having its own copy ProjectWorkspace.ProcessResult result = primary.runBuckCommand("test", "//test:cxxtest", "secondary//test:cxxtest"); result.assertSuccess(); } @Test public void childCellWithCellMappingNotInRootCellShouldThrowError() throws IOException { ProjectWorkspace root = createWorkspace("inter-cell/validation/root"); ProjectWorkspace second = createWorkspace("inter-cell/validation/root"); ProjectWorkspace third = createWorkspace("inter-cell/validation/root"); registerCell(root, "second", second); registerCell(second, "third", third); // should fail if "third" is not specified in root try { root.runBuckBuild("//:dummy"); fail("Should have thrown a HumanReadableException."); } catch (HumanReadableException e) { assertThat( e.getHumanReadableErrorMessage(), containsString("repositories.third must exist in the root cell's cell mappings.")); } // and succeeds when it is registerCell(root, "third", third); ProjectWorkspace.ProcessResult result = root.runBuckBuild("//:dummy"); result.assertSuccess(); } @Test public void childCellWithCellMappingThatDiffersFromRootCellShouldThrowError() throws IOException { ProjectWorkspace root = createWorkspace("inter-cell/validation/root"); ProjectWorkspace second = createWorkspace("inter-cell/validation/root"); ProjectWorkspace third = createWorkspace("inter-cell/validation/root"); registerCell(root, "second", second); registerCell(second, "third", third); // should fail if "third" is not mapped to third in the root. registerCell(root, "third", second); try { root.runBuckBuild("//:dummy"); fail("Should have thrown a HumanReadableException."); } catch (HumanReadableException e) { assertThat( e.getHumanReadableErrorMessage(), containsString( "repositories.third must point to the same directory as the root cell's cell " + "mapping:")); } // and succeeds when it is registerCell(root, "third", third); ProjectWorkspace.ProcessResult result = root.runBuckBuild("//:dummy"); result.assertSuccess(); } @Test public void testCrossCellAndroidLibrary() throws InterruptedException, IOException { AssumeAndroidPlatform.assumeSdkIsAvailable(); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/android/primary", "inter-cell/android/secondary"); ProjectWorkspace primary = cells.getFirst(); String target = "//apps/sample:app_with_cross_cell_android_lib"; ProjectWorkspace.ProcessResult result = primary.runBuckCommand("build", target); result.assertSuccess(); } @Test public void testCrossCellAndroidLibraryMerge() throws IOException, InterruptedException { AssumeAndroidPlatform.assumeSdkIsAvailable(); AssumeAndroidPlatform.assumeNdkIsAvailable(); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/android/primary", "inter-cell/android/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); TestDataHelper.overrideBuckconfig( primary, ImmutableMap.of("ndk", ImmutableMap.of("cpu_abis", "x86"))); TestDataHelper.overrideBuckconfig( secondary, ImmutableMap.of("ndk", ImmutableMap.of("cpu_abis", "x86"))); NdkCxxPlatform platform = AndroidNdkHelper.getNdkCxxPlatform(primary, primary.asCell().getFilesystem()); SourcePathResolver pathResolver = new SourcePathResolver( new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()))); Path tmpDir = tmp.newFolder("merging_tmp"); SymbolGetter syms = new SymbolGetter( new DefaultProcessExecutor(new TestConsole()), tmpDir, platform.getObjdump(), pathResolver); SymbolsAndDtNeeded info; Path apkPath = primary.buildAndReturnOutput("//apps/sample:app_with_merged_cross_cell_libs"); ZipInspector zipInspector = new ZipInspector(apkPath); zipInspector.assertFileDoesNotExist("lib/x86/lib1a.so"); zipInspector.assertFileDoesNotExist("lib/x86/lib1b.so"); zipInspector.assertFileDoesNotExist("lib/x86/lib1g.so"); zipInspector.assertFileDoesNotExist("lib/x86/lib1h.so"); info = syms.getSymbolsAndDtNeeded(apkPath, "lib/x86/lib1.so"); assertThat(info.symbols.global, Matchers.hasItem("A")); assertThat(info.symbols.global, Matchers.hasItem("B")); assertThat(info.symbols.global, Matchers.hasItem("G")); assertThat(info.symbols.global, Matchers.hasItem("H")); assertThat(info.symbols.global, Matchers.hasItem("glue_1")); assertThat(info.symbols.global, not(Matchers.hasItem("glue_2"))); assertThat(info.dtNeeded, not(Matchers.hasItem("libnative_merge_B.so"))); assertThat(info.dtNeeded, not(Matchers.hasItem("libmerge_G.so"))); assertThat(info.dtNeeded, not(Matchers.hasItem("libmerge_H.so"))); } @Test public void targetsReferencingSameTargetsWithDifferentCellNamesAreConsideredTheSame() throws Exception { // This test case builds a cxx binary rule with libraries that all depend on the same targets. // If these targets were treated as distinct targets, the rule will have duplicate symbols. assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/canonicalization/primary", "inter-cell/canonicalization/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); registerCell(primary, "primary", primary); registerCell(secondary, "primary", primary); Path output = primary.buildAndReturnOutput(":a.out"); assertEquals( "The produced binary should give the expected exit code", 111, primary.runCommand(output.toString()).getExitCode()); } @Test public void targetsInOtherCellsArePrintedAsRelativeToRootCell() throws Exception { assumeThat(Platform.detect(), is(not(WINDOWS))); Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/canonicalization/primary", "inter-cell/canonicalization/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); registerCell(primary, "primary", primary); registerCell(secondary, "primary", primary); String queryResult = primary.runBuckCommand("query", "deps(//:a.out)").assertSuccess().getStdout(); assertEquals( "Should refer to root cell targets without prefix and secondary cell targets with prefix", Joiner.on("\n").join("//:a.out", "//:rootlib", "secondary//:lib", "secondary//:lib2"), sortLines(queryResult)); queryResult = primary.runBuckCommand("query", "deps(secondary//:lib)").assertSuccess().getStdout(); assertEquals( "... even if query starts in a non-root cell.", Joiner.on("\n").join("//:rootlib", "secondary//:lib", "secondary//:lib2"), sortLines(queryResult)); } @Test public void testCrossCellCleanCommand() throws IOException, InterruptedException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/export-file/primary", "inter-cell/export-file/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); List<Path> primaryDirs = ImmutableList.of( primary.getPath(primary.getBuckPaths().getScratchDir()), primary.getPath(primary.getBuckPaths().getGenDir()), primary.getPath(primary.getBuckPaths().getTrashDir())); List<Path> secondaryDirs = ImmutableList.of( secondary.getPath(secondary.getBuckPaths().getScratchDir()), secondary.getPath(secondary.getBuckPaths().getGenDir()), secondary.getPath(secondary.getBuckPaths().getTrashDir())); // Set up the directories to be cleaned for (Path dir : primaryDirs) { Files.createDirectories(dir); assertTrue(Files.exists(dir)); } for (Path dir : secondaryDirs) { Files.createDirectories(dir); assertTrue(Files.exists(dir)); } primary.runBuckCommand("clean").assertSuccess(); for (Path dir : primaryDirs) { assertFalse(Files.exists(dir)); } for (Path dir : secondaryDirs) { assertFalse(Files.exists(dir)); } } @Test public void testParserFunctionsWithCells() throws IOException { Pair<ProjectWorkspace, ProjectWorkspace> cells = prepare("inter-cell/parser-functions/primary", "inter-cell/parser-functions/secondary"); ProjectWorkspace primary = cells.getFirst(); ProjectWorkspace secondary = cells.getSecond(); // Set up the remaining cells registerCell(primary, "primary", primary); registerCell(secondary, "primary", primary); registerCell(secondary, "secondary", secondary); String expected = primary.getFileContents("one/.txt"); Path path = primary.buildAndReturnOutput("//one:one"); String actual = new String(Files.readAllBytes(path), UTF_8); assertEquals(expected, actual); expected = secondary.getFileContents("two/secondary.txt"); path = primary.buildAndReturnOutput("//one:two"); actual = new String(Files.readAllBytes(path), UTF_8); assertEquals(expected, actual); expected = primary.getFileContents("one/primary.txt"); path = secondary.buildAndReturnOutput("//two:one"); actual = new String(Files.readAllBytes(path), UTF_8); assertEquals(expected, actual); expected = secondary.getFileContents("two/.txt"); path = secondary.buildAndReturnOutput("//two:two"); actual = new String(Files.readAllBytes(path), UTF_8); assertEquals(expected, actual); } private static String sortLines(String input) { return RichStream.from(Splitter.on('\n').trimResults().omitEmptyStrings().split(input)) .sorted() .collect(Collectors.joining("\n")); } private Pair<ProjectWorkspace, ProjectWorkspace> prepare(String primaryPath, String secondaryPath) throws IOException { ProjectWorkspace primary = createWorkspace(primaryPath); ProjectWorkspace secondary = createWorkspace(secondaryPath); registerCell(primary, "secondary", secondary); return new Pair<>(primary, secondary); } private ProjectWorkspace createWorkspace(String scenarioName) throws IOException { final Path tmpSubfolder = tmp.newFolder(); ProjectWorkspace projectWorkspace = TestDataHelper.createProjectWorkspaceForScenario(this, scenarioName, tmpSubfolder); projectWorkspace.setUp(); return projectWorkspace; } private void registerCell( ProjectWorkspace cellToModifyConfigOf, String cellName, ProjectWorkspace cellToRegisterAsCellName) throws IOException { TestDataHelper.overrideBuckconfig( cellToModifyConfigOf, ImmutableMap.of( "repositories", ImmutableMap.of( cellName, cellToRegisterAsCellName.getPath(".").normalize().toString()))); } }