blob: 3b498d36a617d6facf29ee68e0f6da13385b39ce [file] [log] [blame]
/*
* 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.cli.FakeBuckConfig;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleParamsFactory;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.FakeBuildRuleParamsBuilder;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.TestSourcePath;
import com.facebook.buck.shell.Genrule;
import com.facebook.buck.shell.GenruleBuilder;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import org.junit.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
public class CxxLinkableEnhancerTest {
private static final ProjectFilesystem PROJECT_FILESYSTEM = new FakeProjectFilesystem();
private static final Path DEFAULT_OUTPUT = Paths.get("libblah.a");
private static final ImmutableList<SourcePath> DEFAULT_INPUTS = ImmutableList.<SourcePath>of(
new TestSourcePath("a.o"),
new TestSourcePath("b.o"),
new TestSourcePath("c.o"));
private static final ImmutableSortedSet<BuildRule> EMPTY_DEPS = ImmutableSortedSet.of();
private static final CxxPlatform CXX_PLATFORM = DefaultCxxPlatforms.build(
new FakeBuckConfig());
private static class FakeNativeLinkable extends FakeBuildRule implements NativeLinkable {
private final NativeLinkableInput staticInput;
private final NativeLinkableInput sharedInput;
public FakeNativeLinkable(
BuildRuleParams params,
SourcePathResolver resolver,
NativeLinkableInput staticInput,
NativeLinkableInput sharedInput) {
super(params, resolver);
this.staticInput = Preconditions.checkNotNull(staticInput);
this.sharedInput = Preconditions.checkNotNull(sharedInput);
}
@Override
public NativeLinkableInput getNativeLinkableInput(
CxxPlatform cxxPlatform,
Linker.LinkableDepType type) {
return type == Linker.LinkableDepType.STATIC ? staticInput : sharedInput;
}
}
private static FakeNativeLinkable createNativeLinkable(
String target,
SourcePathResolver resolver,
NativeLinkableInput staticNativeLinkableInput,
NativeLinkableInput sharedNativeLinkableInput,
BuildRule... deps) {
return new FakeNativeLinkable(
new FakeBuildRuleParamsBuilder(BuildTargetFactory.newInstance(target))
.setDeps(ImmutableSortedSet.copyOf(deps))
.build(),
resolver,
staticNativeLinkableInput,
sharedNativeLinkableInput);
}
@Test
public void testThatBuildTargetSourcePathDepsAndPathsArePropagated() {
BuildRuleResolver resolver = new BuildRuleResolver();
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = BuildRuleParamsFactory.createTrivialBuildRuleParams(target);
// Create a couple of genrules to generate inputs for an archive rule.
Genrule genrule1 = (Genrule) GenruleBuilder
.newGenruleBuilder(BuildTargetFactory.newInstance("//:genrule"))
.setOut("foo/bar.o")
.build(resolver);
Genrule genrule2 = (Genrule) GenruleBuilder
.newGenruleBuilder(BuildTargetFactory.newInstance("//:genrule2"))
.setOut("foo/test.o")
.build(resolver);
// Build the archive using a normal input the outputs of the genrules above.
CxxLink cxxLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
new SourcePathResolver(resolver),
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
ImmutableList.<SourcePath>of(
new TestSourcePath("simple.o"),
new BuildTargetSourcePath(PROJECT_FILESYSTEM, genrule1.getBuildTarget()),
new BuildTargetSourcePath(PROJECT_FILESYSTEM, genrule2.getBuildTarget())),
Linker.LinkableDepType.STATIC,
EMPTY_DEPS);
// Verify that the archive dependencies include the genrules providing the
// SourcePath inputs.
assertEquals(
ImmutableSortedSet.<BuildRule>of(genrule1, genrule2),
cxxLink.getDeps());
// Verify that the archive inputs are the outputs of the genrules.
assertEquals(
ImmutableSet.of(Paths.get("simple.o")),
ImmutableSet.copyOf(cxxLink.getInputsToCompareToOutput()));
}
@Test
public void testThatOriginalBuildParamsDepsDoNotPropagateToArchive() {
SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());
// Create an `Archive` rule using build params with an existing dependency,
// as if coming from a `TargetNode` which had declared deps. These should *not*
// propagate to the `Archive` rule, since it only cares about dependencies generating
// it's immediate inputs.
BuildRule dep = new FakeBuildRule(
BuildRuleParamsFactory.createTrivialBuildRuleParams(
BuildTargetFactory.newInstance("//:fake")), pathResolver);
BuildTarget target = BuildTargetFactory.newInstance("//:archive");
BuildRuleParams params =
new FakeBuildRuleParamsBuilder(BuildTargetFactory.newInstance("//:dummy"))
.setDeps(ImmutableSortedSet.of(dep))
.build();
CxxLink cxxLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
EMPTY_DEPS);
// Verify that the archive rules dependencies are empty.
assertEquals(cxxLink.getDeps(), ImmutableSortedSet.<BuildRule>of());
}
@Test
public void testThatBuildTargetsFromNativeLinkableDepsContributeToActualDeps() {
BuildRuleResolver resolver = new BuildRuleResolver();
SourcePathResolver pathResolver = new SourcePathResolver(resolver);
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = BuildRuleParamsFactory.createTrivialBuildRuleParams(target);
// Create a dummy build rule and add it to the resolver.
BuildTarget fakeBuildTarget = BuildTargetFactory.newInstance("//:fake");
FakeBuildRule fakeBuildRule = new FakeBuildRule(
new FakeBuildRuleParamsBuilder(fakeBuildTarget).build(), pathResolver);
resolver.addToIndex(fakeBuildRule);
// Create a native linkable dep and have it list the fake build rule above as a link
// time dependency.
NativeLinkableInput nativeLinkableInput = ImmutableNativeLinkableInput.of(
ImmutableList.<SourcePath>of(
new BuildTargetSourcePath(PROJECT_FILESYSTEM, fakeBuildRule.getBuildTarget())),
ImmutableList.<String>of());
FakeNativeLinkable nativeLinkable = createNativeLinkable(
"//:dep",
pathResolver,
nativeLinkableInput,
nativeLinkableInput);
// Construct a CxxLink object and pass the native linkable above as the dep.
CxxLink cxxLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
ImmutableSortedSet.<BuildRule>of(nativeLinkable));
// Verify that the fake build rule made it in as a dep.
assertTrue(cxxLink.getDeps().contains(fakeBuildRule));
}
@Test
public void createCxxLinkableBuildRuleExecutableVsShared() {
SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = BuildRuleParamsFactory.createTrivialBuildRuleParams(target);
String soname = "soname";
ImmutableList<String> sonameArgs =
ImmutableList.copyOf(
CxxLinkableEnhancer.iXlinker(
CXX_PLATFORM.getLd().soname(soname)));
// Construct a CxxLink object which links as an executable.
CxxLink executable = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
ImmutableSortedSet.<BuildRule>of());
assertFalse(executable.getArgs().contains("-shared"));
assertEquals(Collections.indexOfSubList(executable.getArgs(), sonameArgs), -1);
// Construct a CxxLink object which links as a shared lib.
CxxLink shared = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.SHARED,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
ImmutableSortedSet.<BuildRule>of());
assertTrue(shared.getArgs().contains("-shared"));
assertEquals(Collections.indexOfSubList(shared.getArgs(), sonameArgs), -1);
// Construct a CxxLink object which links as a shared lib with a SONAME.
CxxLink sharedWithSoname = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.SHARED,
Optional.of("soname"),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
ImmutableSortedSet.<BuildRule>of());
assertTrue(sharedWithSoname.getArgs().contains("-shared"));
assertNotEquals(Collections.indexOfSubList(sharedWithSoname.getArgs(), sonameArgs), -1);
}
@Test
public void createCxxLinkableBuildRuleStaticVsSharedDeps() {
SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = BuildRuleParamsFactory.createTrivialBuildRuleParams(target);
// Create a native linkable dep and have it list the fake build rule above as a link
// time dependency
String staticArg = "static";
NativeLinkableInput staticInput = ImmutableNativeLinkableInput.of(
ImmutableList.<SourcePath>of(),
ImmutableList.of(staticArg));
String sharedArg = "shared";
NativeLinkableInput sharedInput = ImmutableNativeLinkableInput.of(
ImmutableList.<SourcePath>of(),
ImmutableList.of(sharedArg));
FakeNativeLinkable nativeLinkable = createNativeLinkable("//:dep",
pathResolver,
staticInput, sharedInput);
// Construct a CxxLink object which links using static dependencies.
CxxLink staticLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.STATIC,
ImmutableSortedSet.<BuildRule>of(nativeLinkable));
assertTrue(staticLink.getArgs().contains(staticArg));
assertFalse(staticLink.getArgs().contains(sharedArg));
// Construct a CxxLink object which links using shared dependencies.
CxxLink sharedLink = CxxLinkableEnhancer.createCxxLinkableBuildRule(
CXX_PLATFORM,
params,
pathResolver,
ImmutableList.<String>of(),
ImmutableList.<String>of(),
target,
Linker.LinkType.EXECUTABLE,
Optional.<String>absent(),
DEFAULT_OUTPUT,
DEFAULT_INPUTS,
Linker.LinkableDepType.SHARED,
ImmutableSortedSet.<BuildRule>of(nativeLinkable));
assertFalse(sharedLink.getArgs().contains(staticArg));
assertTrue(sharedLink.getArgs().contains(sharedArg));
}
@Test
public void getTransitiveNativeLinkableInputDoesNotTraversePastNonNativeLinkables() {
CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new FakeBuckConfig());
SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());
// Create a native linkable that sits at the bottom of the dep chain.
String sentinel = "bottom";
NativeLinkableInput bottomInput = ImmutableNativeLinkableInput.of(
ImmutableList.<SourcePath>of(),
ImmutableList.of(sentinel));
BuildRule bottom = createNativeLinkable("//:bottom", pathResolver, bottomInput, bottomInput);
// Create a non-native linkable that sits in the middle of the dep chain, preventing
// traversals to the bottom native linkable.
BuildRule middle = new FakeBuildRule("//:middle", pathResolver, bottom);
// Create a native linkable that sits at the top of the dep chain.
NativeLinkableInput topInput = ImmutableNativeLinkableInput.of(
ImmutableList.<SourcePath>of(),
ImmutableList.<String>of());
BuildRule top = createNativeLinkable("//:top", pathResolver, topInput, topInput, middle);
// Now grab all input via traversing deps and verify that the middle rule prevents pulling
// in the bottom input.
NativeLinkableInput totalInput =
NativeLinkables.getTransitiveNativeLinkableInput(
cxxPlatform,
ImmutableList.of(top),
Linker.LinkableDepType.STATIC,
/* reverse */ true);
assertTrue(bottomInput.getArgs().contains(sentinel));
assertFalse(totalInput.getArgs().contains(sentinel));
}
}