Adding SpotBugs to my project - android

I've been working on adding SpotBugs to the android project I'm currently working on. I managed to get it working but I'm not overly thrilled of the way it's set up. For now the configuration resides inside my app/build.gradle file, which makes the file less manageable.
I was wondering if there's an expert on SpotBugs/Gradle who knows a way to pull the configuration out into a separate file.
Here's my app/build.gradle (boilerplate removed):
buildscript {
repositories {
...
}
dependencies {
classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2'
classpath 'io.fabric.tools:gradle:1.25.4'
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
}
}
plugins {
id 'com.gladed.androidgitversion' version '0.4.3'
id "com.github.spotbugs" version "1.6.2"
}
...
apply plugin: 'com.github.spotbugs'
apply from: '../config/quality/quality.gradle'
apply from: '../app/jacoco.gradle'
apply from: '../app/ktlint.gradle'
apply from: '../app/androidgit.gradle'
...
spotbugs {
toolVersion = '3.1.3'
ignoreFailures = false
effort = "min"
// This selects what level of bugs to report: low means low priority issues will be reported
// (in addition to medium+high), which corresponds to warning about everything.
// TODO: boost this to low once low priority issues are fixed.
reportLevel = "medium"
excludeFilter = new File("$project.rootDir/config/quality/spotbugs/android-exclude-filter.xml")
}
task spotbugs(type: com.github.spotbugs.SpotBugsTask, dependsOn: 'assemble', group: 'verification') {
classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")
source = fileTree('src/main/java')
// Only one report format is supported. Html is easier to read, so let's use that
// (xml is the one that's enabled by default).
reports {
xml.enabled = false
html.enabled = true
}
classpath = files()
}
EDIT
Whenever I'm trying to separate SpotBugs from my app/build.gradle I run into the following error:
Could not get unknown property 'SpotBugsTask' for project ':app' of type org.gradle.api.Project.
Here's my gradle file:
apply plugin: 'com.github.spotbugs'
dependencies {
checkstyle 'com.puppycrawl.tools:checkstyle:8.11'
spotbugs "gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.2"
// spotbugs configurations.spotbugsPlugins.dependencies
// spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0'
}
def qualityConfigDir = "$project.rootDir/config/quality";
def reportsDir = "$project.buildDir/reports"
check.dependsOn 'checkstyle'
task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code style checks') {
configFile file("$qualityConfigDir/checkstyle/checkstyle-config.xml")
source 'src/main/java'
include '**/*.java'
exclude '**/model/**'
exclude '**/AppLogger.java'
reports {
xml.enabled = true
xml {
destination file("$reportsDir/checkstyle/checkstyle.xml")
}
}
classpath = files()
}
spotbugs {
toolVersion = '3.1.3'
ignoreFailures = false
effort = "min"
// This selects what level of bugs to report: low means low priority issues will be reported
// (in addition to medium+high), which corresponds to warning about everything.
// TODO: boost this to low once low priority issues are fixed.
reportLevel = "medium"
excludeFilter = new File("$project.rootDir/config/quality/spotbugs/android-exclude-filter.xml")
}
task spotbugs(type: SpotBugsTask, dependsOn: 'assemble', group: 'verification') {
classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")
source = fileTree('src/main/java')
// Only one report format is supported. Html is easier to read, so let's use that
// (xml is the one that's enabled by default).
reports {
xml.enabled = false
html.enabled = true
}
classpath = files()
}

Finally managed to find a solution.
I had to add the following to the section where I apply all the plugins in my app/build.gradle file:
project.extensions.extraProperties.set('SpotBugsTask', com.github.spotbugs.SpotBugsTask)
So it ended up looking like this:
buildscript {
repositories {
mavenCentral()
jcenter()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2'
classpath 'io.fabric.tools:gradle:1.25.4'
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
}
}
plugins {
id 'com.gladed.androidgitversion' version '0.4.3'
id "com.github.spotbugs" version "1.6.2"
}
// Workaround to be able to access SpotBugsTask from external gradle script.
// More info: https://discuss.gradle.org/t/buildscript-dependencies-in-external-script/23243
project.extensions.extraProperties.set('SpotBugsTask', com.github.spotbugs.SpotBugsTask)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.dokka-android'
apply plugin: 'io.fabric'
apply plugin: 'spoon'
apply from: '../app/checkstyle.gradle'
apply from: '../app/jacoco.gradle'
apply from: '../app/ktlint.gradle'
apply from: '../app/androidgit.gradle'
apply from: '../app/spotbugs.gradle'
android {
...
My spotbugs.gradle file:
dependencies {
spotbugs configurations.spotbugsPlugins.dependencies
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0'
}
def qualityConfigDir = "$project.rootDir/config/quality"
def reportsDir = "$project.buildDir/reports"
spotbugs {
toolVersion = "$spotbugs_version"
ignoreFailures = false
effort = "min"
// This selects what level of bugs to report: low means low priority issues will be reported
// (in addition to medium+high), which corresponds to warning about everything.
// TODO: boost this to low once low priority issues are fixed.
reportLevel = "medium"
excludeFilter = new File("$qualityConfigDir/config/quality/spotbugs/android-exclude-filter.xml")
}
tasks.register("spotbugs", SpotBugsTask) {
dependsOn 'assemble'
group = "verification"
classes = files("$projectDir.absolutePath/build/intermediates/app_classes/debug")
source = fileTree('src/main/java')
// Only one report format is supported. Html is easier to read, so let's use that
// (xml is the one that's enabled by default).
reports {
xml.enabled = true
xml {
destination file("$reportsDir/spotbugs/spotbugs.xml")
}
html.enabled = true
}
classpath = files()
}

For anyone stumbling across this thread, and not satisfied with the above answer (you should question anytime you see "this works" without a "because ..."), note that if you're using an external buildscript file like the OP, and trying to configure the tasks, the real problem is that the script plugin ClassLoader is isolated from the project buildscript ClassLoader, the java.lang.Class instances representing the type com.github.spotbugs.SpotBugsTask are different, thus thewithType call doesn't match anything.
See gradle-native#742 and gradle#1262 for details, and some solutions to make it work.

Related

Compiler sometimes can't find generated navigation files

This is a pretty bizarre one.
Occasionally, after some arbitrary change has been made to the nav graph XML, the build fails because it can't find two particular functions (nav directions) that should have been generated.
The error is like this:
main/java/com/project/upsell/UpsellFragment.kt: (99, 82): Unresolved reference: toMainFragment
main/java/com/project/upsell/UpsellVM: (35, 53): Unresolved reference: toSuccessFrag
I can "fix" it by renaming the functions, but the next time XML gets touched, the error recurs and the same "fix" is repeated. The same issue occurs in my environment, my teammate's environment and our Jenkins pipeline. Sometimes it builds fine locally but breaks in Jenkins.
The only clue I can offer is that it started happening shortly after bringing Compose into the project.
Some build info:
ext {
dagger_version = '2.28.3'
nav_version = '2.3.5'
fragment_version = '1.3.6'
coroutine_version = '1.5.0'
compose_version = '1.0.1'
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath 'com.google.gms:google-services:4.3.8'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
classpath "org.jetbrains.kotlin:kotlin-serialization:1.5.10"
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
classpath 'com.google.firebase:perf-plugin:1.4.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "kotlin-kapt"
apply plugin: "androidx.navigation.safeargs.kotlin"
apply plugin: 'kotlinx-serialization'
kotlinOptions {
useIR = true
freeCompilerArgs += ['-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi',
'-Xuse-experimental=kotlinx.coroutines.FlowPreview',
'-Xopt-in=androidx.paging.ExperimentalPagingApi',
'-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi',
'-Xopt-in=kotlin.ExperimentalStdlibApi']
}
composeOptions {
kotlinCompilerVersion = "1.5.10"
kotlinCompilerExtensionVersion '1.0.0-rc01'
}
buildFeatures {
dataBinding = true
compose = true
}
Any insight would be appreciated
Fist try Invalidate Cache and Restart.
If that doesn't work, Add package name in your manifest, if it's missing.
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.package"
...

Minimal working SpotBugs setup for Android Studio

How do I set up SpotBugs for Android?
I tried following the official documentation and that of the gradle plugin, but the setup for Android is incomplete and confusing, and didn't work.
I tried the following setup.
build.gradle (project):
buildscript {
repositories {
// ...
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
// ...
classpath "gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.4"
}
}
build.gradle (app):
//...
apply plugin: "com.github.spotbugs"
android {
// ...
sourceSets {
main {
java.srcDirs = ['src/main/java']
}
}
}
// ...
spotbugs {
toolVersion = "3.1.3"
ignoreFailures = true
reportsDir = file("$project.buildDir/findbugsReports")
effort = "max"
reportLevel = "high"
}
tasks.withType(com.github.spotbugs.SpotBugsTask) {
// What do I need to do here?
}
I tried running it with ./gradlew spotbugsMain, but the gradle task is missing.
Am I supposed to add the task manually? How do I do that?
Could you show me an example of a minimal working setup for an Android project?
I made some tests on my side and I manage to make it work like this :
1) Move the sourceSets declaration outside the android block. Leave it empty, it's just for the spotbugsMain task generation, it won't impact the global Android build.
android {
// ...
}
sourceSets {
main {
java.srcDirs = []
}
}
2) Keep your spotbugs block and configure the SpotBugsTask tasks like this :
tasks.withType(com.github.spotbugs.SpotBugsTask) {
classes = files("$projectDir.absolutePath/build/intermediates/classes/debug")
source = fileTree('src/main/java')
}
It will generate reports in app/build/findbugsReports
Important :
It only works with the ./gradlew build command, ./gradlew spotbugsMain won't work as the project must be built before
You can fix that adding an assemble dependency :
tasks.withType(com.github.spotbugs.SpotBugsTask) {
dependsOn 'assemble'
classes = files("$projectDir.absolutePath/build/intermediates/classes/debug")
source = fileTree('src/main/java')
}
Following on from ToYonos answer (9 October 2018); Use this for Android Studio 3.4 and above:
project/build.gradle
buildscript {
repositories {
google()
jcenter()
maven {
url 'https:// maven url 1'
}
maven {
url "https://plugins.gradle.org/m2/" // Add this, for SpotBugs
}
}
dependencies {
classpath '...'
// If you're using gradle 6.x, add this to use SpotBugs app version 4.0.2
classpath "gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.3.0"
// If you're using gradle 4.x or 5.x, add this to use SpotBugs app version 3.1.2
classpath "com.github.spotbugs:spotbugs-gradle-plugin:2.0.1"
}
}
project/app/build.gradle
apply plugin: 'com.android.application'
apply plugin: '...'
apply plugin: "com.github.spotbugs" // <- Add this
dependencies {
...
}
// This block is only needed for gradle 4/5 only.
// It's for SpotBugs to create a 'spotbugsMain' gradle task.
sourceSets {
main {
java.srcDirs = []
}
}
spotbugs {
ignoreFailures = true
reportsDir = file("$project.buildDir/SpotBugsReports")
effort = "max"
reportLevel = "high"
}
// Note: gradle 4/5 should use "com.github.spotbugs.SpotBugsTask"
tasks.withType(com.github.spotbugs.snom.SpotBugsTask) {
dependsOn 'assembleDebug'
classes = files("$project.buildDir/intermediates/javac") // Important to use this path
excludeFilter = file("$project/spot-bugs-exclude.xml") // Optional - Explained below
source = fileTree('src/main/java') // Only needed on gradle 4/5
reports {
// Enable HTML report only
html.enabled = true
xml.enabled = false
}
}
You can generate a report for your debug build by running the gradle task:
For gradle 6.x: ./gradlew spotbugsDebug
For gradle 5 or 4: ./gradlew spotbugsMain
It's important to use classes = files("$project.buildDir/intermediates/javac") , otherwise you'll get an error "java.io.IOException: No files to analyze could be opened" -- see Findbugs fails with "java.io.IOException: No files to analyze could be opened"
You'll also need to enable the HTML report and disable XML report, to see a human-readable format.
ignoreFailures = true is optional. When SpotBugs detects a code warning, by default it will end with "BUILD FAILED" + a report file. Setting ignoreFailures = true means the gradle task will end with "BUILD SUCCESSFUL" + a report file.
To exclude some generated classes from the analysis, setup an excludeFilter. For a sample exclude file, check here or here (same as findbugs-exclude.xml)
More information and tutorial here: https://mikedemaso.com/tech/2020-06-10-spotbugs-gradle-plugin-android/

Firebase unresolved supertype with Kotlin on Android

I'm experiencing a problem in my project that uses Firebase core and messaging v11.4.2.
The gradle sync works perfectly but then I get this error when compiling:
e: Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class com.google.android.gms.internal.zzctr, unresolved supertypes: com.google.android.gms.internal.zzee
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ‘:app:compileDebugKotlin’.
Compilation error. See log for more details
I've tried both with Kotlin version 1.1.51 and 1.2.0-beta-88; Gradle plugins v2.3.3 and 3.0.0
Any help is welcomed, thanks a lot!
This is how I've configured the project:
app build.gradle
// tried adding and removing the -kapt and -android-extensions. Didn't help.
apply plugin: 'com.android.application'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'io.fabric'
debug {
/// added this to be sure the class was not being left out
minifyEnabled false
shrinkResources false
useProguard false
}
...
dependencies {
// Firebase Core
implementation "com.google.firebase:firebase-core:${rootProject.ext.firebaseVersion}"
// Firebase Cloud Messaging
implementation "com.google.firebase:firebase-messaging:${rootProject.ext.firebaseVersion}"
}
...
// Keep this as the last line or the build will fail
apply plugin: 'com.google.gms.google-services'
* project build.gradle *
buildscript {
// App
ext.compileSdkVersion = 26
ext.minSdkVersion = 18
ext.buildToolsVersion = '26.0.2'
ext.targetSdkVersion = 26
// Kotlin beta, stable version doesn't compile either
ext.kotlin_version = '1.2.0-beta-88'
// Android
ext.androidSupportVersion = '26.1.0'
//TODO: implement LifecycleOwner interface from Architecture Components.
ext.lifecycleVersion = '1.0.0-beta2'
// Architecture, RxJava, Injection
ext.daggerVersion = '2.11'
ext.butterKnifeVersion = '8.8.1'
ext.rxJavaVersion = '2.1.0'
ext.rxAndroidVersion = '2.0.1'
// Google/Firebase Cloud Message
ext.firebaseVersion = '11.4.2'
// Libraries shared between modules (TODO)
}
repositories {
maven { url 'https://maven.google.com' } // Google Maven Repository
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2'} // Kotlin beta
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:3.1.1' // google-services plugin
//// tried this too: classpath 'com.google.firebase:firebase-plugins:1.1.1'
}
allprojects {
repositories {
jcenter()
mavenLocal()
maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2'}
maven { url "https://jitpack.io" }
maven { url 'https://maven.google.com' }
mavenCentral()
flatDir {
dirs 'libs'
}
}
Turns out the problem was an incompatible version of the Google Wallet library, being implemented by another dependency of the project and conflicting with the one imported by the com.google.gms.google-services plugin.
For some reason gradle didn't state this problem and I had to go through the code until I found the place where Wallet was trying to access a class that was successfully importing but at the same time it couldn't be found.
Simply by updating the version in the other module everything was fixed :)

Findbugs android gradle plugin

I have an android project. I want to introduce findbugs in my project as a gradle plugin. I tried to edit the project's build.gradle as below.
buildscript {
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: "java"
apply plugin: "findbugs"
findbugs {
toolVersion = "2.0.1"
sourceSets = [sourceSets.main]
ignoreFailures = false
reportsDir = file("$project.buildDir/findbugsReports")
effort = "max"
reportLevel = "high"
includeFilter = file("$rootProject.projectDir/config/findbugs/includeFilter.xml")
excludeFilter = file("$rootProject.projectDir/config/findbugs/excludeFilter.xml")
}
Is this plugin correct?
Does anything neeed to be added or removed?
Now what should I do to get the results of this findbugs check?
What gradle command should I use?
Just place this in your modules build.gradle.
apply plugin: 'findbugs'
task customFindbugs(type: FindBugs) {
ignoreFailures = false
effort = "max"
reportLevel = "low"
classes = files("$project.buildDir/intermediates/classes")
// Use this only if you want exclude some errors
excludeFilter = file("$rootProject.rootDir/config/findbugs/exclude.xml")
source = fileTree('src/main/java/')
classpath = files()
reports {
xml.enabled = false
xml.withMessages = true
html.enabled = !xml.isEnabled()
xml.destination "$project.buildDir/outputs/findbugs/findbugs-output.xml"
html.destination "$project.buildDir/outputs/findbugs/findbugs-output.html"
}
}
build.dependsOn customFindbugs
Then after changing directory to your project path from command line, use
./gradlew build
The error report will be in $project.buildDir/outputs/findbugs/findbugs-output.html
I modified a little bit Nevin Raj Victor's answer.
This version generates a findbug task for each build variant, and (more importantly) it correctly creates dependencies on their respective compilation tasks. Indeed, findbugs requires the code to be compiled before it can be analyzed.
// findbug tasks for each variant
apply plugin: 'findbugs'
android.applicationVariants.all { variant ->
task("findbugs${variant.name.capitalize()}", type: FindBugs) {
description "Analyze ${variant.name} code with the findbugs tool"
group "Verification"
ignoreFailures = true
effort = "default"
reportLevel = "medium"
classes = files("$project.buildDir/intermediates/classes/${variant.dirName}")
excludeFilter = file("$rootProject.rootDir/findbugs/findbugs-filter.xml")
source = variant.javaCompile.source
classpath = variant.javaCompile.classpath
reports {
// Only one of HTML or XML can be turned on at the same time
html.enabled = true
xml.enabled = !html.enabled
xml.withMessages = true
html.destination = "$project.buildDir/outputs/findbugs/findbugs-${variant.name}-output.html"
xml.destination = "$project.buildDir/outputs/findbugs/findbugs-${variant.name}-output.xml"
}
dependsOn "compile${variant.name.capitalize()}JavaWithJavac"
}
}
After this, you can run
./gradlew findbugsDebug
./gradlew findbugsRelease
Or other findbugs tasks on different variants, depending on your configuration.
Please take a look at this project https://github.com/Piasy/AndroidCodeQualityConfig.
This project including lint, pmd, findbugs, checkstyle, jacoco code coverage.And support project with submodules.
I see some problems with your configuration:
instead of 2.0.1 version use latest 3.0.1
set reportLevel to low instead of high to report all the violations
for the first analysis you don't need to configure any includeFilter or excludeFilter - these are only whitelist and blacklists of checks if you need some customization
To run analysis just invoke gradle findbugsMain. Results should be visible in the output.

Android Scala and Gradle

I am new to Gradle. Is there some example how to configure properly gradle-android-plugin for scala classes.
this is what I have now.
buildscript {
repositories { mavenCentral() }
dependencies { classpath 'org.gradle.api.plugins:gradle-android-plugin:1.2.1' }
}
apply plugin: 'android'
apply plugin: 'eclipse'
apply plugin: 'scala'
sourceCompatibility = 1.6
version = "1.0.0"
repositories { mavenCentral() }
dependencies {
compile files('/home/pcu/workspace/workspace-android/emoo/libs/android-support-v4.jar')
compile 'org.scala-lang:scala-library:2.9.1'
scalaTools 'org.scala-lang:scala-compiler:2.9.1'
scalaTools 'org.scala-lang:scala-library:2.9.1'
}
task configureDebug << { jar.classifier = "debug" }
task configureRelease << { proguard.enabled = true }
but compilation fails. Scala class is not compiled.
It is actually much simpler with the right plugin:
https://github.com/saturday06/gradle-android-scala-plugin
This worked perfectly fine for me.
You need only about 3 more lines in your gradle configuration and potentially proguard to reduce the apk size.
The setup is well documented on the github page. Everything beyond steps 1-3 is optional.

Categories

Resources