Java tutorial
/* * 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.features.haskell; import com.facebook.buck.core.cell.CellPathResolver; import com.facebook.buck.core.description.arg.CommonDescriptionArg; import com.facebook.buck.core.description.arg.HasDepsQuery; import com.facebook.buck.core.description.attr.ImplicitDepsInferringDescription; import com.facebook.buck.core.model.BuildTarget; import com.facebook.buck.core.model.Flavor; import com.facebook.buck.core.model.FlavorConvertible; import com.facebook.buck.core.model.FlavorDomain; import com.facebook.buck.core.model.Flavored; import com.facebook.buck.core.model.InternalFlavor; import com.facebook.buck.core.model.impl.BuildTargetPaths; import com.facebook.buck.core.model.targetgraph.BuildRuleCreationContextWithTargetGraph; import com.facebook.buck.core.model.targetgraph.DescriptionWithTargetGraph; import com.facebook.buck.core.rules.ActionGraphBuilder; import com.facebook.buck.core.rules.BuildRule; import com.facebook.buck.core.rules.BuildRuleParams; import com.facebook.buck.core.rules.SourcePathRuleFinder; import com.facebook.buck.core.rules.impl.SymlinkTree; import com.facebook.buck.core.sourcepath.DefaultBuildTargetSourcePath; import com.facebook.buck.core.sourcepath.resolver.SourcePathResolver; import com.facebook.buck.core.sourcepath.resolver.impl.DefaultSourcePathResolver; import com.facebook.buck.core.toolchain.ToolchainProvider; import com.facebook.buck.core.toolchain.tool.impl.CommandTool; import com.facebook.buck.core.util.immutables.BuckStyleImmutable; import com.facebook.buck.cxx.CxxDeps; import com.facebook.buck.cxx.CxxDescriptionEnhancer; import com.facebook.buck.cxx.CxxPreprocessorDep; import com.facebook.buck.cxx.toolchain.CxxBuckConfig; import com.facebook.buck.cxx.toolchain.linker.Linker; import com.facebook.buck.cxx.toolchain.linker.Linkers; import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkable; import com.facebook.buck.io.filesystem.ProjectFilesystem; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.rules.args.StringArg; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.coercer.SourceSortedSet; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.rules.query.QueryUtils; import com.facebook.buck.util.MoreIterables; import com.facebook.buck.util.RichStream; import com.facebook.buck.versions.VersionRoot; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import java.nio.file.Path; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; import org.immutables.value.Value; public class HaskellBinaryDescription implements DescriptionWithTargetGraph<HaskellBinaryDescriptionArg>, ImplicitDepsInferringDescription<HaskellBinaryDescription.AbstractHaskellBinaryDescriptionArg>, Flavored, VersionRoot<HaskellBinaryDescriptionArg> { private static final FlavorDomain<Type> BINARY_TYPE = FlavorDomain.from("Haskell Binary Type", Type.class); private final ToolchainProvider toolchainProvider; private final CxxBuckConfig cxxBuckConfig; public HaskellBinaryDescription(ToolchainProvider toolchainProvider, CxxBuckConfig cxxBuckConfig) { this.toolchainProvider = toolchainProvider; this.cxxBuckConfig = cxxBuckConfig; } @Override public Class<HaskellBinaryDescriptionArg> getConstructorArgType() { return HaskellBinaryDescriptionArg.class; } private Linker.LinkableDepType getLinkStyle(HaskellBinaryDescriptionArg arg, Optional<Type> type) { if (type.isPresent()) { return type.get().getLinkStyle(); } if (arg.getLinkStyle().isPresent()) { return arg.getLinkStyle().get(); } return Linker.LinkableDepType.STATIC; } // Return the C/C++ platform to build against. private HaskellPlatform getPlatform(BuildTarget target, AbstractHaskellBinaryDescriptionArg arg) { HaskellPlatformsProvider haskellPlatformsProvider = getHaskellPlatformsProvider(); FlavorDomain<HaskellPlatform> platforms = haskellPlatformsProvider.getHaskellPlatforms(); Optional<HaskellPlatform> flavorPlatform = platforms.getValue(target); if (flavorPlatform.isPresent()) { return flavorPlatform.get(); } if (arg.getPlatform().isPresent()) { return platforms.getValue(arg.getPlatform().get()); } return haskellPlatformsProvider.getDefaultHaskellPlatform(); } @Override public BuildRule createBuildRule(BuildRuleCreationContextWithTargetGraph context, BuildTarget buildTarget, BuildRuleParams params, HaskellBinaryDescriptionArg args) { ProjectFilesystem projectFilesystem = context.getProjectFilesystem(); CellPathResolver cellRoots = context.getCellPathResolver(); HaskellPlatform platform = getPlatform(buildTarget, args); ActionGraphBuilder graphBuilder = context.getActionGraphBuilder(); Optional<Type> type = BINARY_TYPE.getValue(buildTarget); // Handle #ghci flavor if (type.isPresent() && type.get() == Type.GHCI) { return HaskellDescriptionUtils.requireGhciRule(buildTarget, projectFilesystem, params, cellRoots, graphBuilder, platform, cxxBuckConfig, args.getDeps(), args.getPlatformDeps(), args.getSrcs(), args.getGhciPreloadDeps(), args.getGhciPlatformPreloadDeps(), args.getCompilerFlags(), Optional.empty(), Optional.empty(), ImmutableList.of(), args.isEnableProfiling()); } SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); Linker.LinkableDepType depType = getLinkStyle(args, type); // The target to use for the link rule. BuildTarget binaryTarget = buildTarget.withFlavors(InternalFlavor.of("binary")); // Maintain backwards compatibility to ease upgrade flows. if (platform.shouldUsedOldBinaryOutputLocation().orElse(true)) { binaryTarget = binaryTarget.withAppendedFlavors(platform.getFlavor()); } ImmutableSet.Builder<BuildRule> depsBuilder = ImmutableSet.builder(); depsBuilder.addAll(CxxDeps.builder().addDeps(args.getDeps()).addPlatformDeps(args.getPlatformDeps()).build() .get(graphBuilder, platform.getCxxPlatform())); ImmutableList<BuildRule> depQueryDeps = args.getDepsQuery() .map(query -> Objects.requireNonNull(query.getResolvedQuery()).stream().map(graphBuilder::getRule) .filter(NativeLinkable.class::isInstance)) .orElse(Stream.of()).collect(ImmutableList.toImmutableList()); depsBuilder.addAll(depQueryDeps); ImmutableSet<BuildRule> deps = depsBuilder.build(); // Inputs we'll be linking (archives, objects, etc.) ImmutableList.Builder<Arg> linkInputsBuilder = ImmutableList.builder(); // Additional linker flags passed to the Haskell linker ImmutableList.Builder<Arg> linkFlagsBuilder = ImmutableList.builder(); CommandTool.Builder executableBuilder = new CommandTool.Builder(); // Add the binary as the first argument. executableBuilder.addArg(SourcePathArg.of(DefaultBuildTargetSourcePath.of(binaryTarget))); Path outputDir = BuildTargetPaths.getGenPath(projectFilesystem, binaryTarget, "%s").getParent(); Path outputPath = outputDir.resolve(binaryTarget.getShortName()); Path absBinaryDir = buildTarget.getCellPath().resolve(outputDir); // Special handling for dynamically linked binaries. if (depType == Linker.LinkableDepType.SHARED) { // Create a symlink tree with for all shared libraries needed by this binary. SymlinkTree sharedLibraries = graphBuilder.addToIndex( CxxDescriptionEnhancer.createSharedLibrarySymlinkTree(buildTarget, projectFilesystem, graphBuilder, ruleFinder, platform.getCxxPlatform(), deps, r -> Optional.empty())); // Embed a origin-relative library path into the binary so it can find the shared libraries. // The shared libraries root is absolute. Also need an absolute path to the linkOutput linkFlagsBuilder.addAll(StringArg.from(MoreIterables.zipAndConcat(Iterables.cycle("-optl"), Linkers.iXlinker("-rpath", String.format("%s/%s", platform.getCxxPlatform().getLd().resolve(graphBuilder).origin(), absBinaryDir.relativize(sharedLibraries.getRoot()).toString()))))); // Add all the shared libraries and the symlink tree as inputs to the tool that represents // this binary, so that users can attach the proper deps. executableBuilder.addNonHashableInput(sharedLibraries.getRootSourcePath()); executableBuilder.addInputs(sharedLibraries.getLinks().values()); } // Add in linker flags. linkFlagsBuilder.addAll(ImmutableList.copyOf(Iterables.transform(args.getLinkerFlags(), f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilder, platform.getCxxPlatform(), f)))); // Generate the compile rule and add its objects to the link. HaskellCompileRule compileRule = graphBuilder.addToIndex(HaskellDescriptionUtils.requireCompileRule( buildTarget, projectFilesystem, params, graphBuilder, ruleFinder, RichStream.from(deps) .filter(dep -> dep instanceof HaskellCompileDep || dep instanceof CxxPreprocessorDep) .toImmutableSet(), platform, depType, args.isEnableProfiling(), args.getMain(), Optional.empty(), args.getCompilerFlags(), HaskellSources.from(buildTarget, graphBuilder, pathResolver, ruleFinder, platform, "srcs", args.getSrcs()))); linkInputsBuilder.addAll(SourcePathArg.from(compileRule.getObjects())); ImmutableList<Arg> linkInputs = linkInputsBuilder.build(); ImmutableList<Arg> linkFlags = linkFlagsBuilder.build(); CommandTool executable = executableBuilder.build(); HaskellLinkRule linkRule = HaskellDescriptionUtils.createLinkRule(binaryTarget, projectFilesystem, params, graphBuilder, ruleFinder, platform, Linker.LinkType.EXECUTABLE, linkFlags, linkInputs, RichStream.from(deps).filter(NativeLinkable.class).toImmutableList(), args.getLinkDepsQueryWhole() ? RichStream.from(depQueryDeps).map(BuildRule::getBuildTarget).toImmutableSet() : ImmutableSet.of(), depType, outputPath, Optional.empty(), args.isEnableProfiling()); return new HaskellBinary(buildTarget, projectFilesystem, params.copyAppendingExtraDeps(linkRule), deps, executable, linkRule.getSourcePathToOutput()); } @Override public void findDepsForTargetFromConstructorArgs(BuildTarget buildTarget, CellPathResolver cellRoots, AbstractHaskellBinaryDescriptionArg constructorArg, ImmutableCollection.Builder<BuildTarget> extraDepsBuilder, ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) { HaskellDescriptionUtils.getParseTimeDeps(ImmutableList.of(getPlatform(buildTarget, constructorArg)), targetGraphOnlyDepsBuilder); constructorArg.getDepsQuery() .ifPresent(depsQuery -> QueryUtils.extractParseTimeTargets(buildTarget, cellRoots, depsQuery) .forEach(targetGraphOnlyDepsBuilder::add)); } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { if (getHaskellPlatformsProvider().getHaskellPlatforms().containsAnyOf(flavors)) { return true; } for (Type type : Type.values()) { if (flavors.contains(type.getFlavor())) { return true; } } return false; } private HaskellPlatformsProvider getHaskellPlatformsProvider() { return toolchainProvider.getByName(HaskellPlatformsProvider.DEFAULT_NAME, HaskellPlatformsProvider.class); } protected enum Type implements FlavorConvertible { SHARED(CxxDescriptionEnhancer.SHARED_FLAVOR, Linker.LinkableDepType.SHARED), STATIC_PIC( CxxDescriptionEnhancer.STATIC_PIC_FLAVOR, Linker.LinkableDepType.STATIC_PIC), STATIC( CxxDescriptionEnhancer.STATIC_FLAVOR, Linker.LinkableDepType.STATIC), GHCI( HaskellDescriptionUtils.GHCI_FLAV, Linker.LinkableDepType.STATIC),; private final Flavor flavor; private final Linker.LinkableDepType linkStyle; Type(Flavor flavor, Linker.LinkableDepType linkStyle) { this.flavor = flavor; this.linkStyle = linkStyle; } @Override public Flavor getFlavor() { return flavor; } public Linker.LinkableDepType getLinkStyle() { return linkStyle; } } @BuckStyleImmutable @Value.Immutable(copy = true) interface AbstractHaskellBinaryDescriptionArg extends CommonDescriptionArg, HasDepsQuery { @Value.Default default SourceSortedSet getSrcs() { return SourceSortedSet.EMPTY; } ImmutableList<String> getCompilerFlags(); ImmutableList<StringWithMacros> getLinkerFlags(); @Value.Default default PatternMatchedCollection<ImmutableSortedSet<BuildTarget>> getPlatformDeps() { return PatternMatchedCollection.of(); } @Value.Default default boolean getLinkDepsQueryWhole() { return false; } Optional<String> getMain(); Optional<Linker.LinkableDepType> getLinkStyle(); Optional<Flavor> getPlatform(); @Value.Default default boolean isEnableProfiling() { return false; } @Value.Default default ImmutableSortedSet<BuildTarget> getGhciPreloadDeps() { return ImmutableSortedSet.of(); } @Value.Default default PatternMatchedCollection<ImmutableSortedSet<BuildTarget>> getGhciPlatformPreloadDeps() { return PatternMatchedCollection.of(); } } }