| /* |
| * Copyright 2012-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.ProjectFilesystem; |
| import com.facebook.buck.model.BuildTarget; |
| import com.facebook.buck.model.HasBuildTarget; |
| import com.google.common.annotations.Beta; |
| import com.google.common.collect.ImmutableCollection; |
| import com.google.common.collect.ImmutableSortedSet; |
| |
| import java.nio.file.Path; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Abstract implementation of a {@link BuildRule} that can be cached. If its current {@link RuleKey} |
| * matches the one on disk, then it has no work to do. It should also try to fetch its output from |
| * an {@link ArtifactCache} to avoid doing any computation. |
| */ |
| @Beta |
| public abstract class AbstractBuildRule implements BuildRule { |
| |
| private final BuildTarget buildTarget; |
| private final ImmutableSortedSet<BuildRule> declaredDeps; |
| private final ImmutableSortedSet<BuildRule> deps; |
| private final RuleKeyBuilderFactory ruleKeyBuilderFactory; |
| private final BuildRuleType buildRuleType; |
| private final SourcePathResolver resolver; |
| private final ProjectFilesystem projectFilesystem; |
| /** @see #getInputsToCompareToOutput() */ |
| @Nullable private ImmutableCollection<Path> inputsToCompareToOutputs; |
| @Nullable private volatile RuleKey.Builder.RuleKeyPair ruleKeyPair; |
| |
| protected AbstractBuildRule(BuildRuleParams buildRuleParams, SourcePathResolver resolver) { |
| this.buildTarget = buildRuleParams.getBuildTarget(); |
| this.declaredDeps = buildRuleParams.getDeclaredDeps(); |
| this.deps = buildRuleParams.getDeps(); |
| this.ruleKeyBuilderFactory = buildRuleParams.getRuleKeyBuilderFactory(); |
| this.buildRuleType = buildRuleParams.getBuildRuleType(); |
| this.resolver = resolver; |
| this.projectFilesystem = buildRuleParams.getProjectFilesystem(); |
| } |
| |
| @Override |
| public BuildableProperties getProperties() { |
| return BuildableProperties.NONE; |
| } |
| |
| @Override |
| public final BuildTarget getBuildTarget() { |
| return buildTarget; |
| } |
| |
| @Override |
| public final String getFullyQualifiedName() { |
| return buildTarget.getFullyQualifiedName(); |
| } |
| |
| @Override |
| public final ImmutableSortedSet<BuildRule> getDeps() { |
| return deps; |
| } |
| |
| public final ImmutableSortedSet<BuildRule> getDeclaredDeps() { |
| return declaredDeps; |
| } |
| |
| @Override |
| public final BuildRuleType getType() { |
| return buildRuleType; |
| } |
| |
| public final SourcePathResolver getResolver() { |
| return resolver; |
| } |
| |
| @Override |
| public final ProjectFilesystem getProjectFilesystem() { |
| return projectFilesystem; |
| } |
| |
| @Override |
| public ImmutableCollection<Path> getInputs() { |
| if (inputsToCompareToOutputs == null) { |
| inputsToCompareToOutputs = getInputsToCompareToOutput(); |
| } |
| return inputsToCompareToOutputs; |
| } |
| |
| protected abstract ImmutableCollection<Path> getInputsToCompareToOutput(); |
| |
| @Override |
| public final int compareTo(HasBuildTarget that) { |
| return this.getBuildTarget().compareTo(that.getBuildTarget()); |
| } |
| |
| @Override |
| public final boolean equals(Object obj) { |
| if (!(obj instanceof AbstractBuildRule)) { |
| return false; |
| } |
| AbstractBuildRule that = (AbstractBuildRule) obj; |
| return this.buildTarget.equals(that.buildTarget); |
| } |
| |
| @Override |
| public final int hashCode() { |
| return this.buildTarget.hashCode(); |
| } |
| |
| @Override |
| public final String toString() { |
| return getFullyQualifiedName(); |
| } |
| |
| /** |
| * {@link BuildRule#getRuleKey()} and {@link BuildRule#getRuleKeyWithoutDeps()} uses this when |
| * constructing {@link RuleKey}s for this class. Every subclass that extends the rule state in a |
| * way that matters to idempotency must override {@link #appendToRuleKey(RuleKey.Builder)} and |
| * append its state to the {@link RuleKey.Builder} returned by its superclass's |
| * {@link #appendToRuleKey(RuleKey.Builder)} implementation. Example: |
| * <pre> |
| * @Override |
| * protected RuleKey.Builder appendToRuleKey(RuleKey.Builder builder) { |
| * return super.appendToRuleKey(builder) |
| * .set("srcs", srcs), |
| * .set("resources", resources); |
| * } |
| * </pre> |
| */ |
| public RuleKey.Builder appendToRuleKey(RuleKey.Builder builder) { |
| // For a rule that lists its inputs via a "srcs" argument, this may seem redundant, but it is |
| // not. Here, the inputs are specified as InputRules, which means that the _contents_ of the |
| // files will be hashed. In the case of .set("srcs", srcs), the list of strings itself will be |
| // hashed. It turns out that we need both of these in order to construct a RuleKey correctly. |
| // Note: appendToRuleKey() should not set("srcs", srcs) if the inputs are order-independent. |
| ImmutableCollection<Path> inputs = getInputs(); |
| builder = builder |
| .setReflectively("buck.inputs", inputs.iterator()) |
| .setReflectively( |
| "buck.sourcepaths", |
| SourcePaths.toSourcePathsSortedByNaturalOrder(getProjectFilesystem(), inputs)); |
| // TODO(simons): Rename this when no Buildables extend this class. |
| return appendDetailsToRuleKey(builder); |
| } |
| |
| protected abstract RuleKey.Builder appendDetailsToRuleKey(RuleKey.Builder builder); |
| |
| /** |
| * This method should be overridden only for unit testing. |
| */ |
| @Override |
| public RuleKey getRuleKey() { |
| return getRuleKeyPair().getTotalRuleKey(); |
| } |
| |
| /** |
| * Creates a new {@link RuleKey} for this {@link BuildRule} that does not take {@link #getDeps()} |
| * into account. |
| */ |
| @Override |
| public RuleKey getRuleKeyWithoutDeps() { |
| return getRuleKeyPair().getRuleKeyWithoutDeps(); |
| } |
| |
| private RuleKey.Builder.RuleKeyPair getRuleKeyPair() { |
| // This uses the "double-checked locking using volatile" pattern: |
| // http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html. |
| if (ruleKeyPair == null) { |
| synchronized (this) { |
| if (ruleKeyPair == null) { |
| RuleKey.Builder builder = ruleKeyBuilderFactory.newInstance(this, getResolver()); |
| appendToRuleKey(builder); |
| ruleKeyPair = builder.build(); |
| } |
| } |
| } |
| return ruleKeyPair; |
| } |
| |
| @Override |
| public CacheMode getCacheMode() { |
| return CacheMode.ENABLED; |
| } |
| |
| } |