blob: bb64b94e1bd7c7b6f276834e73c8f6d1c3a5e51c [file] [log] [blame]
cparsons5d85e752018-06-26 13:47:28 -07001// Copyright 2018 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
15package com.google.devtools.build.skydoc;
16
cparsonsd790ce42018-06-27 15:29:02 -070017import com.google.common.base.Functions;
cparsons37fbbf32018-11-21 15:13:24 -080018import com.google.common.base.Strings;
cparsonsd790ce42018-06-27 15:29:02 -070019import com.google.common.collect.ImmutableList;
cparsons5d85e752018-06-26 13:47:28 -070020import com.google.common.collect.ImmutableMap;
cparsons93adb102018-07-11 11:44:17 -070021import com.google.common.collect.ImmutableSet;
dannarkd102a392019-01-09 13:12:01 -080022import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
cparsons6964a0b2018-07-26 16:02:23 -070023import com.google.devtools.build.lib.cmdline.Label;
24import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
cparsons5d85e752018-06-26 13:47:28 -070025import com.google.devtools.build.lib.events.EventHandler;
cparsons37fbbf32018-11-21 15:13:24 -080026import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions;
cparsons5d85e752018-06-26 13:47:28 -070027import com.google.devtools.build.lib.skylarkbuildapi.TopLevelBootstrap;
cparsons63cf39b2018-11-13 16:21:17 -080028import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidAssetsInfoApi;
29import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidBinaryDataInfoApi;
cparsons6645e912018-06-29 11:18:05 -070030import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidBootstrap;
cparsons63cf39b2018-11-13 16:21:17 -080031import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidCcLinkParamsProviderApi;
32import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidDex2OatInfoApi;
33import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidFeatureFlagSetProviderApi;
34import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidIdeInfoProviderApi;
35import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidIdlProviderApi;
36import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidLibraryAarInfoApi;
37import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidLibraryResourceClassJarProviderApi;
38import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidManifestInfoApi;
39import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidPreDexJarProviderApi;
40import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidProguardInfoApi;
41import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidSdkProviderApi;
42import com.google.devtools.build.lib.skylarkbuildapi.android.ProguardMappingProviderApi;
43import com.google.devtools.build.lib.skylarkbuildapi.android.UsesDataBindingProviderApi;
cparsons030448a2018-06-28 12:32:46 -070044import com.google.devtools.build.lib.skylarkbuildapi.apple.AppleBootstrap;
cparsons574eb162018-06-29 07:19:22 -070045import com.google.devtools.build.lib.skylarkbuildapi.config.ConfigBootstrap;
cparsons636f5c02018-06-29 12:34:37 -070046import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcBootstrap;
cparsons63cf39b2018-11-13 16:21:17 -080047import com.google.devtools.build.lib.skylarkbuildapi.java.GeneratedExtensionRegistryProviderApi;
cparsons2fd48282018-06-29 13:20:56 -070048import com.google.devtools.build.lib.skylarkbuildapi.java.JavaBootstrap;
cparsons574eb162018-06-29 07:19:22 -070049import com.google.devtools.build.lib.skylarkbuildapi.platform.PlatformBootstrap;
50import com.google.devtools.build.lib.skylarkbuildapi.repository.RepositoryBootstrap;
51import com.google.devtools.build.lib.skylarkbuildapi.test.TestingBootstrap;
cparsonsd790ce42018-06-27 15:29:02 -070052import com.google.devtools.build.lib.syntax.BaseFunction;
cparsons5d85e752018-06-26 13:47:28 -070053import com.google.devtools.build.lib.syntax.BuildFileAST;
54import com.google.devtools.build.lib.syntax.Environment;
55import com.google.devtools.build.lib.syntax.Environment.Extension;
56import com.google.devtools.build.lib.syntax.Environment.GlobalFrame;
57import com.google.devtools.build.lib.syntax.MethodLibrary;
58import com.google.devtools.build.lib.syntax.Mutability;
59import com.google.devtools.build.lib.syntax.ParserInputSource;
60import com.google.devtools.build.lib.syntax.Runtime;
cparsonsc90764d2018-06-28 15:17:29 -070061import com.google.devtools.build.lib.syntax.SkylarkImport;
cparsons37fbbf32018-11-21 15:13:24 -080062import com.google.devtools.build.lib.syntax.SkylarkSemantics;
cparsons2b98ef72018-11-07 14:05:11 -080063import com.google.devtools.build.lib.syntax.UserDefinedFunction;
cparsons5d85e752018-06-26 13:47:28 -070064import com.google.devtools.build.skydoc.fakebuildapi.FakeActionsInfoProvider;
65import com.google.devtools.build.skydoc.fakebuildapi.FakeBuildApiGlobals;
juliexxia8e358772018-09-28 14:01:12 -070066import com.google.devtools.build.skydoc.fakebuildapi.FakeConfigApi;
cparsons5d85e752018-06-26 13:47:28 -070067import com.google.devtools.build.skydoc.fakebuildapi.FakeDefaultInfoProvider;
68import com.google.devtools.build.skydoc.fakebuildapi.FakeOutputGroupInfo.FakeOutputGroupInfoProvider;
69import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkAttrApi;
70import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkCommandLineApi;
71import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkNativeModuleApi;
72import com.google.devtools.build.skydoc.fakebuildapi.FakeSkylarkRuleFunctionsApi;
73import com.google.devtools.build.skydoc.fakebuildapi.FakeStructApi.FakeStructProviderApi;
cparsons6645e912018-06-29 11:18:05 -070074import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidDeviceBrokerInfo.FakeAndroidDeviceBrokerInfoProvider;
75import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidInstrumentationInfo.FakeAndroidInstrumentationInfoProvider;
76import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidNativeLibsInfo.FakeAndroidNativeLibsInfoProvider;
77import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidResourcesInfo.FakeAndroidResourcesInfoProvider;
78import com.google.devtools.build.skydoc.fakebuildapi.android.FakeAndroidSkylarkCommon;
79import com.google.devtools.build.skydoc.fakebuildapi.android.FakeApkInfo.FakeApkInfoProvider;
cparsons030448a2018-06-28 12:32:46 -070080import com.google.devtools.build.skydoc.fakebuildapi.apple.FakeAppleCommon;
cparsonse0efc142018-10-17 09:39:10 -070081import com.google.devtools.build.skydoc.fakebuildapi.config.FakeConfigGlobalLibrary;
cparsons574eb162018-06-29 07:19:22 -070082import com.google.devtools.build.skydoc.fakebuildapi.config.FakeConfigSkylarkCommon;
cparsons636f5c02018-06-29 12:34:37 -070083import com.google.devtools.build.skydoc.fakebuildapi.cpp.FakeCcModule;
Googler91eb3d22018-10-18 00:05:29 -070084import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaCcLinkParamsProvider;
cparsons2fd48282018-06-29 13:20:56 -070085import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaCommon;
86import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaInfo.FakeJavaInfoProvider;
87import com.google.devtools.build.skydoc.fakebuildapi.java.FakeJavaProtoCommon;
cparsons574eb162018-06-29 07:19:22 -070088import com.google.devtools.build.skydoc.fakebuildapi.platform.FakePlatformCommon;
89import com.google.devtools.build.skydoc.fakebuildapi.repository.FakeRepositoryModule;
cparsons645a35e2018-10-01 13:03:23 -070090import com.google.devtools.build.skydoc.fakebuildapi.test.FakeAnalysisFailureInfoProvider;
cparsons7555df62018-10-03 12:02:28 -070091import com.google.devtools.build.skydoc.fakebuildapi.test.FakeAnalysisTestResultInfoProvider;
cparsons574eb162018-06-29 07:19:22 -070092import com.google.devtools.build.skydoc.fakebuildapi.test.FakeTestingModule;
cparsonsfc0e52f2018-07-09 11:06:57 -070093import com.google.devtools.build.skydoc.rendering.MarkdownRenderer;
cparsonsb68bf022018-10-17 10:33:40 -070094import com.google.devtools.build.skydoc.rendering.ProviderInfo;
cparsons5d85e752018-06-26 13:47:28 -070095import com.google.devtools.build.skydoc.rendering.RuleInfo;
cparsons2b98ef72018-11-07 14:05:11 -080096import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo;
97import com.google.devtools.build.skydoc.rendering.UserDefinedFunctionInfo.DocstringParseException;
cparsons37fbbf32018-11-21 15:13:24 -080098import com.google.devtools.common.options.OptionsParser;
cparsons5d85e752018-06-26 13:47:28 -070099import java.io.IOException;
100import java.io.PrintWriter;
cparsons6964a0b2018-07-26 16:02:23 -0700101import java.nio.file.NoSuchFileException;
cparsons5d85e752018-06-26 13:47:28 -0700102import java.nio.file.Path;
103import java.nio.file.Paths;
104import java.util.ArrayList;
cparsonsc90764d2018-06-28 15:17:29 -0700105import java.util.HashMap;
106import java.util.LinkedHashSet;
cparsons5d85e752018-06-26 13:47:28 -0700107import java.util.List;
108import java.util.Map;
cparsonsd790ce42018-06-27 15:29:02 -0700109import java.util.Map.Entry;
cparsons2b98ef72018-11-07 14:05:11 -0800110import java.util.TreeMap;
cparsonsd790ce42018-06-27 15:29:02 -0700111import java.util.stream.Collectors;
cparsons5d85e752018-06-26 13:47:28 -0700112
113/**
114 * Main entry point for the Skydoc binary.
115 *
116 * <p>Skydoc generates human-readable documentation for relevant details of skylark files by
117 * running a skylark interpreter with a fake implementation of the build API.</p>
118 *
119 * <p>Currently, Skydoc generates documentation for skylark rule definitions (discovered by
120 * invocations of the build API function {@code rule()}.</p>
121 *
122 * <p>Usage:</p>
123 * <pre>
cparsons6964a0b2018-07-26 16:02:23 -0700124 * skydoc {target_skylark_file_label} {output_file} [symbol_name]...
cparsons5d85e752018-06-26 13:47:28 -0700125 * </pre>
cparsons93adb102018-07-11 11:44:17 -0700126 * <p>
127 * Generates documentation for all exported symbols of the target skylark file that are
128 * specified in the list of symbol names. If no symbol names are supplied, outputs documentation
129 * for all exported symbols in the target skylark file.
130 * </p>
cparsons5d85e752018-06-26 13:47:28 -0700131 */
132public class SkydocMain {
133
cparsons5d85e752018-06-26 13:47:28 -0700134 private final EventHandler eventHandler = new SystemOutEventHandler();
cparsonsc90764d2018-06-28 15:17:29 -0700135 private final LinkedHashSet<Path> pending = new LinkedHashSet<>();
136 private final Map<Path, Environment> loaded = new HashMap<>();
137 private final SkylarkFileAccessor fileAccessor;
138
139 public SkydocMain(SkylarkFileAccessor fileAccessor) {
140 this.fileAccessor = fileAccessor;
141 }
cparsons5d85e752018-06-26 13:47:28 -0700142
cparsons6964a0b2018-07-26 16:02:23 -0700143 public static void main(String[] args)
144 throws IOException, InterruptedException, LabelSyntaxException {
cparsons37fbbf32018-11-21 15:13:24 -0800145 OptionsParser parser =
146 OptionsParser.newOptionsParser(SkylarkSemanticsOptions.class, SkydocOptions.class);
147 parser.parseAndExitUponError(args);
148 SkylarkSemanticsOptions semanticsOptions = parser.getOptions(SkylarkSemanticsOptions.class);
149 SkydocOptions skydocOptions = parser.getOptions(SkydocOptions.class);
cparsons5d85e752018-06-26 13:47:28 -0700150
cparsons37fbbf32018-11-21 15:13:24 -0800151 String targetFileLabelString;
152 String outputPath;
153 ImmutableSet<String> symbolNames;
154
155 // TODO(cparsons): Remove optional positional arg parsing.
156 List<String> residualArgs = parser.getResidue();
157 if (Strings.isNullOrEmpty(skydocOptions.targetFileLabel)
158 || Strings.isNullOrEmpty(skydocOptions.outputFilePath)) {
159 if (residualArgs.size() < 2) {
160 throw new IllegalArgumentException(
161 "Expected two or more arguments. Usage:\n"
162 + "{skydoc_bin} {target_skylark_file_label} {output_file} [symbol_names]...");
163 }
164
165 targetFileLabelString = residualArgs.get(0);
166 outputPath = residualArgs.get(1);
167 symbolNames = getSymbolNames(residualArgs);
168 } else {
169 targetFileLabelString = skydocOptions.targetFileLabel;
170 outputPath = skydocOptions.outputFilePath;
171 symbolNames = ImmutableSet.copyOf(skydocOptions.symbolNames);
172 }
cparsons5d85e752018-06-26 13:47:28 -0700173
cparsons6964a0b2018-07-26 16:02:23 -0700174 Label targetFileLabel =
175 Label.parseAbsolute(targetFileLabelString, ImmutableMap.of());
cparsons5d85e752018-06-26 13:47:28 -0700176
cparsonsd790ce42018-06-27 15:29:02 -0700177 ImmutableMap.Builder<String, RuleInfo> ruleInfoMap = ImmutableMap.builder();
cparsonsb68bf022018-10-17 10:33:40 -0700178 ImmutableMap.Builder<String, ProviderInfo> providerInfoMap = ImmutableMap.builder();
cparsons93adb102018-07-11 11:44:17 -0700179 ImmutableList.Builder<RuleInfo> unknownNamedRules = ImmutableList.builder();
cparsons2b98ef72018-11-07 14:05:11 -0800180 ImmutableMap.Builder<String, UserDefinedFunction> userDefinedFunctions = ImmutableMap.builder();
cparsonsd790ce42018-06-27 15:29:02 -0700181
cparsons6964a0b2018-07-26 16:02:23 -0700182 new SkydocMain(new FilesystemFileAccessor())
cparsons2b98ef72018-11-07 14:05:11 -0800183 .eval(
cparsons37fbbf32018-11-21 15:13:24 -0800184 semanticsOptions.toSkylarkSemantics(),
185 targetFileLabel,
186 ruleInfoMap,
187 unknownNamedRules,
188 providerInfoMap,
189 userDefinedFunctions);
cparsons5d85e752018-06-26 13:47:28 -0700190
cparsonsfc0e52f2018-07-09 11:06:57 -0700191 MarkdownRenderer renderer = new MarkdownRenderer();
192
cparsons93adb102018-07-11 11:44:17 -0700193 if (symbolNames.isEmpty()) {
194 try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) {
195 printRuleInfos(printWriter, renderer, ruleInfoMap.build(), unknownNamedRules.build());
cparsonsb68bf022018-10-17 10:33:40 -0700196 printProviderInfos(printWriter, renderer, providerInfoMap.build());
cparsons2b98ef72018-11-07 14:05:11 -0800197 printUserDefinedFunctions(printWriter, renderer, userDefinedFunctions.build());
cparsons93adb102018-07-11 11:44:17 -0700198 }
199 } else {
cparsonsb68bf022018-10-17 10:33:40 -0700200 Map<String, RuleInfo> filteredRuleInfos =
cparsons93adb102018-07-11 11:44:17 -0700201 ruleInfoMap.build().entrySet().stream()
202 .filter(entry -> symbolNames.contains(entry.getKey()))
cparsonsb68bf022018-10-17 10:33:40 -0700203 .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue));
204 Map<String, ProviderInfo> filteredProviderInfos =
205 providerInfoMap.build().entrySet().stream()
206 .filter(entry -> symbolNames.contains(entry.getKey()))
207 .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue));
cparsons2b98ef72018-11-07 14:05:11 -0800208 Map<String, UserDefinedFunction> filteredUserDefinedFunctions =
209 userDefinedFunctions.build().entrySet().stream()
210 .filter(entry -> symbolNames.contains(entry.getKey()))
211 .collect(ImmutableMap.toImmutableMap(Entry::getKey, Entry::getValue));
cparsons93adb102018-07-11 11:44:17 -0700212 try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) {
213 printRuleInfos(printWriter, renderer, filteredRuleInfos, ImmutableList.of());
cparsonsb68bf022018-10-17 10:33:40 -0700214 printProviderInfos(printWriter, renderer, filteredProviderInfos);
cparsons2b98ef72018-11-07 14:05:11 -0800215 printUserDefinedFunctions(printWriter, renderer, filteredUserDefinedFunctions);
cparsons93adb102018-07-11 11:44:17 -0700216 }
cparsons5d85e752018-06-26 13:47:28 -0700217 }
218 }
219
cparsons37fbbf32018-11-21 15:13:24 -0800220 private static ImmutableSet<String> getSymbolNames(List<String> args) {
cparsons93adb102018-07-11 11:44:17 -0700221 ImmutableSet.Builder<String> symbolNameSet = ImmutableSet.builder();
cparsons37fbbf32018-11-21 15:13:24 -0800222 for (int argi = 2; argi < args.size(); argi++) {
223 symbolNameSet.add(args.get(argi));
cparsons93adb102018-07-11 11:44:17 -0700224 }
225 return symbolNameSet.build();
226 }
227
cparsons5d85e752018-06-26 13:47:28 -0700228 private static void printRuleInfos(
cparsonsd790ce42018-06-27 15:29:02 -0700229 PrintWriter printWriter,
cparsonsfc0e52f2018-07-09 11:06:57 -0700230 MarkdownRenderer renderer,
cparsonsd790ce42018-06-27 15:29:02 -0700231 Map<String, RuleInfo> ruleInfos,
cparsons93adb102018-07-11 11:44:17 -0700232 List<RuleInfo> unknownNamedRules) throws IOException {
cparsonsd790ce42018-06-27 15:29:02 -0700233 for (Entry<String, RuleInfo> ruleInfoEntry : ruleInfos.entrySet()) {
cparsonsfc0e52f2018-07-09 11:06:57 -0700234 printRuleInfo(printWriter, renderer, ruleInfoEntry.getKey(), ruleInfoEntry.getValue());
235 printWriter.println();
cparsons5d85e752018-06-26 13:47:28 -0700236 }
cparsons93adb102018-07-11 11:44:17 -0700237 for (RuleInfo unknownNamedRule : unknownNamedRules) {
238 printRuleInfo(printWriter, renderer, "<unknown name>", unknownNamedRule);
cparsonsfc0e52f2018-07-09 11:06:57 -0700239 printWriter.println();
cparsonsd790ce42018-06-27 15:29:02 -0700240 }
241 }
242
cparsonsb68bf022018-10-17 10:33:40 -0700243 private static void printProviderInfos(
244 PrintWriter printWriter,
245 MarkdownRenderer renderer,
246 Map<String, ProviderInfo> providerInfos) throws IOException {
247 for (Entry<String, ProviderInfo> entry : providerInfos.entrySet()) {
248 printProviderInfo(printWriter, renderer, entry.getKey(), entry.getValue());
249 printWriter.println();
250 }
251 }
252
cparsons2b98ef72018-11-07 14:05:11 -0800253 private static void printUserDefinedFunctions(
254 PrintWriter printWriter,
255 MarkdownRenderer renderer,
256 Map<String, UserDefinedFunction> userDefinedFunctions)
257 throws IOException {
258 for (Entry<String, UserDefinedFunction> entry : userDefinedFunctions.entrySet()) {
259 try {
260 UserDefinedFunctionInfo functionInfo =
261 UserDefinedFunctionInfo.fromNameAndFunction(entry.getKey(), entry.getValue());
262 printUserDefinedFunctionInfo(printWriter, renderer, functionInfo);
263 printWriter.println();
264 } catch (DocstringParseException exception) {
265 System.err.println(exception.getMessage());
266 System.err.println();
267 }
268 }
269 }
270
cparsonsd790ce42018-06-27 15:29:02 -0700271 private static void printRuleInfo(
cparsonsfc0e52f2018-07-09 11:06:57 -0700272 PrintWriter printWriter, MarkdownRenderer renderer,
273 String exportedName, RuleInfo ruleInfo) throws IOException {
274 printWriter.println(renderer.render(exportedName, ruleInfo));
cparsons5d85e752018-06-26 13:47:28 -0700275 }
276
cparsonsb68bf022018-10-17 10:33:40 -0700277 private static void printProviderInfo(
278 PrintWriter printWriter, MarkdownRenderer renderer,
279 String exportedName, ProviderInfo providerInfo) throws IOException {
280 printWriter.println(renderer.render(exportedName, providerInfo));
281 }
282
cparsons2b98ef72018-11-07 14:05:11 -0800283 private static void printUserDefinedFunctionInfo(
284 PrintWriter printWriter, MarkdownRenderer renderer, UserDefinedFunctionInfo functionInfo)
285 throws IOException {
286 printWriter.println(renderer.render(functionInfo));
287 }
288
cparsons5d85e752018-06-26 13:47:28 -0700289 /**
cparsonsc90764d2018-06-28 15:17:29 -0700290 * Evaluates/interprets the skylark file at a given path and its transitive skylark dependencies
cparsonsb68bf022018-10-17 10:33:40 -0700291 * using a fake build API and collects information about all rule definitions made in the root
292 * skylark file.
cparsons5d85e752018-06-26 13:47:28 -0700293 *
cparsons6964a0b2018-07-26 16:02:23 -0700294 * @param label the label of the skylark file to evaluate
cparsonsb68bf022018-10-17 10:33:40 -0700295 * @param ruleInfoMap a map builder to be populated with rule definition information for named
296 * rules. Keys are exported names of rules, and values are their {@link RuleInfo} rule
297 * descriptions. For example, 'my_rule = rule(...)' has key 'my_rule'
298 * @param unknownNamedRules a list builder to be populated with rule definition information for
299 * rules which were not exported as top level symbols
300 * @param providerInfoMap a map builder to be populated with provider definition information for
cparsons2b98ef72018-11-07 14:05:11 -0800301 * named providers. Keys are exported names of providers, and values are their {@link
302 * ProviderInfo} descriptions. For example, 'my_provider = provider(...)' has key
cparsonsb68bf022018-10-17 10:33:40 -0700303 * 'my_provider'
cparsons2b98ef72018-11-07 14:05:11 -0800304 * @param userDefinedFunctionMap a map builder to be populated with user-defined functions. Keys
305 * are exported names of functions, and values are the {@link UserDefinedFunction} objects.
306 * For example, 'def my_function(foo):' is a function with key 'my_function'.
cparsons5d85e752018-06-26 13:47:28 -0700307 * @throws InterruptedException if evaluation is interrupted
308 */
cparsonsc90764d2018-06-28 15:17:29 -0700309 public Environment eval(
cparsons37fbbf32018-11-21 15:13:24 -0800310 SkylarkSemantics semantics,
cparsons6964a0b2018-07-26 16:02:23 -0700311 Label label,
cparsonsd790ce42018-06-27 15:29:02 -0700312 ImmutableMap.Builder<String, RuleInfo> ruleInfoMap,
cparsonsb68bf022018-10-17 10:33:40 -0700313 ImmutableList.Builder<RuleInfo> unknownNamedRules,
cparsons2b98ef72018-11-07 14:05:11 -0800314 ImmutableMap.Builder<String, ProviderInfo> providerInfoMap,
315 ImmutableMap.Builder<String, UserDefinedFunction> userDefinedFunctionMap)
cparsons6964a0b2018-07-26 16:02:23 -0700316 throws InterruptedException, IOException, LabelSyntaxException {
cparsons79aa7b42018-07-13 08:00:18 -0700317
cparsons93adb102018-07-11 11:44:17 -0700318 List<RuleInfo> ruleInfoList = new ArrayList<>();
cparsonsb68bf022018-10-17 10:33:40 -0700319 List<ProviderInfo> providerInfoList = new ArrayList<>();
cparsons37fbbf32018-11-21 15:13:24 -0800320 Environment env = recursiveEval(semantics, label, ruleInfoList, providerInfoList);
cparsons93adb102018-07-11 11:44:17 -0700321
322 Map<BaseFunction, RuleInfo> ruleFunctions = ruleInfoList.stream()
323 .collect(Collectors.toMap(
324 RuleInfo::getIdentifierFunction,
325 Functions.identity()));
cparsonsb68bf022018-10-17 10:33:40 -0700326 Map<BaseFunction, ProviderInfo> providerInfos = providerInfoList.stream()
327 .collect(Collectors.toMap(
328 ProviderInfo::getIdentifier,
329 Functions.identity()));
cparsons93adb102018-07-11 11:44:17 -0700330
331 ImmutableSet.Builder<RuleInfo> handledRuleDefinitions = ImmutableSet.builder();
cparsons2b98ef72018-11-07 14:05:11 -0800332
333 // Sort the bindings so their ordering is deterministic.
laurentlbe7ebb7e2018-11-26 09:59:06 -0800334 TreeMap<String, Object> sortedBindings = new TreeMap<>(env.getGlobals().getExportedBindings());
cparsons2b98ef72018-11-07 14:05:11 -0800335
336 for (Entry<String, Object> envEntry : sortedBindings.entrySet()) {
cparsons93adb102018-07-11 11:44:17 -0700337 if (ruleFunctions.containsKey(envEntry.getValue())) {
338 RuleInfo ruleInfo = ruleFunctions.get(envEntry.getValue());
339 ruleInfoMap.put(envEntry.getKey(), ruleInfo);
340 handledRuleDefinitions.add(ruleInfo);
341 }
cparsonsb68bf022018-10-17 10:33:40 -0700342 if (providerInfos.containsKey(envEntry.getValue())) {
343 ProviderInfo providerInfo = providerInfos.get(envEntry.getValue());
344 providerInfoMap.put(envEntry.getKey(), providerInfo);
345 }
cparsons2b98ef72018-11-07 14:05:11 -0800346 if (envEntry.getValue() instanceof UserDefinedFunction) {
347 UserDefinedFunction userDefinedFunction = (UserDefinedFunction) envEntry.getValue();
348 userDefinedFunctionMap.put(envEntry.getKey(), userDefinedFunction);
349 }
cparsons93adb102018-07-11 11:44:17 -0700350 }
351
352 unknownNamedRules.addAll(ruleFunctions.values().stream()
353 .filter(ruleInfo -> !handledRuleDefinitions.build().contains(ruleInfo)).iterator());
354
355 return env;
356 }
357
358 /**
359 * Recursively evaluates/interprets the skylark file at a given path and its transitive skylark
cparsons37fbbf32018-11-21 15:13:24 -0800360 * dependencies using a fake build API and collects information about all rule definitions made in
361 * those files.
cparsons93adb102018-07-11 11:44:17 -0700362 *
cparsons6964a0b2018-07-26 16:02:23 -0700363 * @param label the label of the skylark file to evaluate
cparsons93adb102018-07-11 11:44:17 -0700364 * @param ruleInfoList a collection of all rule definitions made so far (using rule()); this
365 * method will add to this list as it evaluates additional files
366 * @throws InterruptedException if evaluation is interrupted
367 */
368 private Environment recursiveEval(
cparsons37fbbf32018-11-21 15:13:24 -0800369 SkylarkSemantics semantics,
370 Label label,
371 List<RuleInfo> ruleInfoList,
372 List<ProviderInfo> providerInfoList)
cparsons6964a0b2018-07-26 16:02:23 -0700373 throws InterruptedException, IOException, LabelSyntaxException {
cparsonsb749ab12018-08-16 12:45:02 -0700374 Path path = pathOfLabel(label);
375
cparsonsc90764d2018-06-28 15:17:29 -0700376 if (pending.contains(path)) {
377 throw new IllegalStateException("cycle with " + path);
378 } else if (loaded.containsKey(path)) {
379 return loaded.get(path);
380 }
381 pending.add(path);
cparsons5d85e752018-06-26 13:47:28 -0700382
cparsonsc90764d2018-06-28 15:17:29 -0700383 ParserInputSource parserInputSource = fileAccessor.inputSource(path.toString());
384 BuildFileAST buildFileAST = BuildFileAST.parseSkylarkFile(parserInputSource, eventHandler);
385
386 Map<String, Extension> imports = new HashMap<>();
387 for (SkylarkImport anImport : buildFileAST.getImports()) {
dannarkd102a392019-01-09 13:12:01 -0800388 BazelStarlarkContext context =
389 new BazelStarlarkContext("", ImmutableMap.of(), ImmutableMap.of());
390 Label relativeLabel = label.getRelative(anImport.getImportString(), context);
cparsonsc90764d2018-06-28 15:17:29 -0700391
cparsons6964a0b2018-07-26 16:02:23 -0700392 try {
cparsons37fbbf32018-11-21 15:13:24 -0800393 Environment importEnv =
394 recursiveEval(semantics, relativeLabel, ruleInfoList, providerInfoList);
cparsons6964a0b2018-07-26 16:02:23 -0700395 imports.put(anImport.getImportString(), new Extension(importEnv));
396 } catch (NoSuchFileException noSuchFileException) {
397 throw new IllegalStateException(
398 String.format("File %s imported '%s', yet %s was not found.",
cparsonsb749ab12018-08-16 12:45:02 -0700399 path, anImport.getImportString(), pathOfLabel(relativeLabel)));
cparsons6964a0b2018-07-26 16:02:23 -0700400 }
cparsonsc90764d2018-06-28 15:17:29 -0700401 }
402
cparsons37fbbf32018-11-21 15:13:24 -0800403 Environment env =
404 evalSkylarkBody(semantics, buildFileAST, imports, ruleInfoList, providerInfoList);
cparsonsc90764d2018-06-28 15:17:29 -0700405
406 pending.remove(path);
407 env.mutability().freeze();
408 loaded.put(path, env);
409 return env;
410 }
411
cparsonsb749ab12018-08-16 12:45:02 -0700412 private Path pathOfLabel(Label label) {
413 String workspacePrefix = label.getWorkspaceRoot().isEmpty()
414 ? ""
415 : label.getWorkspaceRoot() + "/";
416
417 return Paths.get(workspacePrefix + label.toPathFragment());
418 }
419
cparsons37fbbf32018-11-21 15:13:24 -0800420 /** Evaluates the AST from a single skylark file, given the already-resolved imports. */
cparsonsc90764d2018-06-28 15:17:29 -0700421 private Environment evalSkylarkBody(
cparsons37fbbf32018-11-21 15:13:24 -0800422 SkylarkSemantics semantics,
cparsonsc90764d2018-06-28 15:17:29 -0700423 BuildFileAST buildFileAST,
424 Map<String, Extension> imports,
cparsonsb68bf022018-10-17 10:33:40 -0700425 List<RuleInfo> ruleInfoList,
cparsons37fbbf32018-11-21 15:13:24 -0800426 List<ProviderInfo> providerInfoList)
427 throws InterruptedException {
cparsons5d85e752018-06-26 13:47:28 -0700428
cparsons37fbbf32018-11-21 15:13:24 -0800429 Environment env =
430 createEnvironment(
431 semantics, eventHandler, globalFrame(ruleInfoList, providerInfoList), imports);
cparsons5d85e752018-06-26 13:47:28 -0700432
433 if (!buildFileAST.exec(env, eventHandler)) {
434 throw new RuntimeException("Error loading file");
435 }
436
437 env.mutability().freeze();
438
cparsonsc90764d2018-06-28 15:17:29 -0700439 return env;
cparsons5d85e752018-06-26 13:47:28 -0700440 }
441
442 /**
443 * Initialize and return a global frame containing the fake build API.
444 *
445 * @param ruleInfoList the list of {@link RuleInfo} objects, to which rule() invocation
446 * information will be added
cparsonsb68bf022018-10-17 10:33:40 -0700447 * @param providerInfoList the list of {@link ProviderInfo} objects, to which provider()
448 * invocation information will be added
cparsons5d85e752018-06-26 13:47:28 -0700449 */
cparsonsb68bf022018-10-17 10:33:40 -0700450 private static GlobalFrame globalFrame(List<RuleInfo> ruleInfoList,
451 List<ProviderInfo> providerInfoList) {
cparsons5d85e752018-06-26 13:47:28 -0700452 TopLevelBootstrap topLevelBootstrap =
453 new TopLevelBootstrap(new FakeBuildApiGlobals(),
454 new FakeSkylarkAttrApi(),
455 new FakeSkylarkCommandLineApi(),
456 new FakeSkylarkNativeModuleApi(),
cparsonsb68bf022018-10-17 10:33:40 -0700457 new FakeSkylarkRuleFunctionsApi(ruleInfoList, providerInfoList),
cparsons5d85e752018-06-26 13:47:28 -0700458 new FakeStructProviderApi(),
459 new FakeOutputGroupInfoProvider(),
460 new FakeActionsInfoProvider(),
461 new FakeDefaultInfoProvider());
cparsons6645e912018-06-29 11:18:05 -0700462 AndroidBootstrap androidBootstrap = new AndroidBootstrap(new FakeAndroidSkylarkCommon(),
463 new FakeApkInfoProvider(),
464 new FakeAndroidInstrumentationInfoProvider(),
465 new FakeAndroidDeviceBrokerInfoProvider(),
466 new FakeAndroidResourcesInfoProvider(),
467 new FakeAndroidNativeLibsInfoProvider());
cparsons030448a2018-06-28 12:32:46 -0700468 AppleBootstrap appleBootstrap = new AppleBootstrap(new FakeAppleCommon());
juliexxia8e358772018-09-28 14:01:12 -0700469 ConfigBootstrap configBootstrap =
cparsonse0efc142018-10-17 09:39:10 -0700470 new ConfigBootstrap(new FakeConfigSkylarkCommon(), new FakeConfigApi(),
471 new FakeConfigGlobalLibrary());
cparsons636f5c02018-06-29 12:34:37 -0700472 CcBootstrap ccBootstrap = new CcBootstrap(new FakeCcModule());
Googler91eb3d22018-10-18 00:05:29 -0700473 JavaBootstrap javaBootstrap =
474 new JavaBootstrap(
475 new FakeJavaCommon(),
476 new FakeJavaInfoProvider(),
477 new FakeJavaProtoCommon(),
478 new FakeJavaCcLinkParamsProvider.Provider());
cparsons574eb162018-06-29 07:19:22 -0700479 PlatformBootstrap platformBootstrap = new PlatformBootstrap(new FakePlatformCommon());
480 RepositoryBootstrap repositoryBootstrap = new RepositoryBootstrap(new FakeRepositoryModule());
cparsons645a35e2018-10-01 13:03:23 -0700481 TestingBootstrap testingBootstrap = new TestingBootstrap(new FakeTestingModule(),
cparsons7555df62018-10-03 12:02:28 -0700482 new FakeAnalysisFailureInfoProvider(),
483 new FakeAnalysisTestResultInfoProvider());
cparsons5d85e752018-06-26 13:47:28 -0700484
485 ImmutableMap.Builder<String, Object> envBuilder = ImmutableMap.builder();
486
487 Runtime.addConstantsToBuilder(envBuilder);
488 MethodLibrary.addBindingsToBuilder(envBuilder);
489 topLevelBootstrap.addBindingsToBuilder(envBuilder);
cparsons6645e912018-06-29 11:18:05 -0700490 androidBootstrap.addBindingsToBuilder(envBuilder);
cparsons030448a2018-06-28 12:32:46 -0700491 appleBootstrap.addBindingsToBuilder(envBuilder);
cparsons636f5c02018-06-29 12:34:37 -0700492 ccBootstrap.addBindingsToBuilder(envBuilder);
cparsons574eb162018-06-29 07:19:22 -0700493 configBootstrap.addBindingsToBuilder(envBuilder);
cparsons2fd48282018-06-29 13:20:56 -0700494 javaBootstrap.addBindingsToBuilder(envBuilder);
cparsons574eb162018-06-29 07:19:22 -0700495 platformBootstrap.addBindingsToBuilder(envBuilder);
496 repositoryBootstrap.addBindingsToBuilder(envBuilder);
497 testingBootstrap.addBindingsToBuilder(envBuilder);
cparsons63cf39b2018-11-13 16:21:17 -0800498 addNonBootstrapGlobals(envBuilder);
cparsons5d85e752018-06-26 13:47:28 -0700499
500 return GlobalFrame.createForBuiltins(envBuilder.build());
501 }
502
cparsons63cf39b2018-11-13 16:21:17 -0800503 // TODO(cparsons): Remove this constant by migrating the contained symbols to bootstraps.
504 private static final String[] nonBootstrapGlobals = {"android_data", AndroidDex2OatInfoApi.NAME,
505 AndroidManifestInfoApi.NAME, AndroidAssetsInfoApi.NAME,
506 AndroidLibraryAarInfoApi.NAME, AndroidProguardInfoApi.NAME,
507 AndroidIdlProviderApi.NAME, AndroidIdeInfoProviderApi.NAME,
508 AndroidPreDexJarProviderApi.NAME, UsesDataBindingProviderApi.NAME,
509 AndroidCcLinkParamsProviderApi.NAME, AndroidLibraryResourceClassJarProviderApi.NAME,
510 AndroidSdkProviderApi.NAME, AndroidFeatureFlagSetProviderApi.NAME,
511 ProguardMappingProviderApi.NAME, GeneratedExtensionRegistryProviderApi.NAME,
512 AndroidBinaryDataInfoApi.NAME };
513
514 /**
515 * A hack to add a number of global symbols which are part of the build API but are otherwise
516 * added by Bazel.
517 */
518 // TODO(cparsons): Remove this method by migrating the contained symbols to bootstraps.
519 private static void addNonBootstrapGlobals(ImmutableMap.Builder<String, Object> envBuilder) {
520 for (String global : nonBootstrapGlobals) {
521 envBuilder.put(global, global);
522 }
523 }
524
cparsons37fbbf32018-11-21 15:13:24 -0800525 private static Environment createEnvironment(
526 SkylarkSemantics semantics,
527 EventHandler eventHandler,
528 GlobalFrame globals,
cparsons5d85e752018-06-26 13:47:28 -0700529 Map<String, Extension> imports) {
530 return Environment.builder(Mutability.create("Skydoc"))
cparsons37fbbf32018-11-21 15:13:24 -0800531 .setSemantics(semantics)
cparsons5d85e752018-06-26 13:47:28 -0700532 .setGlobals(globals)
533 .setImportedExtensions(imports)
534 .setEventHandler(eventHandler)
535 .build();
536 }
537}