blob: 4aefbcba454bba36154a0501675c5c08de98fb81 [file] [log] [blame]
/*
* 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.MorePaths;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.HasOutputName;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
public class SourcePathResolver {
private final BuildRuleResolver ruleResolver;
public SourcePathResolver(BuildRuleResolver ruleResolver) {
this.ruleResolver = ruleResolver;
}
public Path getPath(SourcePath sourcePath) {
if (sourcePath instanceof PathSourcePath) {
return ((PathSourcePath) sourcePath).getRelativePath();
}
Preconditions.checkArgument(sourcePath instanceof BuildTargetSourcePath);
BuildTargetSourcePath buildTargetSourcePath = (BuildTargetSourcePath) sourcePath;
Optional<Path> resolvedPath = buildTargetSourcePath.getResolvedPath();
if (resolvedPath.isPresent()) {
return resolvedPath.get();
}
Path path = ruleResolver.getRule(buildTargetSourcePath.getTarget()).getPathToOutputFile();
if (path == null) {
throw new HumanReadableException(
"No known output for: %s",
buildTargetSourcePath.getTarget());
}
return path;
}
public Function<SourcePath, Path> getPathFunction() {
return new Function<SourcePath, Path>() {
@Override
public Path apply(SourcePath input) {
return getPath(input);
}
};
}
public ImmutableList<Path> getAllPaths(Iterable<? extends SourcePath> sourcePaths) {
// Maintain ordering and duplication if necessary.
return FluentIterable.from(sourcePaths).transform(getPathFunction()).toList();
}
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(), getPath(entry.getValue()));
}
return paths.build();
}
/**
* @return An {@link Optional} containing the {@link BuildRule} whose output {@code sourcePath}
* refers to, or {@code absent} if {@code sourcePath} doesn't refer to the output of a
* {@link BuildRule}.
*/
public Optional<BuildRule> getRule(SourcePath sourcePath) {
if (sourcePath instanceof PathSourcePath) {
return Optional.absent();
}
Preconditions.checkState(sourcePath instanceof BuildTargetSourcePath);
return Optional.of(ruleResolver.getRule(((BuildTargetSourcePath) sourcePath).getTarget()));
}
/**
* @return An {@link Optional} containing the {@link Path} the {@code sourcePath} refers to if it
* doesn't refer to the output of a {@link BuildRule}, or {@code absent} if it does.
*/
public Optional<Path> getRelativePath(SourcePath sourcePath) {
if (sourcePath instanceof BuildTargetSourcePath) {
return Optional.absent();
}
Preconditions.checkState(sourcePath instanceof PathSourcePath);
return Optional.of(((PathSourcePath) sourcePath).getRelativePath());
}
/**
* 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.<SourcePath>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.newHashMap();
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) {
if (sourcePath instanceof BuildTargetSourcePath) {
return getNameForRule(ruleResolver.getRule(((BuildTargetSourcePath) sourcePath).getTarget()));
}
Preconditions.checkArgument(sourcePath instanceof PathSourcePath);
Path path = ((PathSourcePath) sourcePath).getRelativePath();
return MorePaths.relativize(target.getBasePath(), path).toString();
}
private String getNameForRule(BuildRule rule) {
// If this build rule implements `HasOutputName`, then return the output name
// it provides.
if (rule instanceof HasOutputName) {
HasOutputName hasOutputName = (HasOutputName) rule;
return hasOutputName.getOutputName();
}
// 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 are suitable to
* be returned by {@link com.facebook.buck.rules.AbstractBuildRule#getInputsToCompareToOutput()}.
*/
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(
new Function<PathSourcePath, Path>() {
@Override
public Path apply(PathSourcePath input) {
return input.getRelativePath();
}
})
.toList();
}
public ImmutableCollection<Path> filterInputsToCompareToOutput(SourcePath... sources) {
return filterInputsToCompareToOutput(Arrays.asList(sources));
}
public Collection<BuildRule> filterBuildRuleInputs(
Iterable<? extends SourcePath> sources) {
return FluentIterable.from(sources)
.filter(BuildTargetSourcePath.class)
.transform(
new Function<BuildTargetSourcePath, BuildRule>() {
@Override
public BuildRule apply(BuildTargetSourcePath input) {
return ruleResolver.getRule(input.getTarget());
}
})
.toList();
}
}