/*
* 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.halide;
import static org.hamcrest.MatcherAssert.assertThat;
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 com.facebook.buck.cxx.Archive;
import com.facebook.buck.cxx.CxxPlatform;
import com.facebook.buck.cxx.CxxPlatformUtils;
import com.facebook.buck.cxx.CxxPreprocessables;
import com.facebook.buck.cxx.CxxSymlinkTreeHeaders;
import com.facebook.buck.cxx.HeaderVisibility;
import com.facebook.buck.cxx.Linker;
import com.facebook.buck.cxx.NativeLinkableInput;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
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.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.step.Step;
import com.facebook.buck.step.TestExecutionContext;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.TargetGraphFactory;
import com.google.common.collect.FluentIterable;
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.Optional;
import java.util.regex.Pattern;
import org.hamcrest.Matchers;
import org.junit.Test;
public class HalideLibraryDescriptionTest {
@Test
public void testCreateBuildRule() throws Exception {
// Set up a #halide-compiler rule, then set up a halide_library rule, and
// check that the library rule depends on the compiler rule.
BuildTarget compilerTarget =
BuildTargetFactory.newInstance("//:rule")
.withFlavors(HalideLibraryDescription.HALIDE_COMPILER_FLAVOR);
BuildTarget libTarget = BuildTargetFactory.newInstance("//:rule");
ProjectFilesystem filesystem = new FakeProjectFilesystem();
HalideLibraryBuilder compilerBuilder = new HalideLibraryBuilder(compilerTarget);
compilerBuilder.setSrcs(
ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("main.cpp"))));
HalideLibraryBuilder libBuilder = new HalideLibraryBuilder(libTarget);
TargetGraph targetGraph =
TargetGraphFactory.newInstance(compilerBuilder.build(), libBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
HalideLibrary lib = (HalideLibrary) libBuilder.build(resolver, filesystem, targetGraph);
// Check that the library rule has the correct preprocessor input.
CxxPlatform cxxPlatform = CxxPlatformUtils.DEFAULT_PLATFORM;
String headerName = "rule.h";
BuildTarget flavoredLibTarget =
libTarget.withFlavors(
HalideLibraryDescription.HALIDE_COMPILE_FLAVOR, cxxPlatform.getFlavor());
Path headerPath =
HalideCompile.headerOutputPath(
flavoredLibTarget, lib.getProjectFilesystem(), Optional.empty());
CxxSymlinkTreeHeaders publicHeaders =
(CxxSymlinkTreeHeaders)
lib.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC).getIncludes().get(0);
assertThat(
publicHeaders.getIncludeType(), Matchers.equalTo(CxxPreprocessables.IncludeType.SYSTEM));
assertThat(
publicHeaders.getNameToPathMap(),
Matchers.equalTo(
ImmutableMap.<Path, SourcePath>of(
Paths.get(headerName),
new ExplicitBuildTargetSourcePath(flavoredLibTarget, headerPath))));
// Check that the library rule has the correct native linkable input.
NativeLinkableInput input =
lib.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
BuildRule buildRule =
FluentIterable.from(input.getArgs())
.transformAndConcat(arg -> arg.getDeps(new SourcePathRuleFinder(resolver)))
.get(0);
assertThat(buildRule, is(instanceOf(Archive.class)));
}
@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.
HalideLibraryBuilder halideLibraryBuilder = new HalideLibraryBuilder(target);
TargetGraph targetGraph1 = TargetGraphFactory.newInstance(halideLibraryBuilder.build());
BuildRuleResolver resolver1 =
new BuildRuleResolver(targetGraph1, new DefaultTargetNodeToBuildRuleTransformer());
HalideLibrary halideLibrary =
(HalideLibrary) halideLibraryBuilder.build(resolver1, filesystem, targetGraph1);
assertThat(
halideLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC)
.getArgs(),
not(Matchers.empty()));
// Now, verify we get nothing when the supported platform regex excludes our platform.
halideLibraryBuilder.setSupportedPlatformsRegex(Pattern.compile("nothing"));
TargetGraph targetGraph2 = TargetGraphFactory.newInstance(halideLibraryBuilder.build());
BuildRuleResolver resolver2 =
new BuildRuleResolver(targetGraph2, new DefaultTargetNodeToBuildRuleTransformer());
halideLibrary = (HalideLibrary) halideLibraryBuilder.build(resolver2, filesystem, targetGraph2);
assertThat(
halideLibrary
.getNativeLinkableInput(
CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC)
.getArgs(),
Matchers.empty());
}
@Test
public void extraCompilerFlags() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
ImmutableList<String> extraCompilerFlags =
ImmutableList.<String>builder()
.add("--test-flag")
.add("test-value")
.add("$TEST_MACRO")
.build();
// Set up a #halide-compile rule, then check its build steps.
BuildTarget compileTarget =
BuildTargetFactory.newInstance("//:rule")
.withFlavors(HalideLibraryDescription.HALIDE_COMPILE_FLAVOR);
HalideLibraryBuilder compileBuilder = new HalideLibraryBuilder(compileTarget);
compileBuilder.setSrcs(
ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("main.cpp"))));
// First, make sure the compile step doesn't include the extra flags.
TargetGraph targetGraph = TargetGraphFactory.newInstance(compileBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
HalideCompile compile = (HalideCompile) compileBuilder.build(resolver, filesystem, targetGraph);
ImmutableList<Step> buildSteps =
compile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
HalideCompilerStep compilerStep = (HalideCompilerStep) buildSteps.get(2);
ImmutableList<String> shellCommand =
compilerStep.getShellCommandInternal(TestExecutionContext.newInstance());
assertThat(shellCommand, not(hasItems("--test-flag", "test-value")));
// Next verify that the shell command picks up on the extra compiler flags.
compileBuilder.setCompilerInvocationFlags(extraCompilerFlags);
targetGraph = TargetGraphFactory.newInstance(compileBuilder.build());
resolver = new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
compile = (HalideCompile) compileBuilder.build(resolver, filesystem, targetGraph);
buildSteps =
compile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
compilerStep = (HalideCompilerStep) buildSteps.get(2);
shellCommand = compilerStep.getShellCommandInternal(TestExecutionContext.newInstance());
assertThat(shellCommand, hasItems("--test-flag", "test-value", "test_macro_expansion"));
}
@Test
public void functionNameOverride() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
// Set up a #halide-compile rule, then check its build steps.
String defaultName = "default-halide-name";
BuildTarget compileTarget =
BuildTargetFactory.newInstance("//:" + defaultName)
.withFlavors(HalideLibraryDescription.HALIDE_COMPILE_FLAVOR);
HalideLibraryBuilder compileBuilder = new HalideLibraryBuilder(compileTarget);
compileBuilder.setSrcs(
ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("main.cpp"))));
// First, make sure the compile step passes the rulename "default-halide-name"
// for the function output name.
TargetGraph targetGraph = TargetGraphFactory.newInstance(compileBuilder.build());
BuildRuleResolver resolver =
new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver));
HalideCompile compile = (HalideCompile) compileBuilder.build(resolver, filesystem, targetGraph);
ImmutableList<Step> buildSteps =
compile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
HalideCompilerStep compilerStep = (HalideCompilerStep) buildSteps.get(2);
ImmutableList<String> shellCommand =
compilerStep.getShellCommandInternal(TestExecutionContext.newInstance());
assertThat(shellCommand, hasItem(defaultName));
// Next verify that the shell command picks up on the override name.
String overrideName = "override-halide-name";
compileBuilder.setFunctionNameOverride(overrideName);
targetGraph = TargetGraphFactory.newInstance(compileBuilder.build());
resolver = new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer());
compile = (HalideCompile) compileBuilder.build(resolver, filesystem, targetGraph);
buildSteps =
compile.getBuildSteps(
FakeBuildContext.withSourcePathResolver(pathResolver), new FakeBuildableContext());
compilerStep = (HalideCompilerStep) buildSteps.get(2);
shellCommand = compilerStep.getShellCommandInternal(TestExecutionContext.newInstance());
assertThat(shellCommand, hasItem(overrideName));
assertThat(shellCommand, not(hasItem(defaultName)));
}
}