/*
* 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.rules.keys;
import com.facebook.buck.hashing.FileHashLoader;
import com.facebook.buck.rules.AbstractBuildRule;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildTargetSourcePath;
import com.facebook.buck.rules.RuleKey;
import com.facebook.buck.rules.RuleKeyAppendable;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.HashCode;
import java.io.IOException;
import java.util.function.Function;
/** A {@link RuleKeyFactory} which adds some default settings to {@link RuleKey}s. */
public class DefaultRuleKeyFactory implements RuleKeyFactoryWithDiagnostics<RuleKey> {
private final RuleKeyFieldLoader ruleKeyFieldLoader;
private final FileHashLoader hashLoader;
private final SourcePathResolver pathResolver;
private final SourcePathRuleFinder ruleFinder;
private final RuleKeyCache<RuleKey> ruleKeyCache;
public DefaultRuleKeyFactory(
RuleKeyFieldLoader ruleKeyFieldLoader,
FileHashLoader hashLoader,
SourcePathResolver pathResolver,
SourcePathRuleFinder ruleFinder,
RuleKeyCache<RuleKey> ruleKeyCache) {
this.ruleKeyFieldLoader = ruleKeyFieldLoader;
this.hashLoader = hashLoader;
this.pathResolver = pathResolver;
this.ruleFinder = ruleFinder;
this.ruleKeyCache = ruleKeyCache;
}
public DefaultRuleKeyFactory(
RuleKeyFieldLoader ruleKeyFieldLoader,
FileHashLoader hashLoader,
SourcePathResolver pathResolver,
SourcePathRuleFinder ruleFinder) {
this(ruleKeyFieldLoader, hashLoader, pathResolver, ruleFinder, new DefaultRuleKeyCache<>());
}
public DefaultRuleKeyFactory(
int seed,
FileHashLoader hashLoader,
SourcePathResolver pathResolver,
SourcePathRuleFinder ruleFinder) {
this(new RuleKeyFieldLoader(seed), hashLoader, pathResolver, ruleFinder);
}
private <HASH> Builder<HASH> newPopulatedBuilder(
BuildRule buildRule, RuleKeyHasher<HASH> hasher) {
Builder<HASH> builder = new Builder<>(hasher);
ruleKeyFieldLoader.setFields(builder, buildRule, RuleKeyType.DEFAULT);
addDepsToRuleKey(buildRule, builder);
return builder;
}
private <HASH> Builder<HASH> newPopulatedBuilder(
RuleKeyAppendable appendable, RuleKeyHasher<HASH> hasher) {
Builder<HASH> builder = new Builder<>(hasher);
appendable.appendToRuleKey(builder);
return builder;
}
@VisibleForTesting
public Builder<HashCode> newBuilderForTesting(BuildRule buildRule) {
return newPopulatedBuilder(buildRule, RuleKeyBuilder.createDefaultHasher());
}
@Override
public RuleKey build(BuildRule buildRule) {
return ruleKeyCache.get(
buildRule,
rule ->
newPopulatedBuilder(rule, RuleKeyBuilder.createDefaultHasher())
.buildResult(RuleKey::new));
}
private RuleKey buildAppendableKey(RuleKeyAppendable appendable) {
return ruleKeyCache.get(
appendable,
app ->
newPopulatedBuilder(app, RuleKeyBuilder.createDefaultHasher())
.buildResult(RuleKey::new));
}
@Override
public <DIAG_KEY> RuleKeyDiagnostics.Result<RuleKey, DIAG_KEY> buildForDiagnostics(
BuildRule buildRule, RuleKeyHasher<DIAG_KEY> hasher) {
return RuleKeyDiagnostics.Result.of(
build(buildRule), // real rule key
newPopulatedBuilder(buildRule, hasher).buildResult(Function.identity()));
}
@Override
public <DIAG_KEY> RuleKeyDiagnostics.Result<RuleKey, DIAG_KEY> buildForDiagnostics(
RuleKeyAppendable appendable, RuleKeyHasher<DIAG_KEY> hasher) {
return RuleKeyDiagnostics.Result.of(
buildAppendableKey(appendable), // real rule key
newPopulatedBuilder(appendable, hasher).buildResult(Function.identity()));
}
private void addDepsToRuleKey(BuildRule buildRule, RuleKeyObjectSink sink) {
if (buildRule instanceof AbstractBuildRule) {
// TODO(mkosiba): We really need to get rid of declared/extra deps in rules. Instead
// rules should explicitly take the needed sub-sets of deps as constructor args.
AbstractBuildRule abstractBuildRule = (AbstractBuildRule) buildRule;
sink.setReflectively("buck.extraDeps", abstractBuildRule.deprecatedGetExtraDeps());
sink.setReflectively("buck.declaredDeps", abstractBuildRule.getDeclaredDeps());
sink.setReflectively("buck.targetGraphOnlyDeps", abstractBuildRule.getTargetGraphOnlyDeps());
} else {
sink.setReflectively("buck.deps", buildRule.getBuildDeps());
}
}
public class Builder<RULE_KEY> extends RuleKeyBuilder<RULE_KEY> {
private final ImmutableList.Builder<Object> deps = ImmutableList.builder();
private final ImmutableList.Builder<RuleKeyInput> inputs = ImmutableList.builder();
public Builder(RuleKeyHasher<RULE_KEY> hasher) {
super(ruleFinder, pathResolver, hashLoader, hasher);
}
@Override
protected RuleKeyBuilder<RULE_KEY> setBuildRule(BuildRule rule) {
// Record the `BuildRule` as an immediate dep.
deps.add(rule);
return setBuildRuleKey(DefaultRuleKeyFactory.this.build(rule));
}
@Override
protected RuleKeyBuilder<RULE_KEY> setAppendableRuleKey(RuleKeyAppendable appendable) {
// Record the `RuleKeyAppendable` as an immediate dep.
deps.add(appendable);
return setAppendableRuleKey(DefaultRuleKeyFactory.this.buildAppendableKey(appendable));
}
@Override
protected RuleKeyBuilder<RULE_KEY> setSourcePath(SourcePath sourcePath) throws IOException {
if (sourcePath instanceof BuildTargetSourcePath) {
return setSourcePathAsRule((BuildTargetSourcePath) sourcePath);
} else {
// Add `PathSourcePath`s to our tracked inputs.
pathResolver
.getPathSourcePath(sourcePath)
.ifPresent(
path -> inputs.add(RuleKeyInput.of(path.getFilesystem(), path.getRelativePath())));
return setSourcePathDirectly(sourcePath);
}
}
@Override
protected RuleKeyBuilder<RULE_KEY> setNonHashingSourcePath(SourcePath sourcePath) {
try {
// The default rule keys must include the hash of the source path to properly propagate
// changes to dependent rule keys.
return setSourcePath(sourcePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public <RESULT> RuleKeyResult<RESULT> buildResult(Function<RULE_KEY, RESULT> mapper) {
return new RuleKeyResult<>(this.build(mapper), deps.build(), inputs.build());
}
}
}