com.google.devtools.build.lib.actions.MiddlemanFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.actions.MiddlemanFactory.java

Source

// Copyright 2014 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.actions;

import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;

import java.util.Iterator;

import javax.annotation.Nullable;

/**
 * A factory to create middleman objects.
 */
@ThreadSafe
public final class MiddlemanFactory {

    private final ArtifactFactory artifactFactory;
    private final ActionRegistry actionRegistry;

    public MiddlemanFactory(ArtifactFactory artifactFactory, ActionRegistry actionRegistry) {
        this.artifactFactory = Preconditions.checkNotNull(artifactFactory);
        this.actionRegistry = Preconditions.checkNotNull(actionRegistry);
    }

    /**
     * Creates a {@link MiddlemanType#AGGREGATING_MIDDLEMAN aggregating} middleman.
     *
     * @param owner the owner of the action that will be created; must not be null
     * @param purpose the purpose for which this middleman is created. This should be a string which
     *        is suitable for use as a filename. A single rule may have many middlemen with distinct
     *        purposes.
     * @param inputs the set of artifacts for which the created artifact is to be the middleman.
     * @param middlemanDir the directory in which to place the middleman.
     * @return null iff {@code inputs} is empty; the single element of {@code inputs} if there's only
     *         one; a new aggregating middleman for the {@code inputs} otherwise
     */
    public Artifact createAggregatingMiddleman(ActionOwner owner, String purpose, Iterable<Artifact> inputs,
            Root middlemanDir) {
        if (hasExactlyOneInput(inputs)) { // Optimization: No middleman for just one input.
            return Iterables.getOnlyElement(inputs);
        }
        Pair<Artifact, Action> result = createMiddleman(owner, Label.print(owner.getLabel()), purpose, inputs,
                middlemanDir, MiddlemanType.AGGREGATING_MIDDLEMAN);
        return result == null ? null : result.getFirst();
    }

    /**
     * Returns <code>null</code> iff inputs is empty. Returns the sole element
     * of inputs iff <code>inputs.size()==1</code>. Otherwise, returns a
     * middleman artifact and creates a middleman action that generates that
     * artifact.
     *
     * @param owner the owner of the action that will be created.
     * @param owningArtifact the artifact of the file for which the runfiles
     *        should be created. There may be at most one set of runfiles for
     *        an owning artifact, unless the owning artifact is null. There
     *        may be at most one set of runfiles per owner with a null
     *        owning artifact.
     *        Further, if the owning Artifact is non-null, the owning Artifacts'
     *        root-relative path must be unique and the artifact must be part
     *        of the runfiles tree for which this middleman is created. Usually
     *        this artifact will be an executable program.
     * @param inputs the set of artifacts for which the created artifact is to be
     *        the middleman.
     * @param middlemanDir the directory in which to place the middleman.
     */
    public Artifact createRunfilesMiddleman(ActionOwner owner, @Nullable Artifact owningArtifact,
            Iterable<Artifact> inputs, Root middlemanDir, String tag) {
        if (hasExactlyOneInput(inputs)) { // Optimization: No middleman for just one input.
            return Iterables.getOnlyElement(inputs);
        }
        String middlemanPath = owningArtifact == null ? Label.print(owner.getLabel())
                : owningArtifact.getRootRelativePath().getPathString();
        return createMiddleman(owner, middlemanPath, tag, inputs, middlemanDir, MiddlemanType.RUNFILES_MIDDLEMAN)
                .getFirst();
    }

    private <T> boolean hasExactlyOneInput(Iterable<T> iterable) {
        Iterator<T> it = iterable.iterator();
        if (!it.hasNext()) {
            return false;
        }
        it.next();
        return !it.hasNext();
    }

    /**
     * Creates a {@link MiddlemanType#ERROR_PROPAGATING_MIDDLEMAN error-propagating} middleman.
     *
     * @param owner the owner of the action that will be created. May not be null.
     * @param middlemanName a unique file name for the middleman artifact in the {@code middlemanDir};
     *        in practice this is usually the owning rule's label (so it gets escaped as such)
     * @param purpose the purpose for which this middleman is created. This should be a string which
     *        is suitable for use as a filename. A single rule may have many middlemen with distinct
     *        purposes.
     * @param inputs the set of artifacts for which the created artifact is to be the middleman; must
     *        not be null or empty
     * @param middlemanDir the directory in which to place the middleman.
     * @return a middleman that enforces scheduling order (just like a scheduling middleman) and
     *         propagates errors, but is ignored by the dependency checker
     * @throws IllegalArgumentException if {@code inputs} is null or empty
     */
    public Artifact createErrorPropagatingMiddleman(ActionOwner owner, String middlemanName, String purpose,
            Iterable<Artifact> inputs, Root middlemanDir) {
        Preconditions.checkArgument(inputs != null);
        Preconditions.checkArgument(!Iterables.isEmpty(inputs));
        // We must always create this middleman even if there is only one input.
        return createMiddleman(owner, middlemanName, purpose, inputs, middlemanDir,
                MiddlemanType.ERROR_PROPAGATING_MIDDLEMAN).getFirst();
    }

    /**
     * Returns the same artifact as {@code createErrorPropagatingMiddleman} would return,
     * but doesn't create any action.
     */
    public Artifact getErrorPropagatingMiddlemanArtifact(String middlemanName, String purpose, Root middlemanDir) {
        return getStampFileArtifact(middlemanName, purpose, middlemanDir);
    }

    /**
     * Creates both normal and scheduling middlemen.
     *
     * <p>Note: there's no need to synchronize this method; the only use of a field is via a call to
     * another synchronized method (getArtifact()).
     *
     * @return null iff {@code inputs} is null or empty; the middleman file and the middleman action
     *         otherwise
     */
    private Pair<Artifact, Action> createMiddleman(ActionOwner owner, String middlemanName, String purpose,
            Iterable<Artifact> inputs, Root middlemanDir, MiddlemanType middlemanType) {
        if (inputs == null || Iterables.isEmpty(inputs)) {
            return null;
        }

        Artifact stampFile = getStampFileArtifact(middlemanName, purpose, middlemanDir);
        Action action = new MiddlemanAction(owner, inputs, stampFile, purpose, middlemanType);
        actionRegistry.registerAction(action);
        return Pair.of(stampFile, action);
    }

    /**
     * Creates a normal middleman.
     *
     * <p>If called multiple times, it always returns the same object depending on the {@code
     * purpose}. It does not check that the list of inputs is identical. In contrast to other
     * middleman methods, this one also returns an object if the list of inputs is empty.
     *
     * <p>Note: there's no need to synchronize this method; the only use of a field is via a call to
     * another synchronized method (getArtifact()).
     */
    public Artifact createMiddlemanAllowMultiple(ActionRegistry registry, ActionOwner owner,
            PathFragment packageDirectory, String purpose, Iterable<Artifact> inputs, Root middlemanDir) {
        String escapedPackageDirectory = Actions.escapedPath(packageDirectory.getPathString());
        PathFragment stampName = new PathFragment("_middlemen/"
                + (purpose.startsWith(escapedPackageDirectory) ? purpose : (escapedPackageDirectory + purpose)));
        Artifact stampFile = artifactFactory.getDerivedArtifact(stampName, middlemanDir, actionRegistry.getOwner());
        MiddlemanAction.create(registry, owner, inputs, stampFile, purpose, MiddlemanType.AGGREGATING_MIDDLEMAN);
        return stampFile;
    }

    private Artifact getStampFileArtifact(String middlemanName, String purpose, Root middlemanDir) {
        String escapedFilename = Actions.escapedPath(middlemanName);
        PathFragment stampName = new PathFragment("_middlemen/" + escapedFilename + "-" + purpose);
        Artifact stampFile = artifactFactory.getDerivedArtifact(stampName, middlemanDir, actionRegistry.getOwner());
        return stampFile;
    }
}