/*
* Copyright 2015-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.cxx;
import static org.hamcrest.MatcherAssert.assertThat;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.FakeSourcePath;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.args.StringArg;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import org.hamcrest.Matchers;
import org.junit.Test;
public class NativeLinkablesTest {
private static class FakeNativeLinkable extends FakeBuildRule implements NativeLinkable {
private final Iterable<NativeLinkable> deps;
private final Iterable<NativeLinkable> exportedDeps;
private final NativeLinkable.Linkage preferredLinkage;
private final NativeLinkableInput nativeLinkableInput;
private final ImmutableMap<String, SourcePath> sharedLibraries;
public FakeNativeLinkable(
String target,
Iterable<? extends NativeLinkable> deps,
Iterable<? extends NativeLinkable> exportedDeps,
NativeLinkable.Linkage preferredLinkage,
NativeLinkableInput nativeLinkableInput,
ImmutableMap<String, SourcePath> sharedLibraries,
BuildRule... ruleDeps) {
super(
BuildTargetFactory.newInstance(target),
new SourcePathResolver(
new SourcePathRuleFinder(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()))),
ImmutableSortedSet.copyOf(ruleDeps));
this.deps = ImmutableList.copyOf(deps);
this.exportedDeps = ImmutableList.copyOf(exportedDeps);
this.preferredLinkage = preferredLinkage;
this.nativeLinkableInput = nativeLinkableInput;
this.sharedLibraries = sharedLibraries;
}
@Override
public Iterable<NativeLinkable> getNativeLinkableDeps() {
return deps;
}
@Override
public Iterable<NativeLinkable> getNativeLinkableExportedDeps() {
return exportedDeps;
}
@Override
public NativeLinkableInput getNativeLinkableInput(
CxxPlatform cxxPlatform, Linker.LinkableDepType type) {
return nativeLinkableInput;
}
@Override
public NativeLinkable.Linkage getPreferredLinkage(CxxPlatform cxxPlatform) {
return preferredLinkage;
}
@Override
public ImmutableMap<String, SourcePath> getSharedLibraries(CxxPlatform cxxPlatform) {
return sharedLibraries;
}
}
@Test
public void regularDepsUsingSharedLinkageAreNotTransitive() {
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("b")).build(),
ImmutableMap.of());
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(b),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("a")).build(),
ImmutableMap.of());
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.SHARED)
.keySet(),
Matchers.not(Matchers.hasItem(b.getBuildTarget())));
}
@Test
public void exportedDepsUsingSharedLinkageAreTransitive() {
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("b")).build(),
ImmutableMap.of());
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(),
ImmutableList.of(b),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("a")).build(),
ImmutableMap.of());
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.SHARED)
.keySet(),
Matchers.hasItem(b.getBuildTarget()));
}
@Test
public void regularDepsFromStaticLibsUsingSharedLinkageAreTransitive() {
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("b")).build(),
ImmutableMap.of());
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(b),
ImmutableList.of(),
NativeLinkable.Linkage.STATIC,
NativeLinkableInput.builder().addAllArgs(StringArg.from("a")).build(),
ImmutableMap.of());
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.SHARED)
.keySet(),
Matchers.hasItem(b.getBuildTarget()));
}
@Test
public void regularDepsUsingStaticLinkageAreTransitive() {
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("b")).build(),
ImmutableMap.of());
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(b),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("a")).build(),
ImmutableMap.of());
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.STATIC)
.keySet(),
Matchers.hasItem(b.getBuildTarget()));
}
@Test
public void gatherTransitiveSharedLibraries() throws Exception {
FakeNativeLinkable c =
new FakeNativeLinkable(
"//:c",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("libc.so", new FakeSourcePath("libc.so")));
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(c),
ImmutableList.of(),
NativeLinkable.Linkage.STATIC,
NativeLinkableInput.builder().build(),
ImmutableMap.of("libb.so", new FakeSourcePath("libb.so")));
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(b),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("liba.so", new FakeSourcePath("liba.so")));
ImmutableSortedMap<String, SourcePath> sharedLibs =
NativeLinkables.getTransitiveSharedLibraries(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
NativeLinkable.class::isInstance);
assertThat(
sharedLibs,
Matchers.equalTo(
ImmutableSortedMap.<String, SourcePath>of(
"liba.so", new FakeSourcePath("liba.so"),
"libc.so", new FakeSourcePath("libc.so"))));
}
@Test
public void nonNativeLinkableDepsAreIgnored() {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
FakeNativeLinkable c =
new FakeNativeLinkable(
"//:c",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("c")).build(),
ImmutableMap.of());
FakeBuildRule b = new FakeBuildRule("//:b", pathResolver, c);
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().addAllArgs(StringArg.from("a")).build(),
ImmutableMap.of(),
b);
assertThat(a.getBuildDeps(), Matchers.hasItem(b));
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.STATIC)
.keySet(),
Matchers.not(Matchers.hasItem(c.getBuildTarget())));
}
@Test
public void getLinkStyle() {
assertThat(
NativeLinkables.getLinkStyle(NativeLinkable.Linkage.STATIC, Linker.LinkableDepType.SHARED),
Matchers.equalTo(Linker.LinkableDepType.STATIC_PIC));
assertThat(
NativeLinkables.getLinkStyle(NativeLinkable.Linkage.SHARED, Linker.LinkableDepType.STATIC),
Matchers.equalTo(Linker.LinkableDepType.SHARED));
assertThat(
NativeLinkables.getLinkStyle(NativeLinkable.Linkage.ANY, Linker.LinkableDepType.STATIC),
Matchers.equalTo(Linker.LinkableDepType.STATIC));
}
@Test(expected = HumanReadableException.class)
public void duplicateDifferentLibsConflict() throws Exception {
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("liba.so", new FakeSourcePath("liba1.so")));
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("liba.so", new FakeSourcePath("liba2.so")));
NativeLinkables.getTransitiveSharedLibraries(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a, b),
NativeLinkable.class::isInstance);
}
@Test
public void duplicateIdenticalLibsDoNotConflict() throws Exception {
FakeSourcePath path = new FakeSourcePath("libc.so");
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("libc.so", path));
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of("libc.so", path));
ImmutableSortedMap<String, SourcePath> sharedLibs =
NativeLinkables.getTransitiveSharedLibraries(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a, b),
NativeLinkable.class::isInstance);
assertThat(
sharedLibs, Matchers.equalTo(ImmutableSortedMap.<String, SourcePath>of("libc.so", path)));
}
@Test
public void traversePredicate() throws Exception {
FakeNativeLinkable b =
new FakeNativeLinkable(
"//:b",
ImmutableList.of(),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of());
FakeNativeLinkable a =
new FakeNativeLinkable(
"//:a",
ImmutableList.<NativeLinkable>of(b),
ImmutableList.of(),
NativeLinkable.Linkage.ANY,
NativeLinkableInput.builder().build(),
ImmutableMap.of());
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.STATIC,
x -> true),
Matchers.equalTo(
ImmutableMap.<BuildTarget, NativeLinkable>of(
a.getBuildTarget(), a,
b.getBuildTarget(), b)));
assertThat(
NativeLinkables.getNativeLinkables(
CxxPlatformUtils.DEFAULT_PLATFORM,
ImmutableList.of(a),
Linker.LinkableDepType.STATIC,
a::equals),
Matchers.equalTo(ImmutableMap.<BuildTarget, NativeLinkable>of(a.getBuildTarget(), a)));
}
}