/* * Copyright 2017-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.rules.BuildRule; import com.facebook.buck.rules.RuleKeyAppendable; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.LinkedList; import java.util.Queue; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.concurrent.ThreadSafe; /** Contains functionality related to rulekey diagnostics. */ @ThreadSafe public class RuleKeyDiagnostics<RULE_KEY, DIAG_KEY> { private final Set<Object> computed = Sets.newConcurrentHashSet(); private final Function<BuildRule, Result<RULE_KEY, DIAG_KEY>> ruleResultSupplier; private final Function<RuleKeyAppendable, Result<RULE_KEY, DIAG_KEY>> appendableResultSupplier; public static final <RULE_KEY, DIAG_KEY> RuleKeyDiagnostics<RULE_KEY, DIAG_KEY> nop() { return new RuleKeyDiagnostics<RULE_KEY, DIAG_KEY>(rulke -> null, appendable -> null) { @Override public void processRule(BuildRule rule, Consumer<Result<RULE_KEY, DIAG_KEY>> consumer) {} }; } public RuleKeyDiagnostics( Function<BuildRule, Result<RULE_KEY, DIAG_KEY>> ruleResultSupplier, Function<RuleKeyAppendable, Result<RULE_KEY, DIAG_KEY>> appendableResultSupplier) { this.ruleResultSupplier = ruleResultSupplier; this.appendableResultSupplier = appendableResultSupplier; } /** * Computes the diagnostic rulekey data for the given rule and all of its appendables recursively. * Previously processed rules and appendables are skipped and not fed to the consumer. Results for * the newly processed rule and appendables are fed to the consumer, but not stored otherwise. * Only information of whether something has been processed or not gets stored. */ public void processRule(BuildRule rule, Consumer<Result<RULE_KEY, DIAG_KEY>> resultConsumer) { if (!computed.add(rule)) { return; } Queue<RuleKeyAppendable> appendableQueue = new LinkedList<>(); Result<RULE_KEY, DIAG_KEY> result = ruleResultSupplier.apply(rule); Iterables.addAll(appendableQueue, result.appendables); resultConsumer.accept(result); while (!appendableQueue.isEmpty()) { RuleKeyAppendable appendable = appendableQueue.remove(); if (computed.add(appendable)) { result = appendableResultSupplier.apply(appendable); Iterables.addAll(appendableQueue, result.appendables); resultConsumer.accept(result); } } } public static class Result<RULE_KEY, DIAG_KEY> { public final RULE_KEY ruleKey; public final DIAG_KEY diagKey; public final Iterable<RuleKeyAppendable> appendables; public Result(RULE_KEY ruleKey, DIAG_KEY diagKey, Iterable<RuleKeyAppendable> appendables) { this.ruleKey = ruleKey; this.diagKey = diagKey; this.appendables = appendables; } public static <RULE_KEY, DIAG_KEY> Result<RULE_KEY, DIAG_KEY> of( RULE_KEY ruleKey, RuleKeyResult<DIAG_KEY> res) { return new Result<>(ruleKey, res.result, Iterables.filter(res.deps, RuleKeyAppendable.class)); } } }