I simply want to modify the directory where the program is run. Normally, it's run from the project root, which annoys me a little bit, because testing the program out can be quite annoying, since my program generates files and folders where it is being run.
A JavaExec task has a property called JavaExec#workingDir, which would be this exact property I wanted to modify to something different of my choice.
My question is: How do I modify the gradle run task in order to access this property?
You can access a property of a task by using tasks.<TaskToModify>.property = YourValue.
So, in this case, you would have to do this:
File runningDir = new File('build/run/')
runningDir.mkdirs()
tasks.run.workingDir = runningDir
The File#mkdirs() call is neccessary, since if the directories do not exist, the call to your system-dependent java executable will cause a error.
Related
I have a Gradle project which at some point in its code needs to act on a folder one level above the Gradle project's. It needs to write some data in it and create a file if it isn't there. However, the acting is the code's responsibility, all Gradle does is have a task which to run the code.
The problem is that when I run the gradle task, the jvm throws an java.nio.File NoSuchFileException.
Running the same program from Intellij's Run executes perfectly as intended, so it is not the code.
The one big difference I see is that the Intellij Run has a Working directory set at a level above my Kotlin project's, whereas Gradle is pointing to the project as root, as it should be.
I am new to Gradle and I find the documentation hard to read, and it confused me quite a lot. I assume that I need to tell somehow Gradle that the code may need to access folders on the filesystem outside the project. However, I'm not sure if that needs to be in the settings.gradle.kt or on the task level and by using with which function.
Could you please point me the right way?
To create a collection of files in a relative path, This snippet may work for you:
tasks.register('list') {
doLast {
File srcDir
// Create a file collection using a closure
collection = layout.files { srcDir.listFiles() }
srcDir= file("../$rootDir")
println "Contents of $srcDir.name"
collection.collect { relativePath(it) }.sort().each { println it }
}
}
reference website:
https://docs.gradle.org/current/userguide/working_with_files.html#sec:file_collections
As mentioned in my previous comment the problem actually boiled down to having the ability to change the working directory of the custom Gradle task.
Inside a task I ended up doing the following:
run.configure {
workingDir = File("./..")
}
This would make the scope of the working directory of the given task to be the one of its parent directory which solves the issue.
I have Jar file which dependency on another project jar. Both are thin jars and are at same location. 1st jar has manifest file which list second jar in its class-path property.
In 1st jar I am launching second jar as a process using ProcesBuilder class in java. To do so I need absolute path of second jar. In 1st jar i have class XClient
If I do XClient.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
i am getting absolute path of 1st jar. Then I can split and add the name of second jar(hard-coded) to build the absolute path
In second jar I have class XServer
If I do
XServer .class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
Its throws exception
I am not sure if I am doing the right approach but my goal is very clear I wanted to get the absolute path to the dependent jar.
Please help
I tried to use the same approach (but used File file=new File(this.getClass().getProtectionDomain().getCodeSource().toUri()) instead of getPath()) but this can fail in different ways:
when the class is inside a jar the File object points to the jar instead the folder the jar is in - so an if(file.isFile()) file=file.getParentFile(); is needed to get the directory instead of the jar file
when the jar file is loaded by something other than the usual URLClassLoader (last time I tried was back in 1.8 - and I only know that since Jigsaw the main classloader can't be cast to an URLClassLoader anymore) this may will return some unspecified result, if at all, so actual behaviour depends on the very system setup - wich can make it difficult to debug when used on a remote system not under your control
UNC paths (Windows shares) are error prone by themselfs - adding another layer on top of it (java) just add a lot of potential other pitfalls you all have to test and debug - wich often ends up you tell the client what to use and how to setup instead of design your code to follow the java principle: "write once, compile once, run everywhere" (btw: this also applies even if you "mount" a network share so you can address it by a local drive letter instead of a remote network path - but this even causes problems when you try to link two machines where one is a clone of the other)
as already mentioned as comment: "it doesn'T work" is not a usefull or meaningfull description - if you get an error message (in this case as you mentioned an exception stacktrace) post it along with the code wich produced it (if accessible)
How I solved my problem? I just ask the user for the directory / file by a swing JFileChooser. Yes, this isn't fool proof and maybe not the best way - but it works as swing still ships with SE JVM (instead of FX).
If you want to find a path use Class.getResource() and let java do the work, pretty much like crypto: don'T do your own.
Aside from all that: Your mentioned "usecase" doesn'T require what you try to do. You said that the server is already in the classpath - so it gets loaded on startup and you can access the XServer class. The easiest way instead of forking another process is to just run it in another thread. If you know wich class has the main (the manifest of the server.jar will tell you) and you can access it in classpath just do something like this:
Thread serverThread=new Thread(new Runnable()
{
public void run()
{
String[] args=Arrays.asList("required", "parameters");
XServer.main(args);
}
});
serverThread.start();
If no paramters required you can just pass an empty String array. As main() should not throw Exceptions (at least no checked ones) no exception should be needed.
Before all those comments are thrown at me: Yes, I am very well aware of possible issues with such approach like classpath issues (same classname in same packagename but different versions) and such it may be more feasible than try to figure out the absolute path and launch a fork / sub process.
Also: Starting another process may require to interact with its streams (provide required input into child process inputstream and read the child process outputstream and errorstream - otherwise the forked process may can "hang" as it waits for the pipelines to get cleared. It's a pain in the but to debug that kind of issue if it's not your own code and you can have a profiler and debugger attached to it to figure out why all just suddenly stopped to work.
If you really want to (I don't think there's any requirement forcing a "you need to") launch your server along with the client do it with a launch script outside of java but with os level stuff.
In Gradle 3.x I was able to get some xml mapping files to copy into the classes directory prior to build/jar via the following block:
copy{
from 'src/main/java/com/company/mapping'
into 'build/classes/main/java/com/company/mapping'
include '**/*.xml'
}
In Gradle 4.9 this has been deprecated in favor of:
task copyMappings(type: Copy){
from 'src/main/java/com/company/mapping'
into 'build/classes/main/java/com/company/mapping'
include '**/*.xml'
}
The copyMappings task succeeds, but build/jar does not wait for copyMappings to finish. I have tried variations on build.dependsOn and doFirst{ copyMappings } doLast{ build } but nothing seems to get me the desired effect of having the copied files in place in the 'into' path prior to jar.
This is for Windows 10.
This works for me with Gradle 4.9 on Mac OS:
apply plugin: 'java'
task copyMappings(type: Copy) {
from 'src/main/java/com/company/mapping'
into 'build/classes/main/java/com/company/mapping'
include '**/*.xml'
}
jar.dependsOn copyMappings
jar.doFirst {
assert new File("${projectDir}/build/classes/main/java/com/company/mapping/abc.xml").exists()
assert new File("${projectDir}/build/classes/main/java/com/company/mapping/def.xml").exists()
}
command line is gradle clean jar
I like to model things around source sets where appropriate as doing so let's the build work more reliably with a wide range of plugins and use cases. For example, imagine you want to run an application direct from its class files and resources rather than packaging it as a JAR first. You could make sure that the "run" task depends on the copy as well, but you'd have to do that for every instance where this is a requirement.
Source sets are the ideal solution because they have the concept of a runtime classpath, which will work for packaging, instrumentation, running, testing and so on.
With that in mind, I would go for this simple declaration and get rid of the copy task:
sourceSets {
main {
resources {
srcDir "src/main/java"
include "**/*.xml"
}
}
}
The XML files will end up in a different directory from your current approach, but that shouldn't matter unless you have tasks that assume the location rather than using the source set model to get the necessary information.
Note The above include directive applies to all the resources in src/main/resources as well. So if you have properties files or text files or anything else in there, they will be excluded. The simplest solution is to add all required resource file patterns to the include directive.
I'm going to have a lot of submodules in my main project directory x, like x/module1, x/module2...
can i avoid manually adding every single module into settings.gradle? can i somehow script it to find all the subdirectories and add them automatically?
As cricket_007 already mentioned, Gradle is based on the Groovy programming language (which is, like Java, executed in the JVM) and the settings.gradle file is nothing more but a Groovy script.
Whenever you use include 'project', the include method of a Settings instance is called, so for your goal, you could simply create a loop which iterates over all folders and calls include for each of them.
A more 'groovyesque' approach would be the usage of a closure for each subdirectory, provided by the Groovy SDK extension for the File class:
file('.').eachDir { sub ->
include sub.name
}
There are multiple ways to solve your problem, e.g. since the include method accepts an array of project path strings, you could also aggregate all required paths first and pass them all together. Simply get familiar with the Gradle docs and decide on your own, what solution suits your case the best.
In my java class I have
System.setProperty("test", drinks);
In my Ant build file I am able to execute the jave class.
How can I use the name "test" to get the result ${drinks} in Ant ??
The only way this is possible is if you use the attribute fork="false" (false being the default value) in the java Ant task. That is, if your Java program is executed in the same JVM as Ant. Otherwise, your Java program will be executed in a separate process and none of the system properties it sets will vbe accessible to Ant. Beware, though, that executing a non-forked Java program may (and often will) have undesired side effects on the rest of the Ant build.
If the property is set at the command line e.g. ant -Dtest=drinks it will be available from the build system as ${test}, however if you're calling this property by means of executing Java code, you will need to find a way to get the value out from the spawned Java process into a new variable, usually using a combination of System.out.println(...) and <exec ...> tasks's outputproperty and parsing as needed.