blob: a8b312dc81063d516276e6212f95663d8ec0fade [file] [log] [blame]
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.analysis.config.transitions;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
/**
* A transition factory that composes two other transition factories in an ordered sequence.
*
* <p>Example:
*
* <pre>
* transitionFactory1: { someSetting = $oldVal + " foo" }
* transitionFactory2: { someSetting = $oldVal + " bar" }
* ComposingTransitionFactory(transitionFactory1, transitionFactory2):
* { someSetting = $oldVal + " foo bar" }
* </pre>
*/
@AutoValue
public abstract class ComposingTransitionFactory<T> implements TransitionFactory<T> {
/**
* Creates a {@link ComposingTransitionFactory} that applies the given factories in sequence:
* {@code fromOptions -> transition1 -> transition2 -> toOptions }.
*
* <p>Note that this method checks for transition factories that cannot be composed, such as if
* one of the transitions is {@link NoTransition} or the host transition, and returns an
* efficiently composed transition.
*/
public static <T> TransitionFactory<T> of(
TransitionFactory<T> transitionFactory1, TransitionFactory<T> transitionFactory2) {
Preconditions.checkNotNull(transitionFactory1);
Preconditions.checkNotNull(transitionFactory2);
Preconditions.checkArgument(
!transitionFactory1.isSplit() || !transitionFactory2.isSplit(),
"can't compose two split transition factories");
if (isFinal(transitionFactory1)) {
// Since no other transition can be composed with transitionFactory1, use it directly.
return transitionFactory1;
} else if (NoTransition.isInstance(transitionFactory1)) {
// Since transitionFactory1 causes no changes, use transitionFactory2 directly.
return transitionFactory2;
}
if (NoTransition.isInstance(transitionFactory2)) {
// Since transitionFactory2 causes no changes, use transitionFactory1 directly.
return transitionFactory1;
} else if (isFinal(transitionFactory2)) {
// When the second transition is null or a HOST transition, there's no need to compose. But
// this also
// improves performance: host transitions are common, and ConfiguredTargetFunction has special
// optimized logic to handle them. If they were buried in the last segment of a
// ComposingTransition, those optimizations wouldn't trigger.
return transitionFactory2;
}
return create(transitionFactory1, transitionFactory2);
}
private static <T> boolean isFinal(TransitionFactory<T> transitionFactory) {
return NullTransition.isInstance(transitionFactory) || transitionFactory.isHost();
}
private static <T> TransitionFactory<T> create(
TransitionFactory<T> transitionFactory1, TransitionFactory<T> transitionFactory2) {
return new AutoValue_ComposingTransitionFactory<T>(transitionFactory1, transitionFactory2);
}
abstract TransitionFactory<T> transitionFactory1();
abstract TransitionFactory<T> transitionFactory2();
@Override
public ConfigurationTransition create(T data) {
ConfigurationTransition transition1 = transitionFactory1().create(data);
ConfigurationTransition transition2 = transitionFactory2().create(data);
return new ComposingTransition(transition1, transition2);
}
@Override
public boolean isHost() {
return transitionFactory1().isHost() || transitionFactory2().isHost();
}
@Override
public boolean isTool() {
return transitionFactory1().isTool() || transitionFactory2().isTool();
}
@Override
public boolean isSplit() {
return transitionFactory1().isSplit() || transitionFactory2().isSplit();
}
}