com.facebook.buck.rust.RustCompile.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.rust.RustCompile.java

Source

/*
 * 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.rust;

import com.facebook.buck.cxx.Linker;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRule;
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.BuildableContext;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.shell.SymlinkFilesIntoDirectoryStep;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.MoreCollectors;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;

/**
 * Work out how to invoke the Rust compiler, rustc.
 *
 * In Rust, a crate is the equivalent of a package in other languages. It's also the basic unit of
 * compilation.
 *
 * A crate can either be a "binary crate" - which generates an executable - or a "library crate",
 * which makes an .rlib file. .rlib files contain both interface details (function signatures,
 * inline functions, macros, etc) and compiled object code, and so are equivalent to both header
 * files and library archives. .rdyn files also exist, which are dynamically linked .rlib files,
 * but they are very rarely used (rustc compiler plugins being the only common example).
 *
 * All crates are compiled from at least one source file, which is its main (or top, or root)
 * module. It may have references to other modules, which may be in other source files. Rustc only
 * needs the main module filename and will find the rest of the source files from there (akin to
 * #include in C/C++). If the crate also has dependencies on other crates, then those .rlib files
 * must also be passed to rustc for the interface details, and to be linked if its a binary crate.
 */
abstract class RustCompile extends AbstractBuildRule {
    @AddToRuleKey
    private final Supplier<Tool> compiler;
    @AddToRuleKey
    private final Supplier<Tool> linker;
    @AddToRuleKey
    private final ImmutableList<String> linkerArgs;
    @AddToRuleKey
    private final ImmutableSet<SourcePath> srcs;
    @AddToRuleKey
    private final ImmutableList<String> flags;
    @AddToRuleKey
    private final ImmutableSet<String> features;
    @AddToRuleKey
    private final Linker.LinkableDepType linkStyle;
    @AddToRuleKey
    private final String crate;
    @AddToRuleKey
    private final Optional<SourcePath> crateRoot;

    private final ImmutableSet<Path> nativePaths;

    private final Path scratchDir;
    private final Path output;

    RustCompile(BuildRuleParams params, SourcePathResolver resolver, String crate, Optional<SourcePath> crateRoot,
            ImmutableSet<SourcePath> srcs, ImmutableList<String> flags, ImmutableSet<String> features,
            ImmutableSet<Path> nativePaths, Path output, Supplier<Tool> compiler, Supplier<Tool> linker,
            ImmutableList<String> linkerArgs, Linker.LinkableDepType linkStyle) {
        super(params, resolver);

        this.srcs = srcs;
        this.flags = ImmutableList.<String>builder().add("--crate-name", crate).addAll(flags).build();
        this.features = features;
        this.crate = crate;
        this.crateRoot = crateRoot;
        this.output = output;
        this.compiler = compiler;
        this.linker = linker;
        this.linkerArgs = linkerArgs;
        this.linkStyle = linkStyle;
        this.scratchDir = BuildTargets.getScratchPath(getProjectFilesystem(), getBuildTarget(), "%s-container");

        this.nativePaths = nativePaths;

        for (String feature : features) {
            if (feature.contains("\"")) {
                throw new HumanReadableException("%s contains an invalid feature name %s",
                        getBuildTarget().getFullyQualifiedName(), feature);
            }
        }
    }

    @Override
    public ImmutableList<Step> getBuildSteps(BuildContext context, BuildableContext buildableContext) {
        ImmutableMap.Builder<String, Path> externalCratesBuilder = ImmutableMap.builder();
        ImmutableSet.Builder<Path> externalDepsBuilder = ImmutableSet.builder();

        buildableContext.recordArtifact(output);

        for (BuildRule buildRule : getDeps()) {
            if (buildRule instanceof RustLinkable) {
                RustLinkable linkable = (RustLinkable) buildRule;
                Path ruleOutput = linkable.getLinkPath();
                externalCratesBuilder.put(linkable.getLinkTarget(), ruleOutput);
                externalDepsBuilder.addAll(linkable.getDependencyPaths());
            }
        }

        Tool compiler = this.compiler.get();
        ImmutableList.Builder<String> linkerArgs = ImmutableList.builder();

        linkerArgs.addAll(linker.get().getCommandPrefix(getResolver()));
        linkerArgs.addAll(this.linkerArgs);

        SourcePathResolver resolver = getResolver();

        return ImmutableList.of(new MakeCleanDirectoryStep(getProjectFilesystem(), scratchDir),
                new SymlinkFilesIntoDirectoryStep(getProjectFilesystem(), getProjectFilesystem().getRootPath(),
                        srcs.stream().map(resolver::getRelativePath).collect(MoreCollectors.toImmutableList()),
                        scratchDir),
                new MakeCleanDirectoryStep(getProjectFilesystem(), output.getParent()),
                new RustCompileStep(getProjectFilesystem().getRootPath(), compiler.getEnvironment(),
                        compiler.getCommandPrefix(getResolver()), linkerArgs.build(), flags, features, output,
                        externalCratesBuilder.build(), externalDepsBuilder.build(), nativePaths, getCrateRoot()));
    }

    /**
     * Return the default top-level source name for a crate of this type.
     *
     * @return The source filename for the top-level module.
     */
    protected abstract ImmutableSet<String> getDefaultSources();

    @VisibleForTesting
    Path getCrateRoot() {
        SourcePathResolver resolver = getResolver();
        Optional<Path> crateRoot = this.crateRoot.map(resolver::getRelativePath);
        String crateName = String.format("%s.rs", getCrateName());
        ImmutableList<Path> candidates = srcs.stream().map(resolver::getRelativePath).filter(Objects::nonNull)
                .filter(p -> crateRoot.map(p::equals).orElse(false) || p.endsWith(crateName)
                        || getDefaultSources().contains(p.getFileName().toString()))
                .collect(MoreCollectors.toImmutableList());

        if (candidates.size() != 1) {
            throw new HumanReadableException("srcs of %s must contain either %s or %s.rs!",
                    getBuildTarget().getFullyQualifiedName(), getDefaultSources(), crate);
        }
        // We end up creating a symlink tree to ensure that the crate only uses the files that it
        // declares in the BUCK file.
        return scratchDir.resolve(candidates.get(0));
    }

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

    public Linker.LinkableDepType getLinkStyle() {
        return linkStyle;
    }

    public String getCrateName() {
        return crate;
    }
}