/*
* 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.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeSourcePath;
import com.facebook.buck.rules.PathSourcePath;
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.TestCellBuilder;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.args.FileListableLinkerInputArg;
import com.facebook.buck.rules.args.SourcePathArg;
import com.facebook.buck.rules.coercer.PatternMatchedCollection;
import com.facebook.buck.rules.coercer.SourceList;
import com.facebook.buck.rules.coercer.VersionMatchedCollection;
import com.facebook.buck.shell.GenruleBuilder;
import com.facebook.buck.testutil.AllExistingProjectFilesystem;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.TargetGraphFactory;
import com.facebook.buck.versions.Version;
import com.google.common.base.Preconditions;
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.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.util.Optional;
import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.Test;
public class PrebuiltCxxLibraryDescriptionTest {
private static final BuildTarget TARGET = BuildTargetFactory.newInstance("//:target");
private static final BuildTarget TARGET_TWO = BuildTargetFactory.newInstance("//two/:target");
private static final CxxPlatform CXX_PLATFORM = CxxPlatformUtils.DEFAULT_PLATFORM;
private static Path getStaticLibraryPath(PrebuiltCxxLibraryDescriptionArg arg) {
String libDir = arg.getLibDir().orElse("lib");
String libName = arg.getLibName().orElse(TARGET.getShortName());
return TARGET.getBasePath().resolve(libDir).resolve(String.format("lib%s.a", libName));
}
private static Path getStaticPicLibraryPath(PrebuiltCxxLibraryDescriptionArg arg) {
String libDir = arg.getLibDir().orElse("lib");
String libName = arg.getLibName().orElse(TARGET.getShortName());
return TARGET.getBasePath().resolve(libDir).resolve(String.format("lib%s_pic.a", libName));
}
private static Path getSharedLibraryPath(PrebuiltCxxLibraryDescriptionArg arg) {
String libDir = arg.getLibDir().orElse("lib");
String libName = arg.getLibName().orElse(TARGET.getShortName());
return TARGET
.getBasePath()
.resolve(libDir)
.resolve(String.format("lib%s.%s", libName, CXX_PLATFORM.getSharedLibraryExtension()));
}
private static String getSharedLibrarySoname(PrebuiltCxxLibraryDescriptionArg arg) {
String libName = arg.getLibName().orElse(TARGET.getShortName());
return arg.getSoname()
.orElse(String.format("lib%s.%s", libName, CXX_PLATFORM.getSharedLibraryExtension()));
}
private static ImmutableSet<BuildTarget> getInputRules(BuildRule buildRule) {
return ImmutableSet.of(
BuildTarget.builder()
.from(buildRule.getBuildTarget())
.addFlavors(CXX_PLATFORM.getFlavor())
.addFlavors(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)
.build());
}
private static ImmutableSet<Path> getHeaderNames(Iterable<CxxHeaders> includes) {
ImmutableSet.Builder<Path> names = ImmutableSet.builder();
for (CxxHeaders headers : includes) {
CxxSymlinkTreeHeaders symlinkTreeHeaders = (CxxSymlinkTreeHeaders) headers;
names.addAll(symlinkTreeHeaders.getNameToPathMap().keySet());
}
return names.build();
}
@Test
public void createBuildRuleDefault() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibraryDescriptionArg arg = libBuilder.build().getConstructorArg();
// Verify static native linkable input.
NativeLinkableInput expectedStaticLinkableInput =
NativeLinkableInput.of(
ImmutableList.of(
FileListableLinkerInputArg.withSourcePathArg(
SourcePathArg.of(new PathSourcePath(filesystem, getStaticLibraryPath(arg))))),
ImmutableSet.of(),
ImmutableSet.of());
assertEquals(
expectedStaticLinkableInput,
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.STATIC));
// Verify shared native linkable input.
NativeLinkableInput expectedSharedLinkableInput =
NativeLinkableInput.of(
ImmutableList.of(
SourcePathArg.of(new PathSourcePath(filesystem, getSharedLibraryPath(arg)))),
ImmutableSet.of(),
ImmutableSet.of());
assertEquals(
expectedSharedLinkableInput,
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED));
}
@Test
public void headerOnly() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder =
new PrebuiltCxxLibraryBuilder(TARGET).setHeaderOnly(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
// Verify static native linkable input.
NativeLinkableInput expectedStaticLinkableInput =
NativeLinkableInput.of(ImmutableList.of(), ImmutableSet.of(), ImmutableSet.of());
assertEquals(
expectedStaticLinkableInput,
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.STATIC));
// Verify shared native linkable input.
NativeLinkableInput expectedSharedLinkableInput =
NativeLinkableInput.of(ImmutableList.of(), ImmutableSet.of(), ImmutableSet.of());
assertEquals(
expectedSharedLinkableInput,
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED));
}
@Test
public void createBuildRuleExternal() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET).setProvided(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibraryDescriptionArg arg = libBuilder.build().getConstructorArg();
// Verify shared native linkable input.
NativeLinkableInput expectedSharedLinkableInput =
NativeLinkableInput.of(
ImmutableList.of(
SourcePathArg.of(new PathSourcePath(filesystem, getSharedLibraryPath(arg)))),
ImmutableSet.of(),
ImmutableSet.of());
assertEquals(
expectedSharedLinkableInput,
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED));
}
@Test
public void missingSharedLibsAreAutoBuilt() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED);
BuildRule rule =
FluentIterable.from(nativeLinkableInput.getArgs())
.transformAndConcat(arg -> arg.getDeps(ruleFinder))
.toList()
.get(0);
assertTrue(rule instanceof CxxLink);
}
@Test
public void missingSharedLibsAreNotAutoBuiltForHeaderOnlyRules() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder =
new PrebuiltCxxLibraryBuilder(TARGET).setHeaderOnly(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED);
assertThat(
FluentIterable.from(nativeLinkableInput.getArgs())
.transformAndConcat(arg -> arg.getDeps(ruleFinder))
.toList(),
empty());
}
@Test
public void addsLibsToAndroidPackageableCollector() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibraryDescriptionArg arg = libBuilder.build().getConstructorArg();
assertEquals(
ImmutableMap.<String, SourcePath>of(
getSharedLibrarySoname(arg), new PathSourcePath(filesystem, getSharedLibraryPath(arg))),
lib.getSharedLibraries(CXX_PLATFORM));
}
@Test
public void locationMacro() throws NoSuchBuildTargetException {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
CellPathResolver cellRoots = TestCellBuilder.createCellRoots(filesystem);
Optional<String> libName = Optional.of("test");
Optional<String> libDir = Optional.of("$(location //other:gen_lib)/");
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
BuildTarget genTarget = BuildTargetFactory.newInstance("//other:gen_lib");
GenruleBuilder genruleBuilder = GenruleBuilder.newGenruleBuilder(genTarget).setOut("lib_dir");
BuildRule genRule = genruleBuilder.build(resolver);
CxxPlatform platform =
CxxPlatformUtils.DEFAULT_PLATFORM.withFlavor(InternalFlavor.of("PLATFORM1"));
Path path =
pathResolver.getAbsolutePath(Preconditions.checkNotNull(genRule.getSourcePathToOutput()));
final SourcePath staticLibraryPath =
PrebuiltCxxLibraryDescription.getStaticLibraryPath(
TARGET, cellRoots, filesystem, resolver, platform, Optional.empty(), libDir, libName);
assertEquals(
TARGET.getBasePath().resolve(String.format("%s/libtest.a", path)),
pathResolver.getAbsolutePath(staticLibraryPath));
}
@Test
public void goodPathNoLocation() {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxPlatform platform =
CxxPlatformUtils.DEFAULT_PLATFORM.withFlavor(InternalFlavor.of("PLATFORM1"));
final SourcePath staticLibraryPath =
PrebuiltCxxLibraryDescription.getStaticLibraryPath(
TARGET_TWO,
TestCellBuilder.createCellRoots(filesystem),
filesystem,
resolver,
platform,
Optional.empty(),
Optional.of("lib"),
Optional.empty());
assertThat(
MorePaths.pathWithUnixSeparators(pathResolver.getAbsolutePath(staticLibraryPath)),
Matchers.containsString(String.format("two/%s/libtarget.a", "lib")));
}
@Test
public void findDepsFromParamsWithLocation() throws NoSuchBuildTargetException {
BuildTarget genTarget = BuildTargetFactory.newInstance("//other:gen_lib");
GenruleBuilder genruleBuilder = GenruleBuilder.newGenruleBuilder(genTarget).setOut("lib_dir");
PrebuiltCxxLibraryBuilder builder = new PrebuiltCxxLibraryBuilder(TARGET);
builder.setSoname("test");
builder.setLibDir("$(location //other:gen_lib)");
TargetGraph targetGraph =
TargetGraphFactory.newInstance(genruleBuilder.build(), builder.build());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
BuildRule genrule = genruleBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibrary lib = (PrebuiltCxxLibrary) builder.build(resolver, filesystem, targetGraph);
ImmutableSortedSet<BuildTarget> implicit = builder.findImplicitDeps();
assertEquals(ImmutableSortedSet.of(genTarget), implicit);
assertThat(lib.getBuildDeps(), Matchers.contains(genrule));
}
@Test
public void findDepsFromParamsWithNone() throws NoSuchBuildTargetException {
PrebuiltCxxLibraryBuilder builder = new PrebuiltCxxLibraryBuilder(TARGET);
builder.setSoname("test");
builder.setLibDir("lib");
assertThat(builder.findImplicitDeps(), empty());
}
@Test
public void findDepsFromParamsWithPlatform() throws NoSuchBuildTargetException {
PrebuiltCxxLibraryBuilder builder = new PrebuiltCxxLibraryBuilder(TARGET);
builder.setSoname("test");
builder.setLibDir("$(platform)");
assertThat(builder.findImplicitDeps(), empty());
}
@Test
public void platformMacro() {
Optional<String> libDir = Optional.of("libs/$(platform)");
Optional<String> libName = Optional.of("test-$(platform)");
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
CellPathResolver cellRoots = TestCellBuilder.createCellRoots(filesystem);
CxxPlatform platform1 =
CxxPlatformUtils.DEFAULT_PLATFORM.withFlavor(InternalFlavor.of("PLATFORM1"));
CxxPlatform platform2 =
CxxPlatformUtils.DEFAULT_PLATFORM.withFlavor(InternalFlavor.of("PLATFORM2"));
assertEquals(
filesystem.resolve(
TARGET
.getBasePath()
.resolve(
String.format(
"libs/PLATFORM1/libtest-PLATFORM1.%s",
platform1.getSharedLibraryExtension()))),
pathResolver.getAbsolutePath(
PrebuiltCxxLibraryDescription.getSharedLibraryPath(
TARGET,
cellRoots,
filesystem,
resolver,
platform1,
Optional.empty(),
libDir,
libName)));
assertEquals(
filesystem.resolve(TARGET.getBasePath().resolve("libs/PLATFORM1/libtest-PLATFORM1.a")),
pathResolver.getAbsolutePath(
PrebuiltCxxLibraryDescription.getStaticLibraryPath(
TARGET,
cellRoots,
filesystem,
resolver,
platform1,
Optional.empty(),
libDir,
libName)));
assertEquals(
filesystem.resolve(
TARGET
.getBasePath()
.resolve(
String.format(
"libs/PLATFORM2/libtest-PLATFORM2.%s",
platform2.getSharedLibraryExtension()))),
pathResolver.getAbsolutePath(
PrebuiltCxxLibraryDescription.getSharedLibraryPath(
TARGET,
cellRoots,
filesystem,
resolver,
platform2,
Optional.empty(),
libDir,
libName)));
assertEquals(
filesystem.resolve(TARGET.getBasePath().resolve("libs/PLATFORM2/libtest-PLATFORM2.a")),
pathResolver.getAbsolutePath(
PrebuiltCxxLibraryDescription.getStaticLibraryPath(
TARGET,
cellRoots,
filesystem,
resolver,
platform2,
Optional.empty(),
libDir,
libName)));
}
@Test
public void exportedHeaders() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder =
new PrebuiltCxxLibraryBuilder(TARGET)
.setExportedHeaders(
SourceList.ofNamedSources(
ImmutableSortedMap.of("foo.h", new FakeSourcePath("foo.h"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
// Verify the preprocessable input is as expected.
CxxPreprocessorInput input = lib.getCxxPreprocessorInput(CXX_PLATFORM, HeaderVisibility.PUBLIC);
assertThat(getHeaderNames(input.getIncludes()), Matchers.hasItem(filesystem.getPath("foo.h")));
assertThat(
ImmutableSortedSet.copyOf(input.getDeps(resolver, ruleFinder)),
Matchers.equalTo(resolver.getAllRules(getInputRules(lib))));
}
@Test
public void exportedPlatformHeaders() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder =
new PrebuiltCxxLibraryBuilder(TARGET)
.setExportedPlatformHeaders(
PatternMatchedCollection.<SourceList>builder()
.add(
Pattern.compile(CXX_PLATFORM.getFlavor().toString()),
SourceList.ofNamedSources(
ImmutableSortedMap.of("foo.h", new FakeSourcePath("foo.h"))))
.add(
Pattern.compile("DO NOT MATCH ANYTNING"),
SourceList.ofNamedSources(
ImmutableSortedMap.of("bar.h", new FakeSourcePath("bar.h"))))
.build());
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
// Verify the preprocessable input is as expected.
CxxPreprocessorInput input = lib.getCxxPreprocessorInput(CXX_PLATFORM, HeaderVisibility.PUBLIC);
assertThat(getHeaderNames(input.getIncludes()), Matchers.hasItem(filesystem.getPath("foo.h")));
assertThat(
getHeaderNames(input.getIncludes()),
Matchers.not(Matchers.hasItem(filesystem.getPath("bar.h"))));
assertThat(
ImmutableSortedSet.copyOf(input.getDeps(resolver, ruleFinder)),
Matchers.equalTo(resolver.getAllRules(getInputRules(lib))));
}
@Test
public void testBuildSharedWithDep() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
CxxPlatform platform = CxxPlatformUtils.DEFAULT_PLATFORM;
BuildTarget target =
BuildTargetFactory.newInstance("//:x")
.withFlavors(platform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR);
GenruleBuilder genruleBuilder =
GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:gen_libx"))
.setOut("gen_libx")
.setCmd("something");
PrebuiltCxxLibraryBuilder builder =
new PrebuiltCxxLibraryBuilder(target).setLibName("x").setLibDir("$(location //:gen_libx)");
TargetGraph targetGraph =
TargetGraphFactory.newInstance(genruleBuilder.build(), builder.build());
BuildRule genSrc = genruleBuilder.build(resolver, filesystem, targetGraph);
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
filesystem.writeContentsToPath(
"class Test {}",
pathResolver
.getAbsolutePath(Preconditions.checkNotNull(genSrc.getSourcePathToOutput()))
.resolve("libx.so"));
CxxLink lib = (CxxLink) builder.build(resolver, filesystem, targetGraph);
assertNotNull(lib);
assertThat(lib.getBuildDeps(), Matchers.contains(genSrc));
}
@Test
public void headerNamespace() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder =
new PrebuiltCxxLibraryBuilder(TARGET)
.setHeaderNamespace("hello")
.setExportedHeaders(
SourceList.ofUnnamedSources(ImmutableSortedSet.of(new FakeSourcePath("foo.h"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
// Verify the preprocessable input is as expected.
CxxPreprocessorInput input = lib.getCxxPreprocessorInput(CXX_PLATFORM, HeaderVisibility.PUBLIC);
assertThat(
getHeaderNames(input.getIncludes()),
Matchers.contains(filesystem.getPath("hello", "foo.h")));
}
@Test
public void staticPicLibsUseCorrectPath() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.STATIC_PIC);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver).get(0),
Matchers.endsWith(
getStaticPicLibraryPath(libBuilder.build().getConstructorArg()).toString()));
}
@Test
public void missingStaticPicLibsUseStaticLibs() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder libBuilder = new PrebuiltCxxLibraryBuilder(TARGET);
filesystem.touch(
filesystem.resolve(getStaticPicLibraryPath(libBuilder.build().getConstructorArg())));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
PrebuiltCxxLibrary lib =
(PrebuiltCxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.STATIC_PIC);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver).get(0),
Matchers.endsWith(
getStaticPicLibraryPath(libBuilder.build().getConstructorArg()).toString()));
}
@Test
public void forceStatic() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder builder = new PrebuiltCxxLibraryBuilder(TARGET).setForceStatic(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary prebuiltCxxLibrary =
(PrebuiltCxxLibrary) builder.build(resolver, filesystem, targetGraph);
assertThat(
prebuiltCxxLibrary.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.equalTo(NativeLinkable.Linkage.STATIC));
}
@Test
public void exportedLinkerFlagsAreUsedToBuildSharedLibrary() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildTarget target =
BuildTarget.builder(BuildTargetFactory.newInstance("//:lib"))
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.addFlavors(CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor())
.build();
PrebuiltCxxLibraryBuilder builder =
new PrebuiltCxxLibraryBuilder(target)
.setExportedLinkerFlags(ImmutableList.of("--some-flag"))
.setForceStatic(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxLink cxxLink = (CxxLink) builder.build(resolver, filesystem, targetGraph);
assertThat(Arg.stringify(cxxLink.getArgs(), pathResolver), Matchers.hasItem("--some-flag"));
}
@Test
public void nativeLinkableDeps() throws Exception {
PrebuiltCxxLibraryBuilder depBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"));
PrebuiltCxxLibraryBuilder ruleBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:r"))
.setDeps(ImmutableSortedSet.of(depBuilder.getTarget()));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(depBuilder.build(), ruleBuilder.build());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary dep =
(PrebuiltCxxLibrary) depBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) ruleBuilder.build(resolver, filesystem, targetGraph);
assertThat(
rule.getNativeLinkableDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(dep));
assertThat(
ImmutableList.copyOf(
rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM)),
empty());
}
@Test
public void nativeLinkableExportedDeps() throws Exception {
PrebuiltCxxLibraryBuilder depBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"));
PrebuiltCxxLibraryBuilder ruleBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:r"))
.setExportedDeps(ImmutableSortedSet.of(depBuilder.getTarget()));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(depBuilder.build(), ruleBuilder.build());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary dep =
(PrebuiltCxxLibrary) depBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) ruleBuilder.build(resolver, filesystem, targetGraph);
assertThat(
ImmutableList.copyOf(
rule.getNativeLinkableDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM)),
empty());
assertThat(
rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(dep));
}
@Test
public void includesDirs() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:r"))
.setIncludeDirs(ImmutableList.of("include"));
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(
rule.getCxxPreprocessorInput(CxxPlatformUtils.DEFAULT_PLATFORM, HeaderVisibility.PUBLIC)
.getIncludes(),
Matchers.contains(
CxxHeadersDir.of(
CxxPreprocessables.IncludeType.SYSTEM,
new PathSourcePath(
filesystem, rule.getBuildTarget().getBasePath().resolve("include")))));
}
@Test
public void ruleWithoutHeadersDoesNotUseSymlinkTree() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setIncludeDirs(ImmutableList.of());
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
CxxPreprocessorInput input =
rule.getCxxPreprocessorInput(CxxPlatformUtils.DEFAULT_PLATFORM, HeaderVisibility.PUBLIC);
assertThat(getHeaderNames(input.getIncludes()), empty());
assertThat(ImmutableList.copyOf(input.getDeps(resolver, ruleFinder)), empty());
}
@Test
public void linkWithoutSoname() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setLinkWithoutSoname(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput input =
rule.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.SHARED);
assertThat(
Arg.stringify(input.getArgs(), pathResolver),
Matchers.contains(
"-L" + filesystem.resolve(rule.getBuildTarget().getBasePath()).resolve("lib"),
"-lrule"));
}
@Test
public void missingStaticLibIsNotANativeLinkTargetSoname() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"));
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertFalse(rule.getNativeLinkTarget(CXX_PLATFORM).isPresent());
}
@Test
public void providedLibIsNotANativeLinkTargetSoname() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule")).setProvided(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertFalse(rule.getNativeLinkTarget(CXX_PLATFORM).isPresent());
}
@Test
public void existingStaticLibIsANativeLinkTargetSoname() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"));
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertTrue(rule.getNativeLinkTarget(CXX_PLATFORM).isPresent());
}
@Test
public void nativeLinkTargetMode() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setSoname("libsoname.so");
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(
rule.getNativeLinkTarget(CXX_PLATFORM).get().getNativeLinkTargetMode(CXX_PLATFORM),
Matchers.equalTo(NativeLinkTargetMode.library("libsoname.so")));
}
@Test
public void nativeLinkTargetDeps() throws Exception {
PrebuiltCxxLibraryBuilder depBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"));
PrebuiltCxxLibraryBuilder exportedDepBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:exported_dep"));
PrebuiltCxxLibraryBuilder ruleBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setExportedDeps(
ImmutableSortedSet.of(depBuilder.getTarget(), exportedDepBuilder.getTarget()));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(
depBuilder.build(), exportedDepBuilder.build(), ruleBuilder.build());
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary dep =
(PrebuiltCxxLibrary) depBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibrary exportedDep =
(PrebuiltCxxLibrary) exportedDepBuilder.build(resolver, filesystem, targetGraph);
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) ruleBuilder.build(resolver, filesystem, targetGraph);
assertThat(
ImmutableList.copyOf(
rule.getNativeLinkTarget(CXX_PLATFORM).get().getNativeLinkTargetDeps(CXX_PLATFORM)),
Matchers.hasItems(dep, exportedDep));
}
@Test
public void nativeLinkTargetInput() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder ruleBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setExportedLinkerFlags(ImmutableList.of("--exported-flag"));
TargetGraph targetGraph = TargetGraphFactory.newInstance(ruleBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) ruleBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput input =
rule.getNativeLinkTarget(CXX_PLATFORM)
.get()
.getNativeLinkTargetInput(CxxPlatformUtils.DEFAULT_PLATFORM);
assertThat(Arg.stringify(input.getArgs(), pathResolver), Matchers.hasItems("--exported-flag"));
}
@Test
public void missingStaticLibPrefersSharedLinking() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"));
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(
rule.getPreferredLinkage(CXX_PLATFORM), Matchers.equalTo(NativeLinkable.Linkage.SHARED));
}
@Test
public void providedDoNotReturnSharedLibs() throws Exception {
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule")).setProvided(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(rule.getSharedLibraries(CXX_PLATFORM).entrySet(), empty());
}
@Test
public void headerOnlyLibPrefersAnyLinking() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder =
new PrebuiltCxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"))
.setHeaderOnly(true);
TargetGraph targetGraph = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary rule =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(
rule.getPreferredLinkage(CXX_PLATFORM), Matchers.equalTo(NativeLinkable.Linkage.ANY));
}
@Test
public void supportedPlatforms() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
// First, make sure without any platform regex, we get something back for each of the interface
// methods.
PrebuiltCxxLibraryBuilder prebuiltCxxLibraryBuilder = new PrebuiltCxxLibraryBuilder(target);
TargetGraph targetGraph1 = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver1 =
new BuildRuleResolver(targetGraph1, new DefaultTargetNodeToBuildRuleTransformer());
PrebuiltCxxLibrary prebuiltCxxLibrary =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver1, filesystem, targetGraph1);
assertThat(
prebuiltCxxLibrary.getSharedLibraries(CxxPlatformUtils.DEFAULT_PLATFORM).entrySet(),
Matchers.not(empty()));
assertThat(
prebuiltCxxLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED)
.getArgs(),
Matchers.not(empty()));
// Now, verify we get nothing when the supported platform regex excludes our platform.
prebuiltCxxLibraryBuilder.setSupportedPlatformsRegex(Pattern.compile("nothing"));
TargetGraph targetGraph2 = TargetGraphFactory.newInstance(prebuiltCxxLibraryBuilder.build());
BuildRuleResolver resolver2 =
new BuildRuleResolver(targetGraph2, new DefaultTargetNodeToBuildRuleTransformer());
prebuiltCxxLibrary =
(PrebuiltCxxLibrary) prebuiltCxxLibraryBuilder.build(resolver2, filesystem, targetGraph2);
assertThat(
prebuiltCxxLibrary.getSharedLibraries(CxxPlatformUtils.DEFAULT_PLATFORM).entrySet(),
empty());
assertThat(
prebuiltCxxLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED)
.getArgs(),
empty());
}
@Test
public void versionSubDir() throws Exception {
BuildTarget dep = BuildTargetFactory.newInstance("//:dep");
PrebuiltCxxLibraryBuilder depBuilder = new PrebuiltCxxLibraryBuilder(dep);
PrebuiltCxxLibraryBuilder builder = new PrebuiltCxxLibraryBuilder(TARGET);
builder.setSelectedVersions(ImmutableMap.of(dep, Version.of("1.0")));
builder.setVersionedSubDir(
VersionMatchedCollection.<String>builder()
.add(ImmutableMap.of(dep, Version.of("1.0")), "sub-dir")
.build());
TargetGraph graph = TargetGraphFactory.newInstance(depBuilder.build(), builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(graph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
depBuilder.build(resolver, filesystem, graph);
PrebuiltCxxLibrary lib = (PrebuiltCxxLibrary) builder.build(resolver, filesystem, graph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(CXX_PLATFORM, Linker.LinkableDepType.STATIC);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver).get(0),
Matchers.equalTo(
filesystem
.resolve("sub-dir")
.resolve(getStaticLibraryPath(builder.build().getConstructorArg()))
.toString()));
}
}