blob: 9d383ce2290aced74dd991815dc319780c843f01 [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
131 // is set to true, Metalava will allow framework SDK to contain API levels annotations.
132 Api_levels_annotations_enabled *bool
133
134 // the dirs which Metalava extracts API levels annotations from.
135 Api_levels_annotations_dirs []string
136
137 // the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
138 Api_levels_jar_filename *string
139
140 // if set to true, collect the values used by the Dev tools and
141 // write them in files packaged with the SDK. Defaults to false.
142 Write_sdk_values *bool
143}
144
Anton Hansson52609322021-05-05 10:36:05 +0100145// Used by xsd_config
146type ApiFilePath interface {
147 ApiFilePath() android.Path
148}
149
150type ApiStubsSrcProvider interface {
151 StubsSrcJar() android.Path
152}
153
154// Provider of information about API stubs, used by java_sdk_library.
155type ApiStubsProvider interface {
Anton Hansson3adf3c52021-09-21 15:25:12 +0100156 AnnotationsZip() android.Path
Anton Hansson52609322021-05-05 10:36:05 +0100157 ApiFilePath
158 RemovedApiFilePath() android.Path
159
160 ApiStubsSrcProvider
161}
162
Colin Cross2207f872021-03-24 12:39:08 -0700163// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
164// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
165// a droiddoc module to generate documentation.
166func DroidstubsFactory() android.Module {
167 module := &Droidstubs{}
168
169 module.AddProperties(&module.properties,
170 &module.Javadoc.properties)
171
172 InitDroiddocModule(module, android.HostAndDeviceSupported)
173 android.InitSdkAwareModule(module)
174 return module
175}
176
177// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
178// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
179// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
180// module when symbols needed by the source files are provided by java_library_host modules.
181func DroidstubsHostFactory() android.Module {
182 module := &Droidstubs{}
183
184 module.AddProperties(&module.properties,
185 &module.Javadoc.properties)
186
187 InitDroiddocModule(module, android.HostSupported)
188 return module
189}
190
191func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
192 switch tag {
193 case "":
194 return android.Paths{d.stubsSrcJar}, nil
195 case ".docs.zip":
196 return android.Paths{d.docZip}, nil
197 case ".api.txt", android.DefaultDistTag:
198 // This is the default dist path for dist properties that have no tag property.
199 return android.Paths{d.apiFilePath}, nil
200 case ".removed-api.txt":
201 return android.Paths{d.removedApiFilePath}, nil
202 case ".annotations.zip":
203 return android.Paths{d.annotationsZip}, nil
204 case ".api_versions.xml":
205 return android.Paths{d.apiVersionsXml}, nil
206 default:
207 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
208 }
209}
210
Anton Hansson3adf3c52021-09-21 15:25:12 +0100211func (d *Droidstubs) AnnotationsZip() android.Path {
212 return d.annotationsZip
213}
214
Colin Cross2207f872021-03-24 12:39:08 -0700215func (d *Droidstubs) ApiFilePath() android.Path {
216 return d.apiFilePath
217}
218
219func (d *Droidstubs) RemovedApiFilePath() android.Path {
220 return d.removedApiFilePath
221}
222
223func (d *Droidstubs) StubsSrcJar() android.Path {
224 return d.stubsSrcJar
225}
226
227var metalavaMergeAnnotationsDirTag = dependencyTag{name: "metalava-merge-annotations-dir"}
228var metalavaMergeInclusionAnnotationsDirTag = dependencyTag{name: "metalava-merge-inclusion-annotations-dir"}
229var metalavaAPILevelsAnnotationsDirTag = dependencyTag{name: "metalava-api-levels-annotations-dir"}
230
231func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
232 d.Javadoc.addDeps(ctx)
233
234 if len(d.properties.Merge_annotations_dirs) != 0 {
235 for _, mergeAnnotationsDir := range d.properties.Merge_annotations_dirs {
236 ctx.AddDependency(ctx.Module(), metalavaMergeAnnotationsDirTag, mergeAnnotationsDir)
237 }
238 }
239
240 if len(d.properties.Merge_inclusion_annotations_dirs) != 0 {
241 for _, mergeInclusionAnnotationsDir := range d.properties.Merge_inclusion_annotations_dirs {
242 ctx.AddDependency(ctx.Module(), metalavaMergeInclusionAnnotationsDirTag, mergeInclusionAnnotationsDir)
243 }
244 }
245
246 if len(d.properties.Api_levels_annotations_dirs) != 0 {
247 for _, apiLevelsAnnotationsDir := range d.properties.Api_levels_annotations_dirs {
248 ctx.AddDependency(ctx.Module(), metalavaAPILevelsAnnotationsDirTag, apiLevelsAnnotationsDir)
249 }
250 }
251}
252
253func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
254 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
255 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
256 String(d.properties.Api_filename) != "" {
257 filename := proptools.StringDefault(d.properties.Api_filename, ctx.ModuleName()+"_api.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700258 d.apiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700259 cmd.FlagWithOutput("--api ", d.apiFile)
260 d.apiFilePath = d.apiFile
261 } else if sourceApiFile := proptools.String(d.properties.Check_api.Current.Api_file); sourceApiFile != "" {
262 // If check api is disabled then make the source file available for export.
263 d.apiFilePath = android.PathForModuleSrc(ctx, sourceApiFile)
264 }
265
266 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
267 apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
268 String(d.properties.Removed_api_filename) != "" {
269 filename := proptools.StringDefault(d.properties.Removed_api_filename, ctx.ModuleName()+"_removed.txt")
Colin Crosscb77f752021-03-24 12:04:44 -0700270 d.removedApiFile = android.PathForModuleOut(ctx, "metalava", filename)
Colin Cross2207f872021-03-24 12:39:08 -0700271 cmd.FlagWithOutput("--removed-api ", d.removedApiFile)
272 d.removedApiFilePath = d.removedApiFile
273 } else if sourceRemovedApiFile := proptools.String(d.properties.Check_api.Current.Removed_api_file); sourceRemovedApiFile != "" {
274 // If check api is disabled then make the source removed api file available for export.
275 d.removedApiFilePath = android.PathForModuleSrc(ctx, sourceRemovedApiFile)
276 }
277
Colin Cross2207f872021-03-24 12:39:08 -0700278 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700279 d.metadataDir = android.PathForModuleOut(ctx, "metalava", "metadata")
Colin Cross2207f872021-03-24 12:39:08 -0700280 cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
281 }
282
283 if stubsDir.Valid() {
284 if Bool(d.properties.Create_doc_stubs) {
285 cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
286 } else {
287 cmd.FlagWithArg("--stubs ", stubsDir.String())
288 if !Bool(d.properties.Output_javadoc_comments) {
289 cmd.Flag("--exclude-documentation-from-stubs")
290 }
291 }
292 }
293}
294
295func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
296 if Bool(d.properties.Annotations_enabled) {
297 cmd.Flag("--include-annotations")
298
Andrei Onea4985e512021-04-29 16:29:34 +0100299 cmd.FlagWithArg("--exclude-annotation ", "androidx.annotation.RequiresApi")
300
Colin Cross2207f872021-03-24 12:39:08 -0700301 validatingNullability :=
Colin Crossbc139922021-03-25 18:33:16 -0700302 strings.Contains(String(d.Javadoc.properties.Args), "--validate-nullability-from-merged-stubs") ||
Colin Cross2207f872021-03-24 12:39:08 -0700303 String(d.properties.Validate_nullability_from_list) != ""
304
305 migratingNullability := String(d.properties.Previous_api) != ""
306 if migratingNullability {
307 previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
308 cmd.FlagWithInput("--migrate-nullness ", previousApi)
309 }
310
311 if s := String(d.properties.Validate_nullability_from_list); s != "" {
312 cmd.FlagWithInput("--validate-nullability-from-list ", android.PathForModuleSrc(ctx, s))
313 }
314
315 if validatingNullability {
Colin Crosscb77f752021-03-24 12:04:44 -0700316 d.nullabilityWarningsFile = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_nullability_warnings.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700317 cmd.FlagWithOutput("--nullability-warnings-txt ", d.nullabilityWarningsFile)
318 }
319
Colin Crosscb77f752021-03-24 12:04:44 -0700320 d.annotationsZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"_annotations.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700321 cmd.FlagWithOutput("--extract-annotations ", d.annotationsZip)
322
323 if len(d.properties.Merge_annotations_dirs) != 0 {
324 d.mergeAnnoDirFlags(ctx, cmd)
325 }
326
327 // TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
328 cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
329 FlagWithArg("--hide ", "SuperfluousPrefix").
330 FlagWithArg("--hide ", "AnnotationExtraction")
331 }
332}
333
334func (d *Droidstubs) mergeAnnoDirFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
335 ctx.VisitDirectDepsWithTag(metalavaMergeAnnotationsDirTag, func(m android.Module) {
336 if t, ok := m.(*ExportedDroiddocDir); ok {
337 cmd.FlagWithArg("--merge-qualifier-annotations ", t.dir.String()).Implicits(t.deps)
338 } else {
339 ctx.PropertyErrorf("merge_annotations_dirs",
340 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
341 }
342 })
343}
344
345func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
346 ctx.VisitDirectDepsWithTag(metalavaMergeInclusionAnnotationsDirTag, func(m android.Module) {
347 if t, ok := m.(*ExportedDroiddocDir); ok {
348 cmd.FlagWithArg("--merge-inclusion-annotations ", t.dir.String()).Implicits(t.deps)
349 } else {
350 ctx.PropertyErrorf("merge_inclusion_annotations_dirs",
351 "module %q is not a metalava merge-annotations dir", ctx.OtherModuleName(m))
352 }
353 })
354}
355
356func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
357 if !Bool(d.properties.Api_levels_annotations_enabled) {
358 return
359 }
360
Colin Crosscb77f752021-03-24 12:04:44 -0700361 d.apiVersionsXml = android.PathForModuleOut(ctx, "metalava", "api-versions.xml")
Colin Cross2207f872021-03-24 12:39:08 -0700362
363 if len(d.properties.Api_levels_annotations_dirs) == 0 {
364 ctx.PropertyErrorf("api_levels_annotations_dirs",
365 "has to be non-empty if api levels annotations was enabled!")
366 }
367
368 cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml)
369 cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml)
370 cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion().String())
Jeff Sharkey46d18db2021-06-07 11:10:14 -0600371 cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename())
Colin Cross2207f872021-03-24 12:39:08 -0700372
373 filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
374
375 ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
376 if t, ok := m.(*ExportedDroiddocDir); ok {
377 for _, dep := range t.deps {
Colin Cross5f6ffc72021-03-29 21:54:45 -0700378 if dep.Base() == filename {
379 cmd.Implicit(dep)
380 }
381 if filename != "android.jar" && dep.Base() == "android.jar" {
382 // Metalava implicitly searches these patterns:
383 // prebuilts/tools/common/api-versions/android-%/android.jar
384 // prebuilts/sdk/%/public/android.jar
385 // Add android.jar files from the api_levels_annotations_dirs directories to try
386 // to satisfy these patterns. If Metalava can't find a match for an API level
387 // between 1 and 28 in at least one pattern it will fail.
Colin Cross2207f872021-03-24 12:39:08 -0700388 cmd.Implicit(dep)
389 }
390 }
391 cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
392 } else {
393 ctx.PropertyErrorf("api_levels_annotations_dirs",
394 "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
395 }
396 })
397}
398
399func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
Anton Hansson363aae42021-06-04 16:20:25 +0100400 srcJarList android.Path, bootclasspath, classpath classpath, homeDir android.WritablePath) *android.RuleBuilderCommand {
Colin Cross2207f872021-03-24 12:39:08 -0700401 rule.Command().Text("rm -rf").Flag(homeDir.String())
402 rule.Command().Text("mkdir -p").Flag(homeDir.String())
403
Anton Hansson363aae42021-06-04 16:20:25 +0100404 cmd := rule.Command()
Colin Cross2207f872021-03-24 12:39:08 -0700405 cmd.FlagWithArg("ANDROID_PREFS_ROOT=", homeDir.String())
406
407 if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_METALAVA") {
408 rule.Remoteable(android.RemoteRuleSupports{RBE: true})
Colin Cross8095c292021-03-30 16:40:48 -0700409 execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
410 labels := map[string]string{"type": "tool", "name": "metalava"}
411 // TODO: metalava pool rejects these jobs
412 pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "java16")
413 rule.Rewrapper(&remoteexec.REParams{
414 Labels: labels,
415 ExecStrategy: execStrategy,
416 ToolchainInputs: []string{config.JavaCmd(ctx).String()},
417 Platform: map[string]string{remoteexec.PoolKey: pool},
418 })
Colin Cross2207f872021-03-24 12:39:08 -0700419 }
420
Colin Cross6aa5c402021-03-24 12:28:50 -0700421 cmd.BuiltTool("metalava").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "metalava.jar")).
Colin Cross2207f872021-03-24 12:39:08 -0700422 Flag(config.JavacVmFlags).
423 Flag("-J--add-opens=java.base/java.util=ALL-UNNAMED").
424 FlagWithArg("-encoding ", "UTF-8").
425 FlagWithArg("-source ", javaVersion.String()).
426 FlagWithRspFileInputList("@", android.PathForModuleOut(ctx, "metalava.rsp"), srcs).
427 FlagWithInput("@", srcJarList)
428
Colin Cross2207f872021-03-24 12:39:08 -0700429 if len(bootclasspath) > 0 {
430 cmd.FlagWithInputList("-bootclasspath ", bootclasspath.Paths(), ":")
431 }
432
433 if len(classpath) > 0 {
434 cmd.FlagWithInputList("-classpath ", classpath.Paths(), ":")
435 }
436
Colin Cross2207f872021-03-24 12:39:08 -0700437 cmd.Flag("--no-banner").
438 Flag("--color").
439 Flag("--quiet").
440 Flag("--format=v2").
441 FlagWithArg("--repeat-errors-max ", "10").
442 FlagWithArg("--hide ", "UnresolvedImport")
443
444 return cmd
445}
446
447func (d *Droidstubs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
448 deps := d.Javadoc.collectDeps(ctx)
449
Jiyong Parkf1691d22021-03-29 20:11:58 +0900450 javaVersion := getJavaVersion(ctx, String(d.Javadoc.properties.Java_version), android.SdkContext(d))
Colin Cross2207f872021-03-24 12:39:08 -0700451
452 // Create rule for metalava
453
Colin Crosscb77f752021-03-24 12:04:44 -0700454 srcJarDir := android.PathForModuleOut(ctx, "metalava", "srcjars")
Colin Cross2207f872021-03-24 12:39:08 -0700455
456 rule := android.NewRuleBuilder(pctx, ctx)
457
Colin Cross8095c292021-03-30 16:40:48 -0700458 rule.Sbox(android.PathForModuleOut(ctx, "metalava"),
459 android.PathForModuleOut(ctx, "metalava.sbox.textproto")).
460 SandboxInputs()
Colin Cross6aa5c402021-03-24 12:28:50 -0700461
Colin Cross2207f872021-03-24 12:39:08 -0700462 if BoolDefault(d.properties.High_mem, false) {
463 // This metalava run uses lots of memory, restrict the number of metalava jobs that can run in parallel.
464 rule.HighMem()
465 }
466
467 generateStubs := BoolDefault(d.properties.Generate_stubs, true)
468 var stubsDir android.OptionalPath
469 if generateStubs {
Colin Crosscb77f752021-03-24 12:04:44 -0700470 d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-"+"stubs.srcjar")
471 stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "metalava", "stubsDir"))
Colin Cross2207f872021-03-24 12:39:08 -0700472 rule.Command().Text("rm -rf").Text(stubsDir.String())
473 rule.Command().Text("mkdir -p").Text(stubsDir.String())
474 }
475
476 srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
477
Colin Crosscb77f752021-03-24 12:04:44 -0700478 homeDir := android.PathForModuleOut(ctx, "metalava", "home")
Colin Cross2207f872021-03-24 12:39:08 -0700479 cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
Anton Hansson363aae42021-06-04 16:20:25 +0100480 deps.bootClasspath, deps.classpath, homeDir)
Colin Cross2207f872021-03-24 12:39:08 -0700481 cmd.Implicits(d.Javadoc.implicits)
482
483 d.stubsFlags(ctx, cmd, stubsDir)
484
485 d.annotationsFlags(ctx, cmd)
486 d.inclusionAnnotationsFlags(ctx, cmd)
487 d.apiLevelsAnnotationsFlags(ctx, cmd)
488
Colin Crossbc139922021-03-25 18:33:16 -0700489 d.expandArgs(ctx, cmd)
Colin Cross2207f872021-03-24 12:39:08 -0700490
Colin Cross2207f872021-03-24 12:39:08 -0700491 for _, o := range d.Javadoc.properties.Out {
492 cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
493 }
494
495 // Add options for the other optional tasks: API-lint and check-released.
496 // We generate separate timestamp files for them.
497
498 doApiLint := false
499 doCheckReleased := false
500
501 // Add API lint options.
502
503 if BoolDefault(d.properties.Check_api.Api_lint.Enabled, false) {
504 doApiLint = true
505
506 newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
507 if newSince.Valid() {
508 cmd.FlagWithInput("--api-lint ", newSince.Path())
509 } else {
510 cmd.Flag("--api-lint")
511 }
Colin Crosscb77f752021-03-24 12:04:44 -0700512 d.apiLintReport = android.PathForModuleOut(ctx, "metalava", "api_lint_report.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700513 cmd.FlagWithOutput("--report-even-if-suppressed ", d.apiLintReport) // TODO: Change to ":api-lint"
514
Colin Cross0d532412021-03-25 09:38:45 -0700515 // TODO(b/154317059): Clean up this allowlist by baselining and/or checking in last-released.
Colin Cross2207f872021-03-24 12:39:08 -0700516 if d.Name() != "android.car-system-stubs-docs" &&
517 d.Name() != "android.car-stubs-docs" {
518 cmd.Flag("--lints-as-errors")
519 cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
520 }
521
522 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700523 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "api_lint_baseline.txt")
524 d.apiLintTimestamp = android.PathForModuleOut(ctx, "metalava", "api_lint.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700525
526 // Note this string includes a special shell quote $' ... ', which decodes the "\n"s.
Colin Cross2207f872021-03-24 12:39:08 -0700527 //
528 // TODO: metalava also has a slightly different message hardcoded. Should we unify this
529 // message and metalava's one?
530 msg := `$'` + // Enclose with $' ... '
531 `************************************************************\n` +
532 `Your API changes are triggering API Lint warnings or errors.\n` +
533 `To make these errors go away, fix the code according to the\n` +
534 `error and/or warning messages above.\n` +
535 `\n` +
536 `If it is not possible to do so, there are workarounds:\n` +
537 `\n` +
538 `1. You can suppress the errors with @SuppressLint("<id>")\n`
539
540 if baselineFile.Valid() {
541 cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
542 cmd.FlagWithOutput("--update-baseline:api-lint ", updatedBaselineOutput)
543
544 msg += fmt.Sprintf(``+
545 `2. You can update the baseline by executing the following\n`+
546 ` command:\n`+
Colin Cross63eeda02021-04-15 19:01:57 -0700547 ` (cd $ANDROID_BUILD_TOP && cp \\\n`+
548 ` "%s" \\\n`+
549 ` "%s")\n`+
Colin Cross2207f872021-03-24 12:39:08 -0700550 ` To submit the revised baseline.txt to the main Android\n`+
551 ` repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
552 } else {
553 msg += fmt.Sprintf(``+
554 `2. You can add a baseline file of existing lint failures\n`+
555 ` to the build rule of %s.\n`, d.Name())
556 }
557 // Note the message ends with a ' (single quote), to close the $' ... ' .
558 msg += `************************************************************\n'`
559
560 cmd.FlagWithArg("--error-message:api-lint ", msg)
561 }
562
563 // Add "check released" options. (Detect incompatible API changes from the last public release)
564
565 if apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") {
566 doCheckReleased = true
567
568 if len(d.Javadoc.properties.Out) > 0 {
569 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
570 }
571
572 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Api_file))
573 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Last_released.Removed_api_file))
574 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Last_released.Baseline_file)
Colin Crosscb77f752021-03-24 12:04:44 -0700575 updatedBaselineOutput := android.PathForModuleOut(ctx, "metalava", "last_released_baseline.txt")
Colin Cross2207f872021-03-24 12:39:08 -0700576
Colin Crosscb77f752021-03-24 12:04:44 -0700577 d.checkLastReleasedApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_last_released_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700578
579 cmd.FlagWithInput("--check-compatibility:api:released ", apiFile)
580 cmd.FlagWithInput("--check-compatibility:removed:released ", removedApiFile)
581
582 if baselineFile.Valid() {
583 cmd.FlagWithInput("--baseline:compatibility:released ", baselineFile.Path())
584 cmd.FlagWithOutput("--update-baseline:compatibility:released ", updatedBaselineOutput)
585 }
586
587 // Note this string includes quote ($' ... '), which decodes the "\n"s.
588 msg := `$'\n******************************\n` +
589 `You have tried to change the API from what has been previously released in\n` +
590 `an SDK. Please fix the errors listed above.\n` +
591 `******************************\n'`
592
593 cmd.FlagWithArg("--error-message:compatibility:released ", msg)
594 }
595
Colin Cross2207f872021-03-24 12:39:08 -0700596 if generateStubs {
597 rule.Command().
598 BuiltTool("soong_zip").
599 Flag("-write_if_changed").
600 Flag("-jar").
601 FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
602 FlagWithArg("-C ", stubsDir.String()).
603 FlagWithArg("-D ", stubsDir.String())
604 }
605
606 if Bool(d.properties.Write_sdk_values) {
Colin Crosscb77f752021-03-24 12:04:44 -0700607 d.metadataZip = android.PathForModuleOut(ctx, "metalava", ctx.ModuleName()+"-metadata.zip")
Colin Cross2207f872021-03-24 12:39:08 -0700608 rule.Command().
609 BuiltTool("soong_zip").
610 Flag("-write_if_changed").
611 Flag("-d").
612 FlagWithOutput("-o ", d.metadataZip).
613 FlagWithArg("-C ", d.metadataDir.String()).
614 FlagWithArg("-D ", d.metadataDir.String())
615 }
616
617 // TODO: We don't really need two separate API files, but this is a reminiscence of how
618 // we used to run metalava separately for API lint and the "last_released" check. Unify them.
619 if doApiLint {
620 rule.Command().Text("touch").Output(d.apiLintTimestamp)
621 }
622 if doCheckReleased {
623 rule.Command().Text("touch").Output(d.checkLastReleasedApiTimestamp)
624 }
625
Colin Cross6aa5c402021-03-24 12:28:50 -0700626 // TODO(b/183630617): rewrapper doesn't support restat rules
Colin Cross8095c292021-03-30 16:40:48 -0700627 // rule.Restat()
Colin Cross2207f872021-03-24 12:39:08 -0700628
629 zipSyncCleanupCmd(rule, srcJarDir)
630
631 rule.Build("metalava", "metalava merged")
632
633 if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") {
634
635 if len(d.Javadoc.properties.Out) > 0 {
636 ctx.PropertyErrorf("out", "out property may not be combined with check_api")
637 }
638
639 apiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Api_file))
640 removedApiFile := android.PathForModuleSrc(ctx, String(d.properties.Check_api.Current.Removed_api_file))
641 baselineFile := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Current.Baseline_file)
642
643 if baselineFile.Valid() {
644 ctx.PropertyErrorf("baseline_file", "current API check can't have a baseline file. (module %s)", ctx.ModuleName())
645 }
646
Colin Crosscb77f752021-03-24 12:04:44 -0700647 d.checkCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "check_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700648
649 rule := android.NewRuleBuilder(pctx, ctx)
650
651 // Diff command line.
652 // -F matches the closest "opening" line, such as "package android {"
653 // and " public class Intent {".
654 diff := `diff -u -F '{ *$'`
655
656 rule.Command().Text("( true")
657 rule.Command().
658 Text(diff).
659 Input(apiFile).Input(d.apiFile)
660
661 rule.Command().
662 Text(diff).
663 Input(removedApiFile).Input(d.removedApiFile)
664
665 msg := fmt.Sprintf(`\n******************************\n`+
666 `You have tried to change the API from what has been previously approved.\n\n`+
667 `To make these errors go away, you have two choices:\n`+
668 ` 1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
669 ` to the new methods, etc. shown in the above diff.\n\n`+
670 ` 2. You can update current.txt and/or removed.txt by executing the following command:\n`+
671 ` m %s-update-current-api\n\n`+
672 ` To submit the revised current.txt to the main Android repository,\n`+
673 ` you will need approval.\n`+
674 `******************************\n`, ctx.ModuleName())
675
676 rule.Command().
677 Text("touch").Output(d.checkCurrentApiTimestamp).
678 Text(") || (").
679 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
680 Text("; exit 38").
681 Text(")")
682
683 rule.Build("metalavaCurrentApiCheck", "check current API")
684
Colin Crosscb77f752021-03-24 12:04:44 -0700685 d.updateCurrentApiTimestamp = android.PathForModuleOut(ctx, "metalava", "update_current_api.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700686
687 // update API rule
688 rule = android.NewRuleBuilder(pctx, ctx)
689
690 rule.Command().Text("( true")
691
692 rule.Command().
693 Text("cp").Flag("-f").
694 Input(d.apiFile).Flag(apiFile.String())
695
696 rule.Command().
697 Text("cp").Flag("-f").
698 Input(d.removedApiFile).Flag(removedApiFile.String())
699
700 msg = "failed to update public API"
701
702 rule.Command().
703 Text("touch").Output(d.updateCurrentApiTimestamp).
704 Text(") || (").
705 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
706 Text("; exit 38").
707 Text(")")
708
709 rule.Build("metalavaCurrentApiUpdate", "update current API")
710 }
711
712 if String(d.properties.Check_nullability_warnings) != "" {
713 if d.nullabilityWarningsFile == nil {
714 ctx.PropertyErrorf("check_nullability_warnings",
715 "Cannot specify check_nullability_warnings unless validating nullability")
716 }
717
718 checkNullabilityWarnings := android.PathForModuleSrc(ctx, String(d.properties.Check_nullability_warnings))
719
Colin Crosscb77f752021-03-24 12:04:44 -0700720 d.checkNullabilityWarningsTimestamp = android.PathForModuleOut(ctx, "metalava", "check_nullability_warnings.timestamp")
Colin Cross2207f872021-03-24 12:39:08 -0700721
722 msg := fmt.Sprintf(`\n******************************\n`+
723 `The warnings encountered during nullability annotation validation did\n`+
724 `not match the checked in file of expected warnings. The diffs are shown\n`+
725 `above. You have two options:\n`+
726 ` 1. Resolve the differences by editing the nullability annotations.\n`+
727 ` 2. Update the file of expected warnings by running:\n`+
728 ` cp %s %s\n`+
729 ` and submitting the updated file as part of your change.`,
730 d.nullabilityWarningsFile, checkNullabilityWarnings)
731
732 rule := android.NewRuleBuilder(pctx, ctx)
733
734 rule.Command().
735 Text("(").
736 Text("diff").Input(checkNullabilityWarnings).Input(d.nullabilityWarningsFile).
737 Text("&&").
738 Text("touch").Output(d.checkNullabilityWarningsTimestamp).
739 Text(") || (").
740 Text("echo").Flag("-e").Flag(`"` + msg + `"`).
741 Text("; exit 38").
742 Text(")")
743
744 rule.Build("nullabilityWarningsCheck", "nullability warnings check")
745 }
746}
747
748func StubsDefaultsFactory() android.Module {
749 module := &DocDefaults{}
750
751 module.AddProperties(
752 &JavadocProperties{},
753 &DroidstubsProperties{},
754 )
755
756 android.InitDefaultsModule(module)
757
758 return module
759}
760
761var _ android.PrebuiltInterface = (*PrebuiltStubsSources)(nil)
762
763type PrebuiltStubsSourcesProperties struct {
764 Srcs []string `android:"path"`
765}
766
767type PrebuiltStubsSources struct {
768 android.ModuleBase
769 android.DefaultableModuleBase
770 prebuilt android.Prebuilt
771 android.SdkBase
772
773 properties PrebuiltStubsSourcesProperties
774
775 stubsSrcJar android.ModuleOutPath
776}
777
778func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
779 switch tag {
780 case "":
781 return android.Paths{p.stubsSrcJar}, nil
782 default:
783 return nil, fmt.Errorf("unsupported module reference tag %q", tag)
784 }
785}
786
787func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
788 return d.stubsSrcJar
789}
790
791func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
792 p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
793
794 if len(p.properties.Srcs) != 1 {
795 ctx.PropertyErrorf("srcs", "must only specify one directory path, contains %d paths", len(p.properties.Srcs))
796 return
797 }
798
799 localSrcDir := p.properties.Srcs[0]
800 // Although PathForModuleSrc can return nil if either the path doesn't exist or
801 // the path components are invalid it won't in this case because no components
802 // are specified and the module directory must exist in order to get this far.
803 srcDir := android.PathForModuleSrc(ctx).(android.SourcePath).Join(ctx, localSrcDir)
804
805 // Glob the contents of the directory just in case the directory does not exist.
806 srcGlob := localSrcDir + "/**/*"
807 srcPaths := android.PathsForModuleSrc(ctx, []string{srcGlob})
808
809 rule := android.NewRuleBuilder(pctx, ctx)
810 rule.Command().
811 BuiltTool("soong_zip").
812 Flag("-write_if_changed").
813 Flag("-jar").
814 FlagWithOutput("-o ", p.stubsSrcJar).
815 FlagWithArg("-C ", srcDir.String()).
816 FlagWithRspFileInputList("-r ", p.stubsSrcJar.ReplaceExtension(ctx, "rsp"), srcPaths)
817
818 rule.Restat()
819
820 rule.Build("zip src", "Create srcjar from prebuilt source")
821}
822
823func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
824 return &p.prebuilt
825}
826
827func (p *PrebuiltStubsSources) Name() string {
828 return p.prebuilt.Name(p.ModuleBase.Name())
829}
830
831// prebuilt_stubs_sources imports a set of java source files as if they were
832// generated by droidstubs.
833//
834// By default, a prebuilt_stubs_sources has a single variant that expects a
835// set of `.java` files generated by droidstubs.
836//
837// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
838// for host modules.
839//
840// Intended only for use by sdk snapshots.
841func PrebuiltStubsSourcesFactory() android.Module {
842 module := &PrebuiltStubsSources{}
843
844 module.AddProperties(&module.properties)
845
846 android.InitPrebuiltModule(module, &module.properties.Srcs)
847 android.InitSdkAwareModule(module)
848 InitDroiddocModule(module, android.HostAndDeviceSupported)
849 return module
850}