/*
* Copyright 2012-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.rules;
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 com.facebook.buck.android.FakeAndroidDirectoryResolver;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.cli.FakeBuckConfig;
import com.facebook.buck.cxx.CxxPlatform;
import com.facebook.buck.cxx.CxxPlatformUtils;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.jvm.java.DefaultJavaLibrary;
import com.facebook.buck.jvm.java.JavaLibraryDescription;
import com.facebook.buck.jvm.java.JavaLibraryDescriptionArg;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.FlavorDomain;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.ocaml.OcamlBinaryDescription;
import com.facebook.buck.ocaml.OcamlLibraryDescription;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.keys.DefaultRuleKeyFactory;
import com.facebook.buck.testutil.FakeFileHashCache;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.util.FakeProcess;
import com.facebook.buck.util.FakeProcessExecutor;
import com.facebook.buck.util.ProcessExecutor;
import com.facebook.buck.util.ProcessExecutorParams;
import com.facebook.buck.util.environment.Platform;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.hash.Hashing;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class KnownBuildRuleTypesTest {
@ClassRule public static TemporaryFolder folder = new TemporaryFolder();
@Rule public TemporaryPaths temporaryFolder = new TemporaryPaths();
private static final String FAKE_XCODE_DEV_PATH = "/Fake/Path/To/Xcode.app/Contents/Developer";
private static final ImmutableMap<String, String> environment =
ImmutableMap.copyOf(System.getenv());
private static BuildRuleParams buildRuleParams;
private static class KnownRuleTestDescription
implements Description<KnownRuleTestDescription.Arg> {
static class Arg extends AbstractDescriptionArg {}
private final String value;
private KnownRuleTestDescription(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public Class<Arg> getConstructorArgType() {
return Arg.class;
}
@Override
public BuildRule createBuildRule(
TargetGraph targetGraph,
BuildRuleParams params,
BuildRuleResolver resolver,
CellPathResolver cellRoots,
Arg args) {
return null;
}
}
@BeforeClass
public static void setupBuildParams() throws IOException {
buildRuleParams =
new FakeBuildRuleParamsBuilder(BuildTargetFactory.newInstance("//:foo")).build();
}
private DefaultJavaLibrary createJavaLibrary(KnownBuildRuleTypes buildRuleTypes)
throws NoSuchBuildTargetException {
JavaLibraryDescription description =
(JavaLibraryDescription)
buildRuleTypes.getDescription(
Description.getBuildRuleType(JavaLibraryDescription.class));
JavaLibraryDescriptionArg arg = JavaLibraryDescriptionArg.builder().setName("foo").build();
BuildRuleResolver resolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
return (DefaultJavaLibrary)
description.createBuildRule(
TargetGraph.EMPTY,
buildRuleParams,
resolver,
TestCellBuilder.createCellRoots(buildRuleParams.getProjectFilesystem()),
arg);
}
@Test
public void whenJavacIsSetInBuckConfigConfiguredRulesCreateJavaLibraryRuleWithDifferentRuleKey()
throws Exception {
final Path javac;
if (Platform.detect() == Platform.WINDOWS) {
javac = Paths.get("C:/Windows/system32/rundll32.exe");
} else {
javac = temporaryFolder.newExecutableFile();
}
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of("tools", ImmutableMap.of("javac", javac.toString()));
BuckConfig buckConfig =
FakeBuckConfig.builder().setFilesystem(filesystem).setSections(sections).build();
KnownBuildRuleTypes buildRuleTypes =
KnownBuildRuleTypesTestUtil.getDefaultKnownBuildRuleTypes(filesystem, environment);
DefaultJavaLibrary libraryRule = createJavaLibrary(buildRuleTypes);
ProcessExecutor processExecutor = createExecutor(javac.toString(), "fakeVersion 0.1");
KnownBuildRuleTypes configuredBuildRuleTypes =
KnownBuildRuleTypes.createBuilder(
buckConfig, filesystem, processExecutor, new FakeAndroidDirectoryResolver())
.build();
DefaultJavaLibrary configuredRule = createJavaLibrary(configuredBuildRuleTypes);
SourcePathRuleFinder ruleFinder =
new SourcePathRuleFinder(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()));
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
FakeFileHashCache hashCache =
new FakeFileHashCache(
ImmutableMap.of(javac, MorePaths.asByteSource(javac).hash(Hashing.sha1())));
RuleKey configuredKey =
new DefaultRuleKeyFactory(0, hashCache, resolver, ruleFinder).build(configuredRule);
RuleKey libraryKey =
new DefaultRuleKeyFactory(0, hashCache, resolver, ruleFinder).build(libraryRule);
assertNotEquals(libraryKey, configuredKey);
}
@Test
public void whenRegisteringDescriptionsLastOneWins() throws Exception {
FlavorDomain<CxxPlatform> cxxPlatforms = FlavorDomain.of("C/C++ platform");
CxxPlatform defaultPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
KnownBuildRuleTypes.Builder buildRuleTypesBuilder = KnownBuildRuleTypes.builder();
buildRuleTypesBuilder.register(new KnownRuleTestDescription("Foo"));
buildRuleTypesBuilder.register(new KnownRuleTestDescription("Bar"));
buildRuleTypesBuilder.register(new KnownRuleTestDescription("Raz"));
buildRuleTypesBuilder.setCxxPlatforms(cxxPlatforms);
buildRuleTypesBuilder.setDefaultCxxPlatform(defaultPlatform);
KnownBuildRuleTypes buildRuleTypes = buildRuleTypesBuilder.build();
assertEquals(
"Only one description should have wound up in the final KnownBuildRuleTypes",
KnownBuildRuleTypes.builder()
.setCxxPlatforms(cxxPlatforms)
.setDefaultCxxPlatform(defaultPlatform)
.build()
.getAllDescriptions()
.size()
+ 1,
buildRuleTypes.getAllDescriptions().size());
boolean foundTestDescription = false;
for (Description<?> description : buildRuleTypes.getAllDescriptions()) {
if (Description.getBuildRuleType(description)
.equals(Description.getBuildRuleType(KnownRuleTestDescription.class))) {
assertFalse("Should only find one test description", foundTestDescription);
foundTestDescription = true;
assertEquals(
"Last description should have won",
"Raz",
((KnownRuleTestDescription) description).getValue());
}
}
}
@Test
public void createInstanceShouldReturnDifferentInstancesIfCalledWithDifferentParameters()
throws Exception {
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
KnownBuildRuleTypes knownBuildRuleTypes1 =
KnownBuildRuleTypes.createInstance(
FakeBuckConfig.builder().build(),
filesystem,
createExecutor(),
new FakeAndroidDirectoryResolver());
final Path javac = temporaryFolder.newExecutableFile();
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of("tools", ImmutableMap.of("javac", javac.toString()));
BuckConfig buckConfig =
FakeBuckConfig.builder().setFilesystem(filesystem).setSections(sections).build();
ProcessExecutor processExecutor = createExecutor(javac.toString(), "");
KnownBuildRuleTypes knownBuildRuleTypes2 =
KnownBuildRuleTypes.createInstance(
buckConfig, filesystem, processExecutor, new FakeAndroidDirectoryResolver());
assertNotEquals(knownBuildRuleTypes1, knownBuildRuleTypes2);
}
@Test
public void canSetDefaultPlatformToDefault() throws Exception {
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of("cxx", ImmutableMap.of("default_platform", "default"));
BuckConfig buckConfig = FakeBuckConfig.builder().setSections(sections).build();
// This would throw if "default" weren't available as a platform.
KnownBuildRuleTypes.createBuilder(
buckConfig, filesystem, createExecutor(), new FakeAndroidDirectoryResolver())
.build();
}
@Test
public void canOverrideMultipleHostPlatforms() throws Exception {
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of(
"cxx#linux-x86_64", ImmutableMap.of("cache_links", "true"),
"cxx#macosx-x86_64", ImmutableMap.of("cache_links", "true"),
"cxx#windows-x86_64", ImmutableMap.of("cache_links", "true"));
BuckConfig buckConfig = FakeBuckConfig.builder().setSections(sections).build();
// It should be legal to override multiple host platforms even though
// only one will be practically used in a build.
KnownBuildRuleTypes.createBuilder(
buckConfig, filesystem, createExecutor(), new FakeAndroidDirectoryResolver())
.build();
}
@Test
public void canOverrideDefaultHostPlatform() throws Exception {
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
Flavor flavor = InternalFlavor.of("flavor");
String flag = "-flag";
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of("cxx#" + flavor, ImmutableMap.of("cflags", flag));
BuckConfig buckConfig = FakeBuckConfig.builder().setSections(sections).build();
KnownBuildRuleTypes knownBuildRuleTypes =
KnownBuildRuleTypes.createBuilder(
buckConfig, filesystem, createExecutor(), new FakeAndroidDirectoryResolver())
.build();
assertThat(
knownBuildRuleTypes.getCxxPlatforms().getValue(flavor).getCflags(),
Matchers.contains(flag));
}
@Test
public void ocamlUsesConfiguredDefaultPlatform() throws Exception {
ProjectFilesystem filesystem = new ProjectFilesystem(temporaryFolder.getRoot());
Flavor flavor = InternalFlavor.of("flavor");
ImmutableMap<String, ImmutableMap<String, String>> sections =
ImmutableMap.of(
"cxx",
ImmutableMap.of("default_platform", flavor.toString()),
"cxx#" + flavor,
ImmutableMap.of());
BuckConfig buckConfig = FakeBuckConfig.builder().setSections(sections).build();
KnownBuildRuleTypes knownBuildRuleTypes =
KnownBuildRuleTypes.createBuilder(
buckConfig, filesystem, createExecutor(), new FakeAndroidDirectoryResolver())
.build();
OcamlLibraryDescription ocamlLibraryDescription =
(OcamlLibraryDescription)
knownBuildRuleTypes.getDescription(
knownBuildRuleTypes.getBuildRuleType("ocaml_library"));
assertThat(
ocamlLibraryDescription.getOcamlBuckConfig().getCxxPlatform(),
Matchers.equalTo(knownBuildRuleTypes.getCxxPlatforms().getValue(flavor)));
OcamlBinaryDescription ocamlBinaryDescription =
(OcamlBinaryDescription)
knownBuildRuleTypes.getDescription(
knownBuildRuleTypes.getBuildRuleType("ocaml_binary"));
assertThat(
ocamlBinaryDescription.getOcamlBuckConfig().getCxxPlatform(),
Matchers.equalTo(knownBuildRuleTypes.getCxxPlatforms().getValue(flavor)));
}
private ProcessExecutor createExecutor() throws IOException {
Path javac = temporaryFolder.newExecutableFile();
return createExecutor(javac.toString(), "");
}
private ProcessExecutor createExecutor(String javac, String version) {
Map<ProcessExecutorParams, FakeProcess> processMap = new HashMap<>();
FakeProcess process = new FakeProcess(0, "", version);
ProcessExecutorParams params =
ProcessExecutorParams.builder().setCommand(ImmutableList.of(javac, "-version")).build();
processMap.put(params, process);
addXcodeSelectProcess(processMap, FAKE_XCODE_DEV_PATH);
processMap.putAll(
KnownBuildRuleTypesTestUtil.getPythonProcessMap(
KnownBuildRuleTypesTestUtil.getPaths(environment)));
return new FakeProcessExecutor(processMap);
}
private static void addXcodeSelectProcess(
Map<ProcessExecutorParams, FakeProcess> processMap, String xcodeSelectPath) {
FakeProcess xcodeSelectOutputProcess = new FakeProcess(0, xcodeSelectPath, "");
ProcessExecutorParams xcodeSelectParams =
ProcessExecutorParams.builder()
.setCommand(ImmutableList.of("xcode-select", "--print-path"))
.build();
processMap.put(xcodeSelectParams, xcodeSelectOutputProcess);
}
}