Add clippy-driver build rule

Depending on the location of the repository (e.g. external/, vendor/), a
different set of lints will be enabled. Add the clippy property to the
rust_* modules. This property can be used to overwrite the default
behaviour.

Test: m checkbuild
Bug: 157238651
Change-Id: Ife0f723ef4a74abb102597f8486a7b9f30e7d351
This commit is contained in:
Thiébaud Weksteen 2020-06-22 13:28:02 +02:00
parent a5d1fab176
commit 92f703b084
7 changed files with 225 additions and 8 deletions

View File

@ -9,24 +9,26 @@ bootstrap_go_package {
],
srcs: [
"androidmk.go",
"compiler.go",
"coverage.go",
"binary.go",
"builder.go",
"clippy.go",
"compiler.go",
"coverage.go",
"library.go",
"prebuilt.go",
"proc_macro.go",
"project_json.go",
"project_json.go",
"rust.go",
"test.go",
"testing.go",
],
testSrcs: [
"binary_test.go",
"clippy_test.go",
"compiler_test.go",
"coverage_test.go",
"library_test.go",
"project_json_test.go",
"project_json_test.go",
"rust_test.go",
"test_test.go",
],

View File

@ -39,6 +39,18 @@ var (
},
"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd")
_ = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
clippyDriver = pctx.AndroidStaticRule("clippy",
blueprint.RuleParams{
Command: "$clippyCmd " +
// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
// Use the metadata output as it has the smallest footprint.
"--emit metadata -o $out $in ${libFlags} " +
"$clippyFlags $rustcFlags",
CommandDeps: []string{"$clippyCmd"},
},
"rustcFlags", "libFlags", "clippyFlags")
zip = pctx.AndroidStaticRule("zip",
blueprint.RuleParams{
Command: "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
@ -125,10 +137,14 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path
rustcFlags = append(rustcFlags, "--target="+targetTriple)
linkFlags = append(linkFlags, "-target "+targetTriple)
}
// TODO once we have static libraries in the host prebuilt .bp, this
// should be unconditionally added.
if !(ctx.Host() && ctx.TargetPrimary()) {
// If we're not targeting the host primary arch, do not use an implicit sysroot
// TODO(b/159718669): Once we have defined static libraries in the host
// prebuilts Blueprint file, sysroot should be unconditionally sourced
// from /dev/null. Explicitly set sysroot to avoid clippy-driver to
// internally call rustc.
if ctx.Host() && ctx.TargetPrimary() {
rustcFlags = append(rustcFlags, "--sysroot=${config.RustPath}")
} else {
// If we're not targeting the host primary arch, do not use a sysroot.
rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
}
// Collect linker flags
@ -179,6 +195,25 @@ func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps Path
output.coverageFile = gcnoFile
}
if flags.Clippy {
clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
ctx.Build(pctx, android.BuildParams{
Rule: clippyDriver,
Description: "clippy " + main.Rel(),
Output: clippyFile,
ImplicitOutputs: nil,
Inputs: inputs,
Implicits: implicits,
Args: map[string]string{
"rustcFlags": strings.Join(rustcFlags, " "),
"libFlags": strings.Join(libFlags, " "),
"clippyFlags": strings.Join(flags.ClippyFlags, " "),
},
})
// Declare the clippy build as an implicit dependency of the original crate.
implicits = append(implicits, clippyFile)
}
ctx.Build(pctx, android.BuildParams{
Rule: rustc,
Description: "rustc " + main.Rel(),

42
rust/clippy.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rust
import (
"android/soong/rust/config"
)
type ClippyProperties struct {
// whether to run clippy.
Clippy *bool
}
type clippy struct {
Properties ClippyProperties
}
func (c *clippy) props() []interface{} {
return []interface{}{&c.Properties}
}
func (c *clippy) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
if c.Properties.Clippy != nil && !*c.Properties.Clippy {
return flags, deps
}
enabled, lints := config.ClippyLintsForDir(ctx.ModuleDir())
flags.Clippy = enabled
flags.ClippyFlags = append(flags.ClippyFlags, lints)
return flags, deps
}

46
rust/clippy_test.go Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rust
import (
"testing"
)
func TestClippy(t *testing.T) {
ctx := testRust(t, `
rust_library {
name: "libfoo",
srcs: ["foo.rs"],
crate_name: "foo",
}
rust_library {
name: "libfoobar",
srcs: ["foo.rs"],
crate_name: "foobar",
clippy: false,
}`)
ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Output("libfoo.so")
fooClippy := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").MaybeRule("clippy")
if fooClippy.Rule.String() != "android/soong/rust.clippy" {
t.Errorf("Clippy output (default) for libfoo was not generated: %+v", fooClippy)
}
ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_shared").Output("libfoobar.so")
foobarClippy := ctx.ModuleForTests("libfoobar", "android_arm64_armv8-a_shared").MaybeRule("clippy")
if foobarClippy.Rule != nil {
t.Errorf("Clippy output for libfoobar is not empty")
}
}

View File

@ -9,6 +9,7 @@ bootstrap_go_package {
"arm_device.go",
"arm64_device.go",
"global.go",
"clippy.go",
"toolchain.go",
"allowed_list.go",
"x86_darwin_host.go",

80
rust/config/clippy.go Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"strings"
"android/soong/android"
)
var (
defaultLints = []string{
"-D missing-docs",
"-D clippy::missing-safety-doc",
}
defaultVendorLints = []string{
"",
}
)
func init() {
// Default Rust lints. These apply to all Google-authored modules.
pctx.VariableFunc("ClippyDefaultLints", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("CLIPPY_DEFAULT_LINTS"); override != "" {
return override
}
return strings.Join(defaultLints, " ")
})
// Rust lints that only applies to external code.
pctx.VariableFunc("ClippyVendorLints", func(ctx android.PackageVarContext) string {
if override := ctx.Config().Getenv("CLIPPY_VENDOR_LINTS"); override != "" {
return override
}
return strings.Join(defaultVendorLints, " ")
})
}
type PathBasedClippyConfig struct {
PathPrefix string
Enabled bool
ClippyConfig string
}
const clippyNone = ""
const clippyDefault = "${config.ClippyDefaultLints}"
const clippyVendor = "${config.ClippyVendorLints}"
// This is a map of local path prefixes to a boolean indicating if the lint
// rule should be generated and if so, the set of lints to use. The first entry
// matching will be used. If no entry is matching, clippyDefault will be used.
var DefaultLocalTidyChecks = []PathBasedClippyConfig{
{"external/", false, clippyNone},
{"hardware/", true, clippyVendor},
{"prebuilts/", false, clippyNone},
{"vendor/google", true, clippyDefault},
{"vendor/", true, clippyVendor},
}
// ClippyLintsForDir returns the Clippy lints to be used for a repository.
func ClippyLintsForDir(dir string) (bool, string) {
for _, pathCheck := range DefaultLocalTidyChecks {
if strings.HasPrefix(dir, pathCheck.PathPrefix) {
return pathCheck.Enabled, pathCheck.ClippyConfig
}
}
return true, clippyDefault
}

View File

@ -49,8 +49,10 @@ type Flags struct {
GlobalLinkFlags []string // Flags that apply globally to linker
RustFlags []string // Flags that apply to rust
LinkFlags []string // Flags that apply to linker
ClippyFlags []string // Flags that apply to clippy-driver, during the linting
Toolchain config.Toolchain
Coverage bool
Clippy bool
}
type BaseProperties struct {
@ -75,6 +77,7 @@ type Module struct {
compiler compiler
coverage *coverage
clippy *clippy
cachedToolchain config.Toolchain
subAndroidMkOnce map[subAndroidMkProvider]bool
outputFile android.OptionalPath
@ -306,6 +309,7 @@ func DefaultsFactory(props ...interface{}) android.Module {
&PrebuiltProperties{},
&TestProperties{},
&cc.CoverageProperties{},
&ClippyProperties{},
)
android.InitDefaultsModule(module)
@ -456,6 +460,9 @@ func (mod *Module) Init() android.Module {
if mod.coverage != nil {
mod.AddProperties(mod.coverage.props()...)
}
if mod.clippy != nil {
mod.AddProperties(mod.clippy.props()...)
}
android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
@ -487,6 +494,7 @@ func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib)
func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
module := newBaseModule(hod, multilib)
module.coverage = &coverage{}
module.clippy = &clippy{}
return module
}
@ -576,6 +584,9 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
if mod.coverage != nil {
flags, deps = mod.coverage.flags(ctx, flags, deps)
}
if mod.clippy != nil {
flags, deps = mod.clippy.flags(ctx, flags, deps)
}
if mod.compiler != nil {
outputFile := mod.compiler.compile(ctx, flags, deps)