Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.analysis; |
| 15 | |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 16 | import com.google.common.base.Function; |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableCollection; |
| 18 | import com.google.common.collect.ImmutableMap; |
Dmitry Lomov | 9b2fc5c | 2016-11-11 11:18:48 +0000 | [diff] [blame] | 19 | import com.google.devtools.build.lib.analysis.MergedConfiguredTarget.DuplicateException; |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 20 | import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 21 | import com.google.devtools.build.lib.packages.SkylarkClassObject; |
| 22 | import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor; |
| 23 | import com.google.devtools.build.lib.rules.SkylarkApiProvider; |
Yun Peng | efd7ca1 | 2016-03-03 13:14:38 +0000 | [diff] [blame] | 24 | import com.google.devtools.build.lib.syntax.EvalException; |
| 25 | import com.google.devtools.build.lib.syntax.SkylarkType; |
Mark Schaller | 6df8179 | 2015-12-10 18:47:47 +0000 | [diff] [blame] | 26 | import com.google.devtools.build.lib.util.Preconditions; |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 27 | import java.util.HashSet; |
| 28 | import java.util.List; |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 29 | import java.util.Map; |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 30 | import java.util.Set; |
| 31 | |
| 32 | /** |
| 33 | * A helper class for transitive infos provided by Skylark rule implementations. |
| 34 | */ |
| 35 | @Immutable |
| 36 | public final class SkylarkProviders implements TransitiveInfoProvider { |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 37 | private final ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> |
| 38 | declaredProviders; |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 39 | private final ImmutableMap<String, Object> skylarkProviders; |
| 40 | |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 41 | 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 Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 54 | } |
| 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 Peng | efd7ca1 | 2016-03-03 13:14:38 +0000 | [diff] [blame] | 71 | * 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 Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 82 | 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 Peng | efd7ca1 | 2016-03-03 13:14:38 +0000 | [diff] [blame] | 106 | /** |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 107 | * Merges skylark providers. The set of providers must be disjoint. |
| 108 | * |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 109 | * @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 Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 111 | * @param providers providers to merge {@code this} with. |
| 112 | */ |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 113 | public static SkylarkProviders merge( |
| 114 | Map<String, Object> premergedProviders, |
| 115 | List<SkylarkProviders> providers) |
Dmitry Lomov | 9b2fc5c | 2016-11-11 11:18:48 +0000 | [diff] [blame] | 116 | throws DuplicateException { |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 117 | if (premergedProviders.size() == 0 && providers.size() == 0) { |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 118 | return null; |
| 119 | } |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 120 | if (premergedProviders.size() == 0 && providers.size() == 1) { |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 121 | return providers.get(0); |
| 122 | } |
| 123 | |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 124 | ImmutableMap<String, Object> skylarkProviders = mergeMaps(providers, |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 125 | SKYLARK_PROVIDERS_MAP_FUNCTION, |
| 126 | premergedProviders); |
| 127 | |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 128 | ImmutableMap<SkylarkClassObjectConstructor.Key, SkylarkClassObject> declaredProviders = |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 129 | mergeMaps(providers, DECLARED_PROVIDERS_MAP_FUNCTION, |
| 130 | ImmutableMap.<SkylarkClassObjectConstructor.Key, SkylarkClassObject>of()); |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 131 | |
| 132 | return new SkylarkProviders(skylarkProviders, declaredProviders); |
| 133 | } |
| 134 | |
| 135 | private static <K, V> ImmutableMap<K, V> mergeMaps(List<SkylarkProviders> providers, |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 136 | Function<SkylarkProviders, Map<K, V>> mapGetter, Map<K, V> premerged) |
Dmitry Lomov | 9b2fc5c | 2016-11-11 11:18:48 +0000 | [diff] [blame] | 137 | throws DuplicateException { |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 138 | Set<K> seenKeys = new HashSet<>(); |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 139 | ImmutableMap.Builder<K, V> resultBuilder = ImmutableMap.builder(); |
| 140 | resultBuilder.putAll(premerged); |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 141 | for (SkylarkProviders provider : providers) { |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 142 | Map<K, V> map = mapGetter.apply(provider); |
| 143 | for (K key : map.keySet()) { |
Dmitry Lomov | 8f45b7c | 2016-11-18 15:14:56 +0000 | [diff] [blame^] | 144 | if (premerged.containsKey(key)) { |
| 145 | continue; |
| 146 | } |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 147 | if (!seenKeys.add(key)) { |
| 148 | // TODO(dslomov): add better diagnostics. |
Dmitry Lomov | 9b2fc5c | 2016-11-11 11:18:48 +0000 | [diff] [blame] | 149 | throw new DuplicateException("Provider " + key + " provided twice"); |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 150 | } |
| 151 | |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 152 | resultBuilder.put(key, map.get(key)); |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 153 | } |
| 154 | } |
Dmitry Lomov | 37a1c1c | 2016-09-01 09:11:02 +0000 | [diff] [blame] | 155 | return resultBuilder.build(); |
Dmitry Lomov | 0b832ce | 2015-10-20 10:03:14 +0000 | [diff] [blame] | 156 | } |
| 157 | } |