/*
* Copyright 2015-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.cxx;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.cli.FakeBuckConfig;
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.rules.BinaryBuildRuleToolProvider;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.DependencyAggregationTestUtil;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.FakeBuildRuleParamsBuilder;
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.coercer.PatternMatchedCollection;
import com.facebook.buck.shell.ShBinary;
import com.facebook.buck.shell.ShBinaryBuilder;
import com.facebook.buck.testutil.AllExistingProjectFilesystem;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.junit.Assume;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@SuppressWarnings("PMD.TestClassWithoutTestCases")
@RunWith(Enclosed.class)
public class CxxSourceRuleFactoryTest {
private static final ProjectFilesystem PROJECT_FILESYSTEM = new FakeProjectFilesystem();
private static final CxxPlatform CXX_PLATFORM =
CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build()));
private static <T> void assertContains(ImmutableList<T> container, Iterable<T> items) {
for (T item : items) {
assertThat(container, Matchers.hasItem(item));
}
}
public static class CxxSourceRuleFactoryTests {
private static FakeBuildRule createFakeBuildRule(
String target, SourcePathResolver resolver, BuildRule... deps) {
return new FakeBuildRule(
new FakeBuildRuleParamsBuilder(BuildTargetFactory.newInstance(target))
.setDeclaredDeps(ImmutableSortedSet.copyOf(deps))
.build(),
resolver);
}
@Test
public void createPreprocessAndCompileBuildRulePropagatesCxxPreprocessorDeps() {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
FakeBuildRule dep = resolver.addToIndex(new FakeBuildRule("//:dep1", pathResolver));
CxxPreprocessorInput cxxPreprocessorInput =
CxxPreprocessorInput.builder().addRules(dep.getBuildTarget()).build();
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(resolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(CXX_PLATFORM)
.addCxxPreprocessorInput(cxxPreprocessorInput)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String name = "foo/bar.cpp";
SourcePath input = new PathSourcePath(PROJECT_FILESYSTEM, target.getBasePath().resolve(name));
CxxSource cxxSource = CxxSource.of(CxxSource.Type.CXX, input, ImmutableList.of());
BuildRule cxxPreprocess =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(cxxPreprocess)::iterator,
contains((BuildRule) dep));
cxxPreprocess = cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(cxxPreprocess)::iterator,
contains((BuildRule) dep));
}
@Test
public void preprocessFlagsFromPlatformArePropagated() {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
ImmutableList<String> platformFlags = ImmutableList.of("-some", "-flags");
CxxPlatform platform =
CxxPlatformUtils.build(
new CxxBuckConfig(
FakeBuckConfig.builder()
.setSections(
ImmutableMap.of(
"cxx",
ImmutableMap.of("cxxppflags", Joiner.on(" ").join(platformFlags))))
.build()));
CxxPreprocessorInput cxxPreprocessorInput = CxxPreprocessorInput.EMPTY;
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(resolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.addCxxPreprocessorInput(cxxPreprocessorInput)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String name = "source.cpp";
CxxSource cxxSource =
CxxSource.of(CxxSource.Type.CXX, new FakeSourcePath(name), ImmutableList.of());
// Verify that platform flags make it to the compile rule.
CxxPreprocessAndCompile cxxPreprocess =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertNotEquals(
-1,
Collections.indexOfSubList(
cxxPreprocess
.getPreprocessorDelegate()
.get()
.getCommand(CxxToolFlags.of(), Optional.empty()),
platformFlags));
CxxPreprocessAndCompile cxxPreprocessAndCompile =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertNotEquals(
-1,
Collections.indexOfSubList(
cxxPreprocessAndCompile
.getPreprocessorDelegate()
.get()
.getCommand(CxxToolFlags.of(), Optional.empty()),
platformFlags));
}
@Test
public void createCompileBuildRulePropagatesBuildRuleSourcePathDeps() {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
FakeBuildRule dep =
createFakeBuildRule(
"//:test", new SourcePathResolver(new SourcePathRuleFinder(resolver)));
dep.setOutputFile("foo");
resolver.addToIndex(dep);
SourcePath input = dep.getSourcePathToOutput();
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(resolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(CXX_PLATFORM)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String nameCompile = "foo/bar.ii";
CxxSource cxxSourceCompile =
CxxSource.of(CxxSource.Type.CXX_CPP_OUTPUT, input, ImmutableList.of());
CxxPreprocessAndCompile cxxCompile =
cxxSourceRuleFactory.requireCompileBuildRule(nameCompile, cxxSourceCompile);
assertEquals(ImmutableSortedSet.<BuildRule>of(dep), cxxCompile.getBuildDeps());
String namePreprocessAndCompile = "foo/bar.cpp";
CxxSource cxxSourcePreprocessAndCompile =
CxxSource.of(CxxSource.Type.CXX, input, ImmutableList.of());
CxxPreprocessAndCompile cxxPreprocessAndCompile =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(
namePreprocessAndCompile, cxxSourcePreprocessAndCompile);
assertThat(
DependencyAggregationTestUtil.getDisaggregatedDeps(cxxPreprocessAndCompile)::iterator,
contains((BuildRule) dep));
}
@Test
public void createCompileBuildRulePicOption() {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
Path scratchDir = Paths.get("scratchDir");
CxxSourceRuleFactory.Builder cxxSourceRuleFactoryBuilder =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(resolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(CXX_PLATFORM);
CxxSourceRuleFactory cxxSourceRuleFactoryPDC =
cxxSourceRuleFactoryBuilder.setPicType(CxxSourceRuleFactory.PicType.PDC).build();
CxxSourceRuleFactory cxxSourceRuleFactoryPIC =
cxxSourceRuleFactoryBuilder.setPicType(CxxSourceRuleFactory.PicType.PIC).build();
String name = "foo/bar.ii";
CxxSource cxxSource =
CxxSource.of(CxxSource.Type.CXX_CPP_OUTPUT, new FakeSourcePath(name), ImmutableList.of());
// Verify building a non-PIC compile rule does *not* have the "-fPIC" flag and has the
// expected compile target.
CxxPreprocessAndCompile noPicCompile =
cxxSourceRuleFactoryPDC.requireCompileBuildRule(name, cxxSource);
assertFalse(
noPicCompile
.makeMainStep(pathResolver, scratchDir, false)
.getCommand()
.contains("-fPIC"));
assertEquals(
cxxSourceRuleFactoryPDC.createCompileBuildTarget(name), noPicCompile.getBuildTarget());
// Verify building a PIC compile rule *does* have the "-fPIC" flag and has the
// expected compile target.
CxxPreprocessAndCompile picCompile =
cxxSourceRuleFactoryPIC.requireCompileBuildRule(name, cxxSource);
assertTrue(
picCompile.makeMainStep(pathResolver, scratchDir, false).getCommand().contains("-fPIC"));
assertEquals(
cxxSourceRuleFactoryPIC.createCompileBuildTarget(name), picCompile.getBuildTarget());
name = "foo/bar.cpp";
cxxSource = CxxSource.of(CxxSource.Type.CXX, new FakeSourcePath(name), ImmutableList.of());
// Verify building a non-PIC compile rule does *not* have the "-fPIC" flag and has the
// expected compile target.
CxxPreprocessAndCompile noPicPreprocessAndCompile =
cxxSourceRuleFactoryPDC.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertFalse(
noPicPreprocessAndCompile
.makeMainStep(pathResolver, scratchDir, false)
.getCommand()
.contains("-fPIC"));
assertEquals(
cxxSourceRuleFactoryPDC.createCompileBuildTarget(name),
noPicPreprocessAndCompile.getBuildTarget());
// Verify building a PIC compile rule *does* have the "-fPIC" flag and has the
// expected compile target.
CxxPreprocessAndCompile picPreprocessAndCompile =
cxxSourceRuleFactoryPIC.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertTrue(
picPreprocessAndCompile
.makeMainStep(pathResolver, scratchDir, false)
.getCommand()
.contains("-fPIC"));
assertEquals(
cxxSourceRuleFactoryPIC.createCompileBuildTarget(name),
picPreprocessAndCompile.getBuildTarget());
}
@Test
public void checkPrefixHeaderIsIncluded() {
BuildRuleResolver buildRuleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(buildRuleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
BuildTarget target = BuildTargetFactory.newInstance("//:target");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
Path scratchDir = Paths.get("scratchDir");
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setFilesystem(filesystem)
.setSections(ImmutableMap.of("cxx", ImmutableMap.of("pch_enabled", "false")))
.build();
CxxBuckConfig cxxBuckConfig = new CxxBuckConfig(buckConfig);
CxxPlatform platform = CxxPlatformUtils.build(cxxBuckConfig);
String prefixHeaderName = "test.pch";
SourcePath prefixHeaderSourcePath = new FakeSourcePath(filesystem, prefixHeaderName);
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(cxxBuckConfig)
.setCxxPlatform(platform)
.setPrefixHeader(prefixHeaderSourcePath)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String objcSourceName = "test.m";
CxxSource objcSource =
CxxSource.of(CxxSource.Type.OBJC, new FakeSourcePath(objcSourceName), ImmutableList.of());
CxxPreprocessAndCompile objcPreprocessAndCompile =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(objcSourceName, objcSource);
ImmutableList<String> explicitPrefixHeaderRelatedFlags =
ImmutableList.of("-include", filesystem.resolve(prefixHeaderName).toString());
CxxPreprocessAndCompileStep step =
objcPreprocessAndCompile.makeMainStep(pathResolver, scratchDir, false);
assertContains(step.getCommand(), explicitPrefixHeaderRelatedFlags);
}
@Test
public void duplicateRuleFetchedFromResolverShouldCreateTheSameTarget() {
BuildRuleResolver buildRuleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(buildRuleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
BuildTarget target = BuildTargetFactory.newInstance("//:target");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
BuckConfig buckConfig = FakeBuckConfig.builder().setFilesystem(filesystem).build();
CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(buckConfig));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String objcSourceName = "test.m";
CxxSource objcSource =
CxxSource.of(CxxSource.Type.OBJC, new FakeSourcePath(objcSourceName), ImmutableList.of());
CxxPreprocessAndCompile objcCompile =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(objcSourceName, objcSource);
// Make sure we can get the same build rule twice.
CxxPreprocessAndCompile objcCompile2 =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(objcSourceName, objcSource);
assertEquals(objcCompile.getBuildTarget(), objcCompile2.getBuildTarget());
}
@Test
public void createPreprocessAndCompileBuildRulePropagatesToolDeps() throws Exception {
BuildTarget target = BuildTargetFactory.newInstance("//foo:bar");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
ShBinary cxxpp =
new ShBinaryBuilder(BuildTargetFactory.newInstance("//:cxxpp"))
.setMain(new FakeSourcePath("blah"))
.build(resolver);
ShBinary cxx =
new ShBinaryBuilder(BuildTargetFactory.newInstance("//:cxx"))
.setMain(new FakeSourcePath("blah"))
.build(resolver);
CxxPlatform cxxPlatform =
CXX_PLATFORM
.withCxxpp(
new PreprocessorProvider(
new BinaryBuildRuleToolProvider(cxxpp.getBuildTarget(), ""),
CxxToolProvider.Type.GCC))
.withCxx(
new CompilerProvider(
new BinaryBuildRuleToolProvider(cxx.getBuildTarget(), ""),
CxxToolProvider.Type.GCC));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(resolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(cxxPlatform)
.addCxxPreprocessorInput(CxxPreprocessorInput.EMPTY)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String name = "foo/bar.cpp";
SourcePath input = new PathSourcePath(PROJECT_FILESYSTEM, target.getBasePath().resolve(name));
CxxSource cxxSource = CxxSource.of(CxxSource.Type.CXX, input, ImmutableList.of());
BuildRule cxxPreprocess =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertThat(cxxPreprocess.getBuildDeps(), hasItems(cxx, cxxpp));
cxxPreprocess = cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(name, cxxSource);
assertThat(cxxPreprocess.getBuildDeps(), hasItems(cxx, cxxpp));
}
}
@RunWith(Parameterized.class)
public static class RulesForDifferentSourcesShouldCreateSeaparateTargets {
@SuppressWarnings("serial")
Map<String, String[]> testExampleSourceSets =
new HashMap<String, String[]>() {
{
put("Preprocessable type", new String[] {"1/2/3.c", "1_2/3.c", "1/2_3.c", "1_2_3.c"});
put("Compilable type", new String[] {"1/2/3.i", "1_2/3.i", "1/2_3.i", "1_2_3.i"});
put(
"Same file in different directories",
new String[] {"one-path/file1.c", "another-path/file1.c"});
put(
"Various ASCII chars",
new String[] {
"special/chars.c",
"special_chars.c",
"special chars.c",
"special!chars.c",
"special(chars.c",
"special,chars.c"
});
put(
"Non-ASCII chars",
new String[] {
"special/chars.c", "specialאchars.c", "special漢chars.c", "specialДchars.c"
});
put("One-char names", new String[] {"_.c", ",.c", "漢.c"});
}
};
@Parameterized.Parameters(name = "{0}")
public static Collection<Object> data() {
return Arrays.asList(
new Object[] {
"Preprocessable type",
"Compilable type",
"Same file in different directories",
"Various ASCII chars",
"Non-ASCII chars",
"One-char names",
});
}
@Parameterized.Parameter(0)
public String testExampleSourceSet;
@Test
public void test() {
BuildRuleResolver buildRuleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(buildRuleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
BuildTarget target = BuildTargetFactory.newInstance("//:target");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
ProjectFilesystem filesystem = new AllExistingProjectFilesystem();
BuckConfig buckConfig = FakeBuckConfig.builder().setFilesystem(filesystem).build();
CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(buckConfig));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(pathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
String[] sourceNames = testExampleSourceSets.get(testExampleSourceSet);
Map<String, CxxSource> sources = new HashMap<>();
for (String sourceName : sourceNames) {
sources.put(
sourceName,
CxxSource.of(CxxSource.Type.OBJC, new FakeSourcePath(sourceName), ImmutableList.of()));
}
ImmutableMap<CxxPreprocessAndCompile, SourcePath> rules =
cxxSourceRuleFactory.requirePreprocessAndCompileRules(ImmutableMap.copyOf(sources));
assertEquals(
String.format("Expected %d rules, but found only %d", sourceNames.length, rules.size()),
sourceNames.length,
rules.size());
}
}
@RunWith(Parameterized.class)
public static class CorrectFlagsAreUsedForCompileAndPreprocessBuildRules {
private static final ImmutableList<String> asflags = ImmutableList.of("-asflag", "-asflag");
private static final ImmutableList<String> cflags = ImmutableList.of("-cflag", "-cflag");
private static final ImmutableList<String> cxxflags = ImmutableList.of("-cxxflag", "-cxxflag");
private static final ImmutableList<String> asppflags =
ImmutableList.of("-asppflag", "-asppflag");
private static final ImmutableList<String> cppflags = ImmutableList.of("-cppflag", "-cppflag");
private static final ImmutableList<String> cxxppflags =
ImmutableList.of("-cxxppflag", "-cxxppflag");
private static final ImmutableList<String> explicitCompilerFlags =
ImmutableList.of("-explicit-compilerflag");
private static final ImmutableList<String> explicitCppflags =
ImmutableList.of("-explicit-cppflag");
private static final ImmutableList<String> explicitCxxppflags =
ImmutableList.of("-explicit-cxxppflag");
private static final ImmutableList<String> empty = ImmutableList.of();
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
{"test.i", cflags, explicitCompilerFlags, empty, empty},
{"test.c", cflags, explicitCompilerFlags, cppflags, explicitCppflags},
{"test.ii", cxxflags, explicitCompilerFlags, empty, empty},
{"test.cpp", cxxflags, explicitCompilerFlags, cxxppflags, explicitCxxppflags},
// asm do not have compiler specific flags, nor (non-as) file type specific flags.
{"test.s", empty, empty, asppflags, explicitCppflags},
// ObjC
{"test.mi", cflags, explicitCompilerFlags, empty, empty},
{"test.m", cflags, explicitCompilerFlags, cppflags, explicitCppflags},
{"test.mii", cxxflags, explicitCompilerFlags, empty, empty},
{"test.mm", cxxflags, explicitCompilerFlags, cxxppflags, explicitCxxppflags},
});
}
@Parameterized.Parameter(0)
public String sourceName;
@Parameterized.Parameter(1)
public ImmutableList<String> expectedTypeSpecificFlags;
@Parameterized.Parameter(2)
public ImmutableList<String> expectedCompilerFlags;
@Parameterized.Parameter(3)
public ImmutableList<String> expectedTypeSpecificPreprocessorFlags;
@Parameterized.Parameter(4)
public ImmutableList<String> expectedPreprocessorFlags;
// Some common boilerplate.
private BuildRuleResolver buildRuleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
private SourcePathRuleFinder sourcePathRuleFinder = new SourcePathRuleFinder(buildRuleResolver);
private SourcePathResolver sourcePathResolver = new SourcePathResolver(sourcePathRuleFinder);
private BuildTarget target = BuildTargetFactory.newInstance("//:target");
private BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
private Joiner space = Joiner.on(" ");
@Test
public void forPreprocess() {
CxxSource.Type sourceType =
CxxSource.Type.fromExtension(MorePaths.getFileExtension(Paths.get(sourceName))).get();
Assume.assumeTrue(sourceType.isPreprocessable());
CxxPreprocessorInput cxxPreprocessorInput =
CxxPreprocessorInput.builder()
.putAllPreprocessorFlags(CxxSource.Type.C, explicitCppflags)
.putAllPreprocessorFlags(CxxSource.Type.OBJC, explicitCppflags)
.putAllPreprocessorFlags(CxxSource.Type.ASSEMBLER_WITH_CPP, explicitCppflags)
.putAllPreprocessorFlags(CxxSource.Type.CXX, explicitCxxppflags)
.putAllPreprocessorFlags(CxxSource.Type.OBJCXX, explicitCxxppflags)
.build();
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setSections(
ImmutableMap.of(
"cxx",
ImmutableMap.<String, String>builder()
.put("asppflags", space.join(asppflags))
.put("cppflags", space.join(cppflags))
.put("cxxppflags", space.join(cxxppflags))
.build()))
.setFilesystem(PROJECT_FILESYSTEM)
.build();
CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(buckConfig));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(sourcePathResolver)
.setRuleFinder(sourcePathRuleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.addCxxPreprocessorInput(cxxPreprocessorInput)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
List<String> perFileFlags = ImmutableList.of("-per-file-flag", "-and-another-per-file-flag");
CxxSource cSource = CxxSource.of(sourceType, new FakeSourcePath(sourceName), perFileFlags);
CxxPreprocessAndCompile cPreprocess =
cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(sourceName, cSource);
ImmutableList<String> cPreprocessCommand =
cPreprocess
.getPreprocessorDelegate()
.get()
.getCommand(CxxToolFlags.of(), Optional.empty());
assertContains(cPreprocessCommand, expectedTypeSpecificPreprocessorFlags);
assertContains(cPreprocessCommand, expectedPreprocessorFlags);
assertContains(cPreprocessCommand, perFileFlags);
}
@Test
public void forCompile() {
CxxSource.Type sourceType =
CxxSource.Type.fromExtension(MorePaths.getFileExtension(Paths.get(sourceName))).get();
Path scratchDir = Paths.get("scratchDir");
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setSections(
ImmutableMap.of(
"cxx",
ImmutableMap.<String, String>builder()
.put("asflags", space.join(asflags))
.put("cflags", space.join(cflags))
.put("cxxflags", space.join(cxxflags))
.build()))
.setFilesystem(PROJECT_FILESYSTEM)
.build();
CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(buckConfig));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(sourcePathResolver)
.setRuleFinder(sourcePathRuleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.setCompilerFlags(
CxxFlags.getLanguageFlags(
expectedCompilerFlags,
PatternMatchedCollection.of(),
ImmutableMap.of(),
platform))
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
List<String> perFileFlags = ImmutableList.of("-per-file-flag");
CxxSource source = CxxSource.of(sourceType, new FakeSourcePath(sourceName), perFileFlags);
CxxPreprocessAndCompile rule;
if (source.getType().isPreprocessable()) {
rule = cxxSourceRuleFactory.requirePreprocessAndCompileBuildRule(sourceName, source);
} else {
rule = cxxSourceRuleFactory.requireCompileBuildRule(sourceName, source);
}
ImmutableList<String> command =
rule.makeMainStep(sourcePathResolver, scratchDir, false).getCommand();
assertContains(command, expectedCompilerFlags);
assertContains(command, expectedTypeSpecificFlags);
assertContains(command, asflags);
assertContains(command, perFileFlags);
}
@Test
public void testHashStringsWithAndWithoutIncludePaths() {
CxxSource.Type sourceType =
CxxSource.Type.fromExtension(MorePaths.getFileExtension(Paths.get(sourceName))).get();
Assume.assumeTrue(sourceType.isPreprocessable());
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
CxxPreprocessorInput cxxPreprocessorInput =
CxxPreprocessorInput.builder()
.addIncludes(
CxxHeadersDir.of(
CxxPreprocessables.IncludeType.SYSTEM, new FakeSourcePath("/tmp/sys")))
.build();
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setSections(
ImmutableMap.of(
"cxx",
ImmutableMap.<String, String>builder()
.put("cflags", "-fno-omit-frame-pointer")
.put("cxxflags", "-fno-rtti -fno-exceptions")
.build()))
.setFilesystem(PROJECT_FILESYSTEM)
.build();
CxxPlatform platform = CxxPlatformUtils.build(new CxxBuckConfig(buckConfig));
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(ruleResolver)
.setRuleFinder(ruleFinder)
.setPathResolver(pathResolver)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(platform)
.addCxxPreprocessorInput(cxxPreprocessorInput)
.setCompilerFlags(
CxxFlags.getLanguageFlags(
expectedCompilerFlags,
PatternMatchedCollection.of(),
ImmutableMap.of(),
platform))
.setPicType(CxxSourceRuleFactory.PicType.PIC)
.build();
CxxSource source =
CxxSource.of(sourceType, new FakeSourcePath(sourceName), ImmutableList.of("-DFOO=1"));
ImmutableList<String> withPaths = cxxSourceRuleFactory.getFlagsForSource(source, true);
ImmutableList<String> withoutPaths = cxxSourceRuleFactory.getFlagsForSource(source, false);
// these should differ by include path flags:
assertNotEquals(-1, Joiner.on("#").join(withPaths).indexOf("-isystem#/tmp/sys"));
assertEquals(-1, Joiner.on("#").join(withoutPaths).indexOf("-isystem#/tmp/sys"));
// the "with" set is a strict superset of the "without" set
assertContains(withPaths, withoutPaths);
// things that should be in both:
// per-source-file flag:
assertContains(withPaths, ImmutableList.of("-DFOO=1"));
assertContains(withoutPaths, ImmutableList.of("-DFOO=1"));
// pic option, set in the cxxSourceRuleFactory:
assertContains(withPaths, ImmutableList.of("-fPIC"));
assertContains(withoutPaths, ImmutableList.of("-fPIC"));
}
}
@RunWith(Parameterized.class)
public static class LanguageFlagsArePassed {
@Parameterized.Parameters(name = "{0} -> {1}")
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
{"foo/bar.ii", "c++-cpp-output"},
{"foo/bar.mi", "objective-c-cpp-output"},
{"foo/bar.mii", "objective-c++-cpp-output"},
{"foo/bar.i", "cpp-output"},
});
}
@Parameterized.Parameter(0)
public String name;
@Parameterized.Parameter(1)
public String expected;
@Test
public void test() {
BuildRuleResolver buildRuleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(buildRuleResolver);
SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder);
BuildTarget target = BuildTargetFactory.newInstance("//:target");
BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build();
Path scratchDir = Paths.get("scratchDir");
CxxSourceRuleFactory cxxSourceRuleFactory =
CxxSourceRuleFactory.builder()
.setParams(params)
.setResolver(buildRuleResolver)
.setPathResolver(sourcePathResolver)
.setRuleFinder(ruleFinder)
.setCxxBuckConfig(CxxPlatformUtils.DEFAULT_CONFIG)
.setCxxPlatform(CXX_PLATFORM)
.setPicType(CxxSourceRuleFactory.PicType.PDC)
.build();
SourcePath input = new PathSourcePath(PROJECT_FILESYSTEM, target.getBasePath().resolve(name));
CxxSource cxxSource =
CxxSource.of(
CxxSource.Type.fromExtension(MorePaths.getFileExtension(Paths.get(name))).get(),
input,
ImmutableList.of());
CxxPreprocessAndCompile cxxCompile =
cxxSourceRuleFactory.createCompileBuildRule(name, cxxSource);
assertThat(
cxxCompile.makeMainStep(sourcePathResolver, scratchDir, false).getCommand(),
hasItems("-x", expected));
}
}
}