Gradle plugin for custom language - gradle

I have a custom language (let's say it is MyLang but it can be any language) and I would like to make a plugin for it. The plugin needs to
be able to recognize the sourcesets for the language given with DSL
be able to compile them using an executable (the compiler)
I was able to create a plugin with a compile task (empty yet) and annotate a function with #LanguageType setting the language name to "mylang".
How can I modify the plugin to be possible to add sourcesets from build.gradle files using DSL like sourceSets { mylang { ... } }?
How can I modify the build task to be able to build the files of the source set?
class MylangBuildPlugin implements Plugin<Project> {
static final String COMPILE_TASK_NAME = 'compileMylang'
void apply(Project project) {
project.getPluginManager().apply(ComponentModelBasePlugin.class);
createCompileTask(project)
}
#LanguageType
void registerLanguage(LanguageTypeBuilder<BaseLanguageSourceSet> builder) {
builder.setLanguageName("mylang");
builder.defaultImplementation(BaseLanguageSourceSet.class);
}
private void createCompileTask(Project project) {
project.task(COMPILE_TASK_NAME) {
}
}
}

Related

Error: JavaFX runtime components are missing, and are required to run this application with Gradle example

I know this has been asked multiple times... but I can't seem to find a solution.
Taken from this official guidelines example: https://openjfx.io/openjfx-docs/#gradle
I went on and added in my build.gradle :
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
javafx {
version = '13'
modules = ['javafx.controls']
}
repositories {
mavenCentral()
}
mainClassName = "MyImage"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
which, by running gradle jar (or gradle build), should actually produce a jar which should include all the packages it builds it with, that is the entire javafx library.
However, when it builds successfully and then I proceed with running:
java -jar build/libs/MyImage.jar
it still throws the error:
Error: JavaFX runtime components are missing, and are required to run this application
What am I missing?
(I use JDK 11)
Many Thanks.
In Java 11 the Java launcher detects that you're extending javafx.application.Application and checks the modules are present. If you're using plain old JARs then you'll get the error
Error: JavaFX runtime components are missing, and are required to run this application
You have two choices. Setup your application to use the Java module system or the following workaround.
This workaround avoids the Java launcher check and will let the application run.
public class MyImage { // <=== note - does not extend Application
public static class YourRealApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// whatever...
}
}
public static void main(String[] args) {
Application.launch(YourRealApplication.class);
}
}

Gradle get dependency programatically without configuration

Is there an option to get Maven dependency in Gradle without using custom configuration for it? For example in custom plugin to just obtain dependency provided from extension? Something like
class DependencyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("deps", DepsExtension)
project.task('useDependency') {
doLast {
//use Gradle api to resolve dependency without custom configuration
project.resolve(project.deps.dependency)
}
}
}
}
class DepsExtension {
def dependency = 'custom:maven:1.0'
}
Something like this:
Configuration config = project.configurations.create('myPrivateConfig')
Dependency dep = project.dependencies.create('custom:maven:1.0') {
exclude group: 'foo', module: 'bar'
}
config.dependencies.add(dep)
Set<File> files = config.files
I do a similar thing in a gradle plugin here
References
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/Configuration.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/DependencySet.html
https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html

How do I set task properties in a Gradle Plugin

I am creating a gradle plugin to apply the sonar-runner plugin and default many of the values such as the sonar host URL and the sonar JDBC URL. I cannot figure out how to set the properties though.
When I set this up in build.gradle I use:
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
My gradle plugin looks like:
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin: 'sonar-runner'
project.configurations {
sonarRunner {
sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
}
}
With this setup I get a No signature of method exception. How should I be setting these properties?
I discovered that I could use project.getExtensions().sonarRunner.sonarProperties{ ... } to set the sonar properties. See example below.
class MySonarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.apply plugin:'sonar-runner'
project.getExtensions().sonarRunner.sonarProperties {
property 'sonar.host.url', 'http://mySonar.company.com'
property 'sonar.jdbc.url', 'jdbc:mysql://127.0.0.1:1234/sonar'
}
}
}
Thank you #mikerylander and #ravikanth! I also had tried the setProperty and .properties solutions but they didn't work for me.
The really tricky thing was that autocomplete did not find the "sonarqube" portion of project.getExtensions().sonarqube.properties for me so I never got to this solution without your post.
I wrote a custom Gradle plugin to run sonarqube for a multi-module Android project and your post helped me. Below is my full custom plugin. Since the plugin is designed to be included in the build.gradle of any submodule of my Android project I prepended "my_product" ${project.path} but of course you can use any values here.
Here is my complete plugin code in case its helpful:
package com.example.gradle.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
class MySonarCodeCoveragePlugin implements Plugin<Project> {
private Project project
void apply(Project project) {
this.project = project
project.apply plugin: 'org.sonarqube'
project.getExtensions().sonarqube.properties
{
property "sonar.sources", "${project.projectDir}/src/main"
property "sonar.organization", "my_org"
property "sonar.projectKey", "my_product${project.path}"
property "sonar.projectName", "my_product${project.path}"
property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/jacocoTestReport/jacocoTestReport.xml"
property "sonar.scanner.metadataFilePath", "${project.buildDir}/sonar/report-task.txt"
}
}
}

How to configure a DSL style gradle plugin from a plugin extension

I have implemented a Gradle plugin using the Gradle DSL style. The plugin is adding multiple aspects such as a adding a custom task, and configuring more other tasks. Overall, the plugin is generating some metadata property file in a source folder that must be configured by a plugin extension.
apply plugin: 'artifactMetadata'
// configure the path for the metadata
artifactMetadata {
destinationDirectory = "src/main/generated/otherlocation/resources"
}
I have been able to figure out how to configure the task using the extension properties, however it's tricking me with the remaining stuff. What is a good approach to configure the source set, the clean task and the idea plugin (see the #n: TODO comments in the plugin code below)? The implementation below will always use the default value, not the one injected through the plugin extension.
class ArtifactMetadataPlugin implements Plugin<Project> {
public static final String EXTENSION_NAME = 'artifactMetadata'
public static final String TASK_NAME = 'generateArtifactMetadata'
void apply(Project project) {
createExtension(project)
project.configure (project) {
task (TASK_NAME, type: GenerateArtifactMetadata) {
group = project.group
artifact = project.name
version = project.version.toString()
}
sourceSets {
main {
// #1:TODO to get the plugin extension property current value here output.dir(project.artifactMetadata.destinationDirectory, builtBy: TASK_NAME)
resources.srcDirs += file(project.artifactMetadata.destinationDirectory)
}
}
clean {
// #2:TODO get the plugin extension property here
delete file(project.artifactMetadata.destinationDirectory)
}
if (project.plugins.hasPlugin(IdeaPlugin)) {
idea {
module {
// #3:TODO get the plugin extension property here
sourceDirs += file(project.artifactMetadata.destinationDirectory)
}
}
}
}
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION_NAME)
project.tasks.withType(GenerateArtifactMetadata).all { task ->
task.destinationDirectory = project.file(extension.destinationDirectory)
}
}
}
private static void createExtension(Project project) {
def extension = project.extensions.create(EXTENSION_NAME, ArtifactMetadataPluginExtension)
extension.with {
destinationDirectory = "src/main/generated/artifactinfo/resources"
}
}
}

Gradle custom plugin : add dependency from extension object

I'm trying to write a plugin which adds dependencies to project.dependencies according to informations gathered in the plugin extension object. But it seems to be impossible.
Indeed, the data from extension object is only available in a new task or in project.afterEvaluate closure, but dependencies added in those places are ignored.
The following code tries to add the dependency in afterEvaluate but the dependency is ignored :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
project.afterEvaluate {
def version = project.myplugin.version
project.dependencies.add("compile", "org.foo:bar:$version") // --> ignored
}
}
}
class MyPluginExtension {
def version
}
In the following code the dependency injection works but I don't have access to the extension object :
apply plugin: MyPlugin
myplugin {
version '1.0'
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('myplugin', MyPluginExtension)
def version = project.myplugin.version // == null
project.dependencies.add("compile", "org.foo:bar:$version") // --> fail because $version is null
}
}
class MyPluginExtension {
def version
}
Is there a solution ?
Update: I managed to figure this out since my original answer. The way do this is to add a DependencyResolutionListener in which you add the dependencies and then remove the listener so it doesn't try to add them on later resolution steps.
compileDeps = project.getConfigurations().getByName("compile").getDependencies()
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
compileDeps.add(project.getDependencies().create("org.foo:bar:$version"))
project.getGradle().removeListener(this)
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {}
})
I have a working example of a plugin that uses this here
Original Answer:
This is also late but for anyone dropping in. With the latest gradle (2.6 at the time of writing), you can add a DependencyResolutionListener and add any dependencies before dependencies are resolved.
project.getGradle().addListener(new DependencyResolutionListener() {
#Override
void beforeResolve(ResolvableDependencies resolvableDependencies) {
depsToAdd.each { dep ->
compileConfig.getDependencies()
.add(project.getDependencies().create(dep))
}
}
#Override
void afterResolve(ResolvableDependencies resolvableDependencies) {
}
})
However, as of this writing I was having some issues getting this to work with Android Studio IDE. The problem is tracked in my question here
I originally implemented this solution using the DependencyResolutionListener approach by Saad. However, the listener itself is called only when something iterates over the configuration associated with the dependency. For example, if you want to dynamically add a dependency to compile, you have to make sure that something later on does something like:
project.configurations.compile.each {
...
}
But this is something that happens as a matter of course, since compile is a known configuration for any project that uses the java plugin. However, if you are using a custom configuration (as I was), then the listener approach won't work unless you explicitly iterate over your custom configuration.
I was able to find a better way to do this, and within afterEvaluate as the OP originally wanted. I'm using a custom configuration here, but I don't see a reason why it wouldn't work for compile either:
project.afterEvaluate {
def version = project.myPlugin.version
project.configurations.myConfig.dependencies.add(
project.dependencies.add("myConfig", "org.foo:bar:$version")
)
}
Of course, at some point something still has to iterate over the dependencies for them to actually get resolved.
The easiest way to do this:
project.dependencies {
delegate.compile("com.android.support:appcompat-v7:25.0.1")
}
Don't know if that's still relevant, but you can workaround this by explicitly adding your compile configuration to Java classpath in doFirst:
variant.javaCompile.doFirst {
variant.javaCompile.classpath += project.configurations.myconfiguration
}

Resources