Automatic Android support without specifying target "src/*/java/**/*.java"
Does this support Android?
EDIT BY @nedtwigg, TOP COMMENT FOR VISIBILITY
On Android, your config file needs to look like this:
spotless {
java {
target "src/*/java/**/*.java" // this line is needed
}
}
We'd love a PR which fixes this, see this comment and this comment for what needs to be done.
It should support Android. If it doesn't work with Android projects, I'd consider it a bug, in which case please let us know. :)
@jbduncan Why close this so fast?
I just tried this plugin on my project here: https://github.com/jaredsburrows/android-gif-example and it does seem to work.
Based on your README.md, are you expecting gradlew build? For Android, it is best for it to run on or after the assemble task.
This does not work:
spotless {
java {
target "**/*.java"
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
Even trying misc, does not work:
spotless {
format "misc", {
target "**/*.java"
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
@jbduncan Why close this so fast?
Oh, sorry! I thought that was the end, as I assumed you just had a question out of curiosity. My bad.
I just tried this plugin on my project here: https://github.com/jaredsburrows/android-gif-example and it does seem to work.
Glad to hear. :+1:
Based on your README.md, are you excepting gradlew build? For Android, it is best for it to run on or after the assemble task.
By "excepting", did you mean "expecting"?
Yes, that's one of 3 ways one can use Spotless for non-Android Java projects:
-
gradlew build -
gradlew check -
gradlew spotlessCheck
The reason all 3 of these tasks work (on non-Android Java builds at least) is because the build task implicitly depends on and runs check, which in turn runs spotlessCheck when Spotless is registered as a plugin.
From your example, I can't tell what about it doesn't work or why. Can you provide an minimal, verifiable, and complete example for us to try out?
@jbduncan I gave you an example to try out already in the previous comment.
-
git clone https://github.com/jaredsburrows/android-gif-example - Apply your plugin to
build.gradle(https://plugins.gradle.org/plugin/com.diffplug.gradle.spotless) -
gradlew spotlessCheck:
$ gradlew spotlessCheck
NDK is missing a "platforms" directory.
If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /Users/noname/android-sdk/ndk-bundle.
If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning.
:spotlessCheck UP-TO-DATE
BUILD SUCCESSFUL
Total time: 5.84 secs
Nothing happens. I have even tried the following:
This does not work:
spotless {
java {
target "**/*.java"
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
Even trying misc, does not work:
spotless {
format "misc", {
target "**/*.java"
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
This worked for me now: gradlew spotlessApply.
For Android, you have to specify a target: target "**/*.java"
The java/java-library plugin expects sourceSets and the com.android.application plugin expects android.sourceSets. We have to manually specify the target sources.
spotless {
java {
target "**/*.java"
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
@jaredsburrows Glad to hear to that spotlessApply works! Does spotlessCheck work on your end too?
It's annoying that you have to manually specify the target as target "**/*.java". I'm just in the middle of installing Android Studio so I have the Android SDK up and running to be able to execute the Gradle buildscript and see for myself what's wrong.
@nedtwigg I'm not terribly familiar with Spotless's internals yet, so do you have any thoughts on what we could do allow Spotless to support Android?
The android plugin must not be friends with this code.
@jbduncan Yes it does. You just have to specify apply.
@nedtwigg Android plugin/library has it's own source sets in it's DSL: android.sourceSets where the Java plugin has sourceSets.
@jaredsburrows Can you clarify for me what you mean when you say "You just have to specify apply"?
Just to make sure we're on the same page, I want to clarify that spotlessCheck and spotlessApply are independent tasks (in theory), where spotlessApply formats code (useful for a general software development flow), and spotlessCheck essentially throws an error if code is not formatted properly (very useful for enforcing a code standard on CI servers and such).
@jbduncan I was talking about the target "**/*.java".
spotless {
java {
target "**/*.java" <--- without this, it doesn't work with Android
googleJavaFormat()
removeUnusedImports()
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
}
I added the workaround in the top comment for others to find, along with links to the places where the code would need to be fixed if anyone wants to submit a PR for this.
@jaredsburrows @nedtwigg Does someone have a full example of where to embed the plugin into the android gradle files? The android-gif example does not include these sections. Thanks in advance.
I always wind up with a Error:org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find com.google.googlejavaformat:google-java-format:1.3.
I posted the example in this thread. The android-gif-repo does not use spotless.
On May 30, 2017 11:02 PM, "Peter Spiess-Knafl" [email protected] wrote:
@jaredsburrows https://github.com/jaredsburrows @nedtwigg https://github.com/nedtwigg Does someone have a full example of where to embed the plugin into the android gradle files? The android-gif example does not include these sections. Thanks in advance.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/diffplug/spotless/issues/111#issuecomment-305092228, or mute the thread https://github.com/notifications/unsubscribe-auth/ABqMSAYOCC-OBC-n-Vogy5CcagPQV0Ugks5r_QKOgaJpZM4NagKb .
I manged to solve it. I was missing what was mentioned in #103.
Maybe others find it useful, so I post my final solution here. This is whats required in the top level gradle file for android projects.
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins {
id "com.diffplug.gradle.spotless" version "3.4.0"
}
allprojects {
repositories {
jcenter()
}
buildscript {
repositories {
maven { url "https://plugins.gradle.org/m2/" }
}
}
apply plugin: 'com.diffplug.gradle.spotless'
spotless {
java {
target "**/*.java"
trimTrailingWhitespace()
removeUnusedImports()
googleJavaFormat()
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@nedtwigg maybe you could add this to the documentation somewhere?
@cinemast I believe your example could be shortened. Does the build file below work?
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins {
id "com.diffplug.gradle.spotless" version "3.4.0"
}
allprojects {
buildscript {
repositories {
jcenter() // to allow Spotless to download formatters
}
}
// @cinemast "apply plugin: ..." shouldn't be needed because it is already applied implicitly through the "plugin {}" block above.
spotless {
java {
target "**/*.java"
trimTrailingWhitespace()
removeUnusedImports()
googleJavaFormat()
}
}
}
// @cinemast No clean task should be needed, I think? (In non-Android Java projects, clean automatically exists and cleans the root project's build directory.)
I required the apply plugin and target "**/*.java" parts in my android project as well.
The target "**/*.java" is currently the cleanest solution without adding a dependency upon the gradle android plugin inside of spotless.
When you start having plugins dependent upon other plugins you end up tied to their breaking changes version bumping.
I've had to deal with this in my own plugin. https://github.com/JLLeitschuh/ktlint-gradle/pull/32
That's fine. Where can/should we document this?
Probably the plugin README
Just a bump that src/*/java/**/*.java will perform far better than **/*.java. We heavily discourage **/blah wildcards.
Not to open up a can of worms again. But can I file a bug with the AGP team or is this a gradle issue (or a spotless issue?)
This bit me again today so I think maybe adding some more docs would help at the site of the documentation: What do we think about this? https://github.com/diffplug/spotless/pull/2438
@nedtwigg holy crap. Following your advice on wildcards. My spotlessApply went down from 20 seconds to 10 seconds!!!
Do you have advice on what wildcard pattern to use for build.gradle.kts/.kts files (i.e. settings.gradle.kts)?
If I comment out my
kotlinGradle {
target("**/*.gradle.kts")
ktfmt()
}
then my spotlessApply goes from 10 seconds to 500ms!!! But I really want my kotlinGradle to be ktfmt'd as well
EDIT: Actually. I lied. looks like spotless doesn't actually get run on any of my files. Once I change from
target("**/*.gradle.kts")
to
target("src/*/java/**/*.kt")
i get the speed improvements, but thats because it doesn't actually fix my files. Back to square one. lol
FWIW, I was in gradle slack chatting with xav (lead on Android Gradle Plugin). he did mention that "The spotless plugin could use AGP APIs to detect the right sourcesets" from the convo above it sounds like we already knew that, but figured it was worth posting in case we didn't know if that was the case.
We do this pretty extensively in the Ktlint-Gradle plugin. The logic is... annoying to implement. Especially ensuring that there is cross-version
We had to write custom adapters for 5 different versions of the AGP due to minor API differences.
https://github.com/JLLeitschuh/ktlint-gradle/tree/e0740a3bfe6d24cdf6736423eedfa3d1cddc7fee/plugin/src
yep. In the meantime...
if anyone know why target("src/*/java/**/*.kt") and target("src/*/kotlin/**/*.kt") dont work. i would be eternally grateful
yep. In the meantime...
if anyone know why
target("src/*/java/**/*.kt")andtarget("src/*/kotlin/**/*.kt")dont work. i would be eternally grateful
@ColtonIdle I'm afraid that these targets work for me on one of my personal projects, so do you have an MRE that we can look into?
@jbduncan this is my top level build.gradle.kts
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.1" apply false
id("com.android.library") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.20" apply false
id("com.diffplug.spotless") version "7.0.2"
id("com.google.dagger.hilt.android") version "2.44" apply false
}
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
kotlinGradle {
target("*.gradle.kts")
ktfmt()
}
kotlin {
target("src/*/java/**/*.kt")
target("src/*/kotlin/**/*.kt")
ktfmt("0.49")
}
}
@ColtonIdle You're not seeing this error, are you?
FAILURE: Build failed with an exception.
* What went wrong:
Configuration cache state could not be cached: field `stepsInternalEquality` of task `:spotlessKotlin` of type `com.diffplug.gradle.spotless.SpotlessTaskImpl`: error writing value of type 'com.diffplug.spotless.ConfigurationCacheHackList'
> Could not resolve all files for configuration ':spotless1316845983'.
> Cannot resolve external dependency com.facebook:ktfmt:0.49 because no repositories are defined.
Required by:
root project :
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
BUILD FAILED in 810ms
If so, the fix is to add the following to the top-level build.gradle.kts.
repositories {
mavenCentral()
}
But I think your problem is two-fold.
- Calling
targettwice in a block (likekotlin { ... }) overrides the first call totarget. - When Spotless is configured from the top-level build file, it formats from the top-level directory, not from the submodules. So with your config, if the source files aren't in
src/...but in, say,app/src/..., then Spotless won't be able to find them. By comparison, using**/*.ktsearches everywhere, but as you've seen, it can be slow on larger projects.
The instructions here, which I guess you followed, were probably written before Kotlin and submodules were used extensively in Android land.
So you most likely want this setup:
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
kotlinGradle {
target("*.gradle.kts", "*/*.gradle.kts")
ktfmt("0.49")
}
kotlin {
target("*/src/*/java/**/*.kt", "*/src/*/kotlin/**/*.kt")
ktfmt("0.49")
}
}
I hope this helps!