-
Notifications
You must be signed in to change notification settings - Fork 72
Add pluginClasspaths to the configuration #23
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
Conversation
tschuchortdev
left a comment
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.
Thanks for this PR. I thought it was already possible to add compiler plugins via the pluginClasspath, but I guess I forgot about that.
This PR overlaps a lot with #15 which @Foso is working on. So it might make sense to start from what he already did, or rather combine it.
In particular we need to make sure that the compiler plugins are run in the correct phases.
| pluginClasspaths.forEach { filepath -> | ||
| if (!filepath.exists()) { | ||
| error("Plugin $filepath not found") | ||
| return ExitCode.INTERNAL_ERROR | ||
| } | ||
| } | ||
|
|
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.
I think this should probably be moved to the calling function, where writing of the source files happens as well.
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.
Please, which function? stubsAndApt is called from compile:
// step 1 and 2: generate stubs and run annotation processors
try {
val exitCode = stubsAndApt(sourceFiles)
if (exitCode != ExitCode.OK) {
val messages = internalMessageBuffer.readUtf8()
searchSystemOutForKnownErrors(messages)
return Result(exitCode, classesDir, messages)
}
} finally {
KaptComponentRegistrar.threadLocalParameters.remove()
}
And previously, the writing of the source files appears:
// write given sources to working directory
val sourceFiles = sources.map { it.writeIfNeeded(sourcesDir) }
Though I don't find an error checking section like the one I found in stubsAndApt.
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.
I think I would just put it after val sourceFiles = sources.map { it.writeIfNeeded(sourcesDir) } in the compile function where all the initialization of directories is done. But you are right that it's kinda confusing where to put this stuff. There should be a clear separation between initialization and the different compile stages. Maybe I will rewrite it in the future.
| it.destination = classesDir.absolutePath | ||
| it.classpath = commonClasspaths().joinToString(separator = File.pathSeparator) | ||
|
|
||
| it.pluginClasspaths = pluginClasspaths.map(File::getAbsolutePath).toTypedArray() |
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.
The commonK2JVMArgs will be used in both steps: stubsAndApt and compileKotlin. So given compiler plugins will be run both times. Is this the desired behaviour? Unless the kapt plugin prevents the compiler from continuing with other plugins, like it short circuits the regular compilation. @Foso may know more.
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.
Oh, I didn't have that problem because I didn't provide annotationProcessors, so stubsAndApt finished in the first conditional. Let's see the opinion by @Foso.
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.
This issue still deserves some attention. Are we really sure that compiler plugins should be executed in both steps?
src/main/kotlin/com/tschuchort/compiletesting/KotlinCompilation.kt
Outdated
Show resolved
Hide resolved
src/test/kotlin/com/tschuchort/compiletesting/KotlinCompilationTests.kt
Outdated
Show resolved
Hide resolved
src/test/kotlin/com/tschuchort/compiletesting/KotlinCompilationTests.kt
Outdated
Show resolved
Hide resolved
|
Thanks @tschuchortdev for the review!! I really appreciate it! |
|
I pushed a new commit with 3 improvements from your comments. |
|
Hi again @tschuchortdev, I found the development by @Foso and I think it's not necessary to combine both developments: see |
tschuchortdev
left a comment
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.
I don't really see how PluginCLIParser is relevant here? It looks like loadPluginsSafe is just a wrapper around loadPlugins that catches exceptions, or am I missing something? Thanks for linking it though, might come in handy.
By "combining both developments" I don't mean necessarily on the same branch. But for example you both need to know during which stages the compiler plugins should be run, so it would be best to be on the same page here so you don't need to do the debugging twice. Similarly, you will both need to define some kind of test compiler plugin in order to test if it works; That could be shared as well.
Regarding the test: We can define a simple TestComponentRegistrar in the project that can be used in both cases. @Foso can just pass it by value and you will have to pass this over the classpath. One (complicated) way to do this is as follows: Add a inheritPluginClasspath option to inherit the host applications classpath as pluginClasspath similarly to the already present inheritClassPath option. Alternatively you can get the path of just the host project classes with the getResourcesPath() function. Then all classes of the host application will be on the pluginClasspath, but that is not enough: For ServiceLoaderLite to find them we also need to provide the service registrations in the corresponding META-INF file. This can be done by manually creating another jar containing just the META-INF file and adding it to the pluginClasspath (see example here). Perhaps there is an easier way to do this by just defining the TestComponentRegistrar in another Gradle module and then declaring it as some kind of jar dependency. But I don't know enough Gradle to do this.
| pluginClasspaths.forEach { filepath -> | ||
| if (!filepath.exists()) { | ||
| error("Plugin $filepath not found") | ||
| return ExitCode.INTERNAL_ERROR | ||
| } | ||
| } | ||
|
|
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.
I think I would just put it after val sourceFiles = sources.map { it.writeIfNeeded(sourcesDir) } in the compile function where all the initialization of directories is done. But you are right that it's kinda confusing where to put this stuff. There should be a clear separation between initialization and the different compile stages. Maybe I will rewrite it in the future.
src/main/kotlin/com/tschuchort/compiletesting/KotlinCompilation.kt
Outdated
Show resolved
Hide resolved
|
I'll follow your ideas to create another test. Thanks!! |
3af25f2 to
2b62824
Compare
2b62824 to
6707daa
Compare
|
Hi @tschuchortdev, Done. There is a new test to check that the new property Please, let me know if I must do something more. |
tschuchortdev
left a comment
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.
Aside from the missing documentation for classpathOf this looks fine to me. BUT we still don't know if given compiler plugins should be executed in both the stubsAndApt and compileKotlin steps! So I'm reluctant to merge this until we can be sure that it's correct... Or we could merge it now and fix it later. Naturally, I would prefer to get it right the first time.
|
|
||
| private fun classpathOf(dependency: String): File { | ||
| val regex = Regex(".*$dependency\\.jar") | ||
| val classGraph = ClassGraph().whitelistJars() |
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.
Why are you using whitelistJars() here? If I read the documentation correctly, this method should return an empty ClassGraph if no arguments are given.
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.
Oh, sorry, fixed!
| it.destination = classesDir.absolutePath | ||
| it.classpath = commonClasspaths().joinToString(separator = File.pathSeparator) | ||
|
|
||
| it.pluginClasspaths = pluginClasspaths.map(File::getAbsolutePath).toTypedArray() |
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.
This issue still deserves some attention. Are we really sure that compiler plugins should be executed in both steps?
|
Hi @tschuchortdev @rachelcarmena , In my branch i used a list of ComponentRegistrars because i wanted to be able to directly interact with my plugin classes in the tests, without compiling the plugin and figuring out the classpath first. I think it's independent from this PR. I can think of usecases where you want add a componentRegistrar and check it in combination with other plugins, that you add to the classpath. Unfortunately i'm no expert when it comes to compiler phases, but the arrow-kt/arrow-meta-prototype@f7c4ea2 contains a few informations. I experienced that the attached ComponentRegistrar gets called when the compiler starts and then the compiler calls the attached Extension-classes, like AbstractKapt3Extension, when the compiler is in the right phase for it. So i'm not sure if we could add the plugins to wrong phase, because the compiler decides when they get called. Btw: |
We should definitely have both, but I thought that maybe the test plugin could be shared.
I think theses phases are actually "outside" the compiler and get orchestrated by the Kotlin Gradle and Kapt Gradle plugin. So Gradle decides what phases should receive which compiler plugins as arguments. But it's also possible that both phases receive all third-party compiler plugins and kapt actually prevents the registration of further compiler plugins by terminating the compiler before it gets there. Or the third option: Compiler plugins really have to be executed in both phases. |
|
Thanks @tschuchortdev and @Foso ! I appreciate a lot your reviews.
Absolutely, it wasn't a good name. It exists in the first commit and it was replaced by I added a warning message in the last commit in case the plugin is executed twice. However, as far as I know, so if a user provides Could a user be interested in adding Please, let me know if I must improve something else. Thanks again! |
Definitely. For example dagger + arrow. Jetpack Compose also uses compiler plugins iirc.
It's possible. If the compiler plugin is used to generate code, it might try to generate the same class twice which will lead to errors. But similarly if we don't execute compiler plugins during the
I have asked on the Kotlin slack about this (we could also summon someone from the compiler team here). If I don't receive an answer until tomorrow, I will probably merge this as it is and we can fix it later if someone complains. I assume you are also waiting for this to be available as a release so you can use it to test arrow-meta? |
|
Thank you very much @tschuchortdev ! Yes, that's the reason why I'm interested in this change. Thanks again Thilo! |
|
@rachelcarmena @raulraja Do you need a proper release or will a SNAPSHOT release suffice? I'd like to wait with the next real release until #15 is done because releasing on sonatype nexus is a real pain. |
|
@tschuchortdev we are fine depending on the snapshot until the release it's ready. Thanks! |
Please, this pull request adds the ability to provide
pluginClasspathsfor the compilation.Thanks in advance!