com.facebook.buck.rules.SourcePathResolver.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.rules.SourcePathResolver.java

Source

/*
 * Copyright 2014-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.rules;

import com.facebook.buck.io.ArchiveMemberPath;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.HasOutputName;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.MoreCollectors;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

public class SourcePathResolver {

    private final SourcePathRuleFinder ruleFinder;

    public SourcePathResolver(SourcePathRuleFinder ruleFinder) {
        this.ruleFinder = ruleFinder;
    }

    public <T> ImmutableMap<T, Path> getMappedPaths(Map<T, SourcePath> sourcePathMap) {
        ImmutableMap.Builder<T, Path> paths = ImmutableMap.builder();
        for (ImmutableMap.Entry<T, SourcePath> entry : sourcePathMap.entrySet()) {
            paths.put(entry.getKey(), getAbsolutePath(entry.getValue()));
        }
        return paths.build();
    }

    /**
     * @return the {@link ProjectFilesystem} associated with {@code sourcePath}.
     */
    public ProjectFilesystem getFilesystem(SourcePath sourcePath) {
        if (sourcePath instanceof PathSourcePath) {
            return ((PathSourcePath) sourcePath).getFilesystem();
        }
        if (sourcePath instanceof BuildTargetSourcePath) {
            return ruleFinder.getRuleOrThrow((BuildTargetSourcePath) sourcePath).getProjectFilesystem();
        }
        throw new IllegalStateException();
    }

    /**
     * @return the {@link Path} for this {@code sourcePath}, resolved using its associated
     *     {@link com.facebook.buck.io.ProjectFilesystem}.
     */
    public Path getAbsolutePath(SourcePath sourcePath) {
        Path path = getPathPrivateImpl(sourcePath);
        if (path.isAbsolute()) {
            return path;
        }

        if (sourcePath instanceof BuildTargetSourcePath) {
            BuildRule rule = ruleFinder.getRuleOrThrow((BuildTargetSourcePath) sourcePath);
            return rule.getProjectFilesystem().resolve(path);
        } else if (sourcePath instanceof PathSourcePath) {
            return ((PathSourcePath) sourcePath).getFilesystem().resolve(path);
        } else {
            throw new UnsupportedOperationException(sourcePath.getClass() + " is not supported here!");
        }
    }

    public ArchiveMemberPath getAbsoluteArchiveMemberPath(SourcePath sourcePath) {
        Preconditions.checkState(sourcePath instanceof ArchiveMemberSourcePath);
        ArchiveMemberSourcePath archiveMemberSourcePath = (ArchiveMemberSourcePath) sourcePath;

        Path archiveAbsolutePath = getAbsolutePath(archiveMemberSourcePath.getArchiveSourcePath());

        return ArchiveMemberPath.of(archiveAbsolutePath, archiveMemberSourcePath.getMemberPath());
    }

    public ArchiveMemberPath getRelativeArchiveMemberPath(SourcePath sourcePath) {
        Preconditions.checkState(sourcePath instanceof ArchiveMemberSourcePath);
        ArchiveMemberSourcePath archiveMemberSourcePath = (ArchiveMemberSourcePath) sourcePath;

        Path archiveRelativePath = getRelativePath(archiveMemberSourcePath.getArchiveSourcePath());

        return ArchiveMemberPath.of(archiveRelativePath, archiveMemberSourcePath.getMemberPath());
    }

    public ImmutableSortedSet<Path> getAllAbsolutePaths(Collection<? extends SourcePath> sourcePaths) {
        return sourcePaths.stream().map(this::getAbsolutePath).collect(MoreCollectors.toImmutableSortedSet());
    }

    /**
     * @return The {@link Path} the {@code sourcePath} refers to, relative to its owning
     * {@link com.facebook.buck.io.ProjectFilesystem}.
     */
    public Path getRelativePath(SourcePath sourcePath) {
        Path toReturn = getPathPrivateImpl(sourcePath);

        Preconditions.checkState(!toReturn.isAbsolute(), "Expected path to be relative, not absolute: %s (from %s)",
                toReturn, sourcePath);

        return toReturn;
    }

    /**
     * @return The {@link Path} the {@code sourcePath} refers to, ideally relative to its owning
     * {@link com.facebook.buck.io.ProjectFilesystem}. Absolute path may get returned however!
     *
     * We should make sure that {@link #getPathPrivateImpl} always returns a relative path after
     * which we should simply call {@link #getRelativePath}. Until then we still need this nonsense.
     */
    public Path getIdeallyRelativePath(SourcePath sourcePath) {
        return getPathPrivateImpl(sourcePath);
    }

    /**
     * @return the {@link SourcePath} as a {@link Path}, with no guarantee whether the return value is
     *     absolute or relative. This should never be exposed to users.
     */
    private Path getPathPrivateImpl(SourcePath sourcePath) {
        if (sourcePath instanceof PathSourcePath) {
            return ((PathSourcePath) sourcePath).getRelativePath();
        } else if (sourcePath instanceof BuildTargetSourcePath) {
            BuildTargetSourcePath targetSourcePath = (BuildTargetSourcePath) sourcePath;
            Optional<Path> resolvedPath = targetSourcePath.getResolvedPath();
            if (resolvedPath.isPresent()) {
                return resolvedPath.get();
            } else {
                Path path = ruleFinder.getRuleOrThrow(targetSourcePath).getPathToOutput();
                if (path == null) {
                    throw new HumanReadableException("No known output for: %s", targetSourcePath.getTarget());
                }
                return path;
            }
        } else {
            throw new UnsupportedOperationException(sourcePath.getClass() + " is not supported here!");
        }
    }

    /**
     * Resolved the logical names for a group of SourcePath objects into a map, throwing an
     * error on duplicates.
     */
    public ImmutableMap<String, SourcePath> getSourcePathNames(BuildTarget target, String parameter,
            Iterable<SourcePath> sourcePaths) {
        return getSourcePathNames(target, parameter, sourcePaths, Functions.identity());
    }

    /**
     * Resolves the logical names for a group of objects that have a SourcePath into a map,
     * throwing an error on duplicates.
     */
    public <T> ImmutableMap<String, T> getSourcePathNames(BuildTarget target, String parameter, Iterable<T> objects,
            Function<T, SourcePath> objectSourcePathFunction) {

        Map<String, T> resolved = Maps.newLinkedHashMap();

        for (T object : objects) {
            SourcePath path = objectSourcePathFunction.apply(object);
            String name = getSourcePathName(target, path);
            T old = resolved.put(name, object);
            if (old != null) {
                throw new HumanReadableException(
                        String.format("%s: parameter '%s': duplicate entries for '%s'", target, parameter, name));
            }
        }

        return ImmutableMap.copyOf(resolved);
    }

    public String getSourcePathName(BuildTarget target, SourcePath sourcePath) {
        Preconditions.checkArgument(!(sourcePath instanceof ArchiveMemberSourcePath));
        if (sourcePath instanceof BuildTargetSourcePath) {
            return getNameForBuildTargetSourcePath((BuildTargetSourcePath) sourcePath);
        }
        Preconditions.checkArgument(sourcePath instanceof PathSourcePath);
        Path path = ((PathSourcePath) sourcePath).getRelativePath();
        return MorePaths.relativize(target.getBasePath(), path).toString();
    }

    private String getNameForBuildTargetSourcePath(BuildTargetSourcePath sourcePath) {
        BuildRule rule = ruleFinder.getRuleOrThrow(sourcePath);

        // If this build rule implements `HasOutputName`, then return the output name
        // it provides.
        if (rule instanceof HasOutputName) {
            HasOutputName hasOutputName = (HasOutputName) rule;
            return hasOutputName.getOutputName();
        }

        // If an explicit path is set, use it's relative path to the build rule's output location to
        // infer a unique name.
        Optional<Path> explicitPath = sourcePath.getResolvedPath();
        if (explicitPath.isPresent()) {
            Path path = explicitPath.get();
            if (path.startsWith(rule.getProjectFilesystem().getBuckPaths().getGenDir())) {
                path = rule.getProjectFilesystem().getBuckPaths().getGenDir().relativize(path);
            }
            if (path.startsWith(rule.getBuildTarget().getBasePath())) {
                return rule.getBuildTarget().getBasePath().relativize(path).toString();
            }
        }

        // Otherwise, fall back to using the short name of rule's build target.
        return rule.getBuildTarget().getShortName();
    }

    /**
     * Takes an {@link Iterable} of {@link SourcePath} objects and filters those that represent
     * {@link Path}s.
     */
    public ImmutableCollection<Path> filterInputsToCompareToOutput(Iterable<? extends SourcePath> sources) {
        // Currently, the only implementation of SourcePath that should be included in the Iterable
        // returned by getInputsToCompareToOutput() is FileSourcePath, so it is safe to filter by that
        // and then use .asReference() to get its path.
        //
        // BuildTargetSourcePath should not be included in the output because it refers to a generated
        // file, and generated files are not hashed as part of a RuleKey.
        return FluentIterable.from(sources).filter(PathSourcePath.class).transform(PathSourcePath::getRelativePath)
                .toList();
    }

    public ImmutableCollection<Path> filterInputsToCompareToOutput(SourcePath... sources) {
        return filterInputsToCompareToOutput(Arrays.asList(sources));
    }
}