blob: 78cf92e45a5de20c2eb92db6ea5ec42d241b79e3 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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
15package com.google.devtools.build.lib.testutil;
16
17import com.google.common.base.Function;
18import com.google.common.base.Joiner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Iterables;
Googlerd43d3912015-02-09 15:23:56 +000021import com.google.common.collect.LinkedHashMultimap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.common.collect.Lists;
Googlerd43d3912015-02-09 15:23:56 +000023import com.google.common.collect.Multimap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.devtools.build.lib.packages.RuleClass;
Mark Schaller6df81792015-12-10 18:47:47 +000025import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026
27import java.util.Collection;
28import java.util.HashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import java.util.Map;
30
31/**
32 * Utility for quickly creating BUILD file rules for use in tests.
33 *
34 * <p>The use case for this class is writing BUILD files where simple
35 * readability for the sake of rules' relationship to the test framework
36 * is more important than detailed semantics and layout.
37 *
38 * <p>The behavior provided by this class is not meant to be exhaustive,
39 * but should handle a majority of simple cases.
40 *
41 * <p>Example:
42 *
43 * <pre>
44 * String text = new BuildRuleBuilder("java_library", "MyRule")
45 .setSources("First.java", "Second.java", "Third.java")
46 .setDeps(":library", "//java/com/google/common/collect")
47 .setResources("schema/myschema.xsd")
48 .build();
49 * </pre>
50 *
51 */
52public class BuildRuleBuilder {
53 protected final RuleClass ruleClass;
54 protected final String ruleName;
Googlerd43d3912015-02-09 15:23:56 +000055 private Multimap<String, String> multiValueAttributes;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056 private Map<String, Object> singleValueAttributes;
57 protected Map<String, RuleClass> ruleClassMap;
58
59 /**
60 * Create a new instance.
61 *
62 * @param ruleClass the rule class of the new rule
63 * @param ruleName the name of the new rule.
64 */
65 public BuildRuleBuilder(String ruleClass, String ruleName) {
66 this(ruleClass, ruleName, getDefaultRuleClassMap());
67 }
68
69 protected static Map<String, RuleClass> getDefaultRuleClassMap() {
70 return TestRuleClassProvider.getRuleClassProvider().getRuleClassMap();
71 }
72
73 public BuildRuleBuilder(String ruleClass, String ruleName, Map<String, RuleClass> ruleClassMap) {
74 this.ruleClass = ruleClassMap.get(ruleClass);
75 this.ruleName = ruleName;
Googlerd43d3912015-02-09 15:23:56 +000076 this.multiValueAttributes = LinkedHashMultimap.create();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010077 this.singleValueAttributes = new HashMap<>();
78 this.ruleClassMap = ruleClassMap;
79 }
80
81 /**
82 * Sets the value of a single valued attribute
83 */
84 public BuildRuleBuilder setSingleValueAttribute(String attrName, Object value) {
Ulf Adams795895a2015-03-06 15:58:35 +000085 Preconditions.checkState(
86 !singleValueAttributes.containsKey(attrName), "attribute '%s' already set", attrName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087 singleValueAttributes.put(attrName, value);
88 return this;
89 }
90
91 /**
92 * Sets the value of a list type attribute
93 */
Googlerd43d3912015-02-09 15:23:56 +000094 public BuildRuleBuilder addMultiValueAttributes(String attrName, String... value) {
95 multiValueAttributes.putAll(attrName, Lists.newArrayList(value));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 return this;
97 }
98
99 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 * Generate the rule
101 *
102 * @return a string representation of the rule.
103 */
104 public String build() {
105 StringBuilder sb = new StringBuilder();
106 sb.append(ruleClass.getName()).append("(");
107 printNormal(sb, "name", ruleName);
Googlerd43d3912015-02-09 15:23:56 +0000108 for (Map.Entry<String, Collection<String>> entry : multiValueAttributes.asMap().entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109 printArray(sb, entry.getKey(), entry.getValue());
110 }
111 for (Map.Entry<String, Object> entry : singleValueAttributes.entrySet()) {
112 printNormal(sb, entry.getKey(), entry.getValue());
113 }
114 sb.append(")\n");
115 return sb.toString();
116 }
117
Googlerd43d3912015-02-09 15:23:56 +0000118 private void printArray(StringBuilder sb, String attr, Collection<String> values) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100119 if (values == null || values.isEmpty()) {
120 return;
121 }
122 sb.append(" ").append(attr).append(" = ");
123 printList(sb, values);
124 sb.append(",");
125 sb.append("\n");
126 }
127
128 private void printNormal(StringBuilder sb, String attr, Object value) {
129 if (value == null) {
130 return;
131 }
132 sb.append(" ").append(attr).append(" = ");
133 if (value instanceof Integer) {
134 sb.append(value);
135 } else {
136 sb.append("'").append(value).append("'");
137 }
138 sb.append(",");
139 sb.append("\n");
140 }
141
142 /**
143 * Turns iterable of {a b c} into string "['a', 'b', 'c']", appends to
144 * supplied StringBuilder.
145 */
Googlerd43d3912015-02-09 15:23:56 +0000146 private void printList(StringBuilder sb, Collection<String> elements) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147 sb.append("[");
148 Joiner.on(",").appendTo(sb,
149 Iterables.transform(elements, new Function<String, String>() {
150 @Override
151 public String apply(String from) {
152 return "'" + from + "'";
153 }
154 }));
155 sb.append("]");
156 }
157
158 /**
159 * Returns the transitive closure of file names need to be generated in order
160 * for this rule to build.
161 */
162 public Collection<String> getFilesToGenerate() {
163 return ImmutableList.of();
164 }
165
166 /**
167 * Returns the transitive closure of BuildRuleBuilders need to be generated in order
168 * for this rule to build.
169 */
170 public Collection<BuildRuleBuilder> getRulesToGenerate() {
171 return ImmutableList.of();
172 }
Googlerd43d3912015-02-09 15:23:56 +0000173
174 /**
175 * Returns a {@link Dependency} of this {@link BuildRuleBuilder} using attrName.
176 */
177 public Dependency dependsVia(String attrName) {
178 return new Dependency(this, attrName);
179 }
180
181 /**
182 * Representing a {@link BuildRuleBuilder} depending on an other rule via a certain attribute.
183 */
184 public class Dependency {
185 private BuildRuleBuilder buildRuleBuilder;
186 private String attrName;
187
188 private Dependency(BuildRuleBuilder buildRuleBuilder, String attrName) {
189 this.buildRuleBuilder = buildRuleBuilder;
190 this.attrName = attrName;
191 }
192
193 /**
194 * Returns this {@link BuildRuleBuilder} with a new dependency on otherRule.
195 */
196 public BuildRuleBuilder on(BuildRuleBuilder otherRule) {
197 buildRuleBuilder.addMultiValueAttributes(attrName, otherRule.ruleName);
198 return buildRuleBuilder;
199 }
200 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100201}