-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#292 Fix custom sources location not being recognized by ktfmt #328
Conversation
…ix it by evaluating it lazy with providers - Add dependency between sqldelight tasks and formatTasks. This is necessary, else gradle will complain that task use outputs from task not specified - update changelog
plugin-build/plugin/src/main/java/com/ncorti/ktfmt/gradle/KtfmtPluginUtils.kt
Outdated
Show resolved
Hide resolved
Thanks for investigating on this one @simonhauck
I think we should filter the source folders we receive (by either using an euristic or applying the includes/excludes the users provide). It's pretty annoying that Gradle doesn't handle this out of the box, despite them providing |
Before implementing something, maybe you could have a look at the following options. Because I am not happy with either one :D Option 1: Heuristic with the regex
From my testing this seems to work. We could also simplify the base task a bit by removing
But that would in theory also be a breaking change. Because now we always exclude the build folder from the formatted files. From what I can see, it would be possible to overwrite the default exclude when specifying anything else in the excludes of the source task. Option 2: Applying the ant matchers from the excludes filed
First, it is not really nice to implement :D now the includes and excludes are still set in the task but the source must be filtered with the provider, that is also accessing the task. That is such a weird circular dependency. Additionally, this does not even work, because the excludes are ANT style paths pattersn. I am not aware of an easy way to filter a file with an ANT path.... Personally, I could live with excluding the build folder, but still, it is a breaking change. If somebody wanted to format sources files in the build folder, they would have to create a custom format and check task, because there the filder would not be applied. If we want to go with option 1 and want to mitigate the risk of breaking the logic for someone, we could add a boolean flag to the ktfmt extension, if the files in the build directory should be excluded. Last but not least, there is always the option to just accept that users have to define the task dependencies. Please let me know what you think about these ideas. Maybe I am missing a completely simple solution. |
I would personally prefer option 2. Gradle has a
Also for option 1., excluding the build folder is a reasonable thing to implement. Adding a boolean flag to toggle this behavior can just be a nice-to-have and probably something not really hard to maintain in the long run. |
Good news first :D yes it works with the filetree But honestly, I am not really sure if it is a good solution. return project.tasks.register(taskName, KtfmtFormatTask::class.java) { task ->
task.description =
"Run Ktfmt formatter validation for sourceSet '$name' on project '${project.name}'"
task.setSource(
project.provider {
srcDir.flatMap { srcRoot ->
project
.fileTree(srcRoot) {
it.include(task.includes)
it.setExcludes(task.excludes)
}
.files
}
})
task.setIncludes(KtfmtPlugin.defaultIncludes)
task.setExcludes(KtfmtPlugin.defaultExcludes)
}
With that it even uses the tasks includes and excludes, but it feels not great :D This is basically a re-implementation of the source task. We filter now the files before they are passed on. But honestly, this feels really hacky... While investigating this, I noticed that the getSource() method just does not apply any exclude rules even though they are set and the documentation also says, that those are the files after the includes and excludes were applied. I have the feeling, if that filtering would be working, this would also solve the problem.
Do you, by any chance, know why the source task does not apply the excludes at all? I think at some point you solve this by manually excluding the files. Maybe it is related to this 12 year old issue https://discuss.gradle.org/t/inconsistent-behavior-from-sourcetask-exclude/5469 ? |
I'm not entirely sure, but it could be related to relative paths. |
Thanks for the advice and you are right. So when we call I was also not able to make the solution above work. Maybe it never did, and I was just misreading something. Additionally, I am a bit concerned about performance in large codebases since this would essentially build the file tree twice (I think) and is also really hard to understand. Therefore, my proposal is to introduce a new optional property in the ktfmt configuration with a default value, something like
This is essentially the regex that is already part of the KtfmtPlugin companion object. So by default, all build folders are completely ignored. No need to build a filetree for that. This offers even more flexibility as you can now exclude any source directory, not just in the build folder. This also simplifies the actual ktfmt base class, since the additional filter is no longer required
If you agree, I would try to finish the implementation this weekend. |
So that's something doable. I guess the only annoying part is that the BaseTask was implemented as a If we create our own "exclusion pattern" we sort of deviate from how SourceTasks behave in Gradle. If there is no other solution, it's something we can go with (is going to be a niche feature nonetheless). |
Yeah i know and fully agree it is annoying. In the end it is in theory something different. The include / exclude work on files in a sourceset but we want to filter the entire sourceset. And for the source task it would probably be weird to just ignore random sources... if you do not want it as source. It should not be added as a source. But yeah... that does not change the fact that it is annoying 😒 |
Yeah that's also a really valid point and I can see users getting frustrated because of this |
… ignored. Set this value by default to the build directory
Soooo :D What I have done
One last note, just FYI: @Test
fun `format task should detect the source and test files in a flattened project structure and format them`() {
appendToBuildGradle(
"""
|kotlin {
| sourceSets.main {
| kotlin.setSrcDirs(listOf("src"))
| }
| sourceSets.test {
| kotlin.setSrcDirs(listOf("test"))
| }
|}
"""
.trimMargin())
// .... It is really important to use setSrcDirs. If not the sourceSet is only appended. And now multiple tasks use the same inputs and Gradle will complain about it. This is, in my opinion, nothing we have to fix, but is relevant for #292. If this PR is merged, I would comment on this issue as well. But that was one of the issues I encountered in the tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍 Sorry for the late review @simonhauck
🚀 Description
As described in #292 the main issue is that the SourceDirectorySet::getSourceDirectories() can change. When the ktfmt plugin is configured before the sourceDirectory is changed, the plugin will format the old directory where no sources are located.
That's why we have to evaluate it lazily. This is done in this PR.
Unfortunately, there are one side effect that I am not able to solve.
In the example module, we get the following error when running the check task
The sqlDelight plugin generates a kotlin main sourceSet. So it is expected that the dependencies between the two tasks must be explicitly configured. This is done on the example
build.gradle.kts
.This is a "breaking change" and can require users of this plugin to perform additional configuration.
📄 Motivation and Context
#292
🧪 How Has This Been Tested?
An integration test was added for the check and format task.
📦 Types of changes
it is in some sense a breaking change because users might have to explicitly specify the dependencies of ktfmt to other tasks that modify the sourceSets.
✅ Checklist