blob: 7fd88fcebb64b22a8ef74f0f3c6df0a58d1af4cf [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 {
Anton Hanssond78eb762021-09-21 15:25:12 +0100159 AnnotationsZip() android.Path
Anton Hansson52609322021-05-05 10:36:05 +0100160 ApiFilePath
161 RemovedApiFilePath() android.Path
162
163 ApiStubsSrcProvider
164}
165
Colin Cross2207f872021-03-24 12:39:08 -0700166// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
167// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
168// a droiddoc module to generate documentation.
169func DroidstubsFactory() android.Module {
170 module := &Droidstubs{}
171
172 module.AddProperties(&module.properties,
173 &module.Javadoc.properties)
174
175 InitDroiddocModule(module, android.HostAndDeviceSupported)
176 android.InitSdkAwareModule(module)
177 return module
178}
179
180// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
181// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
182// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
183// module when symbols needed by the source files are provided by java_library_host modules.
184func DroidstubsHostFactory() android.Module {
185 module := &Droidstubs{}
186
187 module.AddProperties(&module.properties,
188 &module.Javadoc.properties)
189
190 InitDroiddocModule(module, android.HostSupported)
191 return module
192}
193
194func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
195 switch tag {
196 case "":
197 return android.Paths{d.stubsSrcJar}, nil
198 case ".docs.zip":
199 return android.Paths{d.docZip}, nil
200 case ".api.txt", android.DefaultDistTag:
201 // This is the default dist path for dist properties that have no tag property.
202 return android.Paths{d.apiFilePath}, nil
203 case ".removed-api.txt":
204 return android.Paths{d.removedApiFilePath}, nil
205 case ".annotations.zip":
206 return android.Paths{d.annotationsZip}, nil
207 case ".api_versions.xml":
208 return android.Paths{d.apiVersionsXml}, nil
209 default:
210 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
211 }
212}
213
Anton Hanssond78eb762021-09-21 15:25:12 +0100214func (d *Droidstubs) AnnotationsZip() android.Path {
215 return d.annotationsZip
216}
217
Colin Cross2207f872021-03-24 12:39:08 -0700218func (d *Droidstubs) ApiFilePath() android.Path {
219 return d.apiFilePath
220}
221
222func (d *Droidstubs) RemovedApiFilePath() android.Path {
223 return d.removedApiFilePath
224}
225
226func (d *Droidstubs) StubsSrcJar() android.Path {
227 return d.stubsSrcJar
228}
229
230var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
231var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
232var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
233
234func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
235 d.Javadoc.addDeps(ctx)
236
237 if len(d.properties.Merge_annotations_dirs) != 0 {
238 for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
239 ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
240 }
241 }
242
243 if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
244 for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
245 ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
246 }
247 }
248
249 if len(d.properties.Api_levels_annotations_dirs) != 0 {
250 for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
251 ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
252 }
253 }
254}
255
256func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
257 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
258 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
259 String(d.properties.Api_filename) != "" {
260 filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700261 d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700262 cmd.FlagWithOutput("--api ", d.apiFile)
263 d.apiFilePath = d.apiFile
264 } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
265 // If check api is disabled then make the source file available for export.
266 d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
267 }
268
269 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
270 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
271 String(d.properties.Removed_api_filename) != "" {
272 filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700273 d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700274 cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
275 d.removedApiFilePath = d.removedApiFile
276 } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
277 // If check api is disabled then make the source removed api file available for export.
278 d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
279 }
280
Colin Cross2207f872021-03-24 12:39:08 -0700281 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700282 d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
Colin Cross2207f872021-03-24 12:39:08 -0700283 cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
284 }
285
286 if stubsDir.Valid() {
287 if Bool(d.properties.Create_doc_stubs) {
288 cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
289 } else {
290 cmd.FlagWithArg("--stubs ", stubsDir.String())
291 if !Bool(d.properties.Output_javadoc_comments) {
292 cmd.Flag("--exclude-documentation-from-stubs")
293 }
294 }
295 }
296}
297
298func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
299 if Bool(d.properties.Annotations_enabled) {
300 cmd.Flag("--include-annotations")
301
Andrei Onea4985e512021-04-29 16:29:34 +0100302 cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi")
303
Colin Cross2207f872021-03-24 12:39:08 -0700304 validatingNullability :=
Colin Crossbc139922021-03-25 18:33:16 -0700305 strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
Colin Cross2207f872021-03-24 12:39:08 -0700306 String(d.properties.Validate_nullability_from_list) != ""
307
308 migratingNullability := String(d.properties.Previous_api) != ""
309 if migratingNullability {
310 previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
311 cmd.FlagWithInput("--migrate-nullness ", previousApi)
312 }
313
314 if s := String(d.properties.Validate_nullability_from_list); s != "" {
315 cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
316 }
317
318 if validatingNullability {
Colin Crosscb77f752021-03-24 12:04:44 -0700319 d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700320 cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
321 }
322
Colin Crosscb77f752021-03-24 12:04:44 -0700323 d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700324 cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
325
326 if len(d.properties.Merge_annotations_dirs) != 0 {
327 d.mergeAnnoDirFlags(ctx, cmd)
328 }
329
330 // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
331 cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
332 FlagWithArg("--hide ", "SuperfluousPrefix").
333 FlagWithArg("--hide ", "AnnotationExtraction")
334 }
335}
336
337func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
338 ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
339 if t, ok := m.(*ExportedDroiddocDir); ok {
340 cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
341 } else {
342 ctx.PropertyErrorf("merge_annotations_dirs",
343 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
344 }
345 })
346}
347
348func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
349 ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
350 if t, ok := m.(*ExportedDroiddocDir); ok {
351 cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
352 } else {
353 ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
354 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
355 }
356 })
357}
358
359func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
360 if !Bool(d.properties.Api_levels_annotations_enabled) {
361 return
362 }
363
Colin Crosscb77f752021-03-24 12:04:44 -0700364 d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
Colin Cross2207f872021-03-24 12:39:08 -0700365
366 if len(d.properties.Api_levels_annotations_dirs) == 0 {
367 ctx.PropertyErrorf("api_levels_annotations_dirs",
368 "has to be non-empty if api levels annotations was enabled!")
369 }
370
371 cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
372 cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
373 cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
374 cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
375
376 filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
377
satayev783195c2021-06-23 21:49:57 +0100378 var dirs []string
Colin Cross2207f872021-03-24 12:39:08 -0700379 ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
380 if t, ok := m.(*ExportedDroiddocDir); ok {
381 for _, dep := range t.deps {
Colin Cross5f6ffc72021-03-29 21:54:45 -0700382 if dep.Base() == filename {
383 cmd.Implicit(dep)
384 }
385 if filename != "android.jar" && dep.Base() == "android.jar" {
386 // Metalava implicitly searches these patterns:
387 // prebuilts/tools/common/api-versions/android-%/android.jar
388 // prebuilts/sdk/%/public/android.jar
389 // Add android.jar files from the api_levels_annotations_dirs directories to try
390 // to satisfy these patterns. If Metalava can't find a match for an API level
391 // between 1 and 28 in at least one pattern it will fail.
Colin Cross2207f872021-03-24 12:39:08 -0700392 cmd.Implicit(dep)
393 }
394 }
satayev783195c2021-06-23 21:49:57 +0100395
396 dirs = append(dirs, t.dir.String())
Colin Cross2207f872021-03-24 12:39:08 -0700397 } else {
398 ctx.PropertyErrorf("api_levels_annotations_dirs",
399 "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
400 }
401 })
satayev783195c2021-06-23 21:49:57 +0100402
403 // Add all relevant --android-jar-pattern patterns for Metalava.
404 // When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
405 // an actual file present on disk (in the order the patterns were passed). For system APIs for
406 // privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
407 // for older releases.
408 if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
409 if sdkType != "system" {
410 ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
411 }
412 // If building non public stubs, add all sdkType patterns first...
413 for _, dir := range dirs {
414 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
415 }
416 }
417 for _, dir := range dirs {
418 // ... and fallback to public ones, for Metalava to use if needed.
419 cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
420 }
Colin Cross2207f872021-03-24 12:39:08 -0700421}
422
423func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
Anton Hansson556e8142021-06-04 16:20:25 +0100424 srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
Colin Cross2207f872021-03-24 12:39:08 -0700425 rule.Command().Text("rm -rf").Flag(homeDir.String())
426 rule.Command().Text("mkdir -p").Flag(homeDir.String())
427
Anton Hansson556e8142021-06-04 16:20:25 +0100428 cmd := rule.Command()
Colin Cross2207f872021-03-24 12:39:08 -0700429 cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
430
431 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
432 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
Colin Cross8095c292021-03-30 16:40:48 -0700433 execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
434 labels := map[string]string{"type": "tool", "name": "metalava"}
435 // TODO: metalava pool rejects these jobs
436 pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
437 rule.Rewrapper(&remoteexec.REParams{
438 Labels: labels,
439 ExecStrategy: execStrategy,
440 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
441 Platform: map[string]string{remoteexec.PoolKey: pool},
442 })
Colin Cross2207f872021-03-24 12:39:08 -0700443 }
444
Colin Cross6aa5c402021-03-24 12:28:50 -0700445 cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
Colin Cross2207f872021-03-24 12:39:08 -0700446 Flag(config.JavacVmFlags).
447 Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
448 FlagWithArg("-encoding ", "UTF-8").
449 FlagWithArg("-source ", javaVersion.String()).
450 FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
451 FlagWithInput("@", srcJarList)
452
Colin Cross2207f872021-03-24 12:39:08 -0700453 if len(bootclasspath) > 0 {
454 cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
455 }
456
457 if len(classpath) > 0 {
458 cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
459 }
460
Colin Cross2207f872021-03-24 12:39:08 -0700461 cmd.Flag("--no-banner").
462 Flag("--color").
463 Flag("--quiet").
464 Flag("--format=v2").
465 FlagWithArg("--repeat-errors-max ", "10").
466 FlagWithArg("--hide ", "UnresolvedImport")
467
468 return cmd
469}
470
471func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
472 deps := d.Javadoc.collectDeps(ctx)
473
Jiyong Parkf1691d22021-03-29 20:11:58 +0900474 javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
Colin Cross2207f872021-03-24 12:39:08 -0700475
476 // Create rule for metalava
477
Colin Crosscb77f752021-03-24 12:04:44 -0700478 srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
Colin Cross2207f872021-03-24 12:39:08 -0700479
480 rule := android.NewRuleBuilder(pctx, ctx)
481
Colin Cross8095c292021-03-30 16:40:48 -0700482 rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
483 android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
484 SandboxInputs()
Colin Cross6aa5c402021-03-24 12:28:50 -0700485
Colin Cross2207f872021-03-24 12:39:08 -0700486 if BoolDefault(d.properties.High_mem, false) {
487 // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
488 rule.HighMem()
489 }
490
491 generateStubs := BoolDefault(d.properties.Generate_stubs, true)
492 var stubsDir android.OptionalPath
493 if generateStubs {
Colin Crosscb77f752021-03-24 12:04:44 -0700494 d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
495 stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
Colin Cross2207f872021-03-24 12:39:08 -0700496 rule.Command().Text("rm -rf").Text(stubsDir.String())
497 rule.Command().Text("mkdir -p").Text(stubsDir.String())
498 }
499
500 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
501
Colin Crosscb77f752021-03-24 12:04:44 -0700502 homeDir := android.PathForModuleOut(ctx, "metalava", "home")
Colin Cross2207f872021-03-24 12:39:08 -0700503 cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
Anton Hansson556e8142021-06-04 16:20:25 +0100504 deps.bootClasspath, deps.classpath, homeDir)
Colin Cross2207f872021-03-24 12:39:08 -0700505 cmd.Implicits(d.Javadoc.implicits)
506
507 d.stubsFlags(ctx, cmd, stubsDir)
508
509 d.annotationsFlags(ctx, cmd)
510 d.inclusionAnnotationsFlags(ctx, cmd)
511 d.apiLevelsAnnotationsFlags(ctx, cmd)
512
Colin Crossbc139922021-03-25 18:33:16 -0700513 d.expandArgs(ctx, cmd)
Colin Cross2207f872021-03-24 12:39:08 -0700514
Colin Cross2207f872021-03-24 12:39:08 -0700515 for _, o := range d.Javadoc.properties.Out {
516 cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
517 }
518
519 // Add options for the other optional tasks: API-lint and check-released.
520 // We generate separate timestamp files for them.
521
522 doApiLint := false
523 doCheckReleased := false
524
525 // Add API lint options.
526
527 if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
528 doApiLint = true
529
530 newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
531 if newSince.Valid() {
532 cmd.FlagWithInput("--api-lint ", newSince.Path())
533 } else {
534 cmd.Flag("--api-lint")
535 }
Colin Crosscb77f752021-03-24 12:04:44 -0700536 d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700537 cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
538
Colin Cross0d532412021-03-25 09:38:45 -0700539 // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
Colin Cross2207f872021-03-24 12:39:08 -0700540 if d.Name() != "android.car-system-stubs-docs" &&
541 d.Name() != "android.car-stubs-docs" {
542 cmd.Flag("--lints-as-errors")
543 cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
544 }
545
546 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700547 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
548 d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700549
550 // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
Colin Cross2207f872021-03-24 12:39:08 -0700551 //
552 // TODO: metalava also has a slightly different message hardcoded. Should we unify this
553 // message and metalava's one?
554 msg := `$'` + // Enclose with $' ... '
555 `************************************************************\n` +
556 `Your API changes are triggering API Lint warnings or errors.\n` +
557 `To make these errors go away, fix the code according to the\n` +
558 `error and/or warning messages above.\n` +
559 `\n` +
560 `If it is not possible to do so, there are workarounds:\n` +
561 `\n` +
Aurimas Liutikasb23b7452021-05-24 18:00:37 +0000562 `1. You can suppress the errors with @SuppressLint("<id>")\n` +
563 ` where the <id> is given in brackets in the error message above.\n`
Colin Cross2207f872021-03-24 12:39:08 -0700564
565 if baselineFile.Valid() {
566 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
567 cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
568
569 msg += fmt.Sprintf(``+
570 `2. You can update the baseline by executing the following\n`+
571 ` command:\n`+
Colin Cross63eeda02021-04-15 19:01:57 -0700572 ` (cd $ANDROID_BUILD_TOP && cp \\\n`+
573 ` "%s" \\\n`+
574 ` "%s")\n`+
Colin Cross2207f872021-03-24 12:39:08 -0700575 ` To submit the revised baseline.txt to the main Android\n`+
576 ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
577 } else {
578 msg += fmt.Sprintf(``+
579 `2. You can add a baseline file of existing lint failures\n`+
580 ` to the build rule of %s.\n`, d.Name())
581 }
582 // Note the message ends with a ' (single quote), to close the $' ... ' .
583 msg += `************************************************************\n'`
584
585 cmd.FlagWithArg("--error-message:api-lint ", msg)
586 }
587
588 // Add "check released" options. (Detect incompatible API changes from the last public release)
589
590 if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
591 doCheckReleased = true
592
593 if len(d.Javadoc.properties.Out) > 0 {
594 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
595 }
596
597 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
598 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
599 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700600 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700601
Colin Crosscb77f752021-03-24 12:04:44 -0700602 d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700603
604 cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
605 cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
606
607 if baselineFile.Valid() {
608 cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
609 cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
610 }
611
612 // Note this string includes quote ($' ... '), which decodes the "\n"s.
613 msg := `$'\n******************************\n` +
614 `You have tried to change the API from what has been previously released in\n` +
615 `an SDK. Please fix the errors listed above.\n` +
616 `******************************\n'`
617
618 cmd.FlagWithArg("--error-message:compatibility:released ", msg)
619 }
620
Colin Cross2207f872021-03-24 12:39:08 -0700621 if generateStubs {
622 rule.Command().
623 BuiltTool("soong_zip").
624 Flag("-write_if_changed").
625 Flag("-jar").
626 FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
627 FlagWithArg("-C ", stubsDir.String()).
628 FlagWithArg("-D ", stubsDir.String())
629 }
630
631 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700632 d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700633 rule.Command().
634 BuiltTool("soong_zip").
635 Flag("-write_if_changed").
636 Flag("-d").
637 FlagWithOutput("-o ", d.metadataZip).
638 FlagWithArg("-C ", d.metadataDir.String()).
639 FlagWithArg("-D ", d.metadataDir.String())
640 }
641
642 // TODO: We don't really need two separate API files, but this is a reminiscence of how
643 // we used to run metalava separately for API lint and the "last_released" check. Unify them.
644 if doApiLint {
645 rule.Command().Text("touch").Output(d.apiLintTimestamp)
646 }
647 if doCheckReleased {
648 rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
649 }
650
Colin Cross6aa5c402021-03-24 12:28:50 -0700651 // TODO(b/183630617): rewrapper doesn't support restat rules
Colin Cross8095c292021-03-30 16:40:48 -0700652 // rule.Restat()
Colin Cross2207f872021-03-24 12:39:08 -0700653
654 zipSyncCleanupCmd(rule, srcJarDir)
655
656 rule.Build("metalava", "metalava merged")
657
658 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
659
660 if len(d.Javadoc.properties.Out) > 0 {
661 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
662 }
663
664 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
665 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
666 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
667
668 if baselineFile.Valid() {
669 ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
670 }
671
Colin Crosscb77f752021-03-24 12:04:44 -0700672 d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700673
674 rule := android.NewRuleBuilder(pctx, ctx)
675
676 // Diff command line.
677 // -F matches the closest "opening" line, such as "package android {"
678 // and " public class Intent {".
679 diff := `diff -u -F '{ *$'`
680
681 rule.Command().Text("( true")
682 rule.Command().
683 Text(diff).
684 Input(apiFile).Input(d.apiFile)
685
686 rule.Command().
687 Text(diff).
688 Input(removedApiFile).Input(d.removedApiFile)
689
690 msg := fmt.Sprintf(`\n******************************\n`+
691 `You have tried to change the API from what has been previously approved.\n\n`+
692 `To make these errors go away, you have two choices:\n`+
693 ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
694 ` to the new methods, etc. shown in the above diff.\n\n`+
695 ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
696 ` m %s-update-current-api\n\n`+
697 ` To submit the revised current.txt to the main Android repository,\n`+
698 ` you will need approval.\n`+
699 `******************************\n`, ctx.ModuleName())
700
701 rule.Command().
702 Text("touch").Output(d.checkCurrentApiTimestamp).
703 Text(") || (").
704 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
705 Text("; exit 38").
706 Text(")")
707
708 rule.Build("metalavaCurrentApiCheck", "check current API")
709
Colin Crosscb77f752021-03-24 12:04:44 -0700710 d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700711
712 // update API rule
713 rule = android.NewRuleBuilder(pctx, ctx)
714
715 rule.Command().Text("( true")
716
717 rule.Command().
718 Text("cp").Flag("-f").
719 Input(d.apiFile).Flag(apiFile.String())
720
721 rule.Command().
722 Text("cp").Flag("-f").
723 Input(d.removedApiFile).Flag(removedApiFile.String())
724
725 msg = "failed to update public API"
726
727 rule.Command().
728 Text("touch").Output(d.updateCurrentApiTimestamp).
729 Text(") || (").
730 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
731 Text("; exit 38").
732 Text(")")
733
734 rule.Build("metalavaCurrentApiUpdate", "update current API")
735 }
736
737 if String(d.properties.Check_nullability_warnings) != "" {
738 if d.nullabilityWarningsFile == nil {
739 ctx.PropertyErrorf("check_nullability_warnings",
740 "Cannot specify check_nullability_warnings unless validating nullability")
741 }
742
743 checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
744
Colin Crosscb77f752021-03-24 12:04:44 -0700745 d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700746
747 msg := fmt.Sprintf(`\n******************************\n`+
748 `The warnings encountered during nullability annotation validation did\n`+
749 `not match the checked in file of expected warnings. The diffs are shown\n`+
750 `above. You have two options:\n`+
751 ` 1. Resolve the differences by editing the nullability annotations.\n`+
752 ` 2. Update the file of expected warnings by running:\n`+
753 ` cp %s %s\n`+
754 ` and submitting the updated file as part of your change.`,
755 d.nullabilityWarningsFile, checkNullabilityWarnings)
756
757 rule := android.NewRuleBuilder(pctx, ctx)
758
759 rule.Command().
760 Text("(").
761 Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
762 Text("&&").
763 Text("touch").Output(d.checkNullabilityWarningsTimestamp).
764 Text(") || (").
765 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
766 Text("; exit 38").
767 Text(")")
768
769 rule.Build("nullabilityWarningsCheck", "nullability warnings check")
770 }
771}
772
773func StubsDefaultsFactory() android.Module {
774 module := &DocDefaults{}
775
776 module.AddProperties(
777 &JavadocProperties{},
778 &DroidstubsProperties{},
779 )
780
781 android.InitDefaultsModule(module)
782
783 return module
784}
785
786var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
787
788type PrebuiltStubsSourcesProperties struct {
789 Srcs []string `android:"path"`
790}
791
792type PrebuiltStubsSources struct {
793 android.ModuleBase
794 android.DefaultableModuleBase
795 prebuilt android.Prebuilt
796 android.SdkBase
797
798 properties PrebuiltStubsSourcesProperties
799
800 stubsSrcJar android.ModuleOutPath
801}
802
803func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
804 switch tag {
805 case "":
806 return android.Paths{p.stubsSrcJar}, nil
807 default:
808 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
809 }
810}
811
812func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
813 return d.stubsSrcJar
814}
815
816func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
817 p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
818
819 if len(p.properties.Srcs) != 1 {
820 ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
821 return
822 }
823
824 localSrcDir := p.properties.Srcs[0]
825 // Although PathForModuleSrc can return nil if either the path doesn't exist or
826 // the path components are invalid it won't in this case because no components
827 // are specified and the module directory must exist in order to get this far.
828 srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
829
830 // Glob the contents of the directory just in case the directory does not exist.
831 srcGlob := localSrcDir + "/**/*"
832 srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
833
834 rule := android.NewRuleBuilder(pctx, ctx)
835 rule.Command().
836 BuiltTool("soong_zip").
837 Flag("-write_if_changed").
838 Flag("-jar").
839 FlagWithOutput("-o ", p.stubsSrcJar).
840 FlagWithArg("-C ", srcDir.String()).
841 FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
842
843 rule.Restat()
844
845 rule.Build("zip src", "Create srcjar from prebuilt source")
846}
847
848func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
849 return &p.prebuilt
850}
851
852func (p *PrebuiltStubsSources) Name() string {
853 return p.prebuilt.Name(p.ModuleBase.Name())
854}
855
856// prebuilt_stubs_sources imports a set of java source files as if they were
857// generated by droidstubs.
858//
859// By default, a prebuilt_stubs_sources has a single variant that expects a
860// set of `.java` files generated by droidstubs.
861//
862// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
863// for host modules.
864//
865// Intended only for use by sdk snapshots.
866func PrebuiltStubsSourcesFactory() android.Module {
867 module := &PrebuiltStubsSources{}
868
869 module.AddProperties(&module.properties)
870
871 android.InitPrebuiltModule(module, &module.properties.Srcs)
872 android.InitSdkAwareModule(module)
873 InitDroiddocModule(module, android.HostAndDeviceSupported)
874 return module
875}