blob: ec1b04a060dfe592f87afe02203e21d2cdc67a42 [file] [log] [blame]
Colin Cross2207f872021-03-24 12:39:08 -07001// Copyright 2021 Google Inc. 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 java
16
17import (
18 "fmt"
19 "strings"
20
21 "github.com/google/blueprint/proptools"
22
23 "android/soong/android"
24 "android/soong/java/config"
25 "android/soong/remoteexec"
26)
27
28func init() {
29 RegisterStubsBuildComponents(android.InitRegistrationContext)
30}
31
32func RegisterStubsBuildComponents(ctx android.RegistrationContext) {
33 ctx.RegisterModuleType("stubs_defaults", StubsDefaultsFactory)
34
35 ctx.RegisterModuleType("droidstubs", DroidstubsFactory)
36 ctx.RegisterModuleType("droidstubs_host", DroidstubsHostFactory)
37
38 ctx.RegisterModuleType("prebuilt_stubs_sources", PrebuiltStubsSourcesFactory)
39}
40
41//
42// Droidstubs
43//
44type Droidstubs struct {
45 Javadoc
46 android.SdkBase
47
48 properties DroidstubsProperties
49 apiFile android.WritablePath
50 apiXmlFile android.WritablePath
51 lastReleasedApiXmlFile android.WritablePath
52 privateApiFile android.WritablePath
53 removedApiFile android.WritablePath
Colin Cross2207f872021-03-24 12:39:08 -070054 nullabilityWarningsFile android.WritablePath
55
56 checkCurrentApiTimestamp android.WritablePath
57 updateCurrentApiTimestamp android.WritablePath
58 checkLastReleasedApiTimestamp android.WritablePath
59 apiLintTimestamp android.WritablePath
60 apiLintReport android.WritablePath
61
62 checkNullabilityWarningsTimestamp android.WritablePath
63
64 annotationsZip android.WritablePath
65 apiVersionsXml android.WritablePath
66
67 apiFilePath android.Path
68 removedApiFilePath android.Path
69
70 metadataZip android.WritablePath
71 metadataDir android.WritablePath
72}
73
74type DroidstubsProperties struct {
75 // The generated public API filename by Metalava, defaults to <module>_api.txt
76 Api_filename *string
77
78 // the generated removed API filename by Metalava, defaults to <module>_removed.txt
79 Removed_api_filename *string
80
Colin Cross2207f872021-03-24 12:39:08 -070081 Check_api struct {
82 Last_released ApiToCheck
83
84 Current ApiToCheck
85
86 Api_lint struct {
87 Enabled *bool
88
89 // If set, performs api_lint on any new APIs not found in the given signature file
90 New_since *string `android:"path"`
91
92 // If not blank, path to the baseline txt file for approved API lint violations.
93 Baseline_file *string `android:"path"`
94 }
95 }
96
97 // user can specify the version of previous released API file in order to do compatibility check.
98 Previous_api *string `android:"path"`
99
100 // is set to true, Metalava will allow framework SDK to contain annotations.
101 Annotations_enabled *bool
102
103 // a list of top-level directories containing files to merge qualifier annotations (i.e. those intended to be included in the stubs written) from.
104 Merge_annotations_dirs []string
105
106 // a list of top-level directories containing Java stub files to merge show/hide annotations from.
107 Merge_inclusion_annotations_dirs []string
108
109 // a file containing a list of classes to do nullability validation for.
110 Validate_nullability_from_list *string
111
112 // a file containing expected warnings produced by validation of nullability annotations.
113 Check_nullability_warnings *string
114
115 // if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
116 Create_doc_stubs *bool
117
118 // if set to true, cause Metalava to output Javadoc comments in the stubs source files. Defaults to false.
119 // Has no effect if create_doc_stubs: true.
120 Output_javadoc_comments *bool
121
122 // if set to false then do not write out stubs. Defaults to true.
123 //
124 // TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
125 Generate_stubs *bool
126
127 // if set to true, provides a hint to the build system that this rule uses a lot of memory,
128 // whicih can be used for scheduling purposes
129 High_mem *bool
130
satayev783195c2021-06-23 21:49:57 +0100131 // if set to true, Metalava will allow framework SDK to contain API levels annotations.
Colin Cross2207f872021-03-24 12:39:08 -0700132 Api_levels_annotations_enabled *bool
133
134 // the dirs which Metalava extracts API levels annotations from.
135 Api_levels_annotations_dirs []string
136
satayev783195c2021-06-23 21:49:57 +0100137 // the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public.
138 Api_levels_sdk_type *string
139
Colin Cross2207f872021-03-24 12:39:08 -0700140 // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
141 Api_levels_jar_filename *string
142
143 // if set to true, collect the values used by the Dev tools and
144 // write them in files packaged with the SDK. Defaults to false.
145 Write_sdk_values *bool
146}
147
Anton Hansson52609322021-05-05 10:36:05 +0100148// Used by xsd_config
149type ApiFilePath interface {
150 ApiFilePath() android.Path
151}
152
153type ApiStubsSrcProvider interface {
154 StubsSrcJar() android.Path
155}
156
157// Provider of information about API stubs, used by java_sdk_library.
158type ApiStubsProvider interface {
159 ApiFilePath
160 RemovedApiFilePath() android.Path
161
162 ApiStubsSrcProvider
163}
164
Colin Cross2207f872021-03-24 12:39:08 -0700165// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
166// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
167// a droiddoc module to generate documentation.
168func DroidstubsFactory() android.Module {
169 module := &Droidstubs{}
170
171 module.AddProperties(&module.properties,
172 &module.Javadoc.properties)
173
174 InitDroiddocModule(module, android.HostAndDeviceSupported)
175 android.InitSdkAwareModule(module)
176 return module
177}
178
179// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
180// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
181// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
182// module when symbols needed by the source files are provided by java_library_host modules.
183func DroidstubsHostFactory() android.Module {
184 module := &Droidstubs{}
185
186 module.AddProperties(&module.properties,
187 &module.Javadoc.properties)
188
189 InitDroiddocModule(module, android.HostSupported)
190 return module
191}
192
193func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
194 switch tag {
195 case "":
196 return android.Paths{d.stubsSrcJar}, nil
197 case ".docs.zip":
198 return android.Paths{d.docZip}, nil
199 case ".api.txt", android.DefaultDistTag:
200 // This is the default dist path for dist properties that have no tag property.
201 return android.Paths{d.apiFilePath}, nil
202 case ".removed-api.txt":
203 return android.Paths{d.removedApiFilePath}, nil
204 case ".annotations.zip":
205 return android.Paths{d.annotationsZip}, nil
206 case ".api_versions.xml":
207 return android.Paths{d.apiVersionsXml}, nil
208 default:
209 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
210 }
211}
212
213func (d *Droidstubs) ApiFilePath() android.Path {
214 return d.apiFilePath
215}
216
217func (d *Droidstubs) RemovedApiFilePath() android.Path {
218 return d.removedApiFilePath
219}
220
221func (d *Droidstubs) StubsSrcJar() android.Path {
222 return d.stubsSrcJar
223}
224
225var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
226var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
227var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
228
229func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
230 d.Javadoc.addDeps(ctx)
231
232 if len(d.properties.Merge_annotations_dirs) != 0 {
233 for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
234 ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
235 }
236 }
237
238 if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
239 for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
240 ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
241 }
242 }
243
244 if len(d.properties.Api_levels_annotations_dirs) != 0 {
245 for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
246 ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
247 }
248 }
249}
250
251func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
252 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
253 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
254 String(d.properties.Api_filename) != "" {
255 filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700256 d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700257 cmd.FlagWithOutput("--api ", d.apiFile)
258 d.apiFilePath = d.apiFile
259 } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
260 // If check api is disabled then make the source file available for export.
261 d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
262 }
263
264 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
265 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
266 String(d.properties.Removed_api_filename) != "" {
267 filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700268 d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700269 cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
270 d.removedApiFilePath = d.removedApiFile
271 } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
272 // If check api is disabled then make the source removed api file available for export.
273 d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
274 }
275
Colin Cross2207f872021-03-24 12:39:08 -0700276 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700277 d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
Colin Cross2207f872021-03-24 12:39:08 -0700278 cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
279 }
280
281 if stubsDir.Valid() {
282 if Bool(d.properties.Create_doc_stubs) {
283 cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
284 } else {
285 cmd.FlagWithArg("--stubs ", stubsDir.String())
286 if !Bool(d.properties.Output_javadoc_comments) {
287 cmd.Flag("--exclude-documentation-from-stubs")
288 }
289 }
290 }
291}
292
293func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
294 if Bool(d.properties.Annotations_enabled) {
295 cmd.Flag("--include-annotations")
296
Andrei Onea4985e512021-04-29 16:29:34 +0100297 cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi")
298
Colin Cross2207f872021-03-24 12:39:08 -0700299 validatingNullability :=
Colin Crossbc139922021-03-25 18:33:16 -0700300 strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
Colin Cross2207f872021-03-24 12:39:08 -0700301 String(d.properties.Validate_nullability_from_list) != ""
302
303 migratingNullability := String(d.properties.Previous_api) != ""
304 if migratingNullability {
305 previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
306 cmd.FlagWithInput("--migrate-nullness ", previousApi)
307 }
308
309 if s := String(d.properties.Validate_nullability_from_list); s != "" {
310 cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
311 }
312
313 if validatingNullability {
Colin Crosscb77f752021-03-24 12:04:44 -0700314 d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700315 cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
316 }
317
Colin Crosscb77f752021-03-24 12:04:44 -0700318 d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700319 cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
320
321 if len(d.properties.Merge_annotations_dirs) != 0 {
322 d.mergeAnnoDirFlags(ctx, cmd)
323 }
324
325 // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
326 cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
327 FlagWithArg("--hide ", "SuperfluousPrefix").
328 FlagWithArg("--hide ", "AnnotationExtraction")
329 }
330}
331
332func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
333 ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
334 if t, ok := m.(*ExportedDroiddocDir); ok {
335 cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
336 } else {
337 ctx.PropertyErrorf("merge_annotations_dirs",
338 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
339 }
340 })
341}
342
343func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
344 ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
345 if t, ok := m.(*ExportedDroiddocDir); ok {
346 cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
347 } else {
348 ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
349 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
350 }
351 })
352}
353
354func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
355 if !Bool(d.properties.Api_levels_annotations_enabled) {
356 return
357 }
358
Colin Crosscb77f752021-03-24 12:04:44 -0700359 d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
Colin Cross2207f872021-03-24 12:39:08 -0700360
361 if len(d.properties.Api_levels_annotations_dirs) == 0 {
362 ctx.PropertyErrorf("api_levels_annotations_dirs",
363 "has to be non-empty if api levels annotations was enabled!")
364 }
365
366 cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
367 cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
368 cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
369 cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
370
371 filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
372
satayev783195c2021-06-23 21:49:57 +0100373 var dirs []string
Colin Cross2207f872021-03-24 12:39:08 -0700374 ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
375 if t, ok := m.(*ExportedDroiddocDir); ok {
376 for _, dep := range t.deps {
Colin Cross5f6ffc72021-03-29 21:54:45 -0700377 if dep.Base() == filename {
378 cmd.Implicit(dep)
379 }
380 if filename != "android.jar" && dep.Base() == "android.jar" {
381 // Metalava implicitly searches these patterns:
382 // prebuilts/tools/common/api-versions/android-%/android.jar
383 // prebuilts/sdk/%/public/android.jar
384 // Add android.jar files from the api_levels_annotations_dirs directories to try
385 // to satisfy these patterns. If Metalava can't find a match for an API level
386 // between 1 and 28 in at least one pattern it will fail.
Colin Cross2207f872021-03-24 12:39:08 -0700387 cmd.Implicit(dep)
388 }
389 }
satayev783195c2021-06-23 21:49:57 +0100390
391 dirs = append(dirs, t.dir.String())
Colin Cross2207f872021-03-24 12:39:08 -0700392 } else {
393 ctx.PropertyErrorf("api_levels_annotations_dirs",
394 "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
395 }
396 })
satayev783195c2021-06-23 21:49:57 +0100397
398 // Add all relevant --android-jar-pattern patterns for Metalava.
399 // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
400 // an actual file present on disk (in the order the patterns were passed). For system APIs for
401 // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
402 // for older releases.
403 if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
404 if sdkType != "system" {
405 ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
406 }
407 // If building non public stubs, add all sdkType patterns first...
408 for _, dir := range dirs {
409 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
410 }
411 }
412 for _, dir := range dirs {
413 // ... and fallback to public ones, for Metalava to use if needed.
414 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
415 }
Colin Cross2207f872021-03-24 12:39:08 -0700416}
417
418func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
Anton Hansson556e8142021-06-04 16:20:25 +0100419 srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
Colin Cross2207f872021-03-24 12:39:08 -0700420 rule.Command().Text("rm -rf").Flag(homeDir.String())
421 rule.Command().Text("mkdir -p").Flag(homeDir.String())
422
Anton Hansson556e8142021-06-04 16:20:25 +0100423 cmd := rule.Command()
Colin Cross2207f872021-03-24 12:39:08 -0700424 cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
425
426 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
427 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
Colin Cross8095c292021-03-30 16:40:48 -0700428 execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
429 labels := map[string]string{"type": "tool", "name": "metalava"}
430 // TODO: metalava pool rejects these jobs
431 pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
432 rule.Rewrapper(&remoteexec.REParams{
433 Labels: labels,
434 ExecStrategy: execStrategy,
435 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
436 Platform: map[string]string{remoteexec.PoolKey: pool},
437 })
Colin Cross2207f872021-03-24 12:39:08 -0700438 }
439
Colin Cross6aa5c402021-03-24 12:28:50 -0700440 cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
Colin Cross2207f872021-03-24 12:39:08 -0700441 Flag(config.JavacVmFlags).
442 Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
443 FlagWithArg("-encoding ", "UTF-8").
444 FlagWithArg("-source ", javaVersion.String()).
445 FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
446 FlagWithInput("@", srcJarList)
447
Colin Cross2207f872021-03-24 12:39:08 -0700448 if len(bootclasspath) > 0 {
449 cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
450 }
451
452 if len(classpath) > 0 {
453 cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
454 }
455
Colin Cross2207f872021-03-24 12:39:08 -0700456 cmd.Flag("--no-banner").
457 Flag("--color").
458 Flag("--quiet").
459 Flag("--format=v2").
460 FlagWithArg("--repeat-errors-max ", "10").
461 FlagWithArg("--hide ", "UnresolvedImport")
462
463 return cmd
464}
465
466func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
467 deps := d.Javadoc.collectDeps(ctx)
468
Jiyong Parkf1691d22021-03-29 20:11:58 +0900469 javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
Colin Cross2207f872021-03-24 12:39:08 -0700470
471 // Create rule for metalava
472
Colin Crosscb77f752021-03-24 12:04:44 -0700473 srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
Colin Cross2207f872021-03-24 12:39:08 -0700474
475 rule := android.NewRuleBuilder(pctx, ctx)
476
Colin Cross8095c292021-03-30 16:40:48 -0700477 rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
478 android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
479 SandboxInputs()
Colin Cross6aa5c402021-03-24 12:28:50 -0700480
Colin Cross2207f872021-03-24 12:39:08 -0700481 if BoolDefault(d.properties.High_mem, false) {
482 // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
483 rule.HighMem()
484 }
485
486 generateStubs := BoolDefault(d.properties.Generate_stubs, true)
487 var stubsDir android.OptionalPath
488 if generateStubs {
Colin Crosscb77f752021-03-24 12:04:44 -0700489 d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
490 stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
Colin Cross2207f872021-03-24 12:39:08 -0700491 rule.Command().Text("rm -rf").Text(stubsDir.String())
492 rule.Command().Text("mkdir -p").Text(stubsDir.String())
493 }
494
495 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
496
Colin Crosscb77f752021-03-24 12:04:44 -0700497 homeDir := android.PathForModuleOut(ctx, "metalava", "home")
Colin Cross2207f872021-03-24 12:39:08 -0700498 cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
Anton Hansson556e8142021-06-04 16:20:25 +0100499 deps.bootClasspath, deps.classpath, homeDir)
Colin Cross2207f872021-03-24 12:39:08 -0700500 cmd.Implicits(d.Javadoc.implicits)
501
502 d.stubsFlags(ctx, cmd, stubsDir)
503
504 d.annotationsFlags(ctx, cmd)
505 d.inclusionAnnotationsFlags(ctx, cmd)
506 d.apiLevelsAnnotationsFlags(ctx, cmd)
507
Colin Crossbc139922021-03-25 18:33:16 -0700508 d.expandArgs(ctx, cmd)
Colin Cross2207f872021-03-24 12:39:08 -0700509
Colin Cross2207f872021-03-24 12:39:08 -0700510 for _, o := range d.Javadoc.properties.Out {
511 cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
512 }
513
514 // Add options for the other optional tasks: API-lint and check-released.
515 // We generate separate timestamp files for them.
516
517 doApiLint := false
518 doCheckReleased := false
519
520 // Add API lint options.
521
522 if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
523 doApiLint = true
524
525 newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
526 if newSince.Valid() {
527 cmd.FlagWithInput("--api-lint ", newSince.Path())
528 } else {
529 cmd.Flag("--api-lint")
530 }
Colin Crosscb77f752021-03-24 12:04:44 -0700531 d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700532 cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
533
Colin Cross0d532412021-03-25 09:38:45 -0700534 // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
Colin Cross2207f872021-03-24 12:39:08 -0700535 if d.Name() != "android.car-system-stubs-docs" &&
536 d.Name() != "android.car-stubs-docs" {
537 cmd.Flag("--lints-as-errors")
538 cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
539 }
540
541 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700542 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
543 d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700544
545 // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
Colin Cross2207f872021-03-24 12:39:08 -0700546 //
547 // TODO: metalava also has a slightly different message hardcoded. Should we unify this
548 // message and metalava's one?
549 msg := `$'` + // Enclose with $' ... '
550 `************************************************************\n` +
551 `Your API changes are triggering API Lint warnings or errors.\n` +
552 `To make these errors go away, fix the code according to the\n` +
553 `error and/or warning messages above.\n` +
554 `\n` +
555 `If it is not possible to do so, there are workarounds:\n` +
556 `\n` +
Aurimas Liutikasb23b7452021-05-24 18:00:37 +0000557 `1. You can suppress the errors with @SuppressLint("<id>")\n` +
558 ` where the <id> is given in brackets in the error message above.\n`
Colin Cross2207f872021-03-24 12:39:08 -0700559
560 if baselineFile.Valid() {
561 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
562 cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
563
564 msg += fmt.Sprintf(``+
565 `2. You can update the baseline by executing the following\n`+
566 ` command:\n`+
Colin Cross63eeda02021-04-15 19:01:57 -0700567 ` (cd $ANDROID_BUILD_TOP && cp \\\n`+
568 ` "%s" \\\n`+
569 ` "%s")\n`+
Colin Cross2207f872021-03-24 12:39:08 -0700570 ` To submit the revised baseline.txt to the main Android\n`+
571 ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
572 } else {
573 msg += fmt.Sprintf(``+
574 `2. You can add a baseline file of existing lint failures\n`+
575 ` to the build rule of %s.\n`, d.Name())
576 }
577 // Note the message ends with a ' (single quote), to close the $' ... ' .
578 msg += `************************************************************\n'`
579
580 cmd.FlagWithArg("--error-message:api-lint ", msg)
581 }
582
583 // Add "check released" options. (Detect incompatible API changes from the last public release)
584
585 if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
586 doCheckReleased = true
587
588 if len(d.Javadoc.properties.Out) > 0 {
589 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
590 }
591
592 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
593 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
594 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700595 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700596
Colin Crosscb77f752021-03-24 12:04:44 -0700597 d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700598
599 cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
600 cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
601
602 if baselineFile.Valid() {
603 cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
604 cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
605 }
606
607 // Note this string includes quote ($' ... '), which decodes the "\n"s.
608 msg := `$'\n******************************\n` +
609 `You have tried to change the API from what has been previously released in\n` +
610 `an SDK. Please fix the errors listed above.\n` +
611 `******************************\n'`
612
613 cmd.FlagWithArg("--error-message:compatibility:released ", msg)
614 }
615
Colin Cross2207f872021-03-24 12:39:08 -0700616 if generateStubs {
617 rule.Command().
618 BuiltTool("soong_zip").
619 Flag("-write_if_changed").
620 Flag("-jar").
621 FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
622 FlagWithArg("-C ", stubsDir.String()).
623 FlagWithArg("-D ", stubsDir.String())
624 }
625
626 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700627 d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700628 rule.Command().
629 BuiltTool("soong_zip").
630 Flag("-write_if_changed").
631 Flag("-d").
632 FlagWithOutput("-o ", d.metadataZip).
633 FlagWithArg("-C ", d.metadataDir.String()).
634 FlagWithArg("-D ", d.metadataDir.String())
635 }
636
637 // TODO: We don't really need two separate API files, but this is a reminiscence of how
638 // we used to run metalava separately for API lint and the "last_released" check. Unify them.
639 if doApiLint {
640 rule.Command().Text("touch").Output(d.apiLintTimestamp)
641 }
642 if doCheckReleased {
643 rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
644 }
645
Colin Cross6aa5c402021-03-24 12:28:50 -0700646 // TODO(b/183630617): rewrapper doesn't support restat rules
Colin Cross8095c292021-03-30 16:40:48 -0700647 // rule.Restat()
Colin Cross2207f872021-03-24 12:39:08 -0700648
649 zipSyncCleanupCmd(rule, srcJarDir)
650
651 rule.Build("metalava", "metalava merged")
652
653 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
654
655 if len(d.Javadoc.properties.Out) > 0 {
656 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
657 }
658
659 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
660 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
661 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
662
663 if baselineFile.Valid() {
664 ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
665 }
666
Colin Crosscb77f752021-03-24 12:04:44 -0700667 d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700668
669 rule := android.NewRuleBuilder(pctx, ctx)
670
671 // Diff command line.
672 // -F matches the closest "opening" line, such as "package android {"
673 // and " public class Intent {".
674 diff := `diff -u -F '{ *$'`
675
676 rule.Command().Text("( true")
677 rule.Command().
678 Text(diff).
679 Input(apiFile).Input(d.apiFile)
680
681 rule.Command().
682 Text(diff).
683 Input(removedApiFile).Input(d.removedApiFile)
684
685 msg := fmt.Sprintf(`\n******************************\n`+
686 `You have tried to change the API from what has been previously approved.\n\n`+
687 `To make these errors go away, you have two choices:\n`+
688 ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
689 ` to the new methods, etc. shown in the above diff.\n\n`+
690 ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
691 ` m %s-update-current-api\n\n`+
692 ` To submit the revised current.txt to the main Android repository,\n`+
693 ` you will need approval.\n`+
694 `******************************\n`, ctx.ModuleName())
695
696 rule.Command().
697 Text("touch").Output(d.checkCurrentApiTimestamp).
698 Text(") || (").
699 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
700 Text("; exit 38").
701 Text(")")
702
703 rule.Build("metalavaCurrentApiCheck", "check current API")
704
Colin Crosscb77f752021-03-24 12:04:44 -0700705 d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700706
707 // update API rule
708 rule = android.NewRuleBuilder(pctx, ctx)
709
710 rule.Command().Text("( true")
711
712 rule.Command().
713 Text("cp").Flag("-f").
714 Input(d.apiFile).Flag(apiFile.String())
715
716 rule.Command().
717 Text("cp").Flag("-f").
718 Input(d.removedApiFile).Flag(removedApiFile.String())
719
720 msg = "failed to update public API"
721
722 rule.Command().
723 Text("touch").Output(d.updateCurrentApiTimestamp).
724 Text(") || (").
725 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
726 Text("; exit 38").
727 Text(")")
728
729 rule.Build("metalavaCurrentApiUpdate", "update current API")
730 }
731
732 if String(d.properties.Check_nullability_warnings) != "" {
733 if d.nullabilityWarningsFile == nil {
734 ctx.PropertyErrorf("check_nullability_warnings",
735 "Cannot specify check_nullability_warnings unless validating nullability")
736 }
737
738 checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
739
Colin Crosscb77f752021-03-24 12:04:44 -0700740 d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700741
742 msg := fmt.Sprintf(`\n******************************\n`+
743 `The warnings encountered during nullability annotation validation did\n`+
744 `not match the checked in file of expected warnings. The diffs are shown\n`+
745 `above. You have two options:\n`+
746 ` 1. Resolve the differences by editing the nullability annotations.\n`+
747 ` 2. Update the file of expected warnings by running:\n`+
748 ` cp %s %s\n`+
749 ` and submitting the updated file as part of your change.`,
750 d.nullabilityWarningsFile, checkNullabilityWarnings)
751
752 rule := android.NewRuleBuilder(pctx, ctx)
753
754 rule.Command().
755 Text("(").
756 Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
757 Text("&&").
758 Text("touch").Output(d.checkNullabilityWarningsTimestamp).
759 Text(") || (").
760 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
761 Text("; exit 38").
762 Text(")")
763
764 rule.Build("nullabilityWarningsCheck", "nullability warnings check")
765 }
766}
767
768func StubsDefaultsFactory() android.Module {
769 module := &DocDefaults{}
770
771 module.AddProperties(
772 &JavadocProperties{},
773 &DroidstubsProperties{},
774 )
775
776 android.InitDefaultsModule(module)
777
778 return module
779}
780
781var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
782
783type PrebuiltStubsSourcesProperties struct {
784 Srcs []string `android:"path"`
785}
786
787type PrebuiltStubsSources struct {
788 android.ModuleBase
789 android.DefaultableModuleBase
790 prebuilt android.Prebuilt
791 android.SdkBase
792
793 properties PrebuiltStubsSourcesProperties
794
795 stubsSrcJar android.ModuleOutPath
796}
797
798func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
799 switch tag {
800 case "":
801 return android.Paths{p.stubsSrcJar}, nil
802 default:
803 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
804 }
805}
806
807func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
808 return d.stubsSrcJar
809}
810
811func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
812 p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
813
814 if len(p.properties.Srcs) != 1 {
815 ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
816 return
817 }
818
819 localSrcDir := p.properties.Srcs[0]
820 // Although PathForModuleSrc can return nil if either the path doesn't exist or
821 // the path components are invalid it won't in this case because no components
822 // are specified and the module directory must exist in order to get this far.
823 srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
824
825 // Glob the contents of the directory just in case the directory does not exist.
826 srcGlob := localSrcDir + "/**/*"
827 srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
828
829 rule := android.NewRuleBuilder(pctx, ctx)
830 rule.Command().
831 BuiltTool("soong_zip").
832 Flag("-write_if_changed").
833 Flag("-jar").
834 FlagWithOutput("-o ", p.stubsSrcJar).
835 FlagWithArg("-C ", srcDir.String()).
836 FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
837
838 rule.Restat()
839
840 rule.Build("zip src", "Create srcjar from prebuilt source")
841}
842
843func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
844 return &p.prebuilt
845}
846
847func (p *PrebuiltStubsSources) Name() string {
848 return p.prebuilt.Name(p.ModuleBase.Name())
849}
850
851// prebuilt_stubs_sources imports a set of java source files as if they were
852// generated by droidstubs.
853//
854// By default, a prebuilt_stubs_sources has a single variant that expects a
855// set of `.java` files generated by droidstubs.
856//
857// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
858// for host modules.
859//
860// Intended only for use by sdk snapshots.
861func PrebuiltStubsSourcesFactory() android.Module {
862 module := &PrebuiltStubsSources{}
863
864 module.AddProperties(&module.properties)
865
866 android.InitPrebuiltModule(module, &module.properties.Srcs)
867 android.InitSdkAwareModule(module)
868 InitDroiddocModule(module, android.HostAndDeviceSupported)
869 return module
870}