I have the following gradle plugin that does the job of starting up a java process. The code for this lives under a file named startAppServerPlugin.gradle under the project's buildSrc directory.
The code of the plugin looks like this:
repositories.jcenter()
dependencies {
localGroovy()
gradleApi()
}
}
public class StartAppServer implements Plugin<Project> {
#Override
void apply(Project project) {
project.task('startServer', type: StartServerTask)
}
}
public class StartServerTask extends DefaultTask {
String command
String ready
String directory = '.'
StartServerTask(){
description = "Spawn a new server process in the background."
}
#TaskAction
void spawn(){
if(!(command && ready)) {
throw new GradleException("Ensure that mandatory fields command and ready are set.")
}
Process process = buildProcess(directory, command)
waitFor(process)
}
private waitFor(Process process) {
def line
def reader = new BufferedReader(new InputStreamReader(process.getInputStream()))
while ((line = reader.readLine()) != null) {
logger.quiet line
if (line.contains(ready)) {
logger.quiet "$command is ready."
break
}
}
}
private static Process buildProcess(String directory, String command) {
def builder = new ProcessBuilder(command.split(' '))
builder.redirectErrorStream(true)
builder.directory(new File(directory))
def process = builder.start()
process
}
}
I'm trying to figure out a way of having this imported into my main build.gradle file due everything I tried so far has been unsuccessful.
So far I have tried this:
apply from: 'startAppServerPlugin.gradle'
apply plugin: 'fts.gradle.plugins'
But it has been failing. I've tried searching online for examples of doing what I need to do but so far I've been unsuccessful. Can anyone please provide a hint as to how I'm supposed to do so?
The buildSrc folder is treated as an included build, where the code is compiled and put on the classpath of the surrounding project. The actual build.gradle file in buildSrc is only used for compiling that project, and the things you put in it will not be available elsewhere.
You are supposed to create your classes as a normal Java/Groovy/Kotlin project under buildSrc. I don't know if you can use the default package, but it is generally best practice to have a package name anyway.
For example, your StartAppServer plugin should be in buildSrc/src/main/groovy/my/package/StartAppServer.groovy. Then you can apply it in your build scripts with apply plugin: my.package.StartAppServer.
There are a lot of good examples in the user guide.
You are on the right path. The first order of business is to import the external gradle build using:
apply from: 'startAppServerPlugin.gradle'
Then you can apply the plugin with:
apply plugin: StartAppServer
See Script Plugins and Applying Binary Plugins
Related
Trying to communicate with LibGDX project per Javascript with JsInterop. I am following the "Exporting a Java type to JavaScript" example here. It does not work: Uncaught ReferenceError 'com' is not defined. I am not getting any errors with gradle though.
I have already:
checked that generateJsInteropExports is enabled:
My GdxDefinition.gwt.xml:
<module rename-to="html">
<inherits name='com.badlogic.gdx.backends.gdx_backends_gwt' />
<inherits name='com.badlogic.gdx.physics.box2d.box2d-gwt' />
<inherits name='myapp' />
<entry-point class='com.mypackage.myapp.client.HtmlLauncher' />
<set-configuration-property name="gdx.assetpath" value="../android/assets" />
<set-configuration-property name='xsiframe.failIfScriptTag' value='FALSE'/>
<set-configuration-property name='generateJsInteropExports' value='true'/>
<set-property name="user.agent" value="safari"/>
</module>
I was thinking, maybe the entry point HtmlLauncher should be also a #JsType, but this did not work either.
Also checked that generateJsInteropExports is enabled in GdxDefinitionSuperdev.gwt.xml
Accessing the class in the browser console in different ways like:
.
new com.mypackage.myapp.client.Test();
new Test(); // when setting namespace to global
$wnd.Test(); // JSNI syntax
I am compiling like that:
gradlew.bat html:dist --daemon -generateJsInteropExports=true
My class (Right in the html module, also tried in core module, still does not work) looks like that:
package com.mypackage.myapp.client;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsType;
#JsType(namespace = JsPackage.GLOBAL)
public class Test {
public String name;
public Test(String name) {
this.name = name;
}
public void sayHello() {
return "Hello" + this.name;
}
}
I am running out of ideas. Can somenody help me figuring out what to do so that it works.
Some information that might be useful:
Code from my html.gradle:
gwt {
gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend
//...
modules 'com.mypackage.myapp.GdxDefinition'
devModules 'com.mypackage.myapp.GdxDefinitionSuperdev'
project.webAppDirName = 'webapp'
compiler {
strict = true;
disableCastChecking = true;
}
}
import org.wisepersist.gradle.plugins.gwt.GwtSuperDev
//...
task superDev (type: GwtSuperDev) {
dependsOn startHttpServer
doFirst {
gwt.modules = gwt.devModules
}
}
//...
Code from my project gradle
buildscript {
//...
dependencies {
classpath 'org.wisepersist:gwt-gradle-plugin:1.0.6'
//...
}
}
project(":html") {
apply plugin: "gwt"
apply plugin: "war"
dependencies {
compile project(":core")
compile "com.badlogicgames.gdx:gdx-backend-gwt:$gdxVersion"
compile "com.badlogicgames.gdx:gdx:$gdxVersion:sources"
compile "com.badlogicgames.gdx:gdx-backend-gwt:$gdxVersion:sources"
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion:sources"
compile "com.badlogicgames.gdx:gdx-box2d-gwt:$gdxVersion:sources"
compile "com.google.jsinterop:jsinterop-annotations:1.0.1"
}
}
project(":core") {
apply plugin: "java"
dependencies {
//...
compile "com.google.jsinterop:jsinterop-annotations:1.0.1"
}
}
//...
UPDATE 1
I checked Colin Alworth's answer and the links he posted. It still does not work. I changed:
html.gradle
gwt {
gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend
maxHeapSize="1G" // Default 256m is not enough for gwt compiler. GWT is HUNGRY
minHeapSize="1G"
src = files(file("src/")) // Needs to be in front of "modules" below.
modules 'com.mycompany.myapp.GdxDefinition'
devModules 'com.mycompany.myapp.GdxDefinitionSuperdev'
project.webAppDirName = 'webapp'
compiler {
strict = true;
disableCastChecking = true;
}
jsInteropExports {
shouldGenerate = true
includePatterns = ['com.mycompany.myapp.client.*']
}
}
Like it says here.
I call like: gradlew.bat html:dist --daemon and I removed the property generateJsInteropExports from GdxDefinition files since it seems wrong.
Now I get following compilation error:
Task :html:compileGwt FAILED
Unknown argument: -includeJsInteropExports
Why is that?
Big thanks to #Colin Alworth, I found out how to get it work.
html.gradle
gwt {
gwtVersion='2.8.0' // Should match the gwt version used for building the gwt backend
maxHeapSize="1G" // Default 256m is not enough for gwt compiler. GWT is HUNGRY
minHeapSize="1G"
src = files(file("src/")) // Needs to be in front of "modules" below.
modules 'com.mycompany.myapp.GdxDefinition'
devModules 'com.mycompany.myapp.GdxDefinitionSuperdev'
project.webAppDirName = 'webapp'
compiler {
strict = true;
disableCastChecking = true;
}
// important part:
jsInteropExports {
shouldGenerate = true
// breaks if I use includePatterns
}
}
And also
remove <set-configuration-property name='generateJsInteropExports' value='true'/> from Gdx definition files
Not use same name in global namespace for exported classes (stupid mistake, I know)
Compile call like gradlew.bat html:dist --daemon
And the perfect result:
<set-configuration-property name='generateJsInteropExports' value='true'/>
This definitely will not work to generate those exports
gradlew.bat html:dist --daemon -generateJsInteropExports=true
I don't know gradle terrible well, but I'm all but certain this is wrong too (at least it needs a -P prefix, but I don't see that property being used in the gradle file you shared).
Instead, you need to need to pass it to your gradle plugin, both for Super Dev Mode and for the production compile task.
From a quick glance at the docs for org.wisepersist:gwt-gradle-plugin, it looks like the task will take a GwtJsInteropExportsOptions arg (working from http://gwt-gradle-plugin.documentnode.io/javadoc/org/wisepersist/gradle/plugins/gwt/GwtJsInteropExportsOptions.html and http://gwt-gradle-plugin.documentnode.io/javadoc/org/wisepersist/gradle/plugins/gwt/AbstractGwtActionTask.html#setJsInteropExports-org.wisepersist.gradle.plugins.gwt.GwtJsInteropExportsOptions-), which in my limited gradle experience will end up something like
jsInteropExports {
generate = true
}
It looks like this can go in the gwt {} block, alongside compiler {}.
Here's an issue on that plugin's tracker which discusses how to do this https://github.com/jiakuan/gwt-gradle-plugin/issues/19.
If I define a custom gradle task in buildSrc: How do I find out the relative path to the project from which the task is called?
In my buildSrc folder, I have a custom task that creates a Enum our of my message.properties file:
open class GenerateEnumTask : DefaultTask() {
#get:Input
open var inputFolder: String = "src/main/resources"
#get:Input
open val targetFilePath: String = "src/generated/kotlin/MessageCode.kt"
#get:OutputFile
val enumFile = File(targetFilePath)
#TaskAction
fun generateEnum() {
...
}
#Internal
override fun getDescription() = "This task uses downloaded property files and creates an enum kotlin file"
}
I then want to make sure the enum is generated before code compilation.
So I put this in the subproject "core", where I need the Enum.
build.gradle.kts:
tasks {
val generateEnumTask by registering(GenerateEnumTask::class)
withType<KotlinCompile> {
kotlinOptions.jvmTarget = Versions.jvmTarget
dependsOn(generateEnumTask)
dependsOn(formatKotlin)
doFirst{
println("compile kotlin in core project")
}
}
}
This does indeed work if I run gradle compileKotlin directly from the subfolder of the core project.
However, if I run the same command from the root project, the code searches for a src folder in the root directory.
Ah, the answer was simple: DefaultTask inherits from AbstractTask, which has a reference to the project that the task was called in (getProject)
This works nicely:
open var targetFolder: String = this.project.file("src/main/resources").absolutePath
I am creating basic custom tasks in Gradle and learning how to extend them to do more complicated actions (Learning from here: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html).
One of my reference projects, which I am extending to learn Gradle looks something like this
// pmd config
pmd {
ignoreFailures = false
reportsDir = file("$globalOutputDir/pmd")
toolVersion = toolVersions.pmdVersion
}
repositories {
mavenCentral()
}
task listSubProjects{
doLast{
println 'Searching in root dir `'
}
}
My question is around the pmd and repositories sections and why they don't have a clear qualifier like "task" on them but my listSubProjects requires a task qualifier? Are these inherited tasks from plugins and don't need a task qualifier?
The blocks that you see are task extensions, also discussed here.
A plugin creator can define extensions to allow users to configure a plugin:
// plugin code
class GreetingPluginExtension {
// default value
String message = 'Hello from GreetingPlugin'
}
// plugin code
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Add the 'greeting' extension object
def extension = project.extensions.create('greeting', GreetingPluginExtension)
// Add a task that uses configuration from the extension object
...
}
}
In project.extensions.create('greeting',... the greeting block to be used later in build.gradle files is defined.
Then in user build.gradle files
apply plugin: GreetingPlugin
// Configure the extension
greeting.message = 'Hi from Gradle'
// Same effect as previous lines but with different syntax
greeting {
message = 'Hi from Gradle'
}
Often the name of the extension is chosen to be the same as the plugin and/or the task, which can make things confusing.
I'm currently trying to migrate my old ant system to gradle. I'm pretty new to gradle and still learning, so maybe this is something trivial I just overlooked.
I got a file, called delete.list which contains a list of files I want to delete.
This is my code so far:
task deleteLib(type:DeleteFiles) {
deleteList = file("${buildDir}/delete.list")
}
class DeleteFiles extends DefaultTask {
#SkipWhenEmpty
#InputFile
File deleteList
DeleteFiles()
{
description = 'Deletes Libs from Integris zip'
}
#TaskAction
void delete(){
def lines = deleteList.readLines()
lines.each {
delete fileTree(dir: "${project.buildDir}", include: "${it}")
}
}
}
delete.list:
lib/java/activation.jar
lib/java/pdfbox*.jar
lib/java/fontbox*.jar
lib/java/xmpbox*.jar
lib/java/jempbox*.jar
lib/java/iText*.jar
lib/java/itext*.jar
lib/java/jakarta-poi.jar
lib/java/commons-net*.jar
lib/java/jfreechart*.jar
lib/java/jcommon*.jar
lib/java/dom4j*.jar
lib/java/xmlbeans*.jar
lib/java/jaxen*.jar
lib/java/avalon-framework*.jar
lib/java/batik-all*.jar
After googling a bit I found this solution, as my _delete.list may not exist during building phase.
My current problem is that gradle seems to have a problem with the fileTree method:
* What went wrong:
Execution failed for task ':deleteLib'.
> Could not find method fileTree() for arguments [{dir=C:\entwicklung\Testumgebung\testProjectGradle\build, include=lib/java/activation.jar}] on task ':deleteLib' of type DeleteFiles.
Have somebody an idea what I'm missing here?
Since both methods are defined on Project and groovy looks for the method defined in task you need to explicitly invoke the methods on project instance:
project.with {
delete fileTree(dir: "${project.buildDir}", include: "${it}")
}
I am trying to create a Grails plugin that creates a custom Gradle Task which can be depended on by bootRun. I would like to do something like this:
#CompileStatic
static void configureProcessConfig(Project project) {
TaskContainer taskContainer = project.tasks
if(taskContainer.findByName('processConfig') == null) {
taskContainer.create("processConfig") {
List<File> testResources = [project.file("src/test/resources")]
for (t in testResources) {
if (t.name.contains('.properties') || t.name.contains('.groovy')) {
Path originFile = t.toPath()
Path destFile = Paths.get('build/classes/main/' + t.name)
Files.copy(originFile, destFile)
}
}
}
def processConfigTask = taskContainer.findByName('processConfig')
taskContainer.findByName("bootRun")?.dependsOn(processConfigTask)
}
}
However, I can't seem to get it to work in my xxxGrailsPlugin.groovy file. I don't know where to get the Project file to call this. It doesn't create the task. I am happy to do something different, but I can't figure out how to do it. I would prefer not to write to every build.gradle file where this plugin is used, but if that's the best option, I guess I will.
Any help is appreciated. Thanks!