/*
* Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.cxx;
import static org.junit.Assert.assertThat;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.FlavorDomain;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.TargetGraphAndBuildTargets;
import com.facebook.buck.rules.TargetNode;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.coercer.DefaultTypeCoercerFactory;
import com.facebook.buck.rules.macros.StringWithMacrosUtils;
import com.facebook.buck.shell.Genrule;
import com.facebook.buck.testutil.OptionalMatchers;
import com.facebook.buck.testutil.TargetGraphFactory;
import com.facebook.buck.util.OptionalCompat;
import com.facebook.buck.versions.FixedTargetNodeTranslator;
import com.facebook.buck.versions.NaiveVersionSelector;
import com.facebook.buck.versions.TargetNodeTranslator;
import com.facebook.buck.versions.VersionPropagatorBuilder;
import com.facebook.buck.versions.VersionedAliasBuilder;
import com.facebook.buck.versions.VersionedTargetGraphBuilder;
import com.google.common.base.CaseFormat;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.util.Optional;
import java.util.concurrent.ForkJoinPool;
import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.Test;
public class CxxGenruleDescriptionTest {
private static final ForkJoinPool POOL = new ForkJoinPool(1);
@Test
public void toolPlatformParseTimeDeps() {
for (String macro : ImmutableSet.of("ld", "cc", "cxx")) {
CxxGenruleBuilder builder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:rule#default"))
.setCmd(String.format("$(%s)", macro))
.setOut("foo");
assertThat(
ImmutableSet.copyOf(builder.findImplicitDeps()),
Matchers.equalTo(
ImmutableSet.copyOf(
CxxPlatforms.getParseTimeDeps(CxxPlatformUtils.DEFAULT_PLATFORM))));
}
}
@Test
public void ldFlagsFilter() throws Exception {
for (Linker.LinkableDepType style : Linker.LinkableDepType.values()) {
CxxLibraryBuilder bBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:b"))
.setExportedLinkerFlags(ImmutableList.of(StringWithMacrosUtils.format("-b")));
CxxLibraryBuilder aBuilder =
new CxxLibraryBuilder(BuildTargetFactory.newInstance("//:a"))
.setExportedDeps(ImmutableSortedSet.of(bBuilder.getTarget()))
.setExportedLinkerFlags(ImmutableList.of(StringWithMacrosUtils.format("-a")));
CxxGenruleBuilder builder =
new CxxGenruleBuilder(
BuildTargetFactory.newInstance(
"//:rule#" + CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor()))
.setOut("out")
.setCmd(
String.format(
"$(ldflags-%s-filter //:a //:a)",
CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, style.toString())));
TargetGraph targetGraph =
TargetGraphFactory.newInstance(bBuilder.build(), aBuilder.build(), builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
bBuilder.build(resolver);
aBuilder.build(resolver);
Genrule genrule = (Genrule) builder.build(resolver);
assertThat(
Joiner.on(' ')
.join(Arg.stringify(ImmutableList.of(genrule.getCmd().get()), pathResolver)),
Matchers.containsString("-a"));
assertThat(
Joiner.on(' ')
.join(Arg.stringify(ImmutableList.of(genrule.getCmd().get()), pathResolver)),
Matchers.not(Matchers.containsString("-b")));
}
}
@Test
public void cppflagsNoArgs() throws Exception {
CxxPlatform cxxPlatform =
CxxPlatformUtils.DEFAULT_PLATFORM.withCppflags("-cppflag").withCxxppflags("-cxxppflag");
CxxGenruleBuilder builder =
new CxxGenruleBuilder(
BuildTargetFactory.newInstance("//:rule#" + cxxPlatform.getFlavor()),
new FlavorDomain<>(
"C/C++ Platform", ImmutableMap.of(cxxPlatform.getFlavor(), cxxPlatform)))
.setOut("out")
.setCmd("$(cppflags) $(cxxppflags)");
TargetGraph targetGraph = TargetGraphFactory.newInstance(builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
Genrule genrule = (Genrule) builder.build(resolver);
assertThat(
Joiner.on(' ').join(Arg.stringify(ImmutableList.of(genrule.getCmd().get()), pathResolver)),
Matchers.containsString("-cppflag -cxxppflag"));
}
@Test
public void cflagsNoArgs() throws Exception {
CxxPlatform cxxPlatform =
CxxPlatformUtils.DEFAULT_PLATFORM
.withAsflags("-asflag")
.withCflags("-cflag")
.withCxxflags("-cxxflag");
CxxGenruleBuilder builder =
new CxxGenruleBuilder(
BuildTargetFactory.newInstance("//:rule#" + cxxPlatform.getFlavor()),
new FlavorDomain<>(
"C/C++ Platform", ImmutableMap.of(cxxPlatform.getFlavor(), cxxPlatform)))
.setOut("out")
.setCmd("$(cflags) $(cxxflags)");
TargetGraph targetGraph = TargetGraphFactory.newInstance(builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
Genrule genrule = (Genrule) builder.build(resolver);
for (String expected : ImmutableList.of("-asflag", "-cflag", "-cxxflag")) {
assertThat(
Joiner.on(' ')
.join(Arg.stringify(ImmutableList.of(genrule.getCmd().get()), pathResolver)),
Matchers.containsString(expected));
}
}
@Test
public void targetTranslateConstructorArg() throws NoSuchBuildTargetException {
BuildTarget target = BuildTargetFactory.newInstance("//foo:lib");
BuildTarget original = BuildTargetFactory.newInstance("//hello:world");
BuildTarget translated = BuildTargetFactory.newInstance("//something:else");
CxxGenruleBuilder builder =
new CxxGenruleBuilder(target)
.setCmd(String.format("$(cppflags %s)", original))
.setOut("foo");
TargetNode<CxxGenruleDescriptionArg, CxxGenruleDescription> node = builder.build();
TargetNodeTranslator translator =
new FixedTargetNodeTranslator(
new DefaultTypeCoercerFactory(), ImmutableMap.of(original, translated));
Optional<CxxGenruleDescriptionArg> translatedArg =
node.getDescription()
.translateConstructorArg(
target, node.getCellNames(), translator, node.getConstructorArg());
assertThat(
translatedArg.get().getCmd().get(), Matchers.equalTo("$(cppflags //something:else)"));
}
@Test
public void versionedTargetReferenceIsTranslatedInVersionedGraph() throws Exception {
VersionPropagatorBuilder dep = new VersionPropagatorBuilder("//:dep");
VersionedAliasBuilder versionedDep =
new VersionedAliasBuilder("//:versioned").setVersions("1.0", "//:dep");
CxxGenruleBuilder genruleBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:genrule"))
.setCmd("$(ldflags-shared //:versioned)")
.setOut("foo");
TargetGraph graph =
TargetGraphFactory.newInstance(dep.build(), versionedDep.build(), genruleBuilder.build());
TargetGraphAndBuildTargets transformed =
VersionedTargetGraphBuilder.transform(
new NaiveVersionSelector(),
TargetGraphAndBuildTargets.of(graph, ImmutableSet.of(genruleBuilder.getTarget())),
POOL,
new DefaultTypeCoercerFactory());
CxxGenruleDescriptionArg arg =
extractArg(
transformed.getTargetGraph().get(genruleBuilder.getTarget()),
CxxGenruleDescriptionArg.class);
assertThat(
arg.getCmd(), OptionalMatchers.present(Matchers.equalTo("$(ldflags-shared //:dep)")));
}
@Test
public void versionPropagatorTargetReferenceIsTranslatedInVersionedGraph() throws Exception {
VersionPropagatorBuilder transitiveDep = new VersionPropagatorBuilder("//:transitive_dep");
VersionedAliasBuilder versionedDep =
new VersionedAliasBuilder("//:versioned").setVersions("1.0", "//:transitive_dep");
VersionPropagatorBuilder dep = new VersionPropagatorBuilder("//:dep").setDeps("//:versioned");
CxxGenruleBuilder genruleBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:genrule"))
.setCmd("$(ldflags-shared //:dep)")
.setOut("foo");
TargetGraph graph =
TargetGraphFactory.newInstance(
transitiveDep.build(), versionedDep.build(), dep.build(), genruleBuilder.build());
TargetGraphAndBuildTargets transformed =
VersionedTargetGraphBuilder.transform(
new NaiveVersionSelector(),
TargetGraphAndBuildTargets.of(graph, ImmutableSet.of(genruleBuilder.getTarget())),
POOL,
new DefaultTypeCoercerFactory());
CxxGenruleDescriptionArg arg =
extractArg(
transformed.getTargetGraph().get(genruleBuilder.getTarget()),
CxxGenruleDescriptionArg.class);
assertThat(
arg.getCmd(),
OptionalMatchers.present(
Matchers.matchesPattern(
Pattern.quote("$(ldflags-shared //:dep#v") + "[a-zA-Z0-9]*" + Pattern.quote(")"))));
}
@Test
public void cxxGenruleInLocationMacro() throws Exception {
CxxGenruleBuilder depBuilder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:dep")).setOut("out");
CxxGenruleBuilder builder =
new CxxGenruleBuilder(BuildTargetFactory.newInstance("//:rule"))
.setCmd("$(location //:dep)")
.setOut("out");
TargetGraph targetGraph = TargetGraphFactory.newInstance(depBuilder.build(), builder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
CxxGenrule dep = (CxxGenrule) resolver.requireRule(depBuilder.getTarget());
CxxGenrule rule = (CxxGenrule) resolver.requireRule(builder.getTarget());
Genrule genrule =
(Genrule)
ruleFinder
.getRule(rule.getGenrule(CxxPlatformUtils.DEFAULT_PLATFORM))
.orElseThrow(AssertionError::new);
assertThat(
Arg.stringify(OptionalCompat.asSet(genrule.getCmd()), pathResolver),
Matchers.contains(
pathResolver
.getAbsolutePath(dep.getGenrule(CxxPlatformUtils.DEFAULT_PLATFORM))
.toString()));
}
private static <U> U extractArg(TargetNode<?, ?> node, Class<U> clazz) {
return node.castArg(clazz)
.orElseThrow(
() ->
new AssertionError(
String.format(
"%s: expected constructor arg to be of type %s (was %s)",
node, clazz, node.getConstructorArg().getClass())))
.getConstructorArg();
}
}