diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java new file mode 100644 index 0000000000..da8eb70f4f --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/MergedConfiguredTarget.java @@ -0,0 +1,205 @@ +// Copyright 2016 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.configuredtargets; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.analysis.AnalysisUtils; +import com.google.devtools.build.lib.analysis.ConfiguredAspect; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.ExtraActionArtifactsProvider; +import com.google.devtools.build.lib.analysis.OutputGroupProvider; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMap; +import com.google.devtools.build.lib.analysis.TransitiveInfoProviderMapBuilder; +import com.google.devtools.build.lib.packages.Info; +import com.google.devtools.build.lib.packages.Provider; +import com.google.devtools.build.lib.packages.Provider.Key; +import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * A single dependency with its configured target and aspects merged together. + * + * <p>This is an ephemeral object created only for the analysis of a single configured target. After + * that configured target is analyzed, this is thrown away. + */ +public final class MergedConfiguredTarget extends AbstractConfiguredTarget { + private final ConfiguredTarget base; + private final TransitiveInfoProviderMap providers; + + /** + * This exception is thrown when configured targets and aspects + * being merged provide duplicate things that they shouldn't + * (output groups or providers). + */ + public static final class DuplicateException extends Exception { + public DuplicateException(String message) { + super(message); + } + } + + private MergedConfiguredTarget(ConfiguredTarget base, TransitiveInfoProviderMap providers) { + super(base.getTarget(), base.getConfiguration()); + this.base = base; + this.providers = providers; + } + + @Override + public <P extends TransitiveInfoProvider> P getProvider(Class<P> providerClass) { + AnalysisUtils.checkProvider(providerClass); + + P provider = providers.getProvider(providerClass); + if (provider == null) { + provider = base.getProvider(providerClass); + } + + return provider; + } + + @Override + protected void addExtraSkylarkKeys(Consumer<String> result) { + if (base instanceof AbstractConfiguredTarget) { + ((AbstractConfiguredTarget) base).addExtraSkylarkKeys(result); + } + for (int i = 0; i < providers.getProviderCount(); i++) { + Object classAt = providers.getProviderKeyAt(i); + if (classAt instanceof String) { + result.accept((String) classAt); + } + } + } + + @Override + protected Info rawGetSkylarkProvider(Provider.Key providerKey) { + Info provider = providers.getProvider(providerKey); + if (provider == null) { + provider = base.get(providerKey); + } + return provider; + } + + @Override + protected Object rawGetSkylarkProvider(String providerKey) { + Object provider = providers.getProvider(providerKey); + if (provider == null) { + provider = base.get(providerKey); + } + return provider; + } + + /** Creates an instance based on a configured target and a set of aspects. */ + public static ConfiguredTarget of(ConfiguredTarget base, Iterable<ConfiguredAspect> aspects) + throws DuplicateException { + if (Iterables.isEmpty(aspects)) { + // If there are no aspects, don't bother with creating a proxy object + return base; + } + + // Merge output group providers. + OutputGroupProvider mergedOutputGroupProvider = + OutputGroupProvider.merge(getAllOutputGroupProviders(base, aspects)); + + // Merge extra-actions provider. + ExtraActionArtifactsProvider mergedExtraActionProviders = ExtraActionArtifactsProvider.merge( + getAllProviders(base, aspects, ExtraActionArtifactsProvider.class)); + + TransitiveInfoProviderMapBuilder aspectProviders = new TransitiveInfoProviderMapBuilder(); + if (mergedOutputGroupProvider != null) { + aspectProviders.put(mergedOutputGroupProvider); + } + if (mergedExtraActionProviders != null) { + aspectProviders.add(mergedExtraActionProviders); + } + + for (ConfiguredAspect aspect : aspects) { + TransitiveInfoProviderMap providers = aspect.getProviders(); + for (int i = 0; i < providers.getProviderCount(); ++i) { + Object providerKey = providers.getProviderKeyAt(i); + if (OutputGroupProvider.SKYLARK_CONSTRUCTOR.getKey().equals(providerKey) + || ExtraActionArtifactsProvider.class.equals(providerKey)) { + continue; + } + + if (providerKey instanceof Class<?>) { + @SuppressWarnings("unchecked") + Class<? extends TransitiveInfoProvider> providerClass = + (Class<? extends TransitiveInfoProvider>) providerKey; + if (base.getProvider(providerClass) != null + || aspectProviders.contains(providerClass)) { + throw new DuplicateException("Provider " + providerKey + " provided twice"); + } + aspectProviders.put( + providerClass, (TransitiveInfoProvider) providers.getProviderInstanceAt(i)); + } else if (providerKey instanceof String) { + String legacyId = (String) providerKey; + if (base.get(legacyId) != null || aspectProviders.contains(legacyId)) { + throw new DuplicateException("Provider " + legacyId + " provided twice"); + } + aspectProviders.put(legacyId, providers.getProviderInstanceAt(i)); + } else if (providerKey instanceof Provider.Key) { + Provider.Key key = (Key) providerKey; + if (base.get(key) != null || aspectProviders.contains(key)) { + throw new DuplicateException("Provider " + key + " provided twice"); + } + aspectProviders.put((Info) providers.getProviderInstanceAt(i)); + } + } + } + return new MergedConfiguredTarget(base, aspectProviders.build()); + } + + private static ImmutableList<OutputGroupProvider> getAllOutputGroupProviders( + ConfiguredTarget base, Iterable<ConfiguredAspect> aspects) { + OutputGroupProvider baseProvider = OutputGroupProvider.get(base); + ImmutableList.Builder<OutputGroupProvider> providers = ImmutableList.builder(); + if (baseProvider != null) { + providers.add(baseProvider); + } + + for (ConfiguredAspect configuredAspect : aspects) { + OutputGroupProvider aspectProvider = OutputGroupProvider.get(configuredAspect); + if (aspectProvider == null) { + continue; + } + providers.add(aspectProvider); + } + return providers.build(); + } + + private static <T extends TransitiveInfoProvider> List<T> getAllProviders( + ConfiguredTarget base, Iterable<ConfiguredAspect> aspects, Class<T> providerClass) { + T baseProvider = base.getProvider(providerClass); + List<T> providers = new ArrayList<>(); + if (baseProvider != null) { + providers.add(baseProvider); + } + + for (ConfiguredAspect configuredAspect : aspects) { + T aspectProvider = configuredAspect.getProvider(providerClass); + if (aspectProvider == null) { + continue; + } + providers.add(aspectProvider); + } + return providers; + } + + @Override + public void repr(SkylarkPrinter printer) { + printer.append("<merged target " + getLabel() + ">"); + } +} |