Support remoting lint commands with RBE

Bug: 181681346
Bug: 181912787
Test: m USE_RBE=true RBE_LINT=true lint-check
Change-Id: I10596c40dc5e29075ba0cab51ea9a98cc58b3188
This commit is contained in:
Colin Cross 2021-03-04 10:44:12 -08:00
parent 5c113d13eb
commit 31972dc487
2 changed files with 132 additions and 33 deletions

View File

@ -22,6 +22,8 @@ import (
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
"android/soong/android" "android/soong/android"
"android/soong/java/config"
"android/soong/remoteexec"
) )
type LintProperties struct { type LintProperties struct {
@ -172,8 +174,38 @@ func (l *linter) deps(ctx android.BottomUpMutatorContext) {
extraLintCheckTag, extraCheckModules...) extraLintCheckTag, extraCheckModules...)
} }
func (l *linter) writeLintProjectXML(ctx android.ModuleContext, type lintPaths struct {
rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) { projectXML android.WritablePath
configXML android.WritablePath
cacheDir android.WritablePath
homeDir android.WritablePath
srcjarDir android.WritablePath
deps android.Paths
remoteInputs android.Paths
remoteRSPInputs android.Paths
}
func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.RuleBuilder) lintPaths {
var deps android.Paths
var remoteInputs android.Paths
var remoteRSPInputs android.Paths
// Paths passed to trackInputDependency will be added as dependencies of the rule that runs
// lint and passed as inputs to the remote execution proxy.
trackInputDependency := func(paths ...android.Path) {
deps = append(deps, paths...)
remoteInputs = append(remoteInputs, paths...)
}
// Paths passed to trackRSPDependency will be added as dependencies of the rule that runs
// lint, but the RSP file will be used by the remote execution proxy to find the files so that
// it doesn't overflow command line limits.
trackRSPDependency := func(paths android.Paths, rsp android.Path) {
deps = append(deps, paths...)
remoteRSPInputs = append(remoteRSPInputs, rsp)
}
var resourcesList android.WritablePath var resourcesList android.WritablePath
if len(l.resources) > 0 { if len(l.resources) > 0 {
@ -184,17 +216,27 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
resListRule := android.NewRuleBuilder(pctx, ctx) resListRule := android.NewRuleBuilder(pctx, ctx)
resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList) resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
resListRule.Build("lint_resources_list", "lint resources list") resListRule.Build("lint_resources_list", "lint resources list")
deps = append(deps, l.resources...) trackRSPDependency(l.resources, resourcesList)
} }
projectXMLPath = android.PathForModuleOut(ctx, "lint", "project.xml") projectXMLPath := android.PathForModuleOut(ctx, "lint", "project.xml")
// Lint looks for a lint.xml file next to the project.xml file, give it one. // Lint looks for a lint.xml file next to the project.xml file, give it one.
configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml") configXMLPath := android.PathForModuleOut(ctx, "lint", "lint.xml")
cacheDir = android.PathForModuleOut(ctx, "lint", "cache") cacheDir := android.PathForModuleOut(ctx, "lint", "cache")
homeDir = android.PathForModuleOut(ctx, "lint", "home") homeDir := android.PathForModuleOut(ctx, "lint", "home")
srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars") srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars) srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
// TODO(ccross): this is a little fishy. The files extracted from the srcjars are referenced
// by the project.xml and used by the later lint rule, but the lint rule depends on the srcjars,
// not the extracted files.
trackRSPDependency(l.srcJars, srcJarList)
// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
// lint separately.
srcsList := android.PathForModuleOut(ctx, "lint", "srcs.list")
rule.Command().Text("cp").FlagWithRspFileInputList("", l.srcs).Output(srcsList)
trackRSPDependency(l.srcs, srcsList)
cmd := rule.Command(). cmd := rule.Command().
BuiltTool("lint-project-xml"). BuiltTool("lint-project-xml").
@ -209,38 +251,39 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
cmd.Flag("--test") cmd.Flag("--test")
} }
if l.manifest != nil { if l.manifest != nil {
deps = append(deps, l.manifest)
cmd.FlagWithArg("--manifest ", l.manifest.String()) cmd.FlagWithArg("--manifest ", l.manifest.String())
trackInputDependency(l.manifest)
} }
if l.mergedManifest != nil { if l.mergedManifest != nil {
deps = append(deps, l.mergedManifest)
cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String()) cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
trackInputDependency(l.mergedManifest)
} }
// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to cmd.FlagWithInput("--srcs ", srcsList)
// lint separately.
cmd.FlagWithRspFileInputList("--srcs ", l.srcs)
deps = append(deps, l.srcs...)
cmd.FlagWithInput("--generated_srcs ", srcJarList) cmd.FlagWithInput("--generated_srcs ", srcJarList)
deps = append(deps, l.srcJars...)
if resourcesList != nil { if resourcesList != nil {
cmd.FlagWithInput("--resources ", resourcesList) cmd.FlagWithInput("--resources ", resourcesList)
} }
if l.classes != nil { if l.classes != nil {
deps = append(deps, l.classes)
cmd.FlagWithArg("--classes ", l.classes.String()) cmd.FlagWithArg("--classes ", l.classes.String())
trackInputDependency(l.classes)
} }
cmd.FlagForEachArg("--classpath ", l.classpath.Strings()) cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
deps = append(deps, l.classpath...) trackInputDependency(l.classpath...)
cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings()) cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
deps = append(deps, l.extraLintCheckJars...) trackInputDependency(l.extraLintCheckJars...)
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
// TODO(b/181912787): remove these and use "." instead.
cmd.FlagWithArg("--root_dir ", "/b/f/w")
} else {
cmd.FlagWithArg("--root_dir ", "$PWD") cmd.FlagWithArg("--root_dir ", "$PWD")
}
// The cache tag in project.xml is relative to the root dir, or the project.xml file if // The cache tag in project.xml is relative to the root dir, or the project.xml file if
// the root dir is not set. // the root dir is not set.
@ -254,7 +297,18 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
return projectXMLPath, configXMLPath, cacheDir, homeDir, deps return lintPaths{
projectXML: projectXMLPath,
configXML: configXMLPath,
cacheDir: cacheDir,
homeDir: homeDir,
deps: deps,
remoteInputs: remoteInputs,
remoteRSPInputs: remoteRSPInputs,
}
} }
// generateManifest adds a command to the rule to write a simple manifest that contains the // generateManifest adds a command to the rule to write a simple manifest that contains the
@ -297,7 +351,7 @@ func (l *linter) lint(ctx android.ModuleContext) {
l.manifest = manifest l.manifest = manifest
} }
projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule) lintPaths := l.writeLintProjectXML(ctx, rule)
html := android.PathForModuleOut(ctx, "lint-report.html") html := android.PathForModuleOut(ctx, "lint-report.html")
text := android.PathForModuleOut(ctx, "lint-report.txt") text := android.PathForModuleOut(ctx, "lint-report.txt")
@ -311,8 +365,8 @@ func (l *linter) lint(ctx android.ModuleContext) {
} }
}) })
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String()) rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
rule.Command().Text("rm -f").Output(html).Output(text).Output(xml) rule.Command().Text("rm -f").Output(html).Output(text).Output(xml)
var annotationsZipPath, apiVersionsXMLPath android.Path var annotationsZipPath, apiVersionsXMLPath android.Path
@ -324,16 +378,52 @@ func (l *linter) lint(ctx android.ModuleContext) {
apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx) apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
} }
cmd := rule.Command(). cmd := rule.Command()
Text("(").
Flag("JAVA_OPTS=-Xmx3072m"). cmd.Flag("JAVA_OPTS=-Xmx3072m").
FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()). FlagWithArg("ANDROID_SDK_HOME=", lintPaths.homeDir.String()).
FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath). FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath). FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath)
BuiltTool("lint").
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_LINT") {
pool := ctx.Config().GetenvWithDefault("RBE_LINT_POOL", "java16")
// TODO(b/181912787): this should be local fallback once the hack that passes /b/f/w in project.xml
// is removed.
execStrategy := ctx.Config().GetenvWithDefault("RBE_LINT_EXEC_STRATEGY", remoteexec.RemoteExecStrategy)
labels := map[string]string{"type": "tool", "name": "lint"}
rule.Remoteable(android.RemoteRuleSupports{RBE: true})
remoteInputs := lintPaths.remoteInputs
remoteInputs = append(remoteInputs,
lintPaths.projectXML,
lintPaths.configXML,
lintPaths.homeDir,
lintPaths.cacheDir,
ctx.Config().HostJavaToolPath(ctx, "lint.jar"),
annotationsZipPath,
apiVersionsXMLPath,
)
cmd.Text((&remoteexec.REParams{
Labels: labels,
ExecStrategy: execStrategy,
ToolchainInputs: []string{config.JavaCmd(ctx).String()},
Inputs: remoteInputs.Strings(),
OutputFiles: android.Paths{html, text, xml}.Strings(),
RSPFile: strings.Join(lintPaths.remoteRSPInputs.Strings(), ","),
EnvironmentVariables: []string{
"JAVA_OPTS",
"ANDROID_SDK_HOME",
"SDK_ANNOTATIONS",
"LINT_OPTS",
},
Platform: map[string]string{remoteexec.PoolKey: pool},
}).NoVarTemplate(ctx.Config()))
}
cmd.BuiltTool("lint").
Flag("--quiet"). Flag("--quiet").
FlagWithInput("--project ", projectXML). FlagWithInput("--project ", lintPaths.projectXML).
FlagWithInput("--config ", lintXML). FlagWithInput("--config ", lintPaths.configXML).
FlagWithOutput("--html ", html). FlagWithOutput("--html ", html).
FlagWithOutput("--text ", text). FlagWithOutput("--text ", text).
FlagWithOutput("--xml ", xml). FlagWithOutput("--xml ", xml).
@ -343,7 +433,9 @@ func (l *linter) lint(ctx android.ModuleContext) {
FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())). FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
Flag("--exitcode"). Flag("--exitcode").
Flags(l.properties.Lint.Flags). Flags(l.properties.Lint.Flags).
Implicits(deps) Implicit(annotationsZipPath).
Implicit(apiVersionsXMLPath).
Implicits(lintPaths.deps)
if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" { if checkOnly := ctx.Config().Getenv("ANDROID_LINT_CHECK"); checkOnly != "" {
cmd.FlagWithArg("--check ", checkOnly) cmd.FlagWithArg("--check ", checkOnly)
@ -362,9 +454,9 @@ func (l *linter) lint(ctx android.ModuleContext) {
} }
} }
cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)").Text(")") cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String()) rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
rule.Build("lint", "lint") rule.Build("lint", "lint")

View File

@ -81,6 +81,9 @@ type REParams struct {
// ToolchainInputs is a list of paths or ninja variables pointing to the location of // ToolchainInputs is a list of paths or ninja variables pointing to the location of
// toolchain binaries used by the rule. // toolchain binaries used by the rule.
ToolchainInputs []string ToolchainInputs []string
// EnvironmentVariables is a list of environment variables whose values should be passed through
// to the remote execution.
EnvironmentVariables []string
} }
func init() { func init() {
@ -162,6 +165,10 @@ func (r *REParams) wrapperArgs() string {
args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",") args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
} }
if len(r.EnvironmentVariables) > 0 {
args += " --env_var_allowlist=" + strings.Join(r.EnvironmentVariables, ",")
}
return args + " -- " return args + " -- "
} }