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