Closed oehme closed 7 years ago
Implemented incremental .gitattributes
. Biggest need is to test incremental behavior, second is to finish out these checkboxes. I force pushed over your commit-revert pair.
Great, thanks @nedtwigg. I realise that those two commits were a blemish in an otherwise clean commit history, so thanks for getting rid of them for me.
@nedtwigg, how are things going on your end with testing the incremental inputs? Is there anything you need help with to move forward?
@jbduncan Sorry, didn't mean to imply that I was working on testing. Only that testing was the project's greatest need. We've done a bunch of work, does any of it work? Is it faster?
Spotless is the fastest step in most of my builds, so this has always been at the bottom of my priority list. Anytime someone wants to contribute to a project I'm maintaining, enabling them becomes the top of my list. So I don't care a bunch whether this gets finished, but I do care about helping you or anyone else who wants this enough to build it.
In Spotless 2.x, every step needed to test that it applied formatting correctly. In Spotless 3.x, every step also needs to test it recognizes changes in its input properties. I don't know anything about testing Gradle's incremental build, so I see that as out of my wheelhouse, and into the wheelhouse of whoever wants this. Whenever there's a problem with Spotless and its guts (e.g. the .gitattributes
nastiness) I'm happy to hop in again and help :)
Ah okay, I understand now what you meant. :)
Testing incremental behaviour (which I presume would involve microbenchmarking in some way) is something I'm also unfamiliar with. I've had a look at Chapter 39.4 of the Gradle user guide, but it doesn't seem to say anything on how testing should be done.
@oehme Do you have any advice on how I could test the correctness and performance of the parts of the code base we've "incrementalised" so far?
I think @oehme mentioned this a little:
You can use TestKit to verify that your tasks are executed/skipped as expected.
The integration tests (which are already in the test suite) return a BuildResult
where you can query the TaskOutcome
. So that's how you can test the correctness.
For performance, I don't think we necessarily need benchmarks. I'd just run 2.x on a build a few times, then run 3.0.0-SNAPSHOT a few times, using the --profile
flag to do the measuring. Basically, use this in a production-scenario, and roughly quantify how it's helping.
@nedtwigg Thank you very much for you advice. I've just pushed a new commit at https://github.com/diffplug/spotless/commit/5e568df64e2bf1203e808c1bf0dddd4e234d2ca3 which contains a bunch of new incremental-related test improvements, and I wonder if you'd have the time and willingness to review it at some point for me in the near future?
Regarding performance, I did some 'benchmarks' on my Windows laptop on both master
and 3.x
, running ./gradlew clean
and then running ./gradlew build --profile
5 times on each of the two branches, which gave me the following results.
3.x (3.x 93354082f9e3e00984d7db3a72847c666e5306fc):
0. ./gradlew clean
1. ./gradlew build - 1 mins 36.233 secs (:spotlessCheck: 4.778 secs)
2. ./gradlew build - 12.302 secs (:spotlessCheck: 4.507 secs)
3. ./gradlew build - 12.34 secs (:spotlessCheck: 4.27 secs)
4. ./gradlew build - 12.294 secs (:spotlessCheck: 4.288 secs)
5. ./gradlew build - 12.33 secs (:spotlessCheck: 4.423 secs)
2.x (master b8fa86a51da32f294f8080b3042f40d9ab5dcfcd):
0. ./gradlew clean
1. ./gradlew build - 2 mins 13.168 secs (:spotlessCheck: 9.11 secs)
2. ./gradlew build - 15.599 secs (:spotlessCheck: 7.633 secs)
3. ./gradlew build - 15.599 secs (:spotlessCheck: 7.44 secs)
4. ./gradlew build - 15.258 secs (:spotlessCheck: 7.211 secs)
5. ./gradlew build - 15.349 secs (:spotlessCheck: 7.417 secs)
The time improvements seem very promising considering we've only incrementalised part of the code base (~3 seconds faster :spotlessCheck execution on 3.x).
I don't really know why the (1.) running times for 2.x
and 3.x
were so different (~1 mins 36 secs vs. ~2 mins 13 secs), but I think it was due to how both times the build script needed to download stuff from the Eclipse P2 repo, and it coincidentally took longer for it to download everything when I was testing 2.x
(compared to 3.x
).
@jbduncan The new commit is a good start! I did some refactoring in ce2ca9cfb91efa3672bd87a49ffdf3825e9a441f, and then used that refactoring to point out a bug in b35c1ab48e7fbe69d53849f4a21606f0ba9abd81, which I tried and failed to figure out in 04751a855de284ff61ebfe6980206d2a0f113365.
@oehme We're having trouble with up-to-date-checking. Here's what's going on to save you from digging around:
If the build.gradle
changes from this:
spotless { java {
googleJavaFormat('1.1')
} }
to this:
spotless { java {
googleJavaFormat('1.0')
} }
then the check
task needs to rerun. This shows that it is not happening.
This shows that the FormatterStep
themselves have the proper equality semantics, and this shows that a List<FormatterStep>
has the proper equality semantics.
CheckFormatTask is the task in question, and BaseFormatTask is its parent class which actually has @Input public List<FormatterStep> steps = new ArrayList<>();
Does inheritance of annotated input properties not work? Are we doing something else wrong?
@jbduncan the tests you ran don't show the improvement you'd expect from an incremental build. I'm pretty sure you're just seeing a more efficient implementation of spotless itself.
If the task was up-to-date you'd see it marked as such on the command line and it would take a few ms, not several seconds.
Hi @oehme, I didn't actually expect my tests to show the full improvement in speed expected from a proper incremental build. This is because @nedtwigg and I haven't "incrementalised" all of the relevant parts of the Spotless code base yet - we still have to apply the required Serializable
and equals()/hashCode()
semantics to the following files:
I'd therefore argue that the results so far are actually really promising, as the timings are going in the right direction (down from ~7s to ~4s, with the millisecond range being our ultimate goal). :)
Here's an example of what I get from the terminal when I run ./gradlew build -x :test
on the current SNAPSHOT of 3.x.
$ ./gradlew build -x :test
:compileJava UP-TO-DATE
:pluginDescriptors UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:publishPluginJar UP-TO-DATE
:javadoc UP-TO-DATE
:publishPluginJavaDocsJar UP-TO-DATE
:assemble UP-TO-DATE
:findbugsMain UP-TO-DATE
:pluginUnderTestMetadata UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:spotlessCheck
:spotlessFreshmarkCheck UP-TO-DATE
:spotlessJavaCheck UP-TO-DATE
:spotlessMiscCheck UP-TO-DATE
:spotlessCheck UP-TO-DATE
BUILD SUCCESSFUL
Total time: 4.567 secs
:check
:build
BUILD SUCCESSFUL
Total time: 15.061 secs
Interesting, all the spotless tasks in your example are marked as up-to-date. I'm curious where those 4s are spent then. Can you produce a build scan?
@jbduncan's benchmarks were run against the Spotless codebase. spotlessCheck
and spotlessApply
in this case are run through a complex bootstrapping process - I don't think a buildscan will be helpful. I'll dig in to do some testing when the code works, but in the meantime I wouldn't spend much time interpreting these results.
For now, I think our real problem is the correctness issue I described in this comment.
@nedtwigg I'm not sure I understand why you don't think running a build scan would be helpful. Could you elaborate further for me?
In order for a profiling result to be useful, it's important to change as few things as possible. That way you can trace changes in the result to their source. Running spotlessCheck
on master
and 3.x
branches means that we're running different versions of spotless, to format different codebases, possibly with different build.gradle
configurations. So if we find a difference, it's going to be a lot of work to figure out what it means, and even if we do that work we might make a mistake and draw the wrong conclusion.
The second problem is that it's difficult to apply a Gradle plugin to its own sourcecode. So Spotless does some tricks so that spotlessApply
and spotlessCheck
can work, which you can see here. A build scan would provide profiling data on these tricks, which is not helpful in figuring out Spotless' performance for end-users.
A better test would be to take a very large codebase, perhaps this one and run with two different versions of Spotless. That way the only variable is the version of spotless - not the codebase being formatted or the build.gradle. Change a single file, and make sure that spotlessCheck runs again, and how long does it take. Change the rule, and make sure that spotless runs again. etc.
Regardless of any profiling results, I think that everything besides this testcase is probably a distraction. The testcase shows a correctness problem, and rules out several possible causes. I'd like to focus @oehme's skills on that testcase rather than the profiling data.
@nedtwigg Your task needs to have getters for its properties, otherwise Gradle will not pick them up.
Great, that sounds relatively easy to fix. Thank you very much @oehme for looking into this problem for us. :D
I'm currently working on a fix, and I hope to have it done and committed today or tomorrow.
Hmm, I take that back - it's actually proving to be rather difficult for me to fix!
It seems that introducing getters is so reliant on all the right parts of the code being serializable (which we haven't completed yet) that it's causing serialization-related errors whenever I run gradlew check
.
I've uploaded a commit of my working code to https://github.com/diffplug/spotless/commit/8d503aced53e48bb2df474fb4b944f24843a5d40.
@oehme, @nedtwigg, may I have your thoughts on this?
@jbduncan that's not specific to getters, that's how up-to-date checks work ;) Every input of a Gradle task needs to be serializable.
Your commit looks good to me.
How strange! I think my /.gradle folder must have been corrupted, because no matter whether I ran gradle check
via IntelliJ IDEA or on the console, I was continuously getting different error messages to what Travis reported.
I've deleted my /.gradle folder and run gradle spotlessApply
and gradle check
again on my local machine, but now I get some failing tests. Hopefully Travis will report the same error message as I'm seeing here once it finishes scanning.
It looks like the Travis run for https://github.com/diffplug/spotless/commit/722e222e9e41509a2b99a9d205472c8a125a482e produced the same error message as on my local machine.
@oehme @nedtwigg Do you have any thoughts on where things may be going wrong?
GoogleJavaFormatStepTest (the previously failing test) now passes. Great work!
All of the test failures are expected - they are failing on calls to checkRunsThenUpToDate()
on steps which don't implement Serializable properly, so they are never up-to-date. The fi
package with SerializablePredicate/Function
needs to die. Lambdas will never implement serializable properly. Anywhere that SerializablePredicate/Function is needed will have to be replaced with FormatterStep.create
or createLazy
, which lets you use a lambda in a serializable way by capturing and serializing the state.
Hi @nedtwigg, many thanks for the compliment and your suggestion for moving forward. :)
I had a go at translating the Serializable(Predicate|Function)
calls into FormatterStep
calls this evening - but I'm stuck with turning the call to FormatterStep.filterByFile(Predicate<File>)
in JavaExtension.setupTask(BaseFormatTask)
into a serializable, non-lambda-expression-using FormatterStep that filters on a file...
(The FormatterStep.create*
factories don't seem to give access to the file parameter from FormatterStep.format(String, File)
, so I don't think I can use those methods.)
...so I'm struggling to see the way forward again, and I wonder if you have any other suggestions for me?
I just fixed the SerializablePredicate part in a0be07c5a7d29f980a71b93ac87b1034f5bceeca. Hopefully that shows the way forward for similar problems :)
Well, I've now solved some if not all the serialization problems in https://github.com/diffplug/spotless/commit/a1bb630a6ea94d2f90b5444bb7fa8172ca3da781. :)
However, as of https://github.com/diffplug/spotless/commit/83072238eaf1b17fd583adda07a3004984cf864e, the tests are complaining that running spotlessCheck
twice doesn't cause it to report that everything's up-to-date, which suggests to me that the incremental build is no longer working, and that maybe I messed up with the serialization somewhere?
(I've confirmed the lack of incremental build by running spotlessCheck
on the Spotless codebase twice on my laptop, which doesn't report any "UP-TO-DATE" messages next to the spotlessCheck subtasks).
So I'm left feeling rather stumped.
@oehme @nedtwigg Do you have any idea if I missed something or where I went wrong?
I fixed the problems in d51d13a4432db653af971516785976ae4951528d, did some minor test reorg in b33c6ef8cb44a523eb2837655836961c5d3bb76f and 9a76f34a087d91e87fa0c12eb95471cec4af5a87, then did some refactoring in 86c38e66e8aa35d48b1f2674192eb1b0a3bdec9a.
I did a chunk of other work as well, but I'll need to clean it up before I push it up. I expect we'll have a first RC ready by the end of the week.
EDIT: replaced the last refactoring commit with a force push
Can I already write a custom rule as a class and attach it without needing the bump-method? If not, we need a way to do that. It's important so that users can write their rules e.g. in buildSrc
or in a plugin and share it across projects or with others.
@Canonical
class MySpecialRule implements FormatterRule { //FormatterRule extends Serializable
String format(String input) {
input.toUpperCase()
}
}
spotless {
java {
custom 'mySpecialRule', new MySpecialRule()
}
}
@nedtwigg Cool, let me know when you've managed to push your remaining chunk of work, as I've noticed a few little areas for improvement, but I'm unsure if I'd be treading over the same lines of code that you're currently working on.
@oehme I admit I don't really know the answer to your question, and I'm unsure how we'd test it. @nedtwigg, does Spotless already allow users to write such class-based rules? If not, do you have any ideas for how we could allow a user to do so?
Can I already write a custom rule as a class and attach it without needing the bump-method?
Yup.
spotless { java {
addStep(new MyFormatterStep()) // explicit constructor call
addStep(MyFormatterStep.create()) // might be constructor, or FormatterStep.create()
custom 'stepName', { someCustomFunction } // you'll need to do the bump-thing
OO techniques make it hard to implement serialization, equality, and laziness all at once, but the FormatterStep.create
methods make it a lot less error-prone, so I think I'll steer users in that direction. GoogleJavaFormat and EclipseFormatter are good examples, but some of those below will make even simpler ones:
The following do not support up-to-date checking, but it would be easy to fix them:
@jbduncan feel free to implement these or do cleanup as you wish. I'm going to do some exploratory work in a different branch for #56.
Will do @nedtwigg. :)
FormatExtension.customReplace* should now be incremental-build-ready (see https://github.com/diffplug/spotless/commit/52d70d6f5fdf6758e3cad7184fadd5b5134a464f).
I had a go at Freshmark, but when I tried replacing the following lines of code (in FreskMarkExtension)
customLazy(NAME, () -> {
// defaults to all project properties
if (properties == null) {
properties = getProject().getProperties();
}
FreshMark freshMark = new FreshMark(properties, getProject().getLogger()::warn);
return freshMark::compile;
});
with
addStep(FormatterStep.createLazy(NAME,
() -> new FileSignature(target),
key -> {
// defaults to all project properties
if (properties == null) {
properties = getProject().getProperties();
}
return new FreshMark(properties, getProject().getLogger()::warn)::compile;
}));
and ran gradlew build
twice, it would throw the following exception
java.nio.file.AccessDeniedException: C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless\build\reports\tests\classes
suggesting to me that
build/
(I don't understand why it's trying to do so, as build/
should be filtered out when FormatExtension.parseTarget
is called).FormatterSteps are independent of the files they are transforming. They are dependent on anything which determines their configuration. So EclipseFormatter.State needs to have the file that holds the formatter properties file, but it does not need the target files that it is transforming.
For FreshMark, the key should be the Map<String, String> of properties that it is using.
The FormatTask tracks which files are being transformed, not the FormatterSteps.
FormatterSteps are independent of the files they are transforming. They are dependent on anything which determines their configuration. So EclipseFormatter.State needs to have the file that holds the formatter properties file, but it does not need the target files that it is transforming.
Ahh, okay, I see now. Thanks for clearing up my confusion on this.
For FreshMark, the key should be the Map<String, String> of properties that it is using.
Hmm, it seems the default properties are not serializable, so the closest I can get to a version of properties which can be used as a key, is with the following code snippet.
addStep(FormatterStep.createLazy(NAME,
() -> {
// defaults to all project properties
if (properties == null) {
properties = getProject().getProperties();
}
// properties isn't serializable, so make a serializable copy
return new LinkedHashMap<>(Maps.transformValues(properties, value -> (value == null) ? null : value.toString()));
},
key -> new FreshMark(key, getProject().getLogger()::warn)::compile));
The consequence of this solution is :spotlessFreshmarkCheck never reports as being UP-TO-DATE...
Looks like we'll need a Freshmark.State
class. Mapping them all to Map<String, String> is a great fix for up-to-date checking, but it's possible for some props to be actual java objects that need to be passed as the objects themselves, not string. So Map<String, ?> can be transient, and Map<String, String> can be the actual prop.
Possible cause 1: LinkedHashMap<> cares about order, and whatever gradle is using doesn't, so they're getting reordered randomly.
Possible cause 2: There's a property like "currentTime: Date". Try some debug code that dumps all the properties in getProject().getProperties() and see if any are changing. Depending on what it is, it might be appropriate for the default behavior of this method to be that it is never up-to-date unless properties are manually specified. Or, maybe instead of passig getProject().getProperties(), we should manually parse gradle.properties
ourselves.
Gradle definitely doesn't reorder things randomly ;)
My bet is on: There is either a timestamp property or a property that doesn't implement toString()
, so you just get the identity hash code which is different on every invocation.
The solution is pretty simple: The user should be explicit about what properties should be passed to FreshMark. The default should be none.
It's very handy that everything in gradle.properties
shows up in freshmark without much work. One solution is to add a method addProps(File propertiesFile)
. But I still lean towards filtering out the individual property which is causing the issue.
There is no such individual property. Every build out there probably has dozens of properties that either are some kind of timestamp or simply don't have a toString() method. The properties are used by build script authors as a bucket to put all kinds of data.
We could check for unimplemented toString() using System.identityHashCode() and filter them out. Regardless, you're convincing me that adding all props is the wrong way to go :) I'm still curious which is the troublemaker.
When I print out the properties during a run of gradle check
, they look like this:
properties = {
parent: null
classLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@1fd793ea
plugins: [org.gradle.api.plugins.HelpTasksPlugin@4493592c, org.gradle.language.base.plugins.LifecycleBasePlugin@6c33e52a, org.gradle.api.plugins.BasePlugin@32382b0c, org.gradle.api.plugins.ReportingBasePlugin@e0c5757, org.gradle.platform.base.plugins.ComponentBasePlugin@34613fbe, org.gradle.language.base.plugins.LanguageBasePlugin@41fcc7ce, org.gradle.platform.base.plugins.BinaryBasePlugin@7dea052b, org.gradle.api.plugins.JavaBasePlugin@1fca0c52, org.gradle.api.plugins.JavaPlugin@7729233b, com.diffplug.gradle.spotless.SpotlessPlugin@182e787a]
configurations: [configuration ':archives', configuration ':compile', configuration ':compileClasspath', configuration ':compileOnly', configuration ':default', configuration ':runtime', configuration ':testCompile', configuration ':testCompileClasspath', configuration ':testCompileOnly', configuration ':testRuntime']
logger: org.gradle.internal.logging.slf4j.OutputEventListenerBackedLogger@2378b9ca
rootDir: C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless
projectRegistry: org.gradle.api.internal.project.DefaultProjectRegistry@7ee85ce6
path: :
testResultsDirName: test-results
targetCompatibility: 1.8
...
rootProject: root project 'spotless'
libsDirName: libs
artifactIdLib: spotless-lib
properties: {parent=null, classLoaderScope=org.gradle.api.internal.initialization.DefaultClassLoaderScope@1fd793ea, ...}
}
I dare not paste all the properties, because the list is massive.
But looking through the first few props, it seems obvious to me that a number of properties simply do not implement toString() and print out their identities instead, as @oehme suspected.
I didn't notice any timestamp-related property as I was glancing through the list, so I suspect that the objects which don't implement toString() are to blame.
Therefore I'd be inclined to completely throw out the safeguard currently in place to set the properties to getProject().getProperties()
if the user doesn't specify them themselves.
@nedtwigg Do you have any ideas as to what we could do instead? Just throw an exception? Or log it and/or perform some other action?
Therefore I'd be inclined to completely throw out the safeguard currently in place to set the properties to getProject().getProperties() if the user doesn't specify them themselves.
I think that's the wrong default. It'll always be out of date. It should be no properties by default and the user should conciously decide what she needs.
I think I agree with @oehme. I think we should add a propsFile
argument which can load a .properties
file.
@nedtwigg I admit I don't really understand what you mean by adding a propsFile
argument (it wasn't clear to me if you wanted me to add it to FreshmarkExtension::new's parameter list or another method's), but my current solution is to turn FreshmarkExtension.java
, which currently contains:
public class FreshMarkExtension extends FormatExtension {
public static final String NAME = "freshmark";
public Map<String, ?> properties;
public FreshMarkExtension(SpotlessExtension root) {
super(NAME, root);
customLazy(NAME, () -> {
// defaults to all project properties
if (properties == null) {
properties = getProject().getProperties();
}
FreshMark freshMark = new FreshMark(properties, getProject().getLogger()::warn);
return freshMark::compile;
});
}
public void properties(Map<String, ?> properties) {
this.properties = properties;
}
...
}
...into:
public class FreshMarkExtension extends FormatExtension {
public static final String NAME = "freshmark";
public FreshMarkExtension(SpotlessExtension root) {
super(NAME, root);
}
public void properties(Map<String, ?> properties) {
customLazy(NAME, () -> {
FreshMark freshMark = new FreshMark(properties, getProject().getLogger()::warn);
return freshMark::compile;
});
}
...
}
My solution seems to pass all the tests on my machine.
What do you think?
customLazy
tasks will always be out-of-date. We need to capture the state of the Map<String, String>
, and move FreshMark into lib, using same Provisioner construct as EclipseFormatter and GoogleJavaFormat. If I'm being confusing, that's my bad, I might be able to communicate better by just writing it :)
Ooh, yes please! If you could write it down, then I think there's a much higher chance that I'd understand. :)
Hi @nedtwigg, I've been working on incrementalising ImportSorterStep
over the last few days, but I've observed that when I run ./gradlew check
twice on the root project (before and after I've stashed my local changes away with git stash
), it throws an exception at :spotlessFreshmarkCheck
, and I'm struggling to tell what's causing it, so I wondered if you'd be happy to have a look into it?
Here is an abbreviated example of the stack trace I get.
Exception in thread "main" org.gradle.testkit.runner.UnexpectedBuildFailure: Unexpected build execution failure in C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless with arguments [-b, spotlessSelf.gradle, spotlessCheck, --stacktrace]
Output:
:spotlessFreshmarkCheck FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':spotlessFreshmarkCheck'.
> java.nio.file.AccessDeniedException: C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless\plugin-gradle\build\reports
* Try:
Run with --info or --debug option to get more log output.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':spotlessFreshmarkCheck'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:61)
...
at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:293)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: java.io.UncheckedIOException: java.nio.file.AccessDeniedException: C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless\plugin-gradle\build\reports
at com.diffplug.gradle.spotless.CheckFormatTask.addFileIfNotClean(CheckFormatTask.java:61)
at com.diffplug.gradle.spotless.CheckFormatTask.lambda$check$0(CheckFormatTask.java:38)
at org.gradle.api.internal.changedetection.changes.ChangesOnlyIncrementalTaskInputs.doOutOfDate(ChangesOnlyIncrementalTaskInputs.java:46)
at org.gradle.api.internal.changedetection.changes.StatefulIncrementalTaskInputs.outOfDate(StatefulIncrementalTaskInputs.java:39)
at org.gradle.api.internal.changedetection.changes.ChangesOnlyIncrementalTaskInputs.outOfDate(ChangesOnlyIncrementalTaskInputs.java:27)
at com.diffplug.gradle.spotless.CheckFormatTask.check(CheckFormatTask.java:38)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:163)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
... 71 more
Caused by: java.nio.file.AccessDeniedException: C:\Users\Jonathan\dev\Java\IntelliJ Projects\spotless\plugin-gradle\build\reports
at com.diffplug.spotless.Formatter.isClean(Formatter.java:103)
at com.diffplug.gradle.spotless.CheckFormatTask.addFileIfNotClean(CheckFormatTask.java:57)
... 82 more
BUILD FAILED
Total time: 2.188 secs
at org.gradle.testkit.runner.internal.DefaultGradleRunner$1.execute(DefaultGradleRunner.java:222)
at org.gradle.testkit.runner.internal.DefaultGradleRunner$1.execute(DefaultGradleRunner.java:219)
at org.gradle.testkit.runner.internal.DefaultGradleRunner.run(DefaultGradleRunner.java:282)
at org.gradle.testkit.runner.internal.DefaultGradleRunner.build(DefaultGradleRunner.java:219)
at com.diffplug.gradle.spotless.SelfTest.runWithTestKit(SelfTest.java:132)
at com.diffplug.gradle.spotless.SelfTestCheck.main(SelfTestCheck.java:20)
:plugin-gradle:spotlessCheck FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':plugin-gradle:spotlessCheck'.
> Process 'command 'C:\Program Files\Java\jdk1.8.0_101\bin\java.exe'' finished with non-zero exit value 1
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 17.819 secs
Process 'command 'C:\Program Files\Java\jdk1.8.0_101\bin\java.exe'' finished with non-zero exit value 1
14:31:03: External task execution finished 'build'.
I've been having this too. The fix is to delete the .gradle
directory. Dunno the cause, but self-applying the gradle plugin to its own directory is pretty difficult, and quite janky. Might be possible to fix by making spotlessSelf.gradle
more specific.
@nedtwigg Thanks for the advice, deleting .gradle
seems to work nicely as a short-term fix.
I've now made the import sorter related classes ready for incremental builds: https://github.com/diffplug/spotless/commit/5d365b3873351f8fc934db44aea80580a263c244.
Hmm, having thought about it, I wonder if the reason the build fails at :spotlessFreshmarkCheck
on a second run is because it fails to deserialize the Freshmark-related classes that are serialized on the first run...
(By my understanding, the serialized bits are saved in files within .gradle
, so it would make sense to me that deleting .gradle
would 'fix' the problem, as there would be nothing for Gradle to deserialize.)
If that is the case, it should be fixed since d3994cf15533f5e5f2021a04549c4b35dc00d784 on 12/7, because freshMark no longer adds all the project properties.
Hi @nedtwigg, thanks for pointing that out. I've investigated things further, and I now think the exception occurs because it tries to read a file from the plugin-gradle
build directory.
I don't know why it throws an exception there, and probably more importantly, it's not clear to me why it's even trying to read from the build directory in the first place, as I implemented a filter at some point which prevents (or, at least should prevent) Spotless from reading a Gradle project's build/
and .gradle/
dirs.
@oehme Do you have any thoughts regarding this?
As far as I can recall your code only excluded the current projects build dir. That doesn't stop it from recursing into subproject folders and reading their build directory.
Thanks @oehme! It looks like running ./gradlew check
twice on Spotless no longer causes to an exception to be thrown, so I'd say it's fixed now! (See commit https://github.com/diffplug/spotless/commit/e3ec14fe9e951e11027643ca573d7b3f97d66a43.)
The spotless check task currently doesn't have any inputs and outputs declared and is thus never considered up-to-date. This is slowing down user's builds unnecessarily.