Skip to content
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

Make the plugin work #4

Merged
merged 51 commits into from
Dec 13, 2018
Merged

Make the plugin work #4

merged 51 commits into from
Dec 13, 2018

Conversation

SamCarlberg
Copy link
Member

No description provided.

SamCarlberg and others added 30 commits October 12, 2018 13:24
Fixes task caching, but at what cost?
No more eager initialization, no sir
Requires manually setting jlink task dependencies, but that will be fixed later
Modules are determined by analyzing the libraries the project depends on, as well as running jdeps on build/classes directory to determine the modules used directly by the project
Remove the modules and modulePath options, since they are no longer needed
Add a sample JavaFX app project
Simplify the unit test logic by using the plugins block instead
Useful if the application or libraries it uses reference some class reflectively, since jdeps can't detect reflective references to modules
Also mention the extraModules option
OpenJDK 10 and 11 are still running
Launcher scripts will be generated if launcher options are set. No need for a separate task
README.md Outdated

jlink {
"configuration name" {
applicationJar = tasks.getByName<Jar>("jar").archivePath // No default value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces the configuration of the "jar" task, which is not good.
Additionally, if a configuration block is executed later that further configures all the Jar tasks, this won't update. We shouldn't do this.

* ```
*/
operator fun String.invoke(configurationAction: Action<in JLinkOptions>) = register(this, configurationAction)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be exposing the NamedDomainObjectContainer's API as the primary way for adding JLinkOptions. The NamedDomainObjectContainer is a standard API so it's better to just let users use that without providing helper methods for interacting with it. IMO.

* The application JAR. This will be copied into the generated jlink image's `bin` directory, next to the
* `java` executable.
*/
var applicationJar: File? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a RegularFileProperty.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RegularFileProperty or just Provider<File>? RegularFileProperty is difficult to bind to normal File objects like those provided by the outputs of the Jar task

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want RegularFileProperty. I've fixed the Jar task for Gradle 5.1 to support being able to produce a RegularFileProperty directly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not to have to depend on bleeding edge releases. It limits the usability of the plugin

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regardless, we should still just use the RegularFileProperty.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RegularFileProperty is impossible to use in 4.10 when setting it to a normal File object like Jar::archivePath or ShadowJar::archivePath. And as I said before, I don't want to depend on a bleeding edge release and limit the usability of the plugin

@OutputDirectory
var jlinkDir: DirectoryProperty = newOutputDirectory()
@get:InputFile
val applicationJarLocation: RegularFileProperty = newInputFile()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, newInputFile is deprecated in Gradle 5.0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welp. As long as it'll still work

import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.*
import org.gradle.internal.jvm.Jvm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Internal API usage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What alternatives do you propose?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use case or what do you need?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To get the build JDK to determine the location of the jlink and jdeps tools. The java.home system property can't be used since Gradle may be running on a different JDK, like if -Dorg.gradle.java.home is set

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you open an issue with Gradle or ask about an alternative solution in the Gradle Slack channel?

import org.gradle.internal.jvm.Jvm
import org.gradle.internal.os.OperatingSystem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Internal API usage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this is a very useful API with no real public alternative. I could copy the class into the plugin, but that seems hacky

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JLLeitschuh
Copy link
Member

Can we make it so that it's possible to build a windows Jlink executable from a Mac and vice versa?

/**
* Use the endianness of the build system.
*/
SYSTEM_DEFAULT,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just use the system default by default? Or is this because we can't determine the system default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is equivalent to not passing the --endian flag. The jlink tool will use the byte order of the build system.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

* The application JAR. This will be copied into the generated jlink image's `bin` directory, next to the
* `java` executable.
*/
var applicationJar: File? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want RegularFileProperty. I've fixed the Jar task for Gradle 5.1 to support being able to produce a RegularFileProperty directly.

/**
* Options for the levels of compression available for generated runtime images.
*/
enum class CompressionLevel {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link to documentation describing these each in more detail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also linked in the README

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The page doesn't support linking to the individual sections

import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.*
import org.gradle.internal.jvm.Jvm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use case or what do you need?

README.md Outdated Show resolved Hide resolved
`--disable-plugin pluginname`
`--limit-modules`
`--save-opts`
`@filename`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elaborate on what this is supposed to mean.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption here is that people using a jlink plugin will have at least familiarized themselves with the jlink options from the reference page. We shouldn't have to explain standard tool options

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Erm... I don't think that's a valid assumption.


fun getApplicationModuleName(): String = applicationModuleName.get()

fun getMainClassName(): String = mainClassName.get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any of these get*** methods could throw an exception if the value isn't present, just FYI.


/**
* Strips debug symbols from the generated image.
*/
var stripDebug = false
val stripDebug = objectFactory.property(false)

/**
* Optimize `Class.forName` calls to constant class loads.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elaborate? I have no idea what this means from a practical sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It bypasses class lookups

// Your code
Class<?> fooClass = Class.forName("com.example.Foo");

// Is optimized to
Class<?> fooClass = com.example.Foo.class;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fascinating! 👍 Can you add a comment about that?


/**
* Excludes man pages from the generated image.
*/
var excludeManPages = false
val excludeManPages = objectFactory.property(false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are man pages even generated??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's man pages for the Java executable(s) IIRC. Man pages are never generated by jlink

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you declare man pages? Is there a location that you need to declare them to get them bundled? If so, then we should document how a user can get man pages added, ie. what convention they need to follow.


jlink {
register("release image") {
applicationJar.set(provider { tasks.withType(Jar).getByName("jar").archivePath })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't convey the dependency information the way you want. You need to use the suggestion I provided above, (may require Gradle 5.0 to work).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not to have to rely on high versions of Gradle, though it seems 5.0 is near to release

jlink {
"release image" {
useMinimalImage()
applicationJar.set(provider { jarTask.archivePath })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here with not conveying the dependency information.

if (name == "jlinkGenerateReleaseImage") {
dependsOn("jar")
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we set up this plugin correctly, this shouldn't be necessary.

build.gradle.kts Outdated
dependsOn(gradle.includedBuild("simple-jlink").task(":check"))
dependsOn(findProject(":samples:javafx-app")!!.tasks.getByName("jlinkGenerateReleaseImage"))
dependsOn(findProject(":samples:simple-jar")!!.tasks.getByName("jlinkGenerateReleaseImage"))
dependsOn(findProject(":samples:groovy-dsl")!!.tasks.getByName("jlinkGenerateReleaseImage"))
Copy link
Member

@JLLeitschuh JLLeitschuh Nov 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make each sub-project's check task depend upon that project's jlinkGenerateReleaseImage task, not the root check task.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer the samples to be normal projects that use the plugin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These goals aren't contradictory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the way I see it is that the root project just ties the plugin project and the sample projects together. So having the root project's check task make sure the samples play well with the plugin makes more sense to me than having each sample project's standard check task not only check the normal project things (tests, static analysis, etc) but also check the generated jlink distributions

* The name of the generated launcher script. If not set, the launcher name will be the name of the
* corresponding project.
*/
val launcherName = objectFactory.property(project.name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the default lazily evaluate project.name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter? I thought the project name will always be defined since it's an immutable property

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, if it's immutable, then it's fine.

Instead of hardcoding for the task names
For simplicity's sake, make JLinkTasks always depend on the project's jar task (for determining module info and linking/bundling). Support for custom jar files may be re-implemented in the future
Make the JavaFX sample modular
Add a simple modular project sample
Make task caching work (which unfortunately requires manually implementing Serializable)
@SamCarlberg
Copy link
Member Author

Merging this PR as-is. Future issues will be addressed in followup PRs. ATM the plugin is functional and usable, and I don't want to make this PR stay open for ages (it's already been a month!) while small improvements are made.

@SamCarlberg SamCarlberg merged commit 0ad3dc6 into master Dec 13, 2018
@SamCarlberg SamCarlberg deleted the make-plugin-work branch December 13, 2018 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants