lint.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. // Copyright 2020 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. package java
  15. import (
  16. "fmt"
  17. "sort"
  18. "strconv"
  19. "strings"
  20. "github.com/google/blueprint/proptools"
  21. "android/soong/android"
  22. "android/soong/java/config"
  23. "android/soong/remoteexec"
  24. )
  25. // lint checks automatically enforced for modules that have different min_sdk_version than
  26. // sdk_version
  27. var updatabilityChecks = []string{"NewApi"}
  28. type LintProperties struct {
  29. // Controls for running Android Lint on the module.
  30. Lint struct {
  31. // If true, run Android Lint on the module. Defaults to true.
  32. Enabled *bool
  33. // Flags to pass to the Android Lint tool.
  34. Flags []string
  35. // Checks that should be treated as fatal.
  36. Fatal_checks []string
  37. // Checks that should be treated as errors.
  38. Error_checks []string
  39. // Checks that should be treated as warnings.
  40. Warning_checks []string
  41. // Checks that should be skipped.
  42. Disabled_checks []string
  43. // Modules that provide extra lint checks
  44. Extra_check_modules []string
  45. // Name of the file that lint uses as the baseline. Defaults to "lint-baseline.xml".
  46. Baseline_filename *string
  47. // If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false.
  48. Strict_updatability_linting *bool
  49. // Treat the code in this module as test code for @VisibleForTesting enforcement.
  50. // This will be true by default for test module types, false otherwise.
  51. // If soong gets support for testonly, this flag should be replaced with that.
  52. Test *bool
  53. }
  54. }
  55. type linter struct {
  56. name string
  57. manifest android.Path
  58. mergedManifest android.Path
  59. srcs android.Paths
  60. srcJars android.Paths
  61. resources android.Paths
  62. classpath android.Paths
  63. classes android.Path
  64. extraLintCheckJars android.Paths
  65. library bool
  66. minSdkVersion int
  67. targetSdkVersion int
  68. compileSdkVersion int
  69. compileSdkKind android.SdkKind
  70. javaLanguageLevel string
  71. kotlinLanguageLevel string
  72. outputs lintOutputs
  73. properties LintProperties
  74. extraMainlineLintErrors []string
  75. reports android.Paths
  76. buildModuleReportZip bool
  77. }
  78. type lintOutputs struct {
  79. html android.Path
  80. text android.Path
  81. xml android.Path
  82. referenceBaseline android.Path
  83. depSets LintDepSets
  84. }
  85. type lintOutputsIntf interface {
  86. lintOutputs() *lintOutputs
  87. }
  88. type LintDepSetsIntf interface {
  89. LintDepSets() LintDepSets
  90. // Methods used to propagate strict_updatability_linting values.
  91. GetStrictUpdatabilityLinting() bool
  92. SetStrictUpdatabilityLinting(bool)
  93. }
  94. type LintDepSets struct {
  95. HTML, Text, XML *android.DepSet[android.Path]
  96. }
  97. type LintDepSetsBuilder struct {
  98. HTML, Text, XML *android.DepSetBuilder[android.Path]
  99. }
  100. func NewLintDepSetBuilder() LintDepSetsBuilder {
  101. return LintDepSetsBuilder{
  102. HTML: android.NewDepSetBuilder[android.Path](android.POSTORDER),
  103. Text: android.NewDepSetBuilder[android.Path](android.POSTORDER),
  104. XML: android.NewDepSetBuilder[android.Path](android.POSTORDER),
  105. }
  106. }
  107. func (l LintDepSetsBuilder) Direct(html, text, xml android.Path) LintDepSetsBuilder {
  108. l.HTML.Direct(html)
  109. l.Text.Direct(text)
  110. l.XML.Direct(xml)
  111. return l
  112. }
  113. func (l LintDepSetsBuilder) Transitive(depSets LintDepSets) LintDepSetsBuilder {
  114. if depSets.HTML != nil {
  115. l.HTML.Transitive(depSets.HTML)
  116. }
  117. if depSets.Text != nil {
  118. l.Text.Transitive(depSets.Text)
  119. }
  120. if depSets.XML != nil {
  121. l.XML.Transitive(depSets.XML)
  122. }
  123. return l
  124. }
  125. func (l LintDepSetsBuilder) Build() LintDepSets {
  126. return LintDepSets{
  127. HTML: l.HTML.Build(),
  128. Text: l.Text.Build(),
  129. XML: l.XML.Build(),
  130. }
  131. }
  132. type lintDatabaseFiles struct {
  133. apiVersionsModule string
  134. apiVersionsCopiedName string
  135. apiVersionsPrebuiltPath string
  136. annotationsModule string
  137. annotationCopiedName string
  138. annotationPrebuiltpath string
  139. }
  140. var allLintDatabasefiles = map[android.SdkKind]lintDatabaseFiles{
  141. android.SdkPublic: {
  142. apiVersionsModule: "api_versions_public",
  143. apiVersionsCopiedName: "api_versions_public.xml",
  144. apiVersionsPrebuiltPath: "prebuilts/sdk/current/public/data/api-versions.xml",
  145. annotationsModule: "sdk-annotations.zip",
  146. annotationCopiedName: "annotations-public.zip",
  147. annotationPrebuiltpath: "prebuilts/sdk/current/public/data/annotations.zip",
  148. },
  149. android.SdkSystem: {
  150. apiVersionsModule: "api_versions_system",
  151. apiVersionsCopiedName: "api_versions_system.xml",
  152. apiVersionsPrebuiltPath: "prebuilts/sdk/current/system/data/api-versions.xml",
  153. annotationsModule: "sdk-annotations-system.zip",
  154. annotationCopiedName: "annotations-system.zip",
  155. annotationPrebuiltpath: "prebuilts/sdk/current/system/data/annotations.zip",
  156. },
  157. android.SdkModule: {
  158. apiVersionsModule: "api_versions_module_lib",
  159. apiVersionsCopiedName: "api_versions_module_lib.xml",
  160. apiVersionsPrebuiltPath: "prebuilts/sdk/current/module-lib/data/api-versions.xml",
  161. annotationsModule: "sdk-annotations-module-lib.zip",
  162. annotationCopiedName: "annotations-module-lib.zip",
  163. annotationPrebuiltpath: "prebuilts/sdk/current/module-lib/data/annotations.zip",
  164. },
  165. android.SdkSystemServer: {
  166. apiVersionsModule: "api_versions_system_server",
  167. apiVersionsCopiedName: "api_versions_system_server.xml",
  168. apiVersionsPrebuiltPath: "prebuilts/sdk/current/system-server/data/api-versions.xml",
  169. annotationsModule: "sdk-annotations-system-server.zip",
  170. annotationCopiedName: "annotations-system-server.zip",
  171. annotationPrebuiltpath: "prebuilts/sdk/current/system-server/data/annotations.zip",
  172. },
  173. }
  174. func (l *linter) LintDepSets() LintDepSets {
  175. return l.outputs.depSets
  176. }
  177. func (l *linter) GetStrictUpdatabilityLinting() bool {
  178. return BoolDefault(l.properties.Lint.Strict_updatability_linting, false)
  179. }
  180. func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) {
  181. l.properties.Lint.Strict_updatability_linting = &strictLinting
  182. }
  183. var _ LintDepSetsIntf = (*linter)(nil)
  184. var _ lintOutputsIntf = (*linter)(nil)
  185. func (l *linter) lintOutputs() *lintOutputs {
  186. return &l.outputs
  187. }
  188. func (l *linter) enabled() bool {
  189. return BoolDefault(l.properties.Lint.Enabled, true)
  190. }
  191. func (l *linter) deps(ctx android.BottomUpMutatorContext) {
  192. if !l.enabled() {
  193. return
  194. }
  195. extraCheckModules := l.properties.Lint.Extra_check_modules
  196. if extraCheckModulesEnv := ctx.Config().Getenv("ANDROID_LINT_CHECK_EXTRA_MODULES"); extraCheckModulesEnv != "" {
  197. extraCheckModules = append(extraCheckModules, strings.Split(extraCheckModulesEnv, ",")...)
  198. }
  199. ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(),
  200. extraLintCheckTag, extraCheckModules...)
  201. }
  202. // lintPaths contains the paths to lint's inputs and outputs to make it easier to pass them
  203. // around.
  204. type lintPaths struct {
  205. projectXML android.WritablePath
  206. configXML android.WritablePath
  207. cacheDir android.WritablePath
  208. homeDir android.WritablePath
  209. srcjarDir android.WritablePath
  210. }
  211. func lintRBEExecStrategy(ctx android.ModuleContext) string {
  212. return ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
  213. }
  214. func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder, srcsList android.Path) lintPaths {
  215. projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
  216. // Lint looks for a lint.xml file next to the project.xml file, give it one.
  217. configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
  218. cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
  219. homeDir := android.PathForModuleOut(ctx, "lint", "home")
  220. srcJarDir := android.PathForModuleOut(ctx, "lint", "srcjars")
  221. srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
  222. cmd := rule.Command().
  223. BuiltTool("lint_project_xml").
  224. FlagWithOutput("--project_out ", projectXMLPath).
  225. FlagWithOutput("--config_out ", configXMLPath).
  226. FlagWithArg("--name ", ctx.ModuleName())
  227. if l.library {
  228. cmd.Flag("--library")
  229. }
  230. if proptools.BoolDefault(l.properties.Lint.Test, false) {
  231. cmd.Flag("--test")
  232. }
  233. if l.manifest != nil {
  234. cmd.FlagWithInput("--manifest ", l.manifest)
  235. }
  236. if l.mergedManifest != nil {
  237. cmd.FlagWithInput("--merged_manifest ", l.mergedManifest)
  238. }
  239. // TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
  240. // lint separately.
  241. cmd.FlagWithInput("--srcs ", srcsList)
  242. cmd.FlagWithInput("--generated_srcs ", srcJarList)
  243. if len(l.resources) > 0 {
  244. resourcesList := android.PathForModuleOut(ctx, "lint-resources.list")
  245. cmd.FlagWithRspFileInputList("--resources ", resourcesList, l.resources)
  246. }
  247. if l.classes != nil {
  248. cmd.FlagWithInput("--classes ", l.classes)
  249. }
  250. cmd.FlagForEachInput("--classpath ", l.classpath)
  251. cmd.FlagForEachInput("--extra_checks_jar ", l.extraLintCheckJars)
  252. cmd.FlagWithArg("--root_dir ", "$PWD")
  253. // The cache tag in project.xml is relative to the root dir, or the project.xml file if
  254. // the root dir is not set.
  255. cmd.FlagWithArg("--cache_dir ", cacheDir.String())
  256. cmd.FlagWithInput("@",
  257. android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
  258. if l.compileSdkKind == android.SdkPublic {
  259. cmd.FlagForEachArg("--error_check ", l.extraMainlineLintErrors)
  260. } else {
  261. // TODO(b/268261262): Remove this branch. We're demoting NewApi to a warning due to pre-existing issues that need to be fixed.
  262. cmd.FlagForEachArg("--warning_check ", l.extraMainlineLintErrors)
  263. }
  264. cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
  265. cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
  266. cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
  267. cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
  268. // TODO(b/193460475): Re-enable strict updatability linting
  269. //if l.GetStrictUpdatabilityLinting() {
  270. // // Verify the module does not baseline issues that endanger safe updatability.
  271. // if baselinePath := l.getBaselineFilepath(ctx); baselinePath.Valid() {
  272. // cmd.FlagWithInput("--baseline ", baselinePath.Path())
  273. // cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks)
  274. // }
  275. //}
  276. return lintPaths{
  277. projectXML: projectXMLPath,
  278. configXML: configXMLPath,
  279. cacheDir: cacheDir,
  280. homeDir: homeDir,
  281. }
  282. }
  283. // generateManifest adds a command to the rule to write a simple manifest that contains the
  284. // minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
  285. func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.WritablePath {
  286. manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
  287. rule.Command().Text("(").
  288. Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
  289. Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
  290. Text(`echo " android:versionCode='1' android:versionName='1' >" &&`).
  291. Textf(`echo " <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
  292. l.minSdkVersion, l.targetSdkVersion).
  293. Text(`echo "</manifest>"`).
  294. Text(") >").Output(manifestPath)
  295. return manifestPath
  296. }
  297. func (l *linter) getBaselineFilepath(ctx android.ModuleContext) android.OptionalPath {
  298. var lintBaseline android.OptionalPath
  299. if lintFilename := proptools.StringDefault(l.properties.Lint.Baseline_filename, "lint-baseline.xml"); lintFilename != "" {
  300. if String(l.properties.Lint.Baseline_filename) != "" {
  301. // if manually specified, we require the file to exist
  302. lintBaseline = android.OptionalPathForPath(android.PathForModuleSrc(ctx, lintFilename))
  303. } else {
  304. lintBaseline = android.ExistentPathForSource(ctx, ctx.ModuleDir(), lintFilename)
  305. }
  306. }
  307. return lintBaseline
  308. }
  309. func (l *linter) lint(ctx android.ModuleContext) {
  310. if !l.enabled() {
  311. return
  312. }
  313. if l.minSdkVersion != l.compileSdkVersion {
  314. l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
  315. // Skip lint warning checks for NewApi warnings for libcore where they come from source
  316. // files that reference the API they are adding (b/208656169).
  317. if !strings.HasPrefix(ctx.ModuleDir(), "libcore") {
  318. _, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
  319. if len(filtered) != 0 {
  320. ctx.PropertyErrorf("lint.warning_checks",
  321. "Can't treat %v checks as warnings if min_sdk_version is different from sdk_version.", filtered)
  322. }
  323. }
  324. _, filtered := android.FilterList(l.properties.Lint.Disabled_checks, updatabilityChecks)
  325. if len(filtered) != 0 {
  326. ctx.PropertyErrorf("lint.disabled_checks",
  327. "Can't disable %v checks if min_sdk_version is different from sdk_version.", filtered)
  328. }
  329. // TODO(b/238784089): Remove this workaround when the NewApi issues have been addressed in PermissionController
  330. if ctx.ModuleName() == "PermissionController" {
  331. l.extraMainlineLintErrors = android.FilterListPred(l.extraMainlineLintErrors, func(s string) bool {
  332. return s != "NewApi"
  333. })
  334. l.properties.Lint.Warning_checks = append(l.properties.Lint.Warning_checks, "NewApi")
  335. }
  336. }
  337. extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
  338. for _, extraLintCheckModule := range extraLintCheckModules {
  339. if ctx.OtherModuleHasProvider(extraLintCheckModule, JavaInfoProvider) {
  340. dep := ctx.OtherModuleProvider(extraLintCheckModule, JavaInfoProvider).(JavaInfo)
  341. l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars...)
  342. } else {
  343. ctx.PropertyErrorf("lint.extra_check_modules",
  344. "%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
  345. }
  346. }
  347. l.extraLintCheckJars = append(l.extraLintCheckJars, android.PathForSource(ctx,
  348. "prebuilts/cmdline-tools/AndroidGlobalLintChecker.jar"))
  349. rule := android.NewRuleBuilder(pctx, ctx).
  350. Sbox(android.PathForModuleOut(ctx, "lint"),
  351. android.PathForModuleOut(ctx, "lint.sbox.textproto")).
  352. SandboxInputs()
  353. if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
  354. pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
  355. rule.Remoteable(android.RemoteRuleSupports{RBE: true})
  356. rule.Rewrapper(&remoteexec.REParams{
  357. Labels: map[string]string{"type": "tool", "name": "lint"},
  358. ExecStrategy: lintRBEExecStrategy(ctx),
  359. ToolchainInputs: []string{config.JavaCmd(ctx).String()},
  360. Platform: map[string]string{remoteexec.PoolKey: pool},
  361. })
  362. }
  363. if l.manifest == nil {
  364. manifest := l.generateManifest(ctx, rule)
  365. l.manifest = manifest
  366. rule.Temporary(manifest)
  367. }
  368. srcsList := android.PathForModuleOut(ctx, "lint", "lint-srcs.list")
  369. srcsListRsp := android.PathForModuleOut(ctx, "lint-srcs.list.rsp")
  370. rule.Command().Text("cp").FlagWithRspFileInputList("", srcsListRsp, l.srcs).Output(srcsList)
  371. lintPaths := l.writeLintProjectXML(ctx, rule, srcsList)
  372. html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
  373. text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
  374. xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
  375. referenceBaseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
  376. depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
  377. ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
  378. if depLint, ok := dep.(LintDepSetsIntf); ok {
  379. depSetsBuilder.Transitive(depLint.LintDepSets())
  380. }
  381. })
  382. rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
  383. rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
  384. rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
  385. files, ok := allLintDatabasefiles[l.compileSdkKind]
  386. if !ok {
  387. files = allLintDatabasefiles[android.SdkPublic]
  388. }
  389. var annotationsZipPath, apiVersionsXMLPath android.Path
  390. if ctx.Config().AlwaysUsePrebuiltSdks() {
  391. annotationsZipPath = android.PathForSource(ctx, files.annotationPrebuiltpath)
  392. apiVersionsXMLPath = android.PathForSource(ctx, files.apiVersionsPrebuiltPath)
  393. } else {
  394. annotationsZipPath = copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName)
  395. apiVersionsXMLPath = copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName)
  396. }
  397. cmd := rule.Command()
  398. cmd.Flag(`JAVA_OPTS="-Xmx3072m --add-opens java.base/java.util=ALL-UNNAMED"`).
  399. FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
  400. FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
  401. FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
  402. cmd.BuiltTool("lint").ImplicitTool(ctx.Config().HostJavaToolPath(ctx, "lint.jar")).
  403. Flag("--quiet").
  404. FlagWithInput("--project ", lintPaths.projectXML).
  405. FlagWithInput("--config ", lintPaths.configXML).
  406. FlagWithOutput("--html ", html).
  407. FlagWithOutput("--text ", text).
  408. FlagWithOutput("--xml ", xml).
  409. FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
  410. FlagWithArg("--java-language-level ", l.javaLanguageLevel).
  411. FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
  412. FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
  413. Flag("--apply-suggestions"). // applies suggested fixes to files in the sandbox
  414. Flags(l.properties.Lint.Flags).
  415. Implicit(annotationsZipPath).
  416. Implicit(apiVersionsXMLPath)
  417. rule.Temporary(lintPaths.projectXML)
  418. rule.Temporary(lintPaths.configXML)
  419. if exitCode := ctx.Config().Getenv("ANDROID_LINT_SUPPRESS_EXIT_CODE"); exitCode == "" {
  420. cmd.Flag("--exitcode")
  421. }
  422. if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
  423. cmd.FlagWithArg("--check ", checkOnly)
  424. }
  425. lintBaseline := l.getBaselineFilepath(ctx)
  426. if lintBaseline.Valid() {
  427. cmd.FlagWithInput("--baseline ", lintBaseline.Path())
  428. }
  429. cmd.FlagWithOutput("--write-reference-baseline ", referenceBaseline)
  430. cmd.Text("; EXITCODE=$?; ")
  431. // The sources in the sandbox may have been modified by --apply-suggestions, zip them up and
  432. // export them out of the sandbox. Do this before exiting so that the suggestions exit even after
  433. // a fatal error.
  434. cmd.BuiltTool("soong_zip").
  435. FlagWithOutput("-o ", android.PathForModuleOut(ctx, "lint", "suggested-fixes.zip")).
  436. FlagWithArg("-C ", cmd.PathForInput(android.PathForSource(ctx))).
  437. FlagWithInput("-r ", srcsList)
  438. cmd.Text("; if [ $EXITCODE != 0 ]; then if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit $EXITCODE; fi")
  439. rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
  440. // The HTML output contains a date, remove it to make the output deterministic.
  441. rule.Command().Text(`sed -i.tmp -e 's|Check performed at .*\(</nav>\)|\1|'`).Output(html)
  442. rule.Build("lint", "lint")
  443. l.outputs = lintOutputs{
  444. html: html,
  445. text: text,
  446. xml: xml,
  447. referenceBaseline: referenceBaseline,
  448. depSets: depSetsBuilder.Build(),
  449. }
  450. if l.buildModuleReportZip {
  451. l.reports = BuildModuleLintReportZips(ctx, l.LintDepSets())
  452. }
  453. }
  454. func BuildModuleLintReportZips(ctx android.ModuleContext, depSets LintDepSets) android.Paths {
  455. htmlList := android.SortedUniquePaths(depSets.HTML.ToList())
  456. textList := android.SortedUniquePaths(depSets.Text.ToList())
  457. xmlList := android.SortedUniquePaths(depSets.XML.ToList())
  458. if len(htmlList) == 0 && len(textList) == 0 && len(xmlList) == 0 {
  459. return nil
  460. }
  461. htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
  462. lintZip(ctx, htmlList, htmlZip)
  463. textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
  464. lintZip(ctx, textList, textZip)
  465. xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
  466. lintZip(ctx, xmlList, xmlZip)
  467. return android.Paths{htmlZip, textZip, xmlZip}
  468. }
  469. type lintSingleton struct {
  470. htmlZip android.WritablePath
  471. textZip android.WritablePath
  472. xmlZip android.WritablePath
  473. referenceBaselineZip android.WritablePath
  474. }
  475. func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
  476. l.generateLintReportZips(ctx)
  477. l.copyLintDependencies(ctx)
  478. }
  479. func findModuleOrErr(ctx android.SingletonContext, moduleName string) android.Module {
  480. var res android.Module
  481. ctx.VisitAllModules(func(m android.Module) {
  482. if ctx.ModuleName(m) == moduleName {
  483. if res == nil {
  484. res = m
  485. } else {
  486. ctx.Errorf("lint: multiple %s modules found: %s and %s", moduleName,
  487. ctx.ModuleSubDir(m), ctx.ModuleSubDir(res))
  488. }
  489. }
  490. })
  491. return res
  492. }
  493. func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
  494. if ctx.Config().AlwaysUsePrebuiltSdks() {
  495. return
  496. }
  497. for _, sdk := range android.SortedKeys(allLintDatabasefiles) {
  498. files := allLintDatabasefiles[sdk]
  499. apiVersionsDb := findModuleOrErr(ctx, files.apiVersionsModule)
  500. if apiVersionsDb == nil {
  501. if !ctx.Config().AllowMissingDependencies() {
  502. ctx.Errorf("lint: missing module api_versions_public")
  503. }
  504. return
  505. }
  506. sdkAnnotations := findModuleOrErr(ctx, files.annotationsModule)
  507. if sdkAnnotations == nil {
  508. if !ctx.Config().AllowMissingDependencies() {
  509. ctx.Errorf("lint: missing module sdk-annotations.zip")
  510. }
  511. return
  512. }
  513. ctx.Build(pctx, android.BuildParams{
  514. Rule: android.CpIfChanged,
  515. Input: android.OutputFileForModule(ctx, sdkAnnotations, ""),
  516. Output: copiedLintDatabaseFilesPath(ctx, files.annotationCopiedName),
  517. })
  518. ctx.Build(pctx, android.BuildParams{
  519. Rule: android.CpIfChanged,
  520. Input: android.OutputFileForModule(ctx, apiVersionsDb, ".api_versions.xml"),
  521. Output: copiedLintDatabaseFilesPath(ctx, files.apiVersionsCopiedName),
  522. })
  523. }
  524. }
  525. func copiedLintDatabaseFilesPath(ctx android.PathContext, name string) android.WritablePath {
  526. return android.PathForOutput(ctx, "lint", name)
  527. }
  528. func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
  529. if ctx.Config().UnbundledBuild() {
  530. return
  531. }
  532. var outputs []*lintOutputs
  533. var dirs []string
  534. ctx.VisitAllModules(func(m android.Module) {
  535. if ctx.Config().KatiEnabled() && !m.ExportedToMake() {
  536. return
  537. }
  538. if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() {
  539. apexInfo := ctx.ModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
  540. if apexInfo.IsForPlatform() {
  541. // There are stray platform variants of modules in apexes that are not available for
  542. // the platform, and they sometimes can't be built. Don't depend on them.
  543. return
  544. }
  545. }
  546. if l, ok := m.(lintOutputsIntf); ok {
  547. outputs = append(outputs, l.lintOutputs())
  548. }
  549. })
  550. dirs = android.SortedUniqueStrings(dirs)
  551. zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
  552. var paths android.Paths
  553. for _, output := range outputs {
  554. if p := get(output); p != nil {
  555. paths = append(paths, p)
  556. }
  557. }
  558. lintZip(ctx, paths, outputPath)
  559. }
  560. l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
  561. zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
  562. l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
  563. zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
  564. l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
  565. zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
  566. l.referenceBaselineZip = android.PathForOutput(ctx, "lint-report-reference-baselines.zip")
  567. zip(l.referenceBaselineZip, func(l *lintOutputs) android.Path { return l.referenceBaseline })
  568. ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
  569. }
  570. func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
  571. if !ctx.Config().UnbundledBuild() {
  572. ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip, l.referenceBaselineZip)
  573. }
  574. }
  575. var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
  576. func init() {
  577. android.RegisterParallelSingletonType("lint",
  578. func() android.Singleton { return &lintSingleton{} })
  579. registerLintBuildComponents(android.InitRegistrationContext)
  580. }
  581. func registerLintBuildComponents(ctx android.RegistrationContext) {
  582. ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
  583. ctx.TopDown("enforce_strict_updatability_linting", enforceStrictUpdatabilityLintingMutator).Parallel()
  584. })
  585. }
  586. func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
  587. paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
  588. sort.Slice(paths, func(i, j int) bool {
  589. return paths[i].String() < paths[j].String()
  590. })
  591. rule := android.NewRuleBuilder(pctx, ctx)
  592. rule.Command().BuiltTool("soong_zip").
  593. FlagWithOutput("-o ", outputPath).
  594. FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
  595. FlagWithRspFileInputList("-r ", outputPath.ReplaceExtension(ctx, "rsp"), paths)
  596. rule.Build(outputPath.Base(), outputPath.Base())
  597. }
  598. // Enforce the strict updatability linting to all applicable transitive dependencies.
  599. func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) {
  600. m := ctx.Module()
  601. if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() {
  602. ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) {
  603. if a, ok := d.(LintDepSetsIntf); ok {
  604. a.SetStrictUpdatabilityLinting(true)
  605. }
  606. })
  607. }
  608. }