zyxist / chainsaw

Gradle plugin: adds support for building Java 9 modules.
Apache License 2.0
70 stars 4 forks source link

Support for generating module-info.java #23

Open Vampire opened 6 years ago

Vampire commented 6 years ago

It would be really nice if you could generate the module-info.java with some builder style, something like (the syntax can be done like in my example due to Groovy DSL goodness, you can ask if you need more information)

javaModule('com.example.my_module') {
    requires 'com.fasterxml.jackson.annotation'
    exports 'com.example.my_module.foo' to 
    exports 'com.example.my_module.bar' to 'com.example.my_other_module'
    opens 'com.example.my_module.**.json.*' to 'com.fasterxml.jackson.databind'
    opens /com.example.my_module.**.*\.fxml/ exclude 'com.example.my_module.bar.**' to 'javafx.fxml'
}

For the wildcards you should be able to use the Gradle facilities that already resolve them like doing

project.files(sourceSets.main.allSource).asFileTree.matching {
    include 'com.example.my_module.**.*\\.fxml'
        .replaceAll(/(?<!\\)\./, '/') // replace non-escaped dots with slash
        .replaceAll(/\\\./, '.') // replace escaped dots with dot
}
.collect {
    def dotSeparated = it.parent.replace(System.properties.'file.separator', '.')
    def prefix = pattern.substring(0, pattern.findIndexOf { it in ['*', '?'] })
    dotSeparated.substring(dotSeparated.indexOf(prefix))
}
.unique()
.each { println it }

And the same for excludes. If matching explicit packages like in the two exports examples, it can be used directly. If matching packages with wildcards (* or ?) I'd require that the pattern matches files like in the two opens examples, or you need special logic to determine when to add .* in the end - as a FileTree only matches files - which could easily be done wrong. For the files found via the pattern match you then can take their parent directories and write those to the generated module-info.java.

Even without the pattern matching the possibility to generate the module-info.java would be helpful as the pattern matching and so on could be done in the build script itself, it would just be way more convenient if chainsaw would do the pattern matching work.

The generated module-info.java could for example be generated to "$buildDir/generated-module-info" or similar and this directory added to the source set for compilation and packaging via sourceSets.main.java.srcDir("$buildDir/generated-module-info").

If the old-style service file is present it could even be added as provides automatically to the module-info.java without the need to declare it explicitly.

zyxist commented 6 years ago

I understand the pain of crafting long module-info.java files, however I'm afraid it's out of scope of the plugin mission.

Chainsaw is all about reconfiguring JDK tools to use Jigsaw and other JDK9 goodies in Gradle tasks. Code generation is something different. It can be done in several ways, not only via moving this information to Gradle build scripts, but e.g. through annotation processors and package-level annotations.

I don't say the idea is wrong, but that Chainsaw is not the best place to implement it. There could be another plugin or annotation processing library designed for that task. If such a tool appears, I'll do my best to make it work with Chainsaw. Moreover, if it is implemented as a separate plugin, it will be still useful once Gradle gets a native support for Jigsaw. If it is implemented as a part of Chainsaw, the solution will eventually die with Chainsaw, too.

Here, I want to focus on making Jigsaw work, and give the users freedom of choice.

Vampire commented 6 years ago

I hope that native Jigsaw support in Gradle will also get this feature actually. :-) I honestly don't see that this is out-of-scope, as the plugin is about supporting the cool Java 9 stuff and making it easier to dynamically declare the module info class in the build script would for me account to that. But hey, it is your plugin, so if you don't like it, it was just an idea. :-)