com.facebook.buck.haskell.HaskellPackageRule.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.haskell.HaskellPackageRule.java

Source

/*
 * 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 com.facebook.buck.io.MorePaths;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRuleWithResolver;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.shell.ShellStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.step.fs.RmStep;
import com.facebook.buck.step.fs.WriteFileStep;
import com.google.common.base.Joiner;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class HaskellPackageRule extends AbstractBuildRuleWithResolver {

    @AddToRuleKey
    private final Tool ghcPkg;

    private final HaskellVersion haskellVersion;

    @AddToRuleKey
    private final HaskellPackageInfo packageInfo;

    @AddToRuleKey
    private final ImmutableSortedMap<String, HaskellPackage> depPackages;

    @AddToRuleKey
    private final ImmutableSortedSet<String> modules;

    @AddToRuleKey
    private final ImmutableSortedSet<SourcePath> libraries;

    @AddToRuleKey
    private final ImmutableSortedSet<SourcePath> interfaces;

    public HaskellPackageRule(BuildRuleParams buildRuleParams, SourcePathResolver resolver, Tool ghcPkg,
            HaskellVersion haskellVersion, HaskellPackageInfo packageInfo,
            ImmutableSortedMap<String, HaskellPackage> depPackages, ImmutableSortedSet<String> modules,
            ImmutableSortedSet<SourcePath> libraries, ImmutableSortedSet<SourcePath> interfaces) {
        super(buildRuleParams, resolver);
        this.ghcPkg = ghcPkg;
        this.haskellVersion = haskellVersion;
        this.packageInfo = packageInfo;
        this.depPackages = depPackages;
        this.modules = modules;
        this.libraries = libraries;
        this.interfaces = interfaces;
    }

    public static HaskellPackageRule from(BuildTarget target, BuildRuleParams baseParams,
            final SourcePathResolver resolver, SourcePathRuleFinder ruleFinder, final Tool ghcPkg,
            HaskellVersion haskellVersion, HaskellPackageInfo packageInfo,
            final ImmutableSortedMap<String, HaskellPackage> depPackages, ImmutableSortedSet<String> modules,
            final ImmutableSortedSet<SourcePath> libraries, final ImmutableSortedSet<SourcePath> interfaces) {
        return new HaskellPackageRule(
                baseParams.copyWithChanges(target, Suppliers.memoize(() -> ImmutableSortedSet
                        .<BuildRule>naturalOrder().addAll(ghcPkg.getDeps(ruleFinder))
                        .addAll(depPackages.values().stream().flatMap(pkg -> pkg.getDeps(ruleFinder)).iterator())
                        .addAll(ruleFinder.filterBuildRuleInputs(Iterables.concat(libraries, interfaces))).build()),
                        Suppliers.ofInstance(ImmutableSortedSet.of())),
                resolver, ghcPkg, haskellVersion, packageInfo, depPackages, modules, libraries, interfaces);
    }

    private Path getPackageDb() {
        return BuildTargets.getGenPath(getProjectFilesystem(), getBuildTarget(), "%s");
    }

    private WriteFileStep getWriteRegistrationFileStep(Path registrationFile, Path packageDb) {
        Map<String, String> entries = new LinkedHashMap<>();

        entries.put("name", packageInfo.getName());
        entries.put("version", packageInfo.getVersion());
        entries.put("id", packageInfo.getIdentifier());

        if (haskellVersion.getMajorVersion() >= 8) {
            entries.put("key", packageInfo.getIdentifier());
        }

        entries.put("exposed", "True");
        entries.put("exposed-modules", Joiner.on(' ').join(modules));

        Path pkgRoot = getProjectFilesystem().getPath("${pkgroot}");

        if (!modules.isEmpty()) {
            List<String> importDirs = new ArrayList<>();
            for (SourcePath interfaceDir : interfaces) {
                Path relInterfaceDir = pkgRoot
                        .resolve(packageDb.getParent().relativize(getResolver().getRelativePath(interfaceDir)));
                importDirs.add('"' + relInterfaceDir.toString() + '"');
            }
            entries.put("import-dirs", Joiner.on(", ").join(importDirs));
        }

        List<String> libDirs = new ArrayList<>();
        List<String> libs = new ArrayList<>();
        for (SourcePath library : libraries) {
            Path relLibPath = pkgRoot
                    .resolve(packageDb.getParent().relativize(getResolver().getRelativePath(library)));
            libDirs.add('"' + relLibPath.getParent().toString() + '"');
            libs.add(MorePaths.stripPathPrefixAndExtension(relLibPath.getFileName(), "lib"));
        }
        entries.put("library-dirs", Joiner.on(", ").join(libDirs));
        // Use extra libraries here, so GHC won't try to find libraries with any extra suffices
        // (e.g. lib<name>-ghc7.10.3.dylib).
        entries.put("extra-libraries", Joiner.on(", ").join(libs));

        entries.put("depends", Joiner.on(", ").join(depPackages.keySet()));

        return new WriteFileStep(getProjectFilesystem(),
                entries.entrySet().stream().map(input -> input.getKey() + ": " + input.getValue()).collect(
                        Collectors.joining(System.lineSeparator())),
                registrationFile, /* executable */ false);
    }

    @Override
    public ImmutableList<Step> getBuildSteps(BuildContext context, BuildableContext buildableContext) {

        ImmutableList.Builder<Step> steps = ImmutableList.builder();

        // Setup the scratch dir.
        Path scratchDir = BuildTargets.getScratchPath(getProjectFilesystem(), getBuildTarget(), "%s");
        steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), scratchDir));

        // Setup the package DB directory.
        final Path packageDb = getPackageDb();
        steps.add(new RmStep(getProjectFilesystem(), packageDb, RmStep.Mode.FORCED, RmStep.Mode.RECURSIVE));
        buildableContext.recordArtifact(packageDb);

        // Create the registration file.
        Path registrationFile = scratchDir.resolve("registration-file");
        steps.add(getWriteRegistrationFileStep(registrationFile, packageDb));

        // Build the the package DB.
        steps.add(new GhcPkgStep(ImmutableList.of("init", packageDb.toString()), ImmutableMap.of()));
        steps.add(new GhcPkgStep(
                ImmutableList.of("-v0", "register", "--package-conf=" + packageDb, "--no-expand-pkgroot",
                        registrationFile.toString()),
                ImmutableMap.of("GHC_PACKAGE_PATH",
                        depPackages.values().stream()
                                .map(input -> getResolver().getAbsolutePath(input.getPackageDb()).toString())
                                .collect(Collectors.joining(":")))));

        return steps.build();
    }

    @Override
    public Path getPathToOutput() {
        return getPackageDb();
    }

    public HaskellPackage getPackage() {
        return HaskellPackage.builder().setInfo(packageInfo)
                .setPackageDb(new BuildTargetSourcePath(getBuildTarget(), getPackageDb()))
                .addAllLibraries(libraries).addAllInterfaces(interfaces).build();
    }

    private class GhcPkgStep extends ShellStep {

        private final ImmutableList<String> args;
        private final ImmutableMap<String, String> env;

        public GhcPkgStep(ImmutableList<String> args, ImmutableMap<String, String> env) {
            super(getProjectFilesystem().getRootPath());
            this.args = args;
            this.env = env;
        }

        @Override
        protected final ImmutableList<String> getShellCommandInternal(ExecutionContext context) {
            return ImmutableList.<String>builder().addAll(ghcPkg.getCommandPrefix(getResolver())).addAll(args)
                    .build();
        }

        @Override
        public ImmutableMap<String, String> getEnvironmentVariables(ExecutionContext context) {
            return ImmutableMap.<String, String>builder().putAll(super.getEnvironmentVariables(context))
                    .putAll(ghcPkg.getEnvironment()).putAll(env).build();
        }

        @Override
        public final String getShortName() {
            return "ghc-pkg";
        }

    }

}