// Copyright 2022 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.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.BuildOptionsView;
import com.google.devtools.build.lib.analysis.config.CommonOptions;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;

/**
 * Transitions to a stable, empty configuration for rules that don't rely on configuration.
 *
 * <p>This prevents unnecessary configured target forking, which prevents unnecessary build graph
 * bloat. That in turn reduces build time and memory use.
 *
 * <p>For example, imagine {@code cc_library //:foo} in config A depends on config-independent
 * target {@code //:noconfig} and {@code cc_library //:bar} in config B also depends on {@code
 * //:noconfig}. Without transitions, {@code //:noconfig} will be configured and analyzed twice: for
 * configs A and B. This is completely wasteful if {@code //:noconfig} does the same thing
 * regardless of configuration. Instead, apply this transition to {@code //:noconfig}.
 *
 * <p>This is safest for rules that don't produce actions and don't have dependencies. Remember that
 * even if a rule doesn't read configuration, if any of its transitive dependencies read
 * configuration or if the rule has a {@code select()}, its output may still be
 * configuration-dependent. So use with careful discretion.
 */
public class NoConfigTransition implements PatchTransition {

  @SerializationConstant public static final NoConfigTransition INSTANCE = new NoConfigTransition();

  private NoConfigTransition() {}

  @Override
  public ImmutableSet<Class<? extends FragmentOptions>> requiresOptionFragments() {
    return ImmutableSet.of(CoreOptions.class);
  }

  @Override
  public BuildOptions patch(BuildOptionsView options, EventHandler eventHandler) {
    return CommonOptions.EMPTY_OPTIONS;
  }

  /** Returns a {@link TransitionFactory} instance that generates the transition. */
  public static <T extends TransitionFactory.Data> TransitionFactory<T> createFactory() {
    return new AutoValue_NoConfigTransition_Factory<>();
  }

  /** A {@link TransitionFactory} implementation that generates the transition. */
  @AutoValue
  abstract static class Factory<T extends TransitionFactory.Data> implements TransitionFactory<T> {
    @Override
    public PatchTransition create(T unused) {
      return INSTANCE;
    }

    @Override
    public boolean isTool() {
      return true;
    }
  }
}
