How do you tell Jenkins to set a property inside gradle.properties in an Android project? I've figured out how to get the properties from Gradle into Jenkins (by using the EnvInject plugin and simply entering gradle.properties into the property file field) but I also want the build number that is managed by Jenkins to be injected into the build artifact.
I also want to set the filename of the resulting artifact which, I think, needs to be injected by using archivesBaseName. But that property isn't part of gradle.properties so I wonder how do I access it?
So far I've only found solutions that change the build.gradle file (or other gradle scripts) in the Android project itself. But that's not what I want to do because that would make the Android code base rely on Jenkins.
Instead I want Jenkins to provide the build number and artifact file name to the Android project before it compiles the code.
The server runs on a Mac. Does anyone have a solution for this? Any shell/Groovy script that does the job would be welcome.
We are just wrinting a file version.properties, which is generated by jenkins "echo "VERSION_CODE=${BUILD_NUBMER}" > version.properies" script.
Then gradle script imports it as following:
// Version code is loaded from a properties file
def versionPropsFile = file('version.properties')
if (versionPropsFile.canRead()) {
def Properties versionProps = new Properties()
versionProps.load(new FileInputStream(versionPropsFile))
def code = versionProps['VERSION_CODE'].toInteger()
versionCode code
}
else {
throw new GradleException("Could not read version.properties!")
}
buildConfigField "String", " some _VERSION", "\"${getVersionString()}\""
}
Related
The title says all. How do I change the working/runtime directory when using JavaFX with Gradle in Eclipse?
Basically, I have a project that requires log4j and initiates a basic logger which uses the "logs/" directory from the place the jar is run. This directory is being made in the home of the source, but I want it to be made in the "run/" directory. I'm assuming for other files that will be created, they will also have this same issue.
My build.gradle is this:
// Plugins
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.7'
}
// Repositories
repositories {
jcenter()
mavenCentral()
}
// Dependencies
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.apache.logging.log4j:log4j-core:2.12.0'
}
// JavaFX
javafx {
version = '12'
modules = ['javafx.controls']
}
mainClassName = 'net.protolauncher.backtest2.ProtoLauncher'
I am using Eclipse to run it, but this issue also occurs when just running the run task. I tried changing the Working Directory in the "Gradle Project" run configuration, but it didn't work at all (it just loaded forever).
To give an example, here's the directory of my source code: DirectoryX. Now, I made a folder in here called "run", like so: DirectoryX/run. When I run the program, I want my logs to go into DirectoryX/run/logs and similar files to go into the run directory. However, when running with Gradle my log files are being created in DirectoryX/logs.
This probably made no sense, but if it did, I really appreciate any help I can get.
After hours of searching online to no avail, I finally found a StackOverflow answer that solves the question. Turns out, JavaExec is a complicated thing, and what I was doing was specific to that, NOT JavaFx.
I'm building a Java desktop application, using JavaFX, Gradle, javafx-gradle-plugin. This application connects to a server that I also build. When I compile a release version, running gradle jfxNative, I want it to talk to the production server; but otherwise, I want it to talk to localhost.
What's the proper Java/Gradle way of handling this? Some sort of compilation profile?
You can use Gradle's source sets for this:
Sample build.gradle:
apply plugin: 'java'
sourceSets {
prod {
java {
srcDirs = ['src/main/java', 'src/prod/java']
}
}
dev {
java {
srcDirs = ['src/main/java', 'src/dev/java']
}
}
}
task devJar(type: Jar) {
from sourceSets.dev.output
manifest {
attributes("Main-Class": "MyPackage.MyClass")
}
}
task prodJar(type: Jar) {
from sourceSets.prod.output
manifest {
attributes("Main-Class": "MyPackage.MyClass")
}
}
Now you can create two configuration classes for your dev and prod versions:
src/dev/java/MyPackage/Configuration.java
src/prod/java/MyPackage/Configuration.java
All the common code will be in the main source set:
src/main/java/MyPackage/MyClass.java
MyClass can get some values from the configuration class (e.g. Configuration.getBaseUrl())
Running gradle devJar/ gradle prodJar builds one of the variants.
Note: you may need to extend jfxNative/jfxJar instead of Jar in your case.
The simplest solution: Have a configuration file containing such information.
You either compile it into the application as a java resource or place it next to the jar file so it can be easily looked up via the filesystem.
With gradle all you need to do is define two build tasks with different input properties and insert the values into your properties file with groovy templating.
application.properties in src/main/resources:
server.address=${serverAddress}
add to your build.gradle
task setProductionServerAddress {
processResources.expand([serverAddress: "https://app.example.com/v1"])
}
jfxJar.dependsOn(setProductionServerAddress)
jfxNative.dependsOn(setProductionServerAddress)
And then on the application:
Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("/application.properties"));
if (properties.getProperty("server.address").equals("${serverAddress}")) {
setUrl("http://localhost:8080/v1");
} else {
setUrl(properties.getProperty("server.address"));
}
Have it check environment variables for names of configuration files. Nothing to do with gradle or build. The same program should run properly wherever it is deployed.
See, e.g., Properties for dev and production
The easiest approach is to define a system property which specifies where the file system location for your data is. The production appserver would define one value (using java -D in the startup script), and your dev app server would define another value. Your application source would consult the system property value (using System.getProperty()) to discover the appropriate location
Also, this makes sense.
Put the information you need in JNDI - that's what it is designed for.
Consider letting your application refuse to do anything if the information is not there.
Another reference: What is the best way to manage configuration data
EDIT: Well, what you're asking is logically not possible then, it seems to me. "It should connect to production, unless a specific someone wants to connect to development, but that feature should only be available to unknown persons" The start menu is only a shortcut for running the application, so you can install a "dev" shortcut with command line settings that are read as environment vars.
I would go with the one of the "12 factor app" concept which can be read here
One of its main concept is to use system environment variable which should determine whether you are working on a prod or dev or qa env etc.
each project/environment/machine should contain its relevant env property, which after then can be retrieved through the gradle process similar to maven profile plugin.
An example for how to detect:
`if (project.hasProperty('env') && project.getProperty('env') == 'prod') {
apply from: 'gradle/production.gradle'
} else {
apply from: 'gradle/development.gradle'
}`
more on this approach using gradle can be found: gradle profile
In my opinion and like others have suggested, this has little to do with the build and more to do with Run Time.
Therefore you could resort to checking for some kind of run time flag - a convenient and often used approach is to use System Properties.
On your Dev box, you could set an Environment variable - lets say FX _DESKTOP_APP_ENV = DEV or some such.
From your code you can look this up and decide the URL you want to use.
String env = System.getenv("FX _DESKTOP_APP_ENV");
String url = env == null ? "Production" : env;
On windows systems you can set up your system environment variables like so -- enter link description here
On *nix systems enter link description here
Hope this helps
You need to pick a configuration scheme (if JavaFX doesn't pick one for you).
I like https://github.com/typesafehub/config .
The config library will have instructions on how to make your "production" config differ from your "development" config.
See also JavaFX:Editable Configuration Files After Packaging
What's the proper Java/Gradle way of handling this? Some sort of compilation profile?
No, I would strongly recommend against compiling different code for production v.s. test. It will invalidate your testing. This should be handled in configuration, not in code. The conventional Java way of doing this is with configuration files (which can be compiled into the JAR as resources).
How to do this with Typesafe Config
I've had a bit of a look, and I am surprised not to find a good quality tutorial I can link you to here, sorry (I found a few rubbish ones). Perhaps this question will become a reference for others.
I would do something like this:
Create a "dev" and "prod" config files, along the lines of https://stackoverflow.com/a/33261928/8261
Arrange for your app to use the "dev" config file when running in your IDE and "prod" config file when running from a compiled JAR, as per Can you tell on runtime if you're running java from within a jar?
My guess as to why there aren't many tutorials for this is that all application or web Frameworks handle this for you.
In Android Studio, there is a specific file (src/org/luaj/vm2/lib/jse/JavaMethod.java) that I need to overwrite from a package that is pulled in via Gradle (dependencies {compile 'org.luaj:luaj-jse:3.0.1'}).
I copied the file into my source directory with the exact same path and made my changes to it. This was working fine for an individual JUnit test case that was using it. It also looks like it is working for a normal compile of my project (unable to easily confirm at the moment).
However, when I try to run all my tests at once via a configuration of ProjectType="Android Tests", I get Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lorg/luaj/vm2/lib/jse/JavaMethod$Overload;.
Is there a specific task or command that I need to add to my Gradle file to make sure the project selects the file in my local source directory? I tried the Copy task and the sourceSets->main->java->exclude command, but neither seemed to work (I may have done them wrong). I also tried the "exclude module/group" directive under "compile" from this post.
The non-default settings for the Run/Debug Confirmation:
Type=Android Tests
Module=My module
Test: All in package
Package: "test"
All my JUnit test cases are in the "test" package.
Any answer that gets this to work is fine. If not Gradle, perhaps something in the android manifest or the local source file itself.
[Edit on 2016-07-24]
The error is also happening on a normal compile when my android emulator is running lower APIs. API 16 and 19 error out, but API 23 does not.
issue: when linking your app the linker finds two versions
org.luaj:luaj-jse:3.0.1:org.luaj.vm2.lib.jse.JavaMethod and
{localProject}:org.luaj.vm2.lib.jse.JavaMethod
howto fix: tell gradle to exclude org.luaj:luaj-jse:3.0.1:org.luaj.vm2.lib.jse.JavaMethod from building
android {
packagingOptions {
exclude '**/JavaMethod.class'
}
}
I have not tried this with "exclude class" but it works for removing duplicate gpl license files a la "COPYING".
If this "exclude" does not work you can
download the lib org.luaj:luaj-jse:3.0.1 to the local libs folder,
open jar/aar with a zip-app and manually remove the duplicate class.
remove org.luaj:luaj-jse:3.0.1 from dependencies since this is now loaded from lib folder
I am not completely sure I understand your problem; however, it sounds like a classpath ordering issue, not really a file overwrite one.
AFAIK, gradle does not make a 'guarantee' on the ordering from a 'dependencies' section, save for that it will be repeatable. As you are compiling a version of file that you want to customize, to make your test/system use that file, it must come earlier in the classpath than the jar file it is duplicated from.
Fortunately, gradle does allow a fairly easy method of 'prepending' to the classpath:
sourceSets.main.compileClasspath = file("path/to/builddir/named/classes") + sourceSets.main.compileClasspath
I don't know enough about your system to define that better. However, you should be able to easily customize to your needs. That is, you can change the 'compile' to one of the other classpath (runtime, testRuntime, etc) if needed. Also, you can specify the jarfile you build rather than the classes directory if that is better solution. Just remember, it may not be optimal, but it is fairly harmless to have something specified twice in the classpath definition.
This is rather convoluted but it is technically feasible. However it's not a single task as asked by the poster:
Exclude said dependency from build.gradle and make sure it's not indirectly included by another jar (hint: use ./gradlew dependencies to check it)
create a gradle task that downloads said dependency in a known folder
unpack such jar, remove offending .class file
include folder as compile dependency
If it's safe to assume that you're using Linux/Mac you can run a simple command line on item 3, it's only using widely available commands:
mkdir newFolder ; cd newFolder ; jar xf $filename ; rm $offendingFilePath
If you don't care about automatic dependency management you can download the jar file with curl, which I believe to be widely available on both linux and mac.
curl http://somehost.com/some.jar -o some.jar
For a more robust implementation you can substitute such simple command lines with groovy/java code. It's interesting to know that gradle can be seen as a superset of groovy, which is arguable a superset of java in most ways. That means you can put java/groovy code pretty much anywhere into a gradle.build file. It's not clean but it's effective, and it's just another option.
For 4 you can have something along either
sourceSets.main.java.srcDirs += ["newFolder/class"]
at the root level of build.gradle, or
dependencies {
. . .
compile fileTree(dir: 'newFolder', include: ['*.class'])
. . .
This is what I ended up adding after Fabio's suggestion:
//Get LUAJ
buildscript { dependencies { classpath 'de.undercouch:gradle-download-task:3.1.1' }}
apply plugin: 'de.undercouch.download'
task GetLuaJ {
//Configure
def JARDownloadURL='http://central.maven.org/maven2/org/luaj/luaj-jse/3.0.1/luaj-jse-3.0.1.jar' //compile 'org.luaj:luaj-jse:3.0.1'
def BaseDir="$projectDir/luaj"
def ExtractToDir='class'
def ConfirmAlreadyDownloadedFile="$BaseDir/$ExtractToDir/lua.class"
def JarFileName=JARDownloadURL.substring(JARDownloadURL.lastIndexOf('/')+1)
def ClassesToDeleteDir="$BaseDir/$ExtractToDir/org/luaj/vm2/lib/jse"
def ClassNamesToDelete=["JavaMethod", "LuajavaLib"]
//Only run if LuaJ does not already exist
if (!file(ConfirmAlreadyDownloadedFile).exists()) {
//Download and extract the source files to /luaj
println 'Setting up LuaJ' //TODO: For some reason, print statements are not working when the "copy" directive is included below
mkdir BaseDir
download {
src JARDownloadURL
dest BaseDir
}
copy {
from(zipTree("$BaseDir/$JarFileName"))
into("$BaseDir/$ExtractToDir")
}
//Remove the unneeded class files
ClassNamesToDelete=ClassNamesToDelete.join("|")
file(ClassesToDeleteDir).listFiles().each {
if(it.getPath().replace('\\', '/').matches('^.*?/(?:'+ClassNamesToDelete+')[^/]*\\.class$')) {
println "Deleting: $it"
it.delete()
}
}
}
}
I'll upload a version that works directly with the jar later.
Another solution if we got then source jar:
task downloadAndCopy {
def downloadDir = "${buildDir}/downloads"
def generatedSrcDir = "${buildDir}/depSrc"
copy {
from(configurations.detachedConfiguration(dependencies.add('implementation', 'xxx:source')))
file(downloadDir).mkdirs()
into(downloadDir)
}
println("downloading file into ${downloadDir}")
fileTree(downloadDir).visit { FileVisitDetails details ->
if (!details.file.name.endsWith("jar")) {
println("ignore ${details.file.name}")
return
}
println("downloaded ${details.file.name}")
def srcFiles = zipTree(details.file).matching {
include "**/*.java"
exclude "**/NeedEclude*java"
}
srcFiles.visit {FileVisitDetails sourceFile ->
println("include ${sourceFile}")
}
copy {
from(srcFiles)
into(generatedSrcDir)
}
}
}
and remember to add depSrc to srcDirs
android {
sourceSets {
`main.java.srcDirs = ['src/main/java', "${buildDir}/depSrc"]
}
}
I have a Java TestNG project set up with gradle running test suites. Currently, most of the tests are pulling certain parameters out of a constants.properties file. How would I modify these on the command line when running the gradle task? Will gradle -DapplicationKey=0000 replace the line applicationKey=1234 in my constants.properties file?
Edit:
To be a bit more clear about the situation and the question, my constants.properties file contains around 400 to 500 properties that are already defined. I would like to avoid rewriting those properties in gradle completely. I just want to override those properties from the command line when Jenkins runs the same commands as part of that build job.
You want to pass system properties to the JVM used to run tests. Here is a way to configure all tasks of type Test:
tasks.withType(Test) {
systemProperty 'applicationKey', System.getProperty('applicationKey', '1234')
}
or just one task
test {
useTestNG()
systemProperties = [
applicationKey: System.getProperty('applicationKey', '1234')
]
}
You can also copy all system properties from the Gradle environment to the child virtual machine with systemProperties = System.getProperties()
My configuration:
Gradle installed via Homebrew (Mac OS), Intellij Idea 13.
Preferences:
"Use default Gradle wrapper"
and gradle home: /Users/my_username/.gradle
For the following project:
/
build.gradle
gradle.properties
The gradle.properties file contains a variable NAME = PJ.
I also have a gradle.properties under /Users/my_username/.gradle/gradle.properties having a variable TEST=Hello.
For the given task, gradle prints Hello and null
task hello << {
println TEST
println NAME
}
Which means that it does not read by default the gradle.properties in the same directory. Is it a normal behavior or do I miss something in path variable so can the build.gradle script can read it?
A work-around I found is to add:
file("gradle.properties").withInputStream {
stream -> new Properties().load(stream)
}
When I used android studio the .properties file of the same dir was automatically imported.
PS: Although TEST var gets printed I get a warning for Cannot resolve symbol. I saw around and this is typical error (?).
With gradle 2.0 it works perfectly fine:
$HOME/.gradle/gradle.properties
TEST=Hello
gradle.properties
NAME = PJ
build.gradle
task hello << {
println TEST
println NAME
}
When gradle hello run I get the following output:
:hello
Hello
PJ
BUILD SUCCESSFUL
Total time: 2.992 secs
Tested with intelliJ 13.1.4 built-in Gradle 1.7 and downloaded Gradle 2.0 and 2.1 version
Works fine. Make sure that path HOME/.gradle/ is set properly
gradle.properties:
TEST=Hello
NAME=PJ