/*
* 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.cxx;
import static com.facebook.buck.cxx.CxxFlavorSanitizer.sanitize;
import static java.io.File.pathSeparator;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.oneOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import com.facebook.buck.android.AssumeAndroidPlatform;
import com.facebook.buck.cli.FakeBuckConfig;
import com.facebook.buck.io.ExecutableFinder;
import com.facebook.buck.io.MoreFiles;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.BuildRuleStatus;
import com.facebook.buck.rules.BuildRuleSuccessType;
import com.facebook.buck.testutil.integration.BuckBuildLog;
import com.facebook.buck.testutil.integration.InferHelper;
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.util.environment.Platform;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class CxxBinaryIntegrationTest {
@Parameterized.Parameters(name = "sandbox_sources={0}")
public static Collection<Object[]> data() {
return ImmutableList.of(new Object[] {false}, new Object[] {true});
}
@Parameterized.Parameter(0)
public boolean sandboxSources;
@Rule public TemporaryPaths tmp = new TemporaryPaths();
@Test
public void testInferCxxBinaryDepsCaching() throws IOException, InterruptedException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_deps");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
for (BuildTarget buildTarget : buildLog.getAllTargets()) {
buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
}
/*
* Check that if the file in the binary target changes, then all the deps will be fetched
* from the cache
*/
String sourceName = "src_with_deps.c";
workspace.replaceFileContents("foo/" + sourceName, "10", "30");
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), inputBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget captureBuildTarget = cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);
// this is flavored, and denotes the analysis step (generates a local report)
BuildTarget inferAnalysisTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
// this is the flavored version of the top level target (the one give in input to buck)
BuildTarget inferReportTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
BuildTarget aggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
String bt;
for (BuildTarget buildTarget : buildLog.getAllTargets()) {
bt = buildTarget.toString();
if (buildTarget
.getFlavors()
.contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)
|| buildTarget.getFlavors().contains(CxxDescriptionEnhancer.HEADER_SYMLINK_TREE_FLAVOR)
|| buildTarget.getFlavors().contains(CxxDescriptionEnhancer.SANDBOX_TREE_FLAVOR)
|| bt.equals(inferAnalysisTarget.toString())
|| bt.equals(captureBuildTarget.toString())
|| bt.equals(inferReportTarget.toString())
|| bt.equals(aggregatedDepsTarget.toString())) {
buildLog.assertTargetBuiltLocally(bt);
} else {
buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
}
}
}
@Test
public void testInferCxxBinaryDepsInvalidateCacheWhenVersionChanges()
throws IOException, InterruptedException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_deps");
final String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
for (BuildTarget buildTarget : buildLog.getAllTargets()) {
buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
}
/*
* Check that if the version of infer changes, then all the infer-related targets are
* recomputed
*/
workspace.resetBuildLogFile();
workspace.replaceFileContents("fake-infer/fake-bin/infer", "0.12345", "9.9999");
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
String sourceName = "src_with_deps.c";
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), inputBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget topCaptureBuildTarget =
cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);
BuildTarget topInferAnalysisTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
BuildTarget topInferReportTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
BuildTarget depOneBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:dep_one");
String depOneSourceName = "dep_one.c";
CxxSourceRuleFactory depOneSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), depOneBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget depOneCaptureBuildTarget =
depOneSourceRuleFactory.createInferCaptureBuildTarget(depOneSourceName);
BuildTarget depOneInferAnalysisTarget =
depOneCaptureBuildTarget.withFlavors(
cxxPlatform.getFlavor(), CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
BuildTarget depTwoBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:dep_two");
CxxSourceRuleFactory depTwoSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), depTwoBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget depTwoCaptureBuildTarget =
depTwoSourceRuleFactory.createInferCaptureBuildTarget("dep_two.c");
BuildTarget depTwoInferAnalysisTarget =
depTwoCaptureBuildTarget.withFlavors(
cxxPlatform.getFlavor(), CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
ImmutableSet<String> locallyBuiltTargets =
ImmutableSet.of(
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget().toString(),
topCaptureBuildTarget.toString(),
topInferAnalysisTarget.toString(),
topInferReportTarget.toString(),
depOneSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget().toString(),
depOneCaptureBuildTarget.toString(),
depOneInferAnalysisTarget.toString(),
depTwoSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget().toString(),
depTwoCaptureBuildTarget.toString(),
depTwoInferAnalysisTarget.toString());
// check that infer-related targets are getting rebuilt
for (String t : locallyBuiltTargets) {
buildLog.assertTargetBuiltLocally(t);
}
Set<String> builtFromCacheTargets =
FluentIterable.from(buildLog.getAllTargets())
// Filter out header symlink tree rules, as they are always built locally.
.filter(
target ->
(!target
.getFlavors()
.contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)
&& !target
.getFlavors()
.contains(CxxDescriptionEnhancer.HEADER_SYMLINK_TREE_FLAVOR)
&& !target
.getFlavors()
.contains(CxxDescriptionEnhancer.SANDBOX_TREE_FLAVOR)))
.transform(Object::toString)
// Filter out any rules that are explicitly built locally.
.filter(Predicates.not(locallyBuiltTargets::contains))
.toSet();
// check that all the other targets are fetched from the cache
for (String t : builtFromCacheTargets) {
buildLog.assertTargetWasFetchedFromCache(t);
}
}
@Test
public void testInferCxxBinaryWithoutDeps() throws IOException, InterruptedException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:simple");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that all the required build targets have been generated.
*/
String sourceName = "simple.cpp";
String sourceFull = "foo/" + sourceName;
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), inputBuildTarget, cxxPlatform, cxxBuckConfig);
// this is unflavored, but bounded to the InferCapture build rule
BuildTarget captureBuildTarget = cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);
// this is unflavored, but necessary to run the compiler successfully
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
inputBuildTarget, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget sandboxTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
inputBuildTarget, cxxPlatform.getFlavor());
// this is flavored, and denotes the analysis step (generates a local report)
BuildTarget inferAnalysisTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
// this is flavored and corresponds to the top level target (the one give in input to buck)
BuildTarget inferReportTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
BuildTarget aggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
ImmutableSortedSet.Builder<BuildTarget> targetsBuilder =
ImmutableSortedSet.<BuildTarget>naturalOrder()
.add(
aggregatedDepsTarget,
headerSymlinkTreeTarget,
captureBuildTarget,
inferAnalysisTarget,
inferReportTarget);
if (sandboxSources) {
targetsBuilder.add(sandboxTreeTarget);
}
BuckBuildLog buildLog = workspace.getBuildLog();
assertThat(buildLog.getAllTargets(), containsInAnyOrder(targetsBuilder.build().toArray()));
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(captureBuildTarget.toString());
buildLog.assertTargetBuiltLocally(inferAnalysisTarget.toString());
buildLog.assertTargetBuiltLocally(inferReportTarget.toString());
/*
* Check that running a build again results in no builds since nothing has changed.
*/
workspace.resetBuildLogFile(); // clear for new build
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
assertEquals(ImmutableSet.of(inferReportTarget), buildLog.getAllTargets());
buildLog.assertTargetHadMatchingRuleKey(inferReportTarget.toString());
/*
* Check that changing the source file results in running the capture/analysis rules again.
*/
workspace.resetBuildLogFile();
workspace.replaceFileContents(sourceFull, "*s = 42;", "");
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
targetsBuilder =
ImmutableSortedSet.<BuildTarget>naturalOrder()
.add(aggregatedDepsTarget, captureBuildTarget, inferAnalysisTarget, inferReportTarget);
if (sandboxSources) {
targetsBuilder.add(headerSymlinkTreeTarget, sandboxTreeTarget);
}
assertEquals(buildLog.getAllTargets(), targetsBuilder.build());
buildLog.assertTargetBuiltLocally(captureBuildTarget.toString());
buildLog.assertTargetBuiltLocally(inferAnalysisTarget.toString());
if (sandboxSources) {
buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
buildLog.assertTargetHadMatchingInputRuleKey(sandboxTreeTarget.toString());
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
} else {
buildLog.assertTargetHadMatchingRuleKey(aggregatedDepsTarget.toString());
}
}
@Test
public void testInferCxxBinaryWithDeps() throws IOException, InterruptedException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:binary_with_deps");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that all the required build targets have been generated.
*/
String sourceName = "src_with_deps.c";
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), inputBuildTarget, cxxPlatform, cxxBuckConfig);
// 1. create the targets of binary_with_deps
// this is unflavored, but bounded to the InferCapture build rule
BuildTarget topCaptureBuildTarget =
cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);
BuildTarget topSandboxTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
inputBuildTarget, cxxPlatform.getFlavor());
// this is unflavored, but necessary to run the compiler successfully
BuildTarget topHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
inputBuildTarget, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
// this is flavored, and denotes the analysis step (generates a local report)
BuildTarget topInferAnalysisTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
// this is flavored and corresponds to the top level target (the one give in input to buck)
BuildTarget topInferReportTarget =
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
BuildTarget topAggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
// 2. create the targets of dep_one
BuildTarget depOneBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:dep_one");
String depOneSourceName = "dep_one.c";
String depOneSourceFull = "foo/" + depOneSourceName;
CxxSourceRuleFactory depOneSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), depOneBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget depOneCaptureBuildTarget =
depOneSourceRuleFactory.createInferCaptureBuildTarget(depOneSourceName);
BuildTarget depOneSandboxTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
depOneBuildTarget, cxxPlatform.getFlavor());
BuildTarget depOneHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depOneBuildTarget, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget depOneExportedHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depOneBuildTarget,
HeaderVisibility.PUBLIC,
CxxPlatformUtils.getHeaderModeForDefaultPlatform(tmp.getRoot()).getFlavor());
BuildTarget depOneInferAnalysisTarget =
depOneCaptureBuildTarget.withFlavors(
cxxPlatform.getFlavor(), CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
BuildTarget depOneAggregatedDepsTarget =
depOneSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
// 3. create the targets of dep_two
BuildTarget depTwoBuildTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:dep_two");
CxxSourceRuleFactory depTwoSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), depTwoBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget depTwoCaptureBuildTarget =
depTwoSourceRuleFactory.createInferCaptureBuildTarget("dep_two.c");
BuildTarget depTwoSandboxTreeTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(
depTwoBuildTarget, cxxPlatform.getFlavor());
BuildTarget depTwoHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depTwoBuildTarget, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget depTwoExportedHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depTwoBuildTarget,
HeaderVisibility.PUBLIC,
CxxPlatformUtils.getHeaderModeForDefaultPlatform(tmp.getRoot()).getFlavor());
BuildTarget depTwoInferAnalysisTarget =
depTwoCaptureBuildTarget.withFlavors(
cxxPlatform.getFlavor(), CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get());
BuildTarget depTwoAggregatedDepsTarget =
depTwoSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
ImmutableSet.Builder<BuildTarget> buildTargets =
ImmutableSortedSet.<BuildTarget>naturalOrder()
.add(
topAggregatedDepsTarget,
topCaptureBuildTarget,
topHeaderSymlinkTreeTarget,
topInferAnalysisTarget,
topInferReportTarget,
depOneAggregatedDepsTarget,
depOneCaptureBuildTarget,
depOneHeaderSymlinkTreeTarget,
depOneExportedHeaderSymlinkTreeTarget,
depOneInferAnalysisTarget,
depTwoAggregatedDepsTarget,
depTwoCaptureBuildTarget,
depTwoHeaderSymlinkTreeTarget,
depTwoExportedHeaderSymlinkTreeTarget,
depTwoInferAnalysisTarget);
if (sandboxSources) {
buildTargets.add(topSandboxTreeTarget, depOneSandboxTreeTarget, depTwoSandboxTreeTarget);
}
// Check all the targets are in the buildLog
assertEquals(
buildTargets.build(), ImmutableSet.copyOf(workspace.getBuildLog().getAllTargets()));
/*
* Check that running a build again results in no builds since nothing has changed.
*/
workspace.resetBuildLogFile(); // clear for new build
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
assertEquals(ImmutableSet.of(topInferReportTarget), buildLog.getAllTargets());
buildLog.assertTargetHadMatchingRuleKey(topInferReportTarget.toString());
/*
* Check that if a library source file changes then the capture/analysis rules run again on
* the main target and on dep_one only.
*/
workspace.resetBuildLogFile();
workspace.replaceFileContents(depOneSourceFull, "flag > 0", "flag < 0");
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
buildTargets =
ImmutableSortedSet.<BuildTarget>naturalOrder()
.add(
topInferAnalysisTarget, // analysis runs again
topInferReportTarget, // report runs again
topCaptureBuildTarget, // cached
depTwoInferAnalysisTarget, // cached
depOneAggregatedDepsTarget,
depOneCaptureBuildTarget, // capture of the changed file runs again
depOneInferAnalysisTarget // analysis of the library runs again
);
if (sandboxSources) {
buildTargets.add(
depOneSandboxTreeTarget,
depOneHeaderSymlinkTreeTarget,
depOneExportedHeaderSymlinkTreeTarget);
}
assertEquals(buildTargets.build(), buildLog.getAllTargets());
buildLog.assertTargetBuiltLocally(topInferAnalysisTarget.toString());
buildLog.assertTargetBuiltLocally(topInferReportTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(topCaptureBuildTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depTwoInferAnalysisTarget.toString());
buildLog.assertTargetBuiltLocally(depOneCaptureBuildTarget.toString());
buildLog.assertTargetBuiltLocally(depOneInferAnalysisTarget.toString());
if (sandboxSources) {
buildLog.assertTargetHadMatchingInputRuleKey(depOneSandboxTreeTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depOneHeaderSymlinkTreeTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depOneExportedHeaderSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(depOneAggregatedDepsTarget.toString());
} else {
buildLog.assertTargetHadMatchingRuleKey(depOneAggregatedDepsTarget.toString());
}
}
@Test
public void testInferCxxBinaryWithDepsEmitsAllTheDependenciesResultsDirs()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_chain_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
// Build the given target and check that it succeeds.
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
assertTrue(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"))));
String loggedDeps =
workspace.getFileContents(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"));
String sanitizedChainDepOne = sanitize("chain_dep_one.c.o");
String sanitizedTopChain = sanitize("top_chain.c.o");
String sanitizedChainDepTwo = sanitize("chain_dep_two.c.o");
BuildTarget analyzeTopChainTarget =
BuildTargetFactory.newInstance("//foo:binary_with_chain_deps#infer-analyze");
BuildTarget captureTopChainTarget =
BuildTargetFactory.newInstance(
"//foo:binary_with_chain_deps#default,infer-capture-" + sanitizedTopChain);
BuildTarget analyzeChainDepOneTarget =
BuildTargetFactory.newInstance("//foo:chain_dep_one#default,infer-analyze");
BuildTarget captureChainDepOneTarget =
BuildTargetFactory.newInstance(
"//foo:chain_dep_one#default,infer-capture-" + sanitizedChainDepOne);
BuildTarget analyzeChainDepTwoTarget =
BuildTargetFactory.newInstance("//foo:chain_dep_two#default,infer-analyze");
BuildTarget captureChainDepTwoTarget =
BuildTargetFactory.newInstance(
"//foo:chain_dep_two#default,infer-capture-" + sanitizedChainDepTwo);
Path basePath = filesystem.getRootPath().toRealPath();
String expectedOutput =
Joiner.on('\n')
.join(
ImmutableList.of(
analyzeTopChainTarget.getFullyQualifiedName()
+ "\t"
+ "[infer-analyze]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeTopChainTarget, "infer-analysis-%s")),
captureTopChainTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedTopChain
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, captureTopChainTarget, "infer-out-%s")),
analyzeChainDepOneTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-analyze]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeChainDepOneTarget, "infer-analysis-%s")),
captureChainDepOneTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedChainDepOne
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, captureChainDepOneTarget, "infer-out-%s")),
analyzeChainDepTwoTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-analyze]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeChainDepTwoTarget, "infer-analysis-%s")),
captureChainDepTwoTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedChainDepTwo
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(
filesystem, captureChainDepTwoTarget, "infer-out-%s"))));
assertEquals(expectedOutput + "\n", loggedDeps);
}
private static void registerCell(
ProjectWorkspace cellToModifyConfigOf,
String cellName,
ProjectWorkspace cellToRegisterAsCellName)
throws IOException {
TestDataHelper.overrideBuckconfig(
cellToModifyConfigOf,
ImmutableMap.of(
"repositories",
ImmutableMap.of(
cellName, cellToRegisterAsCellName.getPath(".").normalize().toString())));
}
@Test
public void inferShouldBeAbleToUseMultipleXCell() throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
final Path rootWorkspacePath = tmp.getRoot();
// create infertest workspace
ProjectWorkspace root = InferHelper.setupWorkspace(this, rootWorkspacePath, "infertest");
root.setupCxxSandboxing(sandboxSources);
// create infertest/inter-cell/multi-cell/primary sub-workspace as infer-configured one
Path primaryRootPath = tmp.newFolder().toRealPath().normalize();
ProjectWorkspace primary =
InferHelper.setupCxxInferWorkspace(
this,
primaryRootPath,
Optional.empty(),
"infertest/inter-cell/multi-cell/primary",
Optional.of(rootWorkspacePath.resolve("fake-infer")));
primary.setupCxxSandboxing(sandboxSources);
// create infertest/inter-cell/multi-cell/secondary sub-workspace
Path secondaryRootPath = tmp.newFolder().toRealPath().normalize();
ProjectWorkspace secondary =
InferHelper.setupWorkspace(
this, secondaryRootPath, "infertest/inter-cell/multi-cell/secondary");
secondary.setupCxxSandboxing(sandboxSources);
// register cells
registerCell(primary, "secondary", secondary);
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//:cxxbinary")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
// run from primary workspace
ProjectWorkspace.ProcessResult result =
primary.runBuckBuild(
InferHelper.getCxxCLIConfigurationArgs(
rootWorkspacePath.resolve("fake-infer"), Optional.empty(), inputBuildTarget));
result.assertSuccess();
ProjectFilesystem filesystem = new ProjectFilesystem(primary.getDestPath());
String reportPath =
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/report.json").toString();
List<Object> bugs = InferHelper.loadInferReport(primary, reportPath);
Assert.assertThat(
"2 bugs expected in " + reportPath + " not found", bugs.size(), Matchers.equalTo(2));
}
@Test
public void testInferCxxBinaryWithDiamondDepsEmitsAllBuildRulesInvolvedWhenCacheHit()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_diamond_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
String buildTargetName = inputBuildTarget.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", buildTargetName).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", buildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
ImmutableSet<BuildTarget> allInvolvedTargets = buildLog.getAllTargets();
assertEquals(1, allInvolvedTargets.size()); // Only main target should be fetched from cache
for (BuildTarget bt : allInvolvedTargets) {
buildLog.assertTargetWasFetchedFromCache(bt.toString());
}
assertTrue(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"))));
String loggedDeps =
workspace.getFileContents(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"));
BuildTarget analyzeMainTarget =
BuildTargetFactory.newInstance("//foo:binary_with_diamond_deps#infer-analyze");
BuildTarget analyzeDepOneTarget =
BuildTargetFactory.newInstance("//foo:diamond_dep_one#default,infer-analyze");
BuildTarget analyzeDepTwoTarget =
BuildTargetFactory.newInstance("//foo:diamond_dep_two#default,infer-analyze");
BuildTarget analyzeSimpleLibTarget =
BuildTargetFactory.newInstance("//foo:simple_lib#default,infer-analyze");
String sanitizedSimpleCpp = sanitize("simple.cpp.o");
String sanitizedDepOne = sanitize("dep_one.c.o");
String sanitizedDepTwo = sanitize("dep_two.c.o");
String sanitizedSrcWithDeps = sanitize("src_with_deps.c.o");
BuildTarget simpleCppTarget =
BuildTargetFactory.newInstance(
"//foo:simple_lib#default,infer-capture-" + sanitizedSimpleCpp);
BuildTarget depOneTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_one#default,infer-capture-" + sanitizedDepOne);
BuildTarget depTwoTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_two#default,infer-capture-" + sanitizedDepTwo);
BuildTarget srcWithDepsTarget =
BuildTargetFactory.newInstance(
"//foo:binary_with_diamond_deps#default,infer-capture-" + sanitizedSrcWithDeps);
Path basePath = filesystem.getRootPath().toRealPath();
String expectedOutput =
Joiner.on('\n')
.join(
ImmutableList.of(
InferLogLine.fromBuildTarget(
analyzeMainTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeMainTarget, "infer-analysis-%s")))
.toString(),
InferLogLine.fromBuildTarget(
srcWithDepsTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, srcWithDepsTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
analyzeDepOneTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeDepOneTarget, "infer-analysis-%s")))
.toString(),
InferLogLine.fromBuildTarget(
depOneTarget,
basePath.resolve(
BuildTargets.getGenPath(filesystem, depOneTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
analyzeDepTwoTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeDepTwoTarget, "infer-analysis-%s")))
.toString(),
InferLogLine.fromBuildTarget(
depTwoTarget,
basePath.resolve(
BuildTargets.getGenPath(filesystem, depTwoTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
analyzeSimpleLibTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, analyzeSimpleLibTarget, "infer-analysis-%s")))
.toString(),
InferLogLine.fromBuildTarget(
simpleCppTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, simpleCppTarget, "infer-out-%s")))
.toString()));
assertEquals(expectedOutput + "\n", loggedDeps);
}
@Test
public void testInferCaptureAllCxxBinaryWithDiamondDepsEmitsAllBuildRulesInvolvedWhenCacheHit()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_diamond_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get());
String buildTargetName = inputBuildTarget.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", buildTargetName).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", buildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
ImmutableSet<BuildTarget> allInvolvedTargets = buildLog.getAllTargets();
for (BuildTarget bt : allInvolvedTargets) {
buildLog.assertTargetWasFetchedFromCache(bt.toString());
}
assertTrue(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"))));
String loggedDeps =
workspace.getFileContents(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"));
String sanitizedSimpleCpp = sanitize("simple.cpp.o");
String sanitizedDepOne = sanitize("dep_one.c.o");
String sanitizedDepTwo = sanitize("dep_two.c.o");
String sanitizedSrcWithDeps = sanitize("src_with_deps.c.o");
BuildTarget simpleCppTarget =
BuildTargetFactory.newInstance(
"//foo:simple_lib#default,infer-capture-" + sanitizedSimpleCpp);
BuildTarget depOneTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_one#default,infer-capture-" + sanitizedDepOne);
BuildTarget depTwoTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_two#default,infer-capture-" + sanitizedDepTwo);
BuildTarget srcWithDepsTarget =
BuildTargetFactory.newInstance(
"//foo:binary_with_diamond_deps#default,infer-capture-" + sanitizedSrcWithDeps);
Path basePath = filesystem.getRootPath().toRealPath();
String expectedOutput =
Joiner.on('\n')
.join(
ImmutableList.of(
InferLogLine.fromBuildTarget(
srcWithDepsTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, srcWithDepsTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
depOneTarget,
basePath.resolve(
BuildTargets.getGenPath(filesystem, depOneTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
depTwoTarget,
basePath.resolve(
BuildTargets.getGenPath(filesystem, depTwoTarget, "infer-out-%s")))
.toString(),
InferLogLine.fromBuildTarget(
simpleCppTarget,
basePath.resolve(
BuildTargets.getGenPath(
filesystem, simpleCppTarget, "infer-out-%s")))
.toString()));
assertEquals(expectedOutput + "\n", loggedDeps);
}
@Test
public void testInferCxxBinaryWithDiamondDepsHasRuntimeDepsOfAllCaptureRulesWhenCacheHits()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_diamond_deps");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get())
.getFullyQualifiedName();
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
for (BuildTarget buildTarget : buildLog.getAllTargets()) {
buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
}
/*
* Check that runtime deps have been fetched from cache as well
*/
assertTrue(
"This file was expected to exist because it's declared as runtime dep",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:simple_lib#default,infer-capture-" + sanitize("simple.cpp.o")),
"infer-out-%s")
.resolve("captured/simple.cpp_captured/simple.cpp.cfg"))));
assertTrue(
"This file was expected to exist because it's declared as runtime dep",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:diamond_dep_one#default,infer-capture-"
+ sanitize("dep_one.c.o")),
"infer-out-%s")
.resolve("captured/dep_one.c_captured/dep_one.c.cfg"))));
assertTrue(
"This file was expected to exist because it's declared as runtime dep",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:diamond_dep_two#default,infer-capture-"
+ sanitize("dep_two.c.o")),
"infer-out-%s")
.resolve("captured/dep_two.c_captured/dep_two.c.cfg"))));
assertTrue(
"This file was expected to exist because it's declared as runtime dep",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:binary_with_diamond_deps#default,infer-capture-"
+ sanitize("src_with_deps.c.o")),
"infer-out-%s")
.resolve("captured/src_with_deps.c_captured/src_with_deps.c.cfg"))));
}
@Test
public void testInferCxxBinaryWithUnusedDepsDoesNotRebuildWhenUnusedHeaderChanges()
throws IOException, InterruptedException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_unused_header");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get())
.getFullyQualifiedName();
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), inputBuildTarget, cxxPlatform, cxxBuckConfig);
BuildTarget simpleOneCppCaptureTarget =
cxxSourceRuleFactory.createInferCaptureBuildTarget("simple_one.cpp");
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
/*
* Check that when the unused-header is changed, no builds are triggered
*/
workspace.resetBuildLogFile();
workspace.replaceFileContents("foo/unused_header.h", "int* input", "int* input, int* input2");
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
BuckBuildLog.BuildLogEntry simpleOnceCppCaptureTargetEntry =
buildLog.getLogEntry(simpleOneCppCaptureTarget);
assertThat(
simpleOnceCppCaptureTargetEntry.getSuccessType(),
Matchers.equalTo(Optional.of(BuildRuleSuccessType.FETCHED_FROM_CACHE_MANIFEST_BASED)));
/*
* Check that when the used-header is changed, then a build is triggered
*/
workspace.resetBuildLogFile();
workspace.replaceFileContents("foo/used_header.h", "int* input", "int* input, int* input2");
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
buildLog = workspace.getBuildLog();
buildLog.assertTargetBuiltLocally(simpleOneCppCaptureTarget.toString());
}
@Test
public void testInferCxxBinaryWithDiamondDepsEmitsAllTransitiveCaptureRulesOnce()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_diamond_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get());
// Build the given target and check that it succeeds.
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
assertTrue(
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"))));
String loggedDeps =
workspace.getFileContents(
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/infer-deps.txt"));
String sanitizedSimpleCpp = sanitize("simple.cpp.o");
String sanitizedDepOne = sanitize("dep_one.c.o");
String sanitizedDepTwo = sanitize("dep_two.c.o");
String sanitizedSrcWithDeps = sanitize("src_with_deps.c.o");
BuildTarget simpleCppTarget =
BuildTargetFactory.newInstance(
"//foo:simple_lib#default,infer-capture-" + sanitizedSimpleCpp);
BuildTarget depOneTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_one#default,infer-capture-" + sanitizedDepOne);
BuildTarget depTwoTarget =
BuildTargetFactory.newInstance(
"//foo:diamond_dep_two#default,infer-capture-" + sanitizedDepTwo);
BuildTarget srcWithDepsTarget =
BuildTargetFactory.newInstance(
"//foo:binary_with_diamond_deps#default,infer-capture-" + sanitizedSrcWithDeps);
Path basePath = filesystem.getRootPath().toRealPath();
String expectedOutput =
Joiner.on('\n')
.join(
ImmutableList.of(
srcWithDepsTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedSrcWithDeps
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(filesystem, srcWithDepsTarget, "infer-out-%s")),
depOneTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedDepOne
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(filesystem, depOneTarget, "infer-out-%s")),
depTwoTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedDepTwo
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(filesystem, depTwoTarget, "infer-out-%s")),
simpleCppTarget.getFullyQualifiedName()
+ "\t"
+ "[default, infer-capture-"
+ sanitizedSimpleCpp
+ "]\t"
+ basePath.resolve(
BuildTargets.getGenPath(filesystem, simpleCppTarget, "infer-out-%s"))));
assertEquals(expectedOutput + "\n", loggedDeps);
}
@Test
public void testInferCxxBinarySkipsBlacklistedFiles() throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace =
InferHelper.setupCxxInferWorkspace(this, tmp, Optional.of(".*one\\.c"));
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_chain_deps");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
// Build the given target and check that it succeeds.
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
// Check that the cfg associated with chain_dep_one.c does not exist
assertFalse(
"Cfg file for chain_dep_one.c should not exist",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:chain_dep_one#default,infer-capture-"
+ sanitize("chain_dep_one.c.o")),
"infer-out-%s")
.resolve("captured/chain_dep_one.c_captured/chain_dep_one.c.cfg"))));
// Check that the remaining files still have their cfgs
assertTrue(
"Expected cfg for chain_dep_two.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:chain_dep_two#default,infer-capture-"
+ sanitize("chain_dep_two.c.o")),
"infer-out-%s")
.resolve("captured/chain_dep_two.c_captured/chain_dep_two.c.cfg"))));
assertTrue(
"Expected cfg for top_chain.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:binary_with_chain_deps#infer-analyze"),
"infer-analysis-%s")
.resolve("captured/top_chain.c_captured/top_chain.c.cfg"))));
}
@Test
public void testInferCxxBinaryRunsOnAllFilesWhenBlacklistIsNotSpecified()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_chain_deps");
String inputBuildTargetName =
inputBuildTarget
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get())
.getFullyQualifiedName();
// Build the given target and check that it succeeds.
workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
// Check that all cfgs have been created
assertTrue(
"Expected cfg for chain_dep_one.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:chain_dep_one#default,infer-capture-"
+ sanitize("chain_dep_one.c.o")),
"infer-out-%s")
.resolve("captured/chain_dep_one.c_captured/chain_dep_one.c.cfg"))));
assertTrue(
"Expected cfg for chain_dep_two.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
"//foo:chain_dep_two#default,infer-capture-"
+ sanitize("chain_dep_two.c.o")),
"infer-out-%s")
.resolve("captured/chain_dep_two.c_captured/chain_dep_two.c.cfg"))));
assertTrue(
"Expected cfg for top_chain.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance(
filesystem.getRootPath(), "//foo:binary_with_chain_deps#infer-analyze"),
"infer-analysis-%s")
.resolve("captured/top_chain.c_captured/top_chain.c.cfg"))));
}
@Test
public void testInferCxxBinaryWithCachedDepsGetsAllItsTransitiveDeps()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_chain_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
for (BuildTarget buildTarget : buildLog.getAllTargets()) {
buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
}
/*
* Check that if the file in the top target changes, then all the transitive deps will be
* fetched from the cache (even those that are not direct dependencies).
* Make sure there's the specs file of the dependency that has distance 2 from
* the binary target.
*/
String sourceName = "top_chain.c";
workspace.replaceFileContents("foo/" + sourceName, "*p += 1", "*p += 10");
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
// Check all the buildrules were fetched from the cache (and there's the specs file)
assertTrue(
"Expected specs file for func_ret_null() in chain_dep_two.c not found",
Files.exists(
workspace.getPath(
BuildTargets.getGenPath(
filesystem,
BuildTargetFactory.newInstance("//foo:chain_dep_two#default,infer-analyze"),
"infer-analysis-%s/specs/mockedSpec.specs"))));
}
@Test
public void testInferCxxBinaryMergesAllReportsOfDependencies()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_chain_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
/*
* Build the given target and check that it succeeds.
*/
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
String reportPath =
BuildTargets.getGenPath(filesystem, inputBuildTarget, "infer-%s/report.json").toString();
List<Object> bugs = InferHelper.loadInferReport(workspace, reportPath);
// check that the merge step has merged a total of 3 bugs, one for each target
// (chain_dep_two, chain_dep_one, binary_with_chain_deps)
Assert.assertThat(
"3 bugs expected in " + reportPath + " not found", bugs.size(), Matchers.equalTo(3));
}
@Test
public void testInferCxxBinaryWritesSpecsListFilesOfTransitiveDependencies()
throws InterruptedException, IOException {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp, Optional.empty());
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget inputBuildTarget =
BuildTargetFactory.newInstance("//foo:binary_with_chain_deps")
.withFlavors(CxxInferEnhancer.InferFlavors.INFER.get());
//Build the given target and check that it succeeds.
workspace.runBuckCommand("build", inputBuildTarget.getFullyQualifiedName()).assertSuccess();
String specsPathList =
BuildTargets.getGenPath(
filesystem,
inputBuildTarget.withFlavors(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get()),
"infer-analysis-%s/specs_path_list.txt")
.toString();
String out = workspace.getFileContents(specsPathList);
ImmutableList<Path> paths =
FluentIterable.from(out.split("\n")).transform(input -> new File(input).toPath()).toList();
assertSame("There must be 2 paths in total", paths.size(), 2);
for (Path path : paths) {
assertTrue("Path must be absolute", path.isAbsolute());
assertTrue("Path must exist", Files.exists(path));
}
}
@Test
public void testChangingCompilerPathForcesRebuild() throws Exception {
assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget target = BuildTargetFactory.newInstance("//foo:simple");
BuildTarget linkTarget = CxxDescriptionEnhancer.createCxxLinkTarget(target, Optional.empty());
// Get the real location of the compiler executable.
String executable = Platform.detect() == Platform.MACOS ? "clang++" : "g++";
Path executableLocation =
new ExecutableFinder()
.getOptionalExecutable(Paths.get(executable), ImmutableMap.copyOf(System.getenv()))
.orElse(Paths.get("/usr/bin", executable));
// Write script as faux clang++/g++ binary
Path firstCompilerPath = tmp.newFolder("path1");
Path firstCompiler = firstCompilerPath.resolve(executable);
filesystem.writeContentsToPath(
"#!/bin/sh\n" + "exec " + executableLocation.toString() + " \"$@\"\n", firstCompiler);
// Write script as slightly different faux clang++/g++ binary
Path secondCompilerPath = tmp.newFolder("path2");
Path secondCompiler = secondCompilerPath.resolve(executable);
filesystem.writeContentsToPath(
"#!/bin/sh\n"
+ "exec "
+ executableLocation.toString()
+ " \"$@\"\n"
+ "# Comment to make hash different.\n",
secondCompiler);
// Make the second faux clang++/g++ binary executable
MoreFiles.makeExecutable(secondCompiler);
// Run two builds, each with different compiler "binaries". In the first
// instance, both binaries are in the PATH but the first binary is not
// marked executable so is not picked up.
workspace
.runBuckCommandWithEnvironmentOverridesAndContext(
workspace.getDestPath(),
Optional.empty(),
ImmutableMap.of(
"PATH",
firstCompilerPath.toString()
+ pathSeparator
+ secondCompilerPath.toString()
+ pathSeparator
+ System.getenv("PATH")),
"build",
target.getFullyQualifiedName())
.assertSuccess();
workspace.resetBuildLogFile();
// Now, make the first faux clang++/g++ binary executable. In this second
// instance, both binaries are still in the PATH but the first binary is
// now marked executable and so is picked up; causing a rebuild.
MoreFiles.makeExecutable(firstCompiler);
workspace
.runBuckCommandWithEnvironmentOverridesAndContext(
workspace.getDestPath(),
Optional.empty(),
ImmutableMap.of(
"PATH",
firstCompilerPath.toString()
+ pathSeparator
+ secondCompilerPath.toString()
+ pathSeparator
+ System.getenv("PATH")),
"build",
target.getFullyQualifiedName())
.assertSuccess();
// Make sure the binary change caused a rebuild.
workspace.getBuildLog().assertTargetBuiltLocally(linkTarget.toString());
}
@Test
public void testLinkMapIsCached() throws Exception {
// Currently we only support Apple platforms for generating link maps.
assumeTrue(Platform.detect() == Platform.MACOS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
BuildTarget target = BuildTargetFactory.newInstance("//foo:simple");
workspace.runBuckCommand("build", target.getFullyQualifiedName()).assertSuccess();
Path outputPath = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s"));
/*
* Check that building after clean will use the cache
*/
workspace.runBuckCommand("clean").assertSuccess();
workspace.runBuckCommand("build", target.toString()).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
buildLog.assertTargetWasFetchedFromCache(target.toString());
assertThat(Files.exists(Paths.get(outputPath.toString() + "-LinkMap.txt")), is(true));
}
@Test
public void testSimpleCxxBinaryBuilds() throws Exception {
Assume.assumeFalse("Test should be modified for sandboxing", sandboxSources);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget target = BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:simple");
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform, cxxBuckConfig);
BuildTarget binaryTarget =
CxxDescriptionEnhancer.createCxxLinkTarget(target, Optional.<LinkerMapMode>empty());
String sourceName = "simple.cpp";
String sourceFull = "foo/" + sourceName;
BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName);
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget aggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
// Do a clean build, verify that it succeeds, and check that all expected targets built
// successfully.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
assertEquals(
ImmutableSet.<BuildTarget>builder()
.add(aggregatedDepsTarget, headerSymlinkTreeTarget, compileTarget, binaryTarget, target)
.build(),
buildLog.getAllTargets());
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
buildLog.assertTargetBuiltLocally(binaryTarget.toString());
buildLog.assertTargetBuiltLocally(target.toString());
// Clear for new build.
workspace.resetBuildLogFile();
// Check that running a build again results in no builds since everything is up to
// date.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
buildLog = workspace.getBuildLog();
assertEquals(ImmutableSet.of(target, binaryTarget), buildLog.getAllTargets());
buildLog.assertTargetHadMatchingRuleKey(binaryTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(target.toString());
// Clear for new build.
workspace.resetBuildLogFile();
// Update the source file.
workspace.replaceFileContents(sourceFull, "{}", "{ return 0; }");
// Check that running a build again makes the source get recompiled and the binary
// re-linked, but does not cause the header rules to re-run.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
buildLog = workspace.getBuildLog();
assertEquals(
ImmutableSet.<BuildTarget>builder()
.add(aggregatedDepsTarget, compileTarget, binaryTarget, target)
.build(),
buildLog.getAllTargets());
buildLog.assertTargetHadMatchingRuleKey(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
assertThat(
buildLog.getLogEntry(binaryTarget).getSuccessType().get(),
Matchers.not(Matchers.equalTo(BuildRuleSuccessType.MATCHING_RULE_KEY)));
// Clear for new build.
workspace.resetBuildLogFile();
// Update the source file.
workspace.replaceFileContents(sourceFull, "{ return 0; }", "won't compile");
// Check that running a build again makes the source get recompiled and the binary
// re-linked, but does not cause the header rules to re-run.
workspace.runBuckCommand("build", target.toString()).assertFailure();
buildLog = workspace.getBuildLog();
assertEquals(
ImmutableSet.<BuildTarget>builder()
.add(aggregatedDepsTarget, compileTarget, binaryTarget, target)
.build(),
buildLog.getAllTargets());
buildLog.assertTargetHadMatchingRuleKey(aggregatedDepsTarget.toString());
assertThat(
buildLog.getLogEntry(binaryTarget).getStatus(), Matchers.equalTo(BuildRuleStatus.CANCELED));
assertThat(
buildLog.getLogEntry(target).getStatus(), Matchers.equalTo(BuildRuleStatus.CANCELED));
}
@Test
public void testSimpleCxxBinaryWithoutHeader() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckCommand("build", "//foo:simple_without_header").assertFailure();
}
@Test
public void testSimpleCxxBinaryWithHeader() throws IOException, InterruptedException {
Assume.assumeFalse("Test should be modified for sandboxing", sandboxSources);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget target =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:simple_with_header");
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform, cxxBuckConfig);
BuildTarget binaryTarget =
CxxDescriptionEnhancer.createCxxLinkTarget(target, Optional.<LinkerMapMode>empty());
String sourceName = "simple_with_header.cpp";
String headerName = "simple_with_header.h";
String headerFull = "foo/" + headerName;
BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName);
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget aggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
// Do a clean build, verify that it succeeds, and check that all expected targets built
// successfully.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
assertEquals(
ImmutableSet.of(
aggregatedDepsTarget, headerSymlinkTreeTarget, compileTarget, binaryTarget, target),
buildLog.getAllTargets());
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
buildLog.assertTargetBuiltLocally(binaryTarget.toString());
buildLog.assertTargetBuiltLocally(target.toString());
// Clear for new build.
workspace.resetBuildLogFile();
// Update the source file.
workspace.replaceFileContents(headerFull, "blah = 5", "blah = 6");
// Check that running a build again makes the source get recompiled and the binary
// re-linked, but does not cause the header rules to re-run.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
buildLog = workspace.getBuildLog();
assertEquals(
ImmutableSet.of(
headerSymlinkTreeTarget, aggregatedDepsTarget, compileTarget, binaryTarget, target),
buildLog.getAllTargets());
buildLog.assertTargetHadMatchingInputRuleKey(headerSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
assertThat(
buildLog.getLogEntry(binaryTarget).getSuccessType().get(),
Matchers.not(Matchers.equalTo(BuildRuleSuccessType.MATCHING_RULE_KEY)));
}
@Test
public void testSimpleCxxBinaryMissingDependencyOnCxxLibraryWithHeader() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckCommand("build", "//foo:binary_without_dep").assertFailure();
}
@Test
public void testSimpleCxxBinaryWithDependencyOnCxxLibraryWithHeader()
throws IOException, InterruptedException {
Assume.assumeFalse("Test should be modified for sandboxing", sandboxSources);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
// Setup variables pointing to the sources and targets of the top-level binary rule.
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(workspace.asCell().getBuckConfig());
CxxPlatform cxxPlatform = CxxPlatformUtils.build(cxxBuckConfig);
BuildTarget target =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:binary_with_dep");
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform, cxxBuckConfig);
BuildTarget binaryTarget =
CxxDescriptionEnhancer.createCxxLinkTarget(target, Optional.<LinkerMapMode>empty());
String sourceName = "foo.cpp";
BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName);
BuildTarget headerSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget aggregatedDepsTarget =
cxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
// Setup variables pointing to the sources and targets of the library dep.
BuildTarget depTarget =
BuildTargetFactory.newInstance(workspace.getDestPath(), "//foo:library_with_header");
CxxSourceRuleFactory depCxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(
workspace.getDestPath(), depTarget, cxxPlatform, cxxBuckConfig);
String depSourceName = "bar.cpp";
String depSourceFull = "foo/" + depSourceName;
String depHeaderName = "bar.h";
String depHeaderFull = "foo/" + depHeaderName;
BuildTarget depCompileTarget = depCxxSourceRuleFactory.createCompileBuildTarget(depSourceName);
BuildTarget depHeaderSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depTarget, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor());
BuildTarget depHeaderExportedSymlinkTreeTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
depTarget,
HeaderVisibility.PUBLIC,
CxxPlatformUtils.getHeaderModeForDefaultPlatform(tmp.getRoot()).getFlavor());
BuildTarget depSandboxTarget =
CxxDescriptionEnhancer.createSandboxSymlinkTreeTarget(depTarget, cxxPlatform.getFlavor());
BuildTarget depArchiveTarget =
CxxDescriptionEnhancer.createStaticLibraryBuildTarget(
depTarget, cxxPlatform.getFlavor(), CxxSourceRuleFactory.PicType.PDC);
BuildTarget depAggregatedDepsTarget =
depCxxSourceRuleFactory.createAggregatedPreprocessDepsBuildTarget();
ImmutableList.Builder<BuildTarget> builder = ImmutableList.builder();
builder.add(
depAggregatedDepsTarget,
depHeaderSymlinkTreeTarget,
depHeaderExportedSymlinkTreeTarget,
depCompileTarget,
depArchiveTarget,
depTarget,
aggregatedDepsTarget,
headerSymlinkTreeTarget,
compileTarget,
binaryTarget,
target);
if (cxxBuckConfig.sandboxSources()) {
builder.add(depSandboxTarget);
}
// Do a clean build, verify that it succeeds, and check that all expected targets built
// successfully.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
assertThat(
buildLog.getAllTargets(),
containsInAnyOrder(builder.build().toArray(new BuildTarget[] {})));
buildLog.assertTargetBuiltLocally(depHeaderSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(depCompileTarget.toString());
buildLog.assertTargetBuiltLocally(depArchiveTarget.toString());
buildLog.assertTargetBuiltLocally(depTarget.toString());
buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
buildLog.assertTargetBuiltLocally(binaryTarget.toString());
buildLog.assertTargetBuiltLocally(target.toString());
// Clear for new build.
workspace.resetBuildLogFile();
// Update the source file.
workspace.replaceFileContents(depHeaderFull, "int x", "int y");
// Check that running a build again makes the source get recompiled and the binary
// re-linked, but does not cause the header rules to re-run.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
buildLog = workspace.getBuildLog();
builder = ImmutableList.builder();
builder.add(
depAggregatedDepsTarget,
depCompileTarget,
depArchiveTarget,
depTarget,
depHeaderSymlinkTreeTarget,
depHeaderExportedSymlinkTreeTarget,
headerSymlinkTreeTarget,
aggregatedDepsTarget,
compileTarget,
binaryTarget,
target);
if (cxxBuckConfig.sandboxSources()) {
builder.add(depSandboxTarget);
}
assertThat(
buildLog.getAllTargets(),
containsInAnyOrder(builder.build().toArray(new BuildTarget[] {})));
buildLog.assertTargetBuiltLocally(depAggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(depCompileTarget.toString());
buildLog.assertTargetHadMatchingInputRuleKey(depArchiveTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depHeaderSymlinkTreeTarget.toString());
buildLog.assertTargetHadMatchingInputRuleKey(depHeaderExportedSymlinkTreeTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depTarget.toString());
buildLog.assertTargetBuiltLocally(aggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(compileTarget.toString());
assertThat(
buildLog.getLogEntry(binaryTarget).getSuccessType().get(),
Matchers.not(Matchers.equalTo(BuildRuleSuccessType.MATCHING_RULE_KEY)));
// Clear for new build.
workspace.resetBuildLogFile();
// Update the source file.
workspace.replaceFileContents(depSourceFull, "x + 5", "x + 6");
// Check that running a build again makes the source get recompiled and the binary
// re-linked, but does not cause the header rules to re-run.
workspace.runBuckCommand("build", target.toString()).assertSuccess();
buildLog = workspace.getBuildLog();
builder = ImmutableList.builder();
builder.add(
depAggregatedDepsTarget,
depCompileTarget,
depArchiveTarget,
depTarget,
compileTarget,
binaryTarget,
target);
if (cxxBuckConfig.sandboxSources()) {
builder.add(depSandboxTarget);
}
assertThat(
buildLog.getAllTargets(),
containsInAnyOrder(builder.build().toArray(new BuildTarget[] {})));
buildLog.assertTargetHadMatchingRuleKey(depAggregatedDepsTarget.toString());
buildLog.assertTargetBuiltLocally(depCompileTarget.toString());
buildLog.assertTargetBuiltLocally(depArchiveTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(depTarget.toString());
buildLog.assertTargetHadMatchingRuleKey(compileTarget.toString());
buildLog.assertTargetBuiltLocally(binaryTarget.toString());
}
@Test
public void testCxxBinaryDepfileBuildWithChangedHeader() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "cxx_binary_depfile_build_with_changed_header", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:bin");
result.assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
buildLog.assertTargetBuiltLocally("//:bin#binary");
buildLog.assertTargetBuiltLocally("//:bin#compile-" + sanitize("bin.c.o") + ",default");
buildLog.assertTargetBuiltLocally("//:lib1#default,static");
workspace.resetBuildLogFile();
workspace.replaceFileContents("lib2.h", "hello", "world");
result = workspace.runBuckCommand("build", "//:bin");
result.assertSuccess();
buildLog = workspace.getBuildLog();
buildLog.assertTargetBuiltLocally("//:bin#binary");
buildLog.assertTargetHadMatchingDepfileRuleKey(
"//:bin#compile-" + sanitize("bin.c.o") + ",default");
buildLog.assertTargetBuiltLocally("//:lib1#default,static");
}
@Test
public void testCxxBinaryDepfileBuildWithAddedHeader() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "cxx_binary_depfile_build_with_added_header", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", "//:bin");
result.assertSuccess();
BuckBuildLog buildLog = workspace.getBuildLog();
buildLog.assertTargetBuiltLocally("//:bin#binary");
buildLog.assertTargetBuiltLocally("//:bin#compile-" + sanitize("bin.c.o") + ",default");
buildLog.assertTargetBuiltLocally("//:lib1#default,static");
workspace.resetBuildLogFile();
workspace.replaceFileContents("BUCK", "['lib1.h']", "['lib1.h', 'lib2.h']");
result = workspace.runBuckCommand("build", "//:bin");
result.assertSuccess();
buildLog = workspace.getBuildLog();
buildLog.assertTargetHadMatchingInputRuleKey("//:bin#binary");
buildLog.assertTargetHadMatchingDepfileRuleKey(
"//:bin#compile-" + sanitize("bin.c.o") + ",default");
buildLog.assertTargetHadMatchingInputRuleKey("//:lib1#default,static");
}
@Test
public void testCxxBinaryWithGeneratedSourceAndHeader() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckCommand("build", "//foo:binary_without_dep").assertFailure();
}
@Test
public void testHeaderNamespace() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "header_namespace", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckCommand("build", "//:test").assertSuccess();
}
@Test
public void resolveHeadersBehindSymlinkTreesInError() throws InterruptedException, IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "resolved", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
workspace.writeContentsToPath("#invalid_pragma", "lib2.h");
BuildTarget target = BuildTargetFactory.newInstance("//:bin");
ProjectWorkspace.ProcessResult result = workspace.runBuckCommand("build", target.toString());
result.assertFailure();
// Verify that the preprocessed source contains no references to the symlink tree used to
// setup the headers.
String error = result.getStderr();
assertThat(
error,
Matchers.not(
Matchers.containsString(filesystem.getBuckPaths().getScratchDir().toString())));
assertThat(
error,
Matchers.not(Matchers.containsString(filesystem.getBuckPaths().getGenDir().toString())));
assertThat(error, Matchers.containsString("In file included from lib1.h:1"));
assertThat(error, Matchers.containsString("from bin.h:1"));
assertThat(error, Matchers.containsString("from bin.cpp:1:"));
assertThat(error, Matchers.containsString("lib2.h:1:2: error: invalid preprocessing"));
}
@Test
public void ndkCxxPlatforms() throws InterruptedException, IOException {
AssumeAndroidPlatform.assumeNdkIsAvailable();
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.writeContentsToPath(
"[ndk]\n"
+ " gcc_version = 4.9\n"
+ " cpu_abis = arm, armv7, arm64, x86\n"
+ " app_platform = android-21\n",
".buckconfig");
workspace.runBuckCommand("build", "//foo:simple#android-arm").assertSuccess();
workspace.runBuckCommand("build", "//foo:simple#android-armv7").assertSuccess();
workspace.runBuckCommand("build", "//foo:simple#android-arm64").assertSuccess();
workspace.runBuckCommand("build", "//foo:simple#android-x86").assertSuccess();
}
@Test
public void linkerFlags() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "linker_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckBuild("//:binary_with_linker_flag").assertFailure("--bad-flag");
workspace.runBuckBuild("//:binary_with_library_dep").assertSuccess();
workspace.runBuckBuild("//:binary_with_exported_flags_library_dep").assertFailure("--bad-flag");
workspace.runBuckBuild("//:binary_with_prebuilt_library_dep").assertFailure("--bad-flag");
// Build binary that has unresolved symbols. Normally this would fail, but should work
// with the proper linker flag.
switch (Platform.detect()) {
case MACOS:
workspace.runBuckBuild("//:binary_with_unresolved_symbols_macos").assertSuccess();
break;
case LINUX:
workspace.runBuckBuild("//:binary_with_unresolved_symbols_linux").assertSuccess();
break;
// $CASES-OMITTED$
default:
break;
}
}
private void platformLinkerFlags(ProjectWorkspace workspace, String target) throws IOException {
workspace.runBuckBuild("//:binary_matches_default_exactly_" + target).assertSuccess();
workspace.runBuckBuild("//:binary_matches_default_" + target).assertSuccess();
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary_no_match_" + target);
result.assertFailure();
assertThat(result.getStderr(), Matchers.containsString("reference"));
workspace.runBuckBuild("//:binary_with_library_matches_default_" + target).assertSuccess();
workspace
.runBuckBuild("//:binary_with_prebuilt_library_matches_default_" + target)
.assertSuccess();
}
@Test
public void platformLinkerFlags() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "platform_linker_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
// Build binary that has unresolved symbols. Normally this would fail, but should work
// with the proper linker flag.
switch (Platform.detect()) {
case MACOS:
platformLinkerFlags(workspace, "macos");
break;
case LINUX:
platformLinkerFlags(workspace, "linux");
break;
// $CASES-OMITTED$
default:
break;
}
}
@Test
public void perFileFlagsUsedForPreprocessing() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "preprocessing_per_file_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:bin");
result.assertSuccess();
}
@Test
public void correctPerFileFlagsUsedForCompilation() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "compiling_per_file_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:working-bin");
result.assertSuccess();
}
@Test
public void incorrectPerFileFlagsUsedForCompilation() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "compiling_per_file_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:broken-bin");
result.assertFailure();
}
@Test
public void platformPreprocessorFlags() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "platform_preprocessor_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckBuild("//:binary_matches_default_exactly").assertSuccess();
workspace.runBuckBuild("//:binary_matches_default").assertSuccess();
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary_no_match");
result.assertFailure();
assertThat(result.getStderr(), Matchers.containsString("#error"));
workspace.runBuckBuild("//:binary_with_library_matches_default").assertSuccess();
}
@Test
public void platformCompilerFlags() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "platform_compiler_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.writeContentsToPath("[cxx]\n cxxflags = -Wall -Werror", ".buckconfig");
workspace.runBuckBuild("//:binary_matches_default_exactly").assertSuccess();
workspace.runBuckBuild("//:binary_matches_default").assertSuccess();
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary_no_match");
result.assertFailure();
assertThat(
result.getStderr(),
Matchers.allOf(Matchers.containsString("non-void"), Matchers.containsString("function")));
workspace.runBuckBuild("//:binary_with_library_matches_default").assertSuccess();
}
@Test
public void platformHeaders() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "platform_headers", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.writeContentsToPath("[cxx]\n cxxflags = -Wall -Werror", ".buckconfig");
workspace.runBuckBuild("//:binary_matches_default_exactly").assertSuccess();
workspace.runBuckBuild("//:binary_matches_default").assertSuccess();
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary_no_match");
result.assertFailure();
assertThat(result.getStderr(), Matchers.containsString("header.hpp"));
workspace.runBuckBuild("//:binary_with_library_matches_default").assertSuccess();
}
@Test
public void platformSources() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "platform_sources", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.writeContentsToPath("[cxx]\n cxxflags = -Wall -Werror", ".buckconfig");
workspace.runBuckBuild("//:binary_matches_default_exactly").assertSuccess();
workspace.runBuckBuild("//:binary_matches_default").assertSuccess();
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary_no_match");
result.assertFailure();
assertThat(result.getStderr(), Matchers.containsString("answer()"));
workspace.runBuckBuild("//:binary_with_library_matches_default").assertSuccess();
}
@Test
public void buildABinaryIfACxxLibraryDepOnlyDeclaresHeaders() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "cxx_binary_headers_only", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary");
result.assertSuccess();
}
@Test
public void buildABinaryIfACxxBinaryTransitivelyDepOnlyDeclaresHeaders() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "cxx_binary_headers_only", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:transitive");
System.out.println(result.getStdout());
System.err.println(result.getStderr());
result.assertSuccess();
}
@Test
public void buildBinaryWithSharedDependencies() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "shared_library", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary");
result.assertSuccess();
}
@Test
public void buildBinaryWithPerFileFlags() throws IOException {
assumeThat(Platform.detect(), is(Platform.MACOS));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "per_file_flags", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("//:binary");
result.assertSuccess();
}
@Test
public void runBinaryUsingSharedLinkStyle() throws IOException {
assumeThat(Platform.detect(), oneOf(Platform.LINUX, Platform.MACOS));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "shared_link_style", tmp);
workspace.setUp();
workspace.runBuckCommand("run", "//:bar").assertSuccess();
}
@Test
public void genruleUsingBinaryUsingSharedLinkStyle() throws IOException {
assumeThat(Platform.detect(), oneOf(Platform.LINUX, Platform.MACOS));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "shared_link_style", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckBuild("//:gen").assertSuccess();
}
@Test
public void shBinaryAsLinker() throws IOException {
assumeThat(Platform.detect(), oneOf(Platform.LINUX, Platform.MACOS));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "step_test", tmp);
workspace.setUp();
workspace.runBuckBuild("-c", "cxx.ld=//:cxx", "//:binary_with_unused_header").assertSuccess();
}
@Test
public void buildBinaryUsingStaticPicLinkStyle() throws IOException {
assumeThat(Platform.detect(), oneOf(Platform.LINUX, Platform.MACOS));
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "static_pic_link_style", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
workspace
.runBuckCommand(
"build",
// This should only work (on some architectures) if PIC was used to build all included
// object files.
"--config",
"cxx.cxxldflags=-shared",
"//:bar")
.assertSuccess();
}
@Test
public void testStrippedBinaryProducesBothUnstrippedAndStrippedOutputs()
throws IOException, InterruptedException {
assumeTrue(Platform.detect() == Platform.MACOS);
BuildTarget unstrippedTarget = BuildTargetFactory.newInstance("//:test");
BuildTarget strippedTarget =
unstrippedTarget.withAppendedFlavors(StripStyle.DEBUGGING_SYMBOLS.getFlavor());
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "header_namespace", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
workspace
.runBuckCommand(
"build", "--config", "cxx.cxxflags=-g", strippedTarget.getFullyQualifiedName())
.assertSuccess();
Path strippedPath =
workspace.getPath(
BuildTargets.getGenPath(
filesystem, strippedTarget.withAppendedFlavors(CxxStrip.RULE_FLAVOR), "%s"));
Path unstrippedPath =
workspace.getPath(BuildTargets.getGenPath(filesystem, unstrippedTarget, "%s"));
String strippedOut =
workspace.runCommand("dsymutil", "-s", strippedPath.toString()).getStdout().orElse("");
String unstrippedOut =
workspace.runCommand("dsymutil", "-s", unstrippedPath.toString()).getStdout().orElse("");
assertThat(strippedOut, Matchers.containsStringIgnoringCase("dyld_stub_binder"));
assertThat(unstrippedOut, Matchers.containsStringIgnoringCase("dyld_stub_binder"));
assertThat(strippedOut, Matchers.not(Matchers.containsStringIgnoringCase("test.cpp")));
assertThat(unstrippedOut, Matchers.containsStringIgnoringCase("test.cpp"));
}
@Test
public void testStrippedBinaryCanBeFetchedFromCacheAlone() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
BuildTarget strippedTarget =
BuildTargetFactory.newInstance("//:test")
.withFlavors(StripStyle.DEBUGGING_SYMBOLS.getFlavor());
BuildTarget unstrippedTarget =
strippedTarget.withoutFlavors(StripStyle.FLAVOR_DOMAIN.getFlavors());
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "header_namespace", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
workspace
.runBuckCommand(
"build", "--config", "cxx.cxxflags=-g", strippedTarget.getFullyQualifiedName())
.assertSuccess();
workspace.runBuckCommand("clean").assertSuccess();
workspace
.runBuckCommand(
"build", "--config", "cxx.cxxflags=-g", strippedTarget.getFullyQualifiedName())
.assertSuccess();
Path strippedPath =
workspace.getPath(
BuildTargets.getGenPath(
filesystem, strippedTarget.withAppendedFlavors(CxxStrip.RULE_FLAVOR), "%s"));
Path unstrippedPath =
workspace.getPath(BuildTargets.getGenPath(filesystem, unstrippedTarget, "%s"));
assertThat(Files.exists(strippedPath), Matchers.equalTo(true));
assertThat(Files.exists(unstrippedPath), Matchers.equalTo(false));
}
@Test
public void testStrippedBinaryOutputDiffersFromUnstripped()
throws IOException, InterruptedException {
assumeTrue(Platform.detect() == Platform.MACOS);
BuildTarget unstrippedTarget = BuildTargetFactory.newInstance("//:test");
BuildTarget strippedTarget =
unstrippedTarget.withFlavors(StripStyle.DEBUGGING_SYMBOLS.getFlavor());
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "header_namespace", tmp);
workspace.setUp();
workspace.setupCxxSandboxing(sandboxSources);
ProjectWorkspace.ProcessResult strippedResult =
workspace.runBuckCommand(
"targets", "--show-output", strippedTarget.getFullyQualifiedName());
strippedResult.assertSuccess();
ProjectWorkspace.ProcessResult unstrippedResult =
workspace.runBuckCommand(
"targets", "--show-output", unstrippedTarget.getFullyQualifiedName());
unstrippedResult.assertSuccess();
String strippedOutput = strippedResult.getStdout().split(" ")[1];
String unstrippedOutput = unstrippedResult.getStdout().split(" ")[1];
assertThat(strippedOutput, Matchers.not(Matchers.equalTo(unstrippedOutput)));
}
@Test
public void testBuildingWithAndWithoutLinkerMap() throws Exception {
assumeTrue(Platform.detect() == Platform.MACOS);
BuildTarget target = BuildTargetFactory.newInstance("//:test");
BuildTarget withoutLinkerMapTarget =
target.withAppendedFlavors(LinkerMapMode.NO_LINKER_MAP.getFlavor());
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "header_namespace", tmp);
workspace.setUp();
ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath());
workspace
.runBuckCommand("build", "--config", "cxx.cxxflags=-g", target.getFullyQualifiedName())
.assertSuccess();
BuildTarget binaryWithLinkerMap = target;
Path binaryWithLinkerMapPath =
workspace.getPath(BuildTargets.getGenPath(filesystem, binaryWithLinkerMap, "%s"));
Path linkerMapPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, binaryWithLinkerMap, "%s-LinkMap.txt"));
assertThat(Files.exists(binaryWithLinkerMapPath), Matchers.equalTo(true));
assertThat(Files.exists(linkerMapPath), Matchers.equalTo(true));
workspace.runBuckCommand("clean").assertSuccess();
workspace
.runBuckCommand(
"build", "--config", "cxx.cxxflags=-g", withoutLinkerMapTarget.getFullyQualifiedName())
.assertSuccess();
BuildTarget binaryWithoutLinkerMap = withoutLinkerMapTarget;
Path binaryWithoutLinkerMapPath =
workspace.getPath(BuildTargets.getGenPath(filesystem, binaryWithoutLinkerMap, "%s"));
linkerMapPath =
workspace.getPath(
BuildTargets.getGenPath(filesystem, binaryWithoutLinkerMap, "%s-LinkMap.txt"));
assertThat(Files.exists(binaryWithoutLinkerMapPath), Matchers.equalTo(true));
assertThat(Files.exists(linkerMapPath), Matchers.equalTo(false));
}
@Test
public void testDisablingLinkCaching() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckBuild("-c", "cxx.cache_links=false", "//foo:simple").assertSuccess();
workspace.runBuckCommand("clean");
workspace.runBuckBuild("-c", "cxx.cache_links=false", "//foo:simple").assertSuccess();
workspace
.getBuildLog()
.assertTargetBuiltLocally(
CxxDescriptionEnhancer.createCxxLinkTarget(
BuildTargetFactory.newInstance("//foo:simple"), Optional.<LinkerMapMode>empty())
.toString());
}
@Test
public void testThinArchives() throws IOException {
CxxPlatform cxxPlatform =
CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build()));
assumeTrue(cxxPlatform.getAr().supportsThinArchives());
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
workspace
.runBuckBuild(
"-c",
"cxx.cache_links=false",
"-c",
"cxx.archive_contents=thin",
"//foo:binary_with_dep")
.assertSuccess();
ImmutableSortedSet<Path> initialObjects =
findFiles(tmp.getRoot(), tmp.getRoot().getFileSystem().getPathMatcher("glob:**/*.o"));
workspace.runBuckCommand("clean");
workspace
.runBuckBuild(
"-c",
"cxx.cache_links=false",
"-c",
"cxx.archive_contents=thin",
"//foo:binary_with_dep")
.assertSuccess();
workspace
.getBuildLog()
.assertTargetBuiltLocally(
CxxDescriptionEnhancer.createCxxLinkTarget(
BuildTargetFactory.newInstance("//foo:binary_with_dep"),
Optional.<LinkerMapMode>empty())
.toString());
ImmutableSortedSet<Path> subsequentObjects =
findFiles(tmp.getRoot(), tmp.getRoot().getFileSystem().getPathMatcher("glob:**/*.o"));
assertThat(initialObjects, Matchers.equalTo(subsequentObjects));
}
/**
* Tests that, if a file has to be rebuilt, but its header dependencies do not, that the header
* tree is still generated into the correct location.
*/
@Test
public void headersShouldBeSetUpCorrectlyOnRebuild() throws IOException {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "cxx_binary_dep_header_tree_materialize", tmp);
workspace.setUp();
workspace.enableDirCache();
workspace.setupCxxSandboxing(sandboxSources);
workspace.runBuckBuild("//:bin").assertSuccess();
workspace.runBuckCommand("clean");
workspace.copyFile("bin.c.new", "bin.c");
workspace.runBuckBuild("//:bin").assertSuccess();
BuckBuildLog log = workspace.getBuildLog();
log.assertTargetBuiltLocally("//:bin#binary");
}
private ImmutableSortedSet<Path> findFiles(Path root, final PathMatcher matcher)
throws IOException {
final ImmutableSortedSet.Builder<Path> files = ImmutableSortedSet.naturalOrder();
Files.walkFileTree(
root,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (matcher.matches(file)) {
files.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return files.build();
}
}