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
diff --git a/rust/Android.bp b/rust/Android.bp
index b06ea8e..d56de87 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -9,24 +9,26 @@
],
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",
],
diff --git a/rust/builder.go b/rust/builder.go
index 7dbb59d..b191323 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -39,6 +39,18 @@
},
"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 @@
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 @@
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(),
diff --git a/rust/clippy.go b/rust/clippy.go
new file mode 100644
index 0000000..e1f567d
--- /dev/null
+++ b/rust/clippy.go
@@ -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
+}
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
new file mode 100644
index 0000000..af5cd17
--- /dev/null
+++ b/rust/clippy_test.go
@@ -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")
+ }
+}
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 5026da3..1d30f82 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -9,6 +9,7 @@
"arm_device.go",
"arm64_device.go",
"global.go",
+ "clippy.go",
"toolchain.go",
"allowed_list.go",
"x86_darwin_host.go",
diff --git a/rust/config/clippy.go b/rust/config/clippy.go
new file mode 100644
index 0000000..c199ff2
--- /dev/null
+++ b/rust/config/clippy.go
@@ -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
+}
diff --git a/rust/rust.go b/rust/rust.go
index 7b82b1f..7f82873 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -49,8 +49,10 @@
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 @@
compiler compiler
coverage *coverage
+ clippy *clippy
cachedToolchain config.Toolchain
subAndroidMkOnce map[subAndroidMkProvider]bool
outputFile android.OptionalPath
@@ -306,6 +309,7 @@
&PrebuiltProperties{},
&TestProperties{},
&cc.CoverageProperties{},
+ &ClippyProperties{},
)
android.InitDefaultsModule(module)
@@ -456,6 +460,9 @@
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 newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
module := newBaseModule(hod, multilib)
module.coverage = &coverage{}
+ module.clippy = &clippy{}
return module
}
@@ -576,6 +584,9 @@
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)