/* * 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.haskell; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxPlatformUtils; import com.facebook.buck.cxx.Linker; import com.facebook.buck.cxx.NativeLinkable; import com.facebook.buck.cxx.NativeLinkableInput; 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.FakeSourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.args.StringArg; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.coercer.SourceList; import com.facebook.buck.testutil.TargetGraphFactory; import com.facebook.buck.util.MoreCollectors; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.nio.file.Path; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.Test; public class HaskellLibraryDescriptionTest { @Test public void compilerFlags() throws Exception { BuildTarget target = BuildTargetFactory.newInstance("//:rule"); String flag = "-compiler-flag"; HaskellLibraryBuilder builder = new HaskellLibraryBuilder(target).setCompilerFlags(ImmutableList.of(flag)); BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(builder.build()), new DefaultTargetNodeToBuildRuleTransformer()); HaskellLibrary library = builder.build(resolver); library.getCompileInput(CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC); BuildTarget compileTarget = HaskellDescriptionUtils.getCompileBuildTarget( target, CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC); HaskellCompileRule rule = resolver.getRuleWithType(compileTarget, HaskellCompileRule.class); assertThat(rule.getFlags(), Matchers.hasItem(flag)); } @Test public void targetsAndOutputsAreDifferentBetweenLinkStyles() throws Exception { BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(), new DefaultTargetNodeToBuildRuleTransformer()); BuildTarget baseTarget = BuildTargetFactory.newInstance("//:rule"); BuildRule staticLib = new HaskellLibraryBuilder( baseTarget.withFlavors( CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(), HaskellLibraryDescription.Type.STATIC.getFlavor())) .build(resolver); BuildRule staticPicLib = new HaskellLibraryBuilder( baseTarget.withFlavors( CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(), HaskellLibraryDescription.Type.STATIC_PIC.getFlavor())) .build(resolver); BuildRule sharedLib = new HaskellLibraryBuilder( baseTarget.withFlavors( CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor(), HaskellLibraryDescription.Type.SHARED.getFlavor())) .build(resolver); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); ImmutableList<Path> outputs = ImmutableList.of( Preconditions.checkNotNull(staticLib.getSourcePathToOutput()), Preconditions.checkNotNull(staticPicLib.getSourcePathToOutput()), Preconditions.checkNotNull(sharedLib.getSourcePathToOutput())) .stream() .map(pathResolver::getRelativePath) .collect(MoreCollectors.toImmutableList()); assertThat(outputs.size(), Matchers.equalTo(ImmutableSet.copyOf(outputs).size())); ImmutableList<BuildTarget> targets = ImmutableList.of( staticLib.getBuildTarget(), staticPicLib.getBuildTarget(), sharedLib.getBuildTarget()); assertThat(targets.size(), Matchers.equalTo(ImmutableSet.copyOf(targets).size())); } @Test public void linkWhole() throws Exception { BuildTarget target = BuildTargetFactory.newInstance("//:rule"); HaskellLibraryBuilder builder = new HaskellLibraryBuilder(target).setLinkWhole(true); BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(builder.build()), new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); HaskellLibrary library = builder.build(resolver); // Lookup the link whole flags. Linker linker = CxxPlatformUtils.DEFAULT_PLATFORM.getLd().resolve(resolver); ImmutableList<String> linkWholeFlags = FluentIterable.from(linker.linkWhole(StringArg.of("sentinel"))) .transformAndConcat((input) -> Arg.stringifyList(input, pathResolver)) .filter(Predicates.not("sentinel"::equals)) .toList(); // Test static dep type. NativeLinkableInput staticInput = library.getNativeLinkableInput( CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC); assertThat( Arg.stringify(staticInput.getArgs(), pathResolver), hasItems(linkWholeFlags.toArray(new String[linkWholeFlags.size()]))); // Test static-pic dep type. NativeLinkableInput staticPicInput = library.getNativeLinkableInput( CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC_PIC); assertThat( Arg.stringify(staticPicInput.getArgs(), pathResolver), hasItems(linkWholeFlags.toArray(new String[linkWholeFlags.size()]))); // Test shared dep type. NativeLinkableInput sharedInput = library.getNativeLinkableInput( CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.SHARED); assertThat( Arg.stringify(sharedInput.getArgs(), pathResolver), not(hasItems(linkWholeFlags.toArray(new String[linkWholeFlags.size()])))); } @Test public void preferredLinkage() throws Exception { BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(), new DefaultTargetNodeToBuildRuleTransformer()); // Test default value. HaskellLibrary defaultLib = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:default")).build(resolver); assertThat( defaultLib.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM), Matchers.is(NativeLinkable.Linkage.ANY)); // Test `ANY` value. HaskellLibrary anyLib = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:any")) .setPreferredLinkage(NativeLinkable.Linkage.ANY) .build(resolver); assertThat( anyLib.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM), Matchers.is(NativeLinkable.Linkage.ANY)); // Test `STATIC` value. HaskellLibrary staticLib = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:static")) .setPreferredLinkage(NativeLinkable.Linkage.STATIC) .build(resolver); assertThat( staticLib.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM), Matchers.is(NativeLinkable.Linkage.STATIC)); // Test `SHARED` value. HaskellLibrary sharedLib = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:shared")) .setPreferredLinkage(NativeLinkable.Linkage.SHARED) .build(resolver); assertThat( sharedLib.getPreferredLinkage(CxxPlatformUtils.DEFAULT_PLATFORM), Matchers.is(NativeLinkable.Linkage.SHARED)); } @Test public void thinArchivesPropagatesDepFromObjects() throws Exception { BuildTarget target = BuildTargetFactory.newInstance("//:rule"); CxxBuckConfig cxxBuckConfig = new CxxBuckConfig( FakeBuckConfig.builder().setSections("[cxx]", "archive_contents=thin").build()); HaskellLibraryBuilder builder = new HaskellLibraryBuilder( target, FakeHaskellConfig.DEFAULT, cxxBuckConfig, CxxPlatformUtils.DEFAULT_PLATFORMS) .setSrcs( SourceList.ofUnnamedSources(ImmutableSortedSet.of(new FakeSourcePath("Test.hs")))) .setLinkWhole(true); BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(builder.build()), new DefaultTargetNodeToBuildRuleTransformer()); HaskellLibrary library = builder.build(resolver); // Test static dep type. NativeLinkableInput staticInput = library.getNativeLinkableInput( CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC); assertThat( FluentIterable.from(staticInput.getArgs()) .transformAndConcat(arg -> arg.getDeps(new SourcePathRuleFinder(resolver))) .transform(BuildRule::getBuildTarget) .toList(), Matchers.hasItem( HaskellDescriptionUtils.getCompileBuildTarget( library.getBuildTarget(), CxxPlatformUtils.DEFAULT_PLATFORM, Linker.LinkableDepType.STATIC))); } @Test public void platformDeps() throws Exception { HaskellLibraryBuilder depABuilder = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:depA")); HaskellLibraryBuilder depBBuilder = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:depB")); HaskellLibraryBuilder ruleBuilder = new HaskellLibraryBuilder(BuildTargetFactory.newInstance("//:rule")) .setPlatformDeps( PatternMatchedCollection.<ImmutableSortedSet<BuildTarget>>builder() .add( Pattern.compile( CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor().toString(), Pattern.LITERAL), ImmutableSortedSet.of(depABuilder.getTarget())) .add( Pattern.compile("matches nothing", Pattern.LITERAL), ImmutableSortedSet.of(depBBuilder.getTarget())) .build()); TargetGraph targetGraph = TargetGraphFactory.newInstance( depABuilder.build(), depBBuilder.build(), ruleBuilder.build()); BuildRuleResolver resolver = new BuildRuleResolver(targetGraph, new DefaultTargetNodeToBuildRuleTransformer()); HaskellLibrary depA = (HaskellLibrary) resolver.requireRule(depABuilder.getTarget()); HaskellLibrary depB = (HaskellLibrary) resolver.requireRule(depBBuilder.getTarget()); HaskellLibrary rule = (HaskellLibrary) resolver.requireRule(ruleBuilder.getTarget()); assertThat( rule.getCompileDeps(CxxPlatformUtils.DEFAULT_PLATFORM), Matchers.allOf(Matchers.hasItem(depA), not(Matchers.hasItem(depB)))); assertThat( ImmutableList.copyOf( rule.getNativeLinkableExportedDepsForPlatform(CxxPlatformUtils.DEFAULT_PLATFORM)), Matchers.allOf(Matchers.hasItem(depA), not(Matchers.hasItem(depB)))); } }