If you would try to run the last master commit, you will get a FileNotFoundException, which is caused by a missing file from the ressource folder. The directory structure is as follows:
jComponent Library Sorcerer
-|-settings.gradle
-|-ressources (Contains stuff like pictures or properties files for multilanguage support)
-|-jComponent Library Sorcerer
--|-build.gradle
--|-src
My programm tries to get a file, asserted in the same directory as the programm itself. It does not find the file, and fires the exception.
I could fix this by copying the content from ‘jComponent Library Sorcerer/ressources’ into ‘jComponent Library Sorcerer/jComponent Library Sorcerer’, but this seems to messi for me.
I would like more to have a custom folder where my program resides, let’s call this folder ‘app’. Before the program runs, I would clean the app folder and copy the content from ressources into the app folder. Like this:
jComponent Library Sorcerer
-|-settings.gradle
-|-ressources (Contains stuff like pictures or properties files for multilanguage support)
-|-jComponent Library Sorcerer
--|-build.gradle
--|-src
--|-app (Programm should run from here)
I think I will get the run task manipulation, but I stuck with establishing the app folder. I found some different solutions (e.g. manupulating the settings file like (projectname).buildDir=‘NewDir’), but everything I did failed here. Possibly it is because a composite project?
Has anyone a nice idea?
PS: After fixing this, it will lead to another exception caused by controlsfx, but this is another story.
From a very quick and cursory look, you apply the application plugin, so you should configure the main distribution to put the files where you need them, then don’t use the run task directly (or reconfigure it to do the same) but instead use the installDist task which assembles the application like in the final distribution, just exploded in build/install/<appname> and then use the start scripts in there to run the application.
The documentation of the application and distribution plugins should give you more detailed information.
Btw. keep in mind, that if you use new File(...) with a relative path in a Java program, it will not be relative to the program directory or the start script or the JAR. It will be relative to the current working directory of the caller, so it could be any directory. new File(...) with a relative paths is usually only appropriate for a path given by the user on the command line.
Thank you very much, the distribution plugin seems to run nicely. I’m not really sure if it works because the structure is a little bit different as I made it before I switched to gradle. Earlier, the .jar resides directly in the dist folder. But nevertheless, I get the ressources folder content where I want to have it, so this is fine.
Thanks also for the advise with the new File(…)-operation. In fact, I use a call new File("."), I have never thought about to call the program from another path, I will elaborate it again.
As I understand you right, I have to customize the run task in way, that this task copies the files by itself into the directory, where the gradle.build is? Or did you mean to run from a distribution?
Edit: I customized the build-task (and the clean-task, too, to revert the copy operation). It’s not bulletproof enough because the poor new File-operation and I will fix it soon.
But now, I get a new error (not the controlsfx modul error I expected), but this seems to be stuff for a new thread.
Thank you for you help (and hopefully see you in the thread for the new problem).
As I understand you right, I have to customize the run task in way, that this task copies the files by itself into the directory, where the gradle.build is? Or did you mean to run from a distribution?
I customized the build-task (and the clean-task, too, to revert the copy operation).
No, that’s not at all what I meant.
What I meant is something like
val run by tasks.existing(JavaExec::class) {
dependsOn(tasks.installDist)
classpath = files(tasks.installDist.map { "${it.destinationDir}/lib/*" })
// mitigate `new File(<relative path>)` usage
workingDir(tasks.installDist.map { it.destinationDir })
}
Thank you very much for the code snippet, unfortunately gradle comes back with some errors if I paste it in. The errors are about “unknown method val()” and if I remove ‘val’, gradle complains about a missing property ‘by’. Do I miss a plugin?
I committed the changes in my gradle file, just if you want to take a look on it.
Oh dear…and this, where I do not have any idea about Kotlin code. I have even no idea about groovy code, these few gradle lines are my only groovy experience. Let’s say how far I get it…
Ok, I think I understand your code snippet, but I fail to translate it into groovy language.
It seems there is no method like “existing” in groovy, but as far I understand, tasks.named() should also do the job. But in the Java plugin documentation, I can’t find something useful to get all execution tasks (but compiling, testing, … as well). I’m not sure if compiling will do the job or does bring any unintended actions. What should I use best?
Edit:
A “tasks.named(‘compileJava’)” results in a fail. Not really sure if it is the method declaration or some of the next lines…
Oh dear…and this, where I do not have any idea about Kotlin code. I have even no idea about groovy code, these few gradle lines are my only groovy experience.
Then you should definitely switch to Kotlin DSL and should have used it right from the start. With Kotlin DSL (which now also is the default choice and recommendation officially), you immediately get typesafe build scripts, much more helpful error messages if you mess up the syntax and amazingly better IDE support if you use a proper IDE like IntelliJ IDEA including code-completion, documentation, …
No signature of method: org.gradle.api.tasks.Sync.map() is applicable for argument types
In Kotlin DSL the accessors like tasks.installDist properly use lazy API to not destroy task configuration avoidance and thus return a TaskProvider<...>.
In Groovy DSL due to backwards compatibility, those accessors use eager API and thus should not be used. They directly give the task instance, so make the task be realized and configured even if not necessary and you directly get a task instance, so you of course cannot use map as that is a method of Provider.
Oh, thats new for me, I thought that Groovy was the default script language.
I will keep that in mind, for the next Gradle project I need to set up (no idea if this day will come) or for bigger changes.
But now I will try your code snippet…let’s hope that it will work and I get the last exception I expect (some trouble with a library and the Java module system…).
Oh, thats new for me, I thought that Groovy was the default script language.
It was, up to Gradle 8.2: Kotlin DSL is Now the Default for New Gradle Builds
But unless you had a good reason like some monster-setup that majorly degrades performance, I already use and recommend it since years.
The pros imho always outweighed the cons.
I will keep that in mind, for the next Gradle project I need to set up (no idea if this day will come) or for bigger changes.
Never too late to switch over. You can even do it build script by build script if you have many and don’t want to switch as a big-bang. Migrating build logic from Groovy to Kotlin
But now I will try your code snippet…let’s hope that it will work and I get the last exception I expect (some trouble with a library and the Java module system…).
I decided to follow our recommanation and changed every build script to Kotlin. It seemed to be very simple, mostly change the string designators and put some brackets in, and I need to say that I’m much more familiar with Kotlin syntax. It’s much clearer to me and better than groovy gap style, it was good to switch.
Two things are still unclear to me:
I replaced the "apply plugin : ‘Java’ " with “apply([plugin: “java”])”, is that right?
I still struggling with the out commented line in your code snippet. It seems that Kotlin can’t handle “new File”, what to do?
I make a new upload, so you can see it by yourself if neccessary.
I replaced the "apply plugin : ‘Java’ " with “apply([plugin: “java”])”, is that right?
As it is not valid Kotlin code and thus would not compile, no.
The correct replacement would be apply(plugin = "java").
Though both variants are legacy and discouraged.
Instead you should use the plugins DSL like
plugins {
java
}
I still struggling with the out commented line in your code snippet. It seems that Kotlin can’t handle “new File”, what to do?
Don’t uncomment explanatory text.
It just explains what the line below is there for.
That line does not even look like valid code.
I already wondered about that in your Groovy DSL try but didn’t think you intentionally commented in that text line. o_O
(and yes, in Kotlin it would be File(...) instead of new File(...))
No, it just sets the working dir of the JavaExec task.
As I explained, new File(<relativePath>) always resolves files relative to the working directory.
So if you call the program from a non-expected working directory, it will not find the correct file.
And by setting the working dir for the JavaExec task, you make sure it is always the intended working dir so that those erroneous File usages find the correct files if you use that JavaExec task.
You should still fix those usages and can then remove that workaround.
Because even with that work-around, you will have the same problem in the end product if someone calls it from an “unexpected” working directory.
I still get some errors, but you was right: the Kotlin error messages are much better and more helpfully then the almost useless groovy output and I could fix the most errors by myself. Changing to Kotlin was a very good advise.
Here are the errors, I didn’t found a solution for, maybe you know a solution (I know, its just a lack of knowledge and training in Kotlin):
Script compilation errors:
Line 40: applicationDefaultJvmArgs = ["--add-opens", "java.base/java.util=ALL-UNNAMED", ]
^ Type mismatch: inferred type is Array<String> but (Mutable)Iterable<String!>! was expected
Line 40: applicationDefaultJvmArgs = ["--add-opens", "java.base/java.util=ALL-UNNAMED", ]
^ Unsupported [Collection literals outside of annotations]
Line 51: workingDir = file("$buildDir/dist")
^ Unresolved reference: workingDir
Line 54: run.doFirst {
^ Variable 'run' must be initialized
Line 54: run.doFirst {
^ Unresolved reference: doFirst
Dam, I hope I get this bad guy running soon. It’s no fun to think about that I have first public beta tests in sight, and stuck with a build system.
Unfortunately, tasks.run { ... } does not work like the other task accessors, as there is a Kotlin built-in run extension function that wins.
You can also see that in IntelliJ if you look at the displayed this, it is of type TaskContainer, not JavaExec.
There are various mitigation strategies though.
You can for example use tasks.named<JavaExec>("run") { ... }.
Or you can use tasks.run.configure { ... }.
Or you can use an import alias to rename the built-in run extension function like import kotlin.run as run_.
My programm creates a settings file at its first start, but it seems there are issues with loading this file. (Yes, I will fix that soon, but before I can fix I need to get it running.) My actual workaround is to delete this file, but maybe it is not nesseccary with the distribution plugin.
Nevertheless, I outcommented this part, the first run should always work because there is no settings file. That leads to the next fail I expected and waited for it. The error message is:
I can not see it, but I’m quietly sure that the bad guy is the controlsfx dependency. I got this error as I changed to higher Java version, and fixed it in Netbeans Ant by inculding the controlsfx library as a build. Have you a better idea?
I update the git repo in a few minutes, so you may take a look by yourself.