blob: e8fe2d581cf915a5e2e33d8df0a00f58f4f67c36 [file] [log] [blame]
Dmitry Lomov0b832ce2015-10-20 10:03:14 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.analysis;
15
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000016import com.google.common.base.Function;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000017import com.google.common.collect.ImmutableCollection;
18import com.google.common.collect.ImmutableMap;
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +000019import com.google.devtools.build.lib.analysis.MergedConfiguredTarget.DuplicateException;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000020import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000021import com.google.devtools.build.lib.packages.SkylarkClassObject;
22import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
23import com.google.devtools.build.lib.rules.SkylarkApiProvider;
Yun Pengefd7ca12016-03-03 13:14:38 +000024import com.google.devtools.build.lib.syntax.EvalException;
25import com.google.devtools.build.lib.syntax.SkylarkType;
Mark Schaller6df81792015-12-10 18:47:47 +000026import com.google.devtools.build.lib.util.Preconditions;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000027import java.util.HashSet;
28import java.util.List;
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000029import java.util.Map;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000030import java.util.Set;
31
32/**
33 * A helper class for transitive infos provided by Skylark rule implementations.
34 */
35@Immutable
36public final class SkylarkProviders implements TransitiveInfoProvider {
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000037 private final ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject>
38 declaredProviders;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000039 private final ImmutableMap<String, Object> skylarkProviders;
40
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000041 SkylarkProviders(
42 ImmutableMap<String, Object> skylarkProviders,
43 ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> declaredProviders) {
44 this.declaredProviders = Preconditions.checkNotNull(declaredProviders);
45 this.skylarkProviders = Preconditions.checkNotNull(skylarkProviders);
46 }
47
48 public void init(ConfiguredTarget target) {
49 for (Object o : skylarkProviders.values()) {
50 if (o instanceof SkylarkApiProvider) {
51 ((SkylarkApiProvider) o).init(target);
52 }
53 }
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000054 }
55
56 /**
57 * Returns the keys for the Skylark providers.
58 */
59 public ImmutableCollection<String> getKeys() {
60 return skylarkProviders.keySet();
61 }
62
63 /**
64 * Returns a Skylark provider; "key" must be one from {@link #getKeys()}.
65 */
66 public Object getValue(String key) {
67 return skylarkProviders.get(key);
68 }
69
70 /**
Yun Pengefd7ca12016-03-03 13:14:38 +000071 * Returns a Skylark provider and try to cast it into the specified type
72 */
73 public <TYPE> TYPE getValue(String key, Class<TYPE> type) throws EvalException {
74 Object obj = skylarkProviders.get(key);
75 if (obj == null) {
76 return null;
77 }
78 SkylarkType.checkType(obj, type, key);
79 return type.cast(obj);
80 }
81
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +000082 public SkylarkClassObject getDeclaredProvider(SkylarkClassObjectConstructor.Key key) {
83 return declaredProviders.get(key);
84 }
85
86
87 private static final Function<SkylarkProviders, Map<String, Object>>
88 SKYLARK_PROVIDERS_MAP_FUNCTION = new Function<SkylarkProviders, Map<String, Object>>() {
89 @Override
90 public Map<String, Object> apply(SkylarkProviders skylarkProviders) {
91 return skylarkProviders.skylarkProviders;
92 }
93 };
94
95 public static final Function<SkylarkProviders,
96 Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject>>
97 DECLARED_PROVIDERS_MAP_FUNCTION =
98 new Function<SkylarkProviders, Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject>>() {
99 @Override
100 public Map<SkylarkClassObjectConstructor.Key, SkylarkClassObject> apply(
101 SkylarkProviders skylarkProviders) {
102 return skylarkProviders.declaredProviders;
103 }
104 };
105
Yun Pengefd7ca12016-03-03 13:14:38 +0000106 /**
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000107 * Merges skylark providers. The set of providers must be disjoint.
108 *
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000109 * @param premergedProviders providers that has already been merged. They will
110 * be put into the result as-is and their presence will be ignored among {@code providers}.
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000111 * @param providers providers to merge {@code this} with.
112 */
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000113 public static SkylarkProviders merge(
114 Map<String, Object> premergedProviders,
115 List<SkylarkProviders> providers)
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +0000116 throws DuplicateException {
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000117 if (premergedProviders.size() == 0 && providers.size() == 0) {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000118 return null;
119 }
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000120 if (premergedProviders.size() == 0 && providers.size() == 1) {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000121 return providers.get(0);
122 }
123
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000124 ImmutableMap<String, Object> skylarkProviders = mergeMaps(providers,
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000125 SKYLARK_PROVIDERS_MAP_FUNCTION,
126 premergedProviders);
127
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000128 ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> declaredProviders =
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000129 mergeMaps(providers, DECLARED_PROVIDERS_MAP_FUNCTION,
130 ImmutableMap.<SkylarkClassObjectConstructor.Key, SkylarkClassObject>of());
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000131
132 return new SkylarkProviders(skylarkProviders, declaredProviders);
133 }
134
135 private static <K, V> ImmutableMap<K, V> mergeMaps(List<SkylarkProviders> providers,
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000136 Function<SkylarkProviders, Map<K, V>> mapGetter, Map<K, V> premerged)
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +0000137 throws DuplicateException {
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000138 Set<K> seenKeys = new HashSet<>();
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000139 ImmutableMap.Builder<K, V> resultBuilder = ImmutableMap.builder();
140 resultBuilder.putAll(premerged);
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000141 for (SkylarkProviders provider : providers) {
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000142 Map<K, V> map = mapGetter.apply(provider);
143 for (K key : map.keySet()) {
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000144 if (premerged.containsKey(key)) {
145 continue;
146 }
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000147 if (!seenKeys.add(key)) {
148 // TODO(dslomov): add better diagnostics.
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +0000149 throw new DuplicateException("Provider " + key + " provided twice");
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000150 }
151
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000152 resultBuilder.put(key, map.get(key));
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000153 }
154 }
Dmitry Lomov37a1c1c2016-09-01 09:11:02 +0000155 return resultBuilder.build();
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000156 }
157}