/*
* 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.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import com.facebook.buck.apple.xcode.xcodeproj.PBXReference;
import com.facebook.buck.apple.xcode.xcodeproj.SourceTreePath;
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.BuildContext;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.DefaultBuildTargetSourcePath;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.DependencyAggregationTestUtil;
import com.facebook.buck.rules.ExplicitBuildTargetSourcePath;
import com.facebook.buck.rules.FakeBuildContext;
import com.facebook.buck.rules.FakeBuildableContext;
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.SourceWithFlags;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.args.FileListableLinkerInputArg;
import com.facebook.buck.rules.args.StringArg;
import com.facebook.buck.rules.coercer.FrameworkPath;
import com.facebook.buck.rules.coercer.PatternMatchedCollection;
import com.facebook.buck.rules.coercer.SourceList;
import com.facebook.buck.rules.macros.LocationMacro;
import com.facebook.buck.rules.macros.StringWithMacros;
import com.facebook.buck.rules.macros.StringWithMacrosUtils;
import com.facebook.buck.shell.ExportFile;
import com.facebook.buck.shell.ExportFileBuilder;
import com.facebook.buck.shell.Genrule;
import com.facebook.buck.shell.GenruleBuilder;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.TargetGraphFactory;
import com.facebook.buck.util.MoreCollectors;
import com.facebook.buck.util.RichStream;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
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.nio.file.Paths;
import java.util.Collection;
import java.util.Optional;
import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class CxxLibraryDescriptionTest {
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
return ImmutableList.of(
new Object[] {"sandbox_sources=false"}, new Object[] {"sandbox_sources=true"});
}
public CxxLibraryDescriptionTest(String sandboxConfig) {
this.cxxBuckConfig =
new CxxBuckConfig(FakeBuckConfig.builder().setSections("[cxx]", sandboxConfig).build());
}
private final CxxBuckConfig cxxBuckConfig;
private static Optional<SourcePath> getHeaderMaps(
ProjectFilesystem filesystem,
BuildTarget target,
BuildRuleResolver resolver,
CxxPlatform cxxPlatform,
HeaderVisibility headerVisibility) {
if (cxxPlatform.getCpp().resolve(resolver).supportsHeaderMaps()
&& cxxPlatform.getCxxpp().resolve(resolver).supportsHeaderMaps()) {
BuildTarget headerMapBuildTarget =
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, headerVisibility, cxxPlatform.getFlavor());
return Optional.of(
new ExplicitBuildTargetSourcePath(
headerMapBuildTarget,
HeaderSymlinkTreeWithHeaderMap.getPath(filesystem, headerMapBuildTarget)));
} else {
return Optional.empty();
}
}
private 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 createBuildRule() throws Exception {
Assume.assumeFalse("This test assumes no sandboxing", cxxBuckConfig.sandboxSources());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform cxxPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
// Setup a genrule the generates a header we'll list.
String genHeaderName = "test/foo.h";
BuildTarget genHeaderTarget = BuildTargetFactory.newInstance("//:genHeader");
GenruleBuilder genHeaderBuilder =
GenruleBuilder.newGenruleBuilder(genHeaderTarget).setOut(genHeaderName);
// Setup a genrule the generates a source we'll list.
String genSourceName = "test/foo.cpp";
BuildTarget genSourceTarget = BuildTargetFactory.newInstance("//:genSource");
GenruleBuilder genSourceBuilder =
GenruleBuilder.newGenruleBuilder(genSourceTarget).setOut(genSourceName);
// Setup a C/C++ library that we'll depend on form the C/C++ binary description.
BuildTarget depTarget = BuildTargetFactory.newInstance("//:dep");
CxxLibraryBuilder depBuilder =
new CxxLibraryBuilder(depTarget, cxxBuckConfig)
.setExportedHeaders(
SourceList.ofUnnamedSources(ImmutableSortedSet.of(new FakeSourcePath("blah.h"))))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("test.cpp"))));
BuildTarget headerSymlinkTreeTarget =
BuildTarget.builder(depTarget)
.addFlavors(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)
.addFlavors(CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())
.build();
// Setup the build params we'll pass to description when generating the build rules.
BuildTarget target = BuildTargetFactory.newInstance("//:rule");
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactoryHelper.of(filesystem.getRootPath(), target, cxxPlatform, cxxBuckConfig);
String headerName = "test/bar.h";
String privateHeaderName = "test/bar_private.h";
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setExportedHeaders(
ImmutableSortedSet.of(
new FakeSourcePath(headerName),
new DefaultBuildTargetSourcePath(genHeaderTarget)))
.setHeaders(ImmutableSortedSet.of(new FakeSourcePath(privateHeaderName)))
.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new FakeSourcePath("test/bar.cpp")),
SourceWithFlags.of(new DefaultBuildTargetSourcePath(genSourceTarget))))
.setFrameworks(
ImmutableSortedSet.of(
FrameworkPath.ofSourcePath(new FakeSourcePath("/some/framework/path/s.dylib")),
FrameworkPath.ofSourcePath(
new FakeSourcePath("/another/framework/path/a.dylib"))))
.setDeps(ImmutableSortedSet.of(depTarget));
// Build the target graph.
TargetGraph targetGraph =
TargetGraphFactory.newInstance(
genHeaderBuilder.build(),
genSourceBuilder.build(),
depBuilder.build(),
cxxLibraryBuilder.build());
// Build the rules.
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
genHeaderBuilder.build(resolver, filesystem, targetGraph);
genSourceBuilder.build(resolver, filesystem, targetGraph);
depBuilder.build(resolver, filesystem, targetGraph);
CxxLibrary rule = (CxxLibrary) cxxLibraryBuilder.build(resolver, filesystem, targetGraph);
// Verify public preprocessor input.
CxxPreprocessorInput publicInput =
rule.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC);
assertThat(
publicInput.getFrameworks(),
containsInAnyOrder(
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/some/framework/path/s.dylib"))),
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/another/framework/path/a.dylib")))));
CxxSymlinkTreeHeaders publicHeaders = (CxxSymlinkTreeHeaders) publicInput.getIncludes().get(0);
assertThat(publicHeaders.getIncludeType(), equalTo(CxxPreprocessables.IncludeType.LOCAL));
assertThat(
publicHeaders.getNameToPathMap(),
equalTo(
ImmutableMap.<Path, SourcePath>of(
Paths.get(headerName),
new FakeSourcePath(headerName),
Paths.get(genHeaderName),
new DefaultBuildTargetSourcePath(genHeaderTarget))));
assertThat(
publicHeaders.getHeaderMap(),
equalTo(getHeaderMaps(filesystem, target, resolver, cxxPlatform, HeaderVisibility.PUBLIC)));
// Verify private preprocessor input.
CxxPreprocessorInput privateInput =
rule.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PRIVATE);
assertThat(
privateInput.getFrameworks(),
containsInAnyOrder(
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/some/framework/path/s.dylib"))),
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/another/framework/path/a.dylib")))));
CxxSymlinkTreeHeaders privateHeaders =
(CxxSymlinkTreeHeaders) privateInput.getIncludes().get(0);
assertThat(privateHeaders.getIncludeType(), equalTo(CxxPreprocessables.IncludeType.LOCAL));
assertThat(
privateHeaders.getNameToPathMap(),
equalTo(
ImmutableMap.<Path, SourcePath>of(
Paths.get(privateHeaderName), new FakeSourcePath(privateHeaderName))));
assertThat(
privateHeaders.getHeaderMap(),
equalTo(
getHeaderMaps(filesystem, target, resolver, cxxPlatform, HeaderVisibility.PRIVATE)));
// Verify that the archive rule has the correct deps: the object files from our sources.
rule.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
BuildRule archiveRule =
resolver.getRule(
CxxDescriptionEnhancer.createStaticLibraryBuildTarget(
target, cxxPlatform.getFlavor(), CxxSourceRuleFactory.PicType.PDC));
assertNotNull(archiveRule);
assertEquals(
ImmutableSet.of(
cxxSourceRuleFactory.createCompileBuildTarget("test/bar.cpp"),
cxxSourceRuleFactory.createCompileBuildTarget(genSourceName)),
archiveRule
.getBuildDeps()
.stream()
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()));
// Verify that the compile rule for our user-provided source has correct deps setup
// for the various header rules.
BuildRule compileRule1 =
resolver.getRule(cxxSourceRuleFactory.createCompileBuildTarget("test/bar.cpp"));
assertNotNull(compileRule1);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(compileRule1)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
// Verify that the compile rule for our genrule-generated source has correct deps setup
// for the various header rules and the generating genrule.
BuildRule compileRule2 =
resolver.getRule(cxxSourceRuleFactory.createCompileBuildTarget(genSourceName));
assertNotNull(compileRule2);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(compileRule2)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
genSourceTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
}
@Test
public void overrideSoname() throws Exception {
FakeProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform cxxPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
String soname = "test_soname";
// Generate the C++ library rules.
BuildTarget target =
BuildTargetFactory.newInstance("//:rule")
.withFlavors(
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(),
CxxDescriptionEnhancer.SHARED_FLAVOR,
LinkerMapMode.NO_LINKER_MAP.getFlavor());
CxxLibraryBuilder ruleBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setSoname(soname)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.cpp"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(ruleBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxLink rule = (CxxLink) ruleBuilder.build(resolver, filesystem, targetGraph);
Linker linker = cxxPlatform.getLd().resolve(resolver);
ImmutableList<String> sonameArgs = ImmutableList.copyOf(linker.soname(soname));
assertThat(
Arg.stringify(rule.getArgs(), pathResolver),
hasItems(sonameArgs.toArray(new String[sonameArgs.size()])));
}
@Test
public void linkWhole() throws Exception {
FakeProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform cxxPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
// Setup the target name and build params.
BuildTarget target = BuildTargetFactory.newInstance("//:test");
// First, create a cxx library without using link whole.
CxxLibraryBuilder normalBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("test.cpp"))));
TargetGraph normalGraph = TargetGraphFactory.newInstance(normalBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(normalGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxLibrary normal = (CxxLibrary) normalBuilder.build(resolver, filesystem, normalGraph);
// Lookup the link whole flags.
Linker linker = cxxPlatform.getLd().resolve(resolver);
ImmutableList<String> linkWholeFlags =
FluentIterable.from(linker.linkWhole(StringArg.of("sentinel")))
.transformAndConcat((input1) -> Arg.stringifyList(input1, pathResolver))
.filter(Predicates.not("sentinel"::equals))
.toList();
// Verify that the linker args contains the link whole flags.
NativeLinkableInput input =
normal.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
assertThat(
Arg.stringify(input.getArgs(), pathResolver),
not(hasItems(linkWholeFlags.toArray(new String[linkWholeFlags.size()]))));
// Create a cxx library using link whole.
CxxLibraryBuilder linkWholeBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setLinkWhole(true)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.cpp"))));
TargetGraph linkWholeGraph = TargetGraphFactory.newInstance(linkWholeBuilder.build());
resolver = new BuildRuleResolver(normalGraph, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary linkWhole =
(CxxLibrary)
linkWholeBuilder
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("test.cpp"))))
.build(resolver, filesystem, linkWholeGraph);
// Verify that the linker args contains the link whole flags.
NativeLinkableInput linkWholeInput =
linkWhole.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
assertThat(
Arg.stringify(linkWholeInput.getArgs(), pathResolver),
hasItems(linkWholeFlags.toArray(new String[linkWholeFlags.size()])));
}
@Test
public void createCxxLibraryBuildRules() throws Exception {
Assume.assumeFalse("This test assumes no sandboxing", cxxBuckConfig.sandboxSources());
FakeProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform cxxPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
// Setup a normal C++ source
String sourceName = "test/bar.cpp";
// Setup a genrule the generates a header we'll list.
String genHeaderName = "test/foo.h";
BuildTarget genHeaderTarget = BuildTargetFactory.newInstance("//:genHeader");
GenruleBuilder genHeaderBuilder =
GenruleBuilder.newGenruleBuilder(genHeaderTarget).setOut(genHeaderName);
// Setup a genrule the generates a source we'll list.
String genSourceName = "test/foo.cpp";
BuildTarget genSourceTarget = BuildTargetFactory.newInstance("//:genSource");
GenruleBuilder genSourceBuilder =
GenruleBuilder.newGenruleBuilder(genSourceTarget).setOut(genSourceName);
// Setup a C/C++ library that we'll depend on form the C/C++ binary description.
BuildTarget depTarget = BuildTargetFactory.newInstance("//:dep");
CxxLibraryBuilder depBuilder =
new CxxLibraryBuilder(depTarget, cxxBuckConfig)
.setExportedHeaders(
SourceList.ofUnnamedSources(ImmutableSortedSet.of(new FakeSourcePath("blah.h"))))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("test.cpp"))));
BuildTarget sharedLibraryDepTarget =
BuildTarget.builder(depTarget)
.addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR)
.addFlavors(cxxPlatform.getFlavor())
.build();
BuildTarget headerSymlinkTreeTarget =
BuildTarget.builder(depTarget)
.addFlavors(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)
.addFlavors(CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())
.build();
// Setup the build params we'll pass to description when generating the build rules.
BuildTarget target = BuildTargetFactory.newInstance("//:rule");
CxxSourceRuleFactory cxxSourceRuleFactoryPDC =
CxxSourceRuleFactoryHelper.of(
filesystem.getRootPath(),
target,
cxxPlatform,
cxxBuckConfig,
CxxSourceRuleFactory.PicType.PDC);
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setExportedHeaders(
ImmutableSortedMap.of(
genHeaderName, new DefaultBuildTargetSourcePath(genHeaderTarget)))
.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new FakeSourcePath(sourceName)),
SourceWithFlags.of(new DefaultBuildTargetSourcePath(genSourceTarget))))
.setFrameworks(
ImmutableSortedSet.of(
FrameworkPath.ofSourcePath(new FakeSourcePath("/some/framework/path/s.dylib")),
FrameworkPath.ofSourcePath(
new FakeSourcePath("/another/framework/path/a.dylib"))))
.setDeps(ImmutableSortedSet.of(depTarget));
// Build target graph.
TargetGraph targetGraph =
TargetGraphFactory.newInstance(
genHeaderBuilder.build(),
genSourceBuilder.build(),
depBuilder.build(),
cxxLibraryBuilder.build());
// Construct C/C++ library build rules.
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
genHeaderBuilder.build(resolver, filesystem, targetGraph);
genSourceBuilder.build(resolver, filesystem, targetGraph);
depBuilder.build(resolver, filesystem, targetGraph);
CxxLibrary rule = (CxxLibrary) cxxLibraryBuilder.build(resolver, filesystem, targetGraph);
// Verify the C/C++ preprocessor input is setup correctly.
CxxPreprocessorInput publicInput =
rule.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC);
assertThat(
publicInput.getFrameworks(),
containsInAnyOrder(
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/some/framework/path/s.dylib"))),
FrameworkPath.ofSourcePath(
new PathSourcePath(filesystem, Paths.get("/another/framework/path/a.dylib")))));
CxxSymlinkTreeHeaders publicHeaders = (CxxSymlinkTreeHeaders) publicInput.getIncludes().get(0);
assertThat(publicHeaders.getIncludeType(), equalTo(CxxPreprocessables.IncludeType.LOCAL));
assertThat(
publicHeaders.getNameToPathMap(),
equalTo(
ImmutableMap.<Path, SourcePath>of(
Paths.get(genHeaderName), new DefaultBuildTargetSourcePath(genHeaderTarget))));
assertThat(
publicHeaders.getHeaderMap(),
equalTo(getHeaderMaps(filesystem, target, resolver, cxxPlatform, HeaderVisibility.PUBLIC)));
// Verify that the archive rule has the correct deps: the object files from our sources.
rule.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
BuildRule staticRule =
resolver.getRule(
CxxDescriptionEnhancer.createStaticLibraryBuildTarget(
target, cxxPlatform.getFlavor(), CxxSourceRuleFactory.PicType.PDC));
assertNotNull(staticRule);
assertEquals(
ImmutableSet.of(
cxxSourceRuleFactoryPDC.createCompileBuildTarget("test/bar.cpp"),
cxxSourceRuleFactoryPDC.createCompileBuildTarget(genSourceName)),
staticRule
.getBuildDeps()
.stream()
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()));
// Verify that the compile rule for our user-provided source has correct deps setup
// for the various header rules.
BuildRule staticCompileRule1 =
resolver.getRule(cxxSourceRuleFactoryPDC.createCompileBuildTarget("test/bar.cpp"));
assertNotNull(staticCompileRule1);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(staticCompileRule1)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
// Verify that the compile rule for our user-provided source has correct deps setup
// for the various header rules.
BuildRule staticCompileRule2 =
resolver.getRule(cxxSourceRuleFactoryPDC.createCompileBuildTarget(genSourceName));
assertNotNull(staticCompileRule2);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(staticCompileRule2)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
genSourceTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
// Verify that the archive rule has the correct deps: the object files from our sources.
CxxSourceRuleFactory cxxSourceRuleFactoryPIC =
CxxSourceRuleFactoryHelper.of(
filesystem.getRootPath(),
target,
cxxPlatform,
cxxBuckConfig,
CxxSourceRuleFactory.PicType.PIC);
rule.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.SHARED);
BuildRule sharedRule =
resolver.getRule(
CxxDescriptionEnhancer.createSharedLibraryBuildTarget(
target, cxxPlatform.getFlavor(), Linker.LinkType.SHARED));
assertNotNull(sharedRule);
assertEquals(
ImmutableSet.of(
sharedLibraryDepTarget,
cxxSourceRuleFactoryPIC.createCompileBuildTarget("test/bar.cpp"),
cxxSourceRuleFactoryPIC.createCompileBuildTarget(genSourceName)),
sharedRule
.getBuildDeps()
.stream()
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()));
// Verify that the compile rule for our user-provided source has correct deps setup
// for the various header rules.
BuildRule sharedCompileRule1 =
resolver.getRule(cxxSourceRuleFactoryPIC.createCompileBuildTarget("test/bar.cpp"));
assertNotNull(sharedCompileRule1);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(sharedCompileRule1)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
// Verify that the compile rule for our user-provided source has correct deps setup
// for the various header rules.
BuildRule sharedCompileRule2 =
resolver.getRule(cxxSourceRuleFactoryPIC.createCompileBuildTarget(genSourceName));
assertNotNull(sharedCompileRule2);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(sharedCompileRule2)
.map(BuildRule::getBuildTarget)
.collect(MoreCollectors.toImmutableSet()),
containsInAnyOrder(
genHeaderTarget,
genSourceTarget,
headerSymlinkTreeTarget,
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target, HeaderVisibility.PRIVATE, cxxPlatform.getFlavor()),
CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
target,
HeaderVisibility.PUBLIC,
CxxPreprocessables.HeaderMode.SYMLINK_TREE_ONLY.getFlavor())));
}
@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.
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("test.c"))));
TargetGraph targetGraph1 = TargetGraphFactory.newInstance(cxxLibraryBuilder.build());
BuildRuleResolver resolver1 =
new BuildRuleResolver(targetGraph1, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary cxxLibrary =
(CxxLibrary) cxxLibraryBuilder.build(resolver1, filesystem, targetGraph1);
assertThat(
cxxLibrary.getSharedLibraries(CxxPlatformUtils.DEFAULT_PLATFORM).entrySet(), not(empty()));
assertThat(
cxxLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED)
.getArgs(),
not(empty()));
// Now, verify we get nothing when the supported platform regex excludes our platform.
cxxLibraryBuilder.setSupportedPlatformsRegex(Pattern.compile("nothing"));
TargetGraph targetGraph2 = TargetGraphFactory.newInstance(cxxLibraryBuilder.build());
BuildRuleResolver resolver2 =
new BuildRuleResolver(targetGraph2, new DefaultTargetNodeToBuildRuleTransformer());
cxxLibrary = (CxxLibrary) cxxLibraryBuilder.build(resolver2, filesystem, targetGraph2);
assertThat(
cxxLibrary.getSharedLibraries(CxxPlatformUtils.DEFAULT_PLATFORM).entrySet(), empty());
assertThat(
cxxLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED)
.getArgs(),
empty());
}
@Test
public void staticPicLibUsedForStaticPicLinkage() throws Exception {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary lib = (CxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC_PIC);
Arg firstArg = nativeLinkableInput.getArgs().get(0);
assertThat(firstArg, instanceOf(FileListableLinkerInputArg.class));
ImmutableCollection<BuildRule> deps = firstArg.getDeps(new SourcePathRuleFinder(resolver));
assertThat(deps.size(), is(1));
BuildRule buildRule = deps.asList().get(0);
assertThat(
buildRule.getBuildTarget().getFlavors(), hasItem(CxxDescriptionEnhancer.STATIC_PIC_FLAVOR));
}
@Test
public void linkerFlagsLocationMacro() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildTarget target =
BuildTargetFactory.newInstance("//:rule")
.withFlavors(
CxxDescriptionEnhancer.SHARED_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
GenruleBuilder depBuilder =
GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:dep")).setOut("out");
CxxLibraryBuilder builder =
new CxxLibraryBuilder(target, cxxBuckConfig)
.setLinkerFlags(
ImmutableList.of(
StringWithMacrosUtils.format(
"--linker-script=%s", LocationMacro.of(depBuilder.getTarget()))))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.c"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(depBuilder.build(), builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
Genrule dep = depBuilder.build(resolver, filesystem, targetGraph);
assertThat(builder.build().getExtraDeps(), hasItem(dep.getBuildTarget()));
BuildRule binary = builder.build(resolver, filesystem, targetGraph);
assertThat(binary, instanceOf(CxxLink.class));
assertThat(
Arg.stringify(((CxxLink) binary).getArgs(), pathResolver),
hasItem(String.format("--linker-script=%s", dep.getAbsoluteOutputFilePath(pathResolver))));
assertThat(binary.getBuildDeps(), hasItem(dep));
}
@Test
public void locationMacroExpandedLinkerFlag() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(
CxxDescriptionEnhancer.SHARED_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setLinkerFlags(
ImmutableList.of(
StringWithMacrosUtils.format("-Wl,--version-script=%s", LocationMacro.of(location))));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLink lib = (CxxLink) libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getBuildDeps(), hasItem(loc));
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
assertThat(
Arg.stringify(lib.getArgs(), pathResolver),
hasItem(
containsString(
pathResolver
.getRelativePath(Preconditions.checkNotNull(loc.getSourcePathToOutput()))
.toString())));
}
@Test
public void locationMacroExpandedPlatformLinkerFlagPlatformMatch() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(
CxxDescriptionEnhancer.SHARED_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setPlatformLinkerFlags(
PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder()
.add(
Pattern.compile(CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor().toString()),
ImmutableList.of(
StringWithMacrosUtils.format(
"-Wl,--version-script=%s", LocationMacro.of(location))))
.build());
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLink lib = (CxxLink) libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getBuildDeps(), hasItem(loc));
assertThat(
Arg.stringify(lib.getArgs(), pathResolver),
hasItem(
String.format(
"-Wl,--version-script=%s",
pathResolver.getAbsolutePath(
Preconditions.checkNotNull(loc.getSourcePathToOutput())))));
assertThat(
Arg.stringify(lib.getArgs(), pathResolver),
not(hasItem(pathResolver.getAbsolutePath(loc.getSourcePathToOutput()).toString())));
}
@Test
public void locationMacroExpandedPlatformLinkerFlagNoPlatformMatch() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(
CxxDescriptionEnhancer.SHARED_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setPlatformLinkerFlags(
PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder()
.add(
Pattern.compile("notarealplatform"),
ImmutableList.of(
StringWithMacrosUtils.format(
"-Wl,--version-script=%s", LocationMacro.of(location))))
.build());
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLink lib = (CxxLink) libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getBuildDeps(), not(hasItem(loc)));
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
assertThat(
Arg.stringify(lib.getArgs(), pathResolver),
not(
hasItem(
containsString(
pathResolver
.getRelativePath(Preconditions.checkNotNull(loc.getSourcePathToOutput()))
.toString()))));
}
@Test
public void locationMacroExpandedExportedLinkerFlag() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setExportedLinkerFlags(
ImmutableList.of(
StringWithMacrosUtils.format("-Wl,--version-script=%s", LocationMacro.of(location))));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLibrary lib = (CxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
assertThat(
FluentIterable.from(nativeLinkableInput.getArgs())
.transformAndConcat(arg -> arg.getDeps(ruleFinder))
.toSet(),
hasItem(loc));
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver),
hasItem(
containsString(
pathResolver
.getRelativePath(Preconditions.checkNotNull(loc.getSourcePathToOutput()))
.toString())));
}
@Test
public void locationMacroExpandedExportedPlatformLinkerFlagPlatformMatch() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setExportedPlatformLinkerFlags(
PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder()
.add(
Pattern.compile(CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor().toString()),
ImmutableList.of(
StringWithMacrosUtils.format(
"-Wl,--version-script=%s", LocationMacro.of(location))))
.build());
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLibrary lib = (CxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
assertThat(
FluentIterable.from(nativeLinkableInput.getArgs())
.transformAndConcat(arg -> arg.getDeps(ruleFinder))
.toSet(),
hasItem(loc));
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver),
hasItem(
containsString(
pathResolver
.getRelativePath(Preconditions.checkNotNull(loc.getSourcePathToOutput()))
.toString())));
}
@Test
public void locationMacroExpandedExportedPlatformLinkerFlagNoPlatformMatch() throws Exception {
BuildTarget location = BuildTargetFactory.newInstance("//:loc");
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ExportFileBuilder locBuilder = ExportFileBuilder.newExportFileBuilder(location);
locBuilder.setOut("somewhere.over.the.rainbow");
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
libBuilder.setExportedPlatformLinkerFlags(
PatternMatchedCollection.<ImmutableList<StringWithMacros>>builder()
.add(
Pattern.compile("notarealplatform"),
ImmutableList.of(
StringWithMacrosUtils.format(
"-Wl,--version-script=%s", LocationMacro.of(location))))
.build());
TargetGraph targetGraph =
TargetGraphFactory.newInstance(libBuilder.build(), locBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ExportFile loc = locBuilder.build(resolver, filesystem, targetGraph);
CxxLibrary lib = (CxxLibrary) libBuilder.build(resolver, filesystem, targetGraph);
NativeLinkableInput nativeLinkableInput =
lib.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
assertThat(
FluentIterable.from(nativeLinkableInput.getArgs())
.transformAndConcat(arg -> arg.getDeps(ruleFinder))
.toSet(),
not(hasItem(loc)));
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
assertThat(
Arg.stringify(nativeLinkableInput.getArgs(), pathResolver),
not(
hasItem(
containsString(
pathResolver
.getRelativePath(Preconditions.checkNotNull(loc.getSourcePathToOutput()))
.toString()))));
}
@Test
public void libraryWithoutSourcesDoesntHaveOutput() throws Exception {
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(
CxxDescriptionEnhancer.STATIC_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
BuildRule lib = libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getSourcePathToOutput(), nullValue());
}
@Test
public void libraryWithoutSourcesDoesntBuildAnything() throws Exception {
BuildTarget target =
BuildTargetFactory.newInstance("//foo:bar")
.withFlavors(
CxxDescriptionEnhancer.STATIC_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxLibraryBuilder libBuilder = new CxxLibraryBuilder(target, cxxBuckConfig);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
BuildRule lib = libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getBuildDeps(), is(empty()));
assertThat(
lib.getBuildSteps(FakeBuildContext.NOOP_CONTEXT, new FakeBuildableContext()), is(empty()));
}
@Test
public void nativeLinkableDeps() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary dep =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"), cxxBuckConfig)
.build(resolver);
CxxLibrary rule =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setDeps(ImmutableSortedSet.of(dep.getBuildTarget()))
.build(resolver);
assertThat(
rule.getNativeLinkableDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(dep));
assertThat(
ImmutableList.copyOf(
rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM)),
Matchers.<NativeLinkable>empty());
}
@Test
public void nativeLinkableExportedDeps() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary dep =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"), cxxBuckConfig)
.build(resolver);
CxxLibrary rule =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setExportedDeps(ImmutableSortedSet.of(dep.getBuildTarget()))
.build(resolver);
assertThat(
ImmutableList.copyOf(
rule.getNativeLinkableDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM)),
empty());
assertThat(
rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(dep));
}
@Test
public void nativeLinkTargetMode() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary rule =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setSoname("libsoname.so")
.build(resolver);
assertThat(
rule.getNativeLinkTargetMode(CxxPlatformUtils.DEFAULT_PLATFORM),
equalTo(NativeLinkTargetMode.library("libsoname.so")));
}
@Test
public void nativeLinkTargetDeps() throws Exception {
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary dep =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep"), cxxBuckConfig)
.build(resolver);
CxxLibrary exportedDep =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:exported_dep"), cxxBuckConfig)
.build(resolver);
CxxLibrary rule =
(CxxLibrary)
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setExportedDeps(
ImmutableSortedSet.of(dep.getBuildTarget(), exportedDep.getBuildTarget()))
.build(resolver);
assertThat(
ImmutableList.copyOf(rule.getNativeLinkTargetDeps(CxxPlatformUtils.DEFAULT_PLATFORM)),
hasItems(dep, exportedDep));
}
@Test
public void nativeLinkTargetInput() throws Exception {
CxxLibraryBuilder ruleBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setLinkerFlags(ImmutableList.of(StringWithMacrosUtils.format("--flag")))
.setExportedLinkerFlags(
ImmutableList.of(StringWithMacrosUtils.format("--exported-flag")));
BuildRuleResolver resolver =
new BuildRuleResolver(
TargetGraphFactory.newInstance(ruleBuilder.build()),
new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxLibrary rule = (CxxLibrary) ruleBuilder.build(resolver);
NativeLinkableInput input = rule.getNativeLinkTargetInput(CxxPlatformUtils.DEFAULT_PLATFORM);
assertThat(Arg.stringify(input.getArgs(), pathResolver), hasItems("--flag", "--exported-flag"));
}
@Test
public void exportedDepsArePropagatedToRuntimeDeps() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxBinaryBuilder cxxBinaryBuilder =
new CxxBinaryBuilder(BuildTargetFactory.newInstance("//:dep"));
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:lib"), cxxBuckConfig)
.setExportedDeps(ImmutableSortedSet.of(cxxBinaryBuilder.getTarget()));
BuildRuleResolver resolver =
new BuildRuleResolver(
TargetGraphFactory.newInstance(cxxLibraryBuilder.build(), cxxBinaryBuilder.build()),
new DefaultTargetNodeToBuildRuleTransformer());
cxxBinaryBuilder.build(resolver, filesystem);
CxxLibrary cxxLibrary = (CxxLibrary) cxxLibraryBuilder.build(resolver, filesystem);
assertThat(
cxxLibrary.getRuntimeDeps().collect(MoreCollectors.toImmutableSet()),
hasItem(cxxBinaryBuilder.getTarget()));
}
@Test
public void sharedLibraryShouldLinkOwnRequiredLibraries() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform platform = CxxPlatformUtils.DEFAULT_PLATFORM;
CxxLibraryBuilder libraryBuilder =
new CxxLibraryBuilder(
BuildTargetFactory.newInstance("//:foo")
.withFlavors(platform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR),
cxxBuckConfig);
libraryBuilder
.setLibraries(
ImmutableSortedSet.of(
FrameworkPath.ofSourceTreePath(
new SourceTreePath(
PBXReference.SourceTree.SDKROOT,
Paths.get("/usr/lib/libz.dylib"),
Optional.empty())),
FrameworkPath.ofSourcePath(new FakeSourcePath("/another/path/liba.dylib"))))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.c"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxLink library = (CxxLink) libraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(
Arg.stringify(library.getArgs(), pathResolver),
hasItems("-L", "/another/path", "$SDKROOT/usr/lib", "-la", "-lz"));
}
@Test
public void sharedLibraryShouldLinkOwnRequiredLibrariesForCxxLibrary() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxPlatform platform = CxxPlatformUtils.DEFAULT_PLATFORM;
ImmutableSortedSet<FrameworkPath> libraries =
ImmutableSortedSet.of(
FrameworkPath.ofSourcePath(new FakeSourcePath("/some/path/libs.dylib")),
FrameworkPath.ofSourcePath(new FakeSourcePath("/another/path/liba.dylib")));
CxxLibraryBuilder libraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:foo"), cxxBuckConfig);
libraryBuilder
.setLibraries(libraries)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.c"))));
TargetGraph targetGraph = TargetGraphFactory.newInstance(libraryBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary library = (CxxLibrary) libraryBuilder.build(resolver, filesystem, targetGraph);
assertThat(library.getNativeLinkTargetInput(platform).getLibraries(), equalTo(libraries));
}
@Test
public void ruleWithoutHeadersDoesNotUseSymlinkTree() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig);
BuildRuleResolver resolver =
new BuildRuleResolver(
TargetGraphFactory.newInstance(cxxLibraryBuilder.build()),
new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
CxxLibrary rule = (CxxLibrary) cxxLibraryBuilder.build(resolver, filesystem);
CxxPreprocessorInput input =
rule.getCxxPreprocessorInput(CxxPlatformUtils.DEFAULT_PLATFORM, HeaderVisibility.PUBLIC);
assertThat(getHeaderNames(input.getIncludes()), empty());
assertThat(ImmutableSortedSet.copyOf(input.getDeps(resolver, ruleFinder)), empty());
}
@Test
public void thinArchiveSettingIsPropagatedToArchive() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildTarget target =
BuildTargetFactory.newInstance("//:rule")
.withFlavors(
CxxDescriptionEnhancer.STATIC_FLAVOR,
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor());
CxxLibraryBuilder libBuilder =
new CxxLibraryBuilder(
target,
new CxxBuckConfig(
FakeBuckConfig.builder()
.setSections(
"[cxx]",
"archive_contents=thin",
"sandbox_sources=" + cxxBuckConfig.sandboxSources())
.build()),
CxxPlatformUtils.DEFAULT_PLATFORMS);
TargetGraph targetGraph = TargetGraphFactory.newInstance(libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
libBuilder.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new PathSourcePath(filesystem, Paths.get("test.cpp")))));
Archive lib = (Archive) libBuilder.build(resolver, filesystem, targetGraph);
assertThat(lib.getContents(), equalTo(Archive.Contents.THIN));
}
@Test
public void forceStatic() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setPreferredLinkage(NativeLinkable.Linkage.STATIC);
BuildRuleResolver resolver =
new BuildRuleResolver(
TargetGraphFactory.newInstance(cxxLibraryBuilder.build()),
new DefaultTargetNodeToBuildRuleTransformer());
CxxLibrary rule = (CxxLibrary) cxxLibraryBuilder.build(resolver, filesystem);
assertThat(
rule.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM),
equalTo(NativeLinkable.Linkage.STATIC));
}
@Test
public void srcsFromCxxGenrule() throws Exception {
CxxGenruleBuilder srcBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:src")).setOut("foo.cpp");
CxxLibraryBuilder libraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:lib"))
.setSrcs(
ImmutableSortedSet.of(
SourceWithFlags.of(new DefaultBuildTargetSourcePath(srcBuilder.getTarget()))));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(srcBuilder.build(), libraryBuilder.build());
BuildRuleResolver ruleResolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.requireRule(
libraryBuilder
.getTarget()
.withAppendedFlavors(
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(),
CxxLibraryDescription.Type.STATIC.getFlavor()));
verifySourcePaths(ruleResolver);
}
@Test
public void headersFromCxxGenrule() throws Exception {
CxxGenruleBuilder srcBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:src")).setOut("foo.h");
CxxLibraryBuilder libraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:lib"))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.cpp"))))
.setHeaders(
ImmutableSortedSet.of(new DefaultBuildTargetSourcePath(srcBuilder.getTarget())));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(srcBuilder.build(), libraryBuilder.build());
BuildRuleResolver ruleResolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.requireRule(
libraryBuilder
.getTarget()
.withAppendedFlavors(
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(),
CxxLibraryDescription.Type.STATIC.getFlavor()));
verifySourcePaths(ruleResolver);
}
@Test
public void locationMacroFromCxxGenrule() throws Exception {
CxxGenruleBuilder srcBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:src")).setOut("linker.script");
CxxLibraryBuilder libraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:lib"))
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("foo.cpp"))))
.setLinkerFlags(
ImmutableList.of(
StringWithMacrosUtils.format("%s", LocationMacro.of(srcBuilder.getTarget()))));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(srcBuilder.build(), libraryBuilder.build());
BuildRuleResolver ruleResolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.requireRule(
libraryBuilder
.getTarget()
.withAppendedFlavors(
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(),
CxxLibraryDescription.Type.SHARED.getFlavor()));
verifySourcePaths(ruleResolver);
}
@Test
public void platformDeps() throws Exception {
CxxLibraryBuilder depABuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep_a"), cxxBuckConfig);
CxxLibraryBuilder depBBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:dep_b"), cxxBuckConfig);
CxxLibraryBuilder cxxLibraryBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setExportedPlatformDeps(
PatternMatchedCollection.<ImmutableSortedSet<BuildTarget>>builder()
.add(
Pattern.compile(CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor().toString()),
ImmutableSortedSet.of(depABuilder.getTarget()))
.add(Pattern.compile("other"), ImmutableSortedSet.of(depBBuilder.getTarget()))
.build());
BuildRuleResolver resolver =
new BuildRuleResolver(
TargetGraphFactory.newInstance(
depABuilder.build(), depBBuilder.build(), cxxLibraryBuilder.build()),
new DefaultTargetNodeToBuildRuleTransformer());
resolver.requireRule(depABuilder.getTarget());
resolver.requireRule(depBBuilder.getTarget());
CxxLibrary rule = (CxxLibrary) resolver.requireRule(cxxLibraryBuilder.getTarget());
assertThat(
rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(resolver.requireRule(depABuilder.getTarget())));
assertThat(
rule.getCxxPreprocessorDeps(CxxPlatformUtils.DEFAULT_PLATFORM),
Matchers.contains(resolver.requireRule(depABuilder.getTarget())));
}
@Test
public void inferCaptureAllIncludesExportedDeps() throws Exception {
CxxLibraryBuilder exportedDepBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:exported_dep"), cxxBuckConfig)
.setSrcs(ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("dep.c"))));
CxxLibraryBuilder ruleBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:rule"), cxxBuckConfig)
.setExportedDeps(ImmutableSortedSet.of(exportedDepBuilder.getTarget()));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(exportedDepBuilder.build(), ruleBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
BuildRule rule =
resolver.requireRule(
ruleBuilder
.getTarget()
.withFlavors(
CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get(),
CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor()));
assertThat(
RichStream.from(rule.getBuildDeps())
.map(BuildRule::getBuildTarget)
.map(t -> t.withFlavors())
.toImmutableSet(),
hasItem(exportedDepBuilder.getTarget()));
}
/**
* Verify that all source paths are resolvable, which wouldn't be the case if `cxx_genrule`
* outputs were not handled correctly.
*/
private void verifySourcePaths(BuildRuleResolver resolver) {
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
BuildContext buildContext = FakeBuildContext.withSourcePathResolver(pathResolver);
BuildableContext buildableContext = new FakeBuildableContext();
for (BuildRule rule : resolver.getBuildRules()) {
rule.getBuildSteps(buildContext, buildableContext);
}
}
}