Gradle: Add dependency from Java to Native compilation - java

I try to setup gradle for a proper JNI compilation, so I need to build first a shared library (with the c plugin), and then compile and test the java code (which consumes the library).
Here a sample of the build.gradle, related to the native compilation:
model {
components {
yli(NativeLibrarySpec) {
sources {
c {
source {
srcDir 'src/main/c'
include "Yli.c"
commonFolders.each {
include "$it/**/*.c"
}
}
}
}
buildTypes {
release
}
}
}
}
What is the best way to tell gradle that the compileJava should wait for the build of the NativeLibrarySpec?
Edit: When I try to add
compileJava.dependsOn(yliSharedLibrary)
I have the following error during gradle build:
* What went wrong:
A problem occurred evaluating root project 'yli'.
> Could not get unknown property 'sharedLibrary' for root project 'yli' of type org.gradle.api.Project.
Note: I used the command 'gradle tasks' in order to found the name of the task: 'yliSharedLibrary'.

I played around with this and discovered that you can access the tasks created by the software model within closures. For example, if you want to depend on one of the native tasks, you can do so with:
compileJava.dependsOn { yliNativeCompileTask }
Of course, if you want the Java task to come after the native one, but not force an actual dependency between them, you can use mustRunAfter():
compileJava.mustRunAfter { yliNativeCompileTask }
This syntax also works for declared inputs and outputs:
compileJava.inputs.files { yliNativeCompileTask }
Note that if you tie the inputs of a task to the outputs of another task, you don't have to explicitly declare a dependsOn. Gradle infers the task dependency.
Disclaimer I don't know if this is the correct way to do this, or how far you can take this approach.
One final thing: the old native software model is being replaced by a new set of native plugins based on Gradle's original model. It should be much easier to integrate Java projects with these new plugins, but you may want to wait until the plugins have been fully fleshed out before attempting a migration.

Related

Fail gradle build if file does not exist [duplicate]

I am starting to use Gradle for an Android application. I would like the build to fail if the developer fails to create a file at a specific location such as ./src/res/values/specialfile.xml
A bit of searching led me to believe that a .doFirst would work
android.doFirst {
assert file("./src/res/values/specialfile.txt").exists()
}
However, Gradle says "unsupported Gradle DSL method found: 'doFirst()'!"
What can I do to assert a file's existence?
doFirst only exists on tasks object. android is not a task.
If would want this test to always be done even if the developer doesn't try to build (for example when running the tasks task), you should simply put in your build.gradle
assert file("./src/res/values/specialfile.txt").exists()
However this is really not recommended as this would be executed even for non build tasks, or even when the model is built for IDE integration.
There is a task called preBuild that is executed before anything in the android build, so you can hook your test to it, either through another task or through doFirst:
preBuild.doFirst {
assert file("./src/res/values/specialfile.txt").exists()
}
One may want to check in a module build.gradle ...
below the plugins configuration block
above the android configuration block
eg. in order to determine which Gradle plugins to apply:
plugins {
id "com.android.application"
}
def json_google = "src/google-services.json";
if (project.file(json_google).exists()) {
println "found: ${project.file(json_google)}"
apply plugin: "com.google.gms.google-services"
apply plugin: "com.google.firebase.crashlytics"
...
} else {
def message = "missing: ${project.file(json_google)}"
// throw new GradleException(message)
println message
}
android { ... }
rootProject.file() and project.file() might be more convenient than file() (it depends). Using assert seems a bit harsh - but then one has to take care that it will not crash elsewhere.
My projects usually have a few optional config files, which the environment may or may not provide, because not every product flavor may require the same set of plugins & config files.

Recreation of jpa metamodels

I want to use JPA meta-models in my project. I added required dependency to my project, added generation to JavaCompile task and meta-models are successfully generated. If I want to run the code again, It doesn't compile. It fails with:
Error:java: Problem with Filer: Attempt to recreate a file for type project.models.AdministrationUser_
for every single meta-model. I am running it in Idea as spring boot run. If I use gradle task boot run than it will run just fine, no problem, but I need Idea run, because I need to set active profiles. It also shows, that problem is probably not in code but somewhere in run task configuration but I have no idea what to change and I tried to change several things but I'm just firing blanks.
I'm using Gradle 5.4.1., Idea 2019.2 and Java 11.
Here are important parts of my build.gradle file:
dependencies {
annotationProcessor("javax.xml.bind:jaxb-api")
annotationProcessor("org.hibernate:hibernate-jpamodelgen")
}
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Something similar was already asked here but one answer suggest to delete hibernate-jpamodelgen what (if I understand it correctly) seems like absurd solution - because it wont work if you delete it. Other answer suggest using some maven plugin, so not an option for me either.
I'm stuck on this problem for a longer time, have no one to talk to about it and I'm completely out of ideas so I'm pretty desperate and any help will be much appreciated.
First, make modifications to your build.gradle.
You point to the wrong directory in the sourceSets section. Fix it:
sourceSets {
generated {
java {
srcDirs = ['src/generated']
}
}
}
Then make further improvements:
With Gradle 5.2 and Intellij 2019.1 annotationProcessors is the only piece of configuration you need. See https://github.com/tbroyer/gradle-apt-plugin/blob/v0.21/README.md
Get rid of
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory =
file("src/generated/java")
}
sourceSets {
generated {
java {
srcDirs = ['src/generated/java']
}
}
}
Lastly, consider delegating your build and test tasks to Gradle (this is now the default). Go to File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using Gradle

Gradle: Force another Operating System

I use OperatingSystem.current() for my daily work with gradle. Now, I want to assemble my java project for different platforms, so I change manually my build.gradle files to build for a specific OS.
My question: Is there a way to specify the OS to use (returned by OperatingSystem.current()) directly in the gradle command line? If no, what is the best strategy to do cross building ?
Note: I depend on some libraries that use themselves the OperatingSystem.current().
I'm cringing while writing this, it's wrong on so many levels - I suggest you'll avoid forcing Gradle to think it's in a different OS.
But assuming you can't avoid it ->
I all depends on the version of Gradle you're using, I'd assume you're using the latest version (in older version that might be simpler)
OperatingSystem.current() works with the "os.name" environment variable which you can override very simply with a -D flag on the Gradle command.
BUT, and this is a big but, Gradle is not the problem here. The underlying JRE being used to execute the build contains OS specific code - see UNIXProcess on UNIX systems.
The current implementation of UNIXProcess blocks overriding the "os.name" value as it performs validations on it.
It's possible you'de be able to bypass that by creating a class in org.gradle.internal.os package that exposes the package-private OperatingSystem.resetCurrent() method and then force OperatingSystem.current() to re-evaluate, bypassing any real JRE checks.
Something like so:
print OperatingSystem.current()
System.setProperty("os.name", <some other OS>)
OperatingSystemWrapper.resetCurrent()
print OperatingSystem.current()
After some additional conversation over the comments, now I understand that the real requirement here is to take conditional dependency on native libs in a simple way that will allow the OP to remove dependency on OperatingSyste.current()
Taking a sample
apply plugin: "java"
dependencies {
compile "org.lwjgl:lwjgl:3.2.0"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-windows"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-linux"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-osx"
}
One can add conditional dependency via a "-P" flag (see https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_properties_and_system_properties)
apply plugin: "java"
dependencies {
compile "org.lwjgl:lwjgl:3.2.0"
if (buildos == "windows") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-windows"
} else if (buildos == "linux") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-linux"
} else if (buildos == "osx") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-osx"
}
}
gradle build -Pbuildos=windows
A similar thing can be done with a "-D" flag, but then you need to access it with System.getProperty
One can take it even further by building custom tasks and configurations (inheriting from compile/implementation) for each flavor instead of relying on -P flags

Firebase Performance Plugin causing slow build time

When using Firebase Performance in Android Studio the gradle task app:transformClassesWithFirebasePerformancePluginForDebug is taking significantly longer than any other task and is therefore dramatically slowing down my gradle build times.
Slow Build shown in Profiler
Firebase in our project caused 40% build time increase. To speed up debug builds we added a possibility to switch it on/off using build parameters in app/build.gradle and root build.gradle files:
app:
if (!project.hasProperty("disable-performance-plugin")) {
apply plugin: 'com.google.firebase.firebase-perf'
}
root/buildscript/dependencies:
if (!project.hasProperty("disable-performance-plugin")) {
classpath('com.google.firebase:firebase-plugins:1.1.5') {
exclude group: 'com.google.guava', module: 'guava-jdk5'
}
}
when running from the command line use
./gradlew your-task -Pdisable-performance-plugin
when working from Android Studio, add the flag to compiler options:
All of the existing answers are valid, but they all miss something.
To deal with this issue, you have 2 major options to choose from.
1. Use firebasePerformanceInstrumentationEnabled property
This is the official way provided by the SDK itself to disable it during the build process.
What this does:
Reduces transformClassesWithFirebasePerformancePluginFor* task execution time to ~5-10s.
Disables automatic traces and request monitoring, but leaves custom traces enabled. You can control the latter with AndroidManifest <meta-data> tags and calls to FirebasePerformance.getInstance().setPerformanceCollectionEnabled(). More info in the docs.
How to do this:
I think it's much easier to only enable plugin in those rare cases when we need it (usually it will be only when we publish the app) rather than disable it in all other cases.
Note: Of course, with manual builds you might forget to enable it. So if you don't have CI, it might be worth adding some other automatic scripting in Gradle, or sticking to the opposite approach that is used in other answers.
In general though, we only need two steps:
Add the following line to gradle.properties file:
firebasePerformanceInstrumentationEnabled=false
Use the following command in your CI config or manual builds:
./gradlew assembleRelease -PfirebasePerformanceInstrumentationEnabled=true
Pros:
Only one property to set up.
Cons:
Plugin still adds additional ~5-15s to the build time.
2. Use custom Gradle project property to avoid applying firebase-perf Gradle plugin
What this does:
transformClassesWithFirebasePerformancePluginFor* task is not executed at all. Also we save some additional ~5–10s overhead that is present when using the first solution.
Same as the first method – disables automatic traces and request monitoring, but leaves custom traces enabled. You can control the latter with AndroidManifest <meta-data> tags and calls to FirebasePerformance.getInstance().setPerformanceCollectionEnabled(). More info in the docs.
How to do this:
This approach has similar points and warnings, and also includes two steps:
Modify your app module's build.gradle file:
if (project.hasProperty('useFirebasePerf')) {
apply plugin: 'com.google.firebase.firebase-perf'
}
Note: you don't need to apply the same check to your project-level build.gradle:
classpath "com.google.firebase:firebase-plugins:$firebase_plugins_version"
This declaration won't be used in any way by Gradle when the plugin itself is not enabled.
And you don't need to exclude guava-jdk5 dependency there, if you're using firebase-plugins v1.1.1 or later as stated in the docs.
Use the following command in your CI config or manual builds:
./gradlew assembleRelease -PuseFirebasePerf
Pros:
Completely eliminates time expenses associated with Firebase Performance Gradle plugin.
Cons:
Introduces conditional check for applying plugin in your Gradle script, some might argue that it's not an idiomatic approach.
* (Bonus option) Use custom Gradle project property to exclude firebase-perf SDK
If you don't use custom traces or any other features from Firebase Performance SDK and only rely on automatic monitoring (that is, you don't have any dependencies on SDK in your code), then you can exclude this dependency for non-production builds.
How to do this:
All you need to do is update your app module's build.gradle file:
If you chose to use the first option, then change your dependency like this:
if (project.property('firebasePerformanceInstrumentationEnabled') == 'true') {
implementation "com.google.firebase:firebase-perf:${firebase_perf_version}"
}
If you chose the second one:
if (project.hasProperty('useFirebasePerf')) {
implementation "com.google.firebase:firebase-perf:${firebase_perf_version}"
}
Advantage:
This might save you some additional ~5-10s, spent on configuring dependency and
"ProGuarding" it.
Drawbacks:
Your production APK size will be larger than debug one by ~0.5mb. This might disrupt your reports or predictions, so you need to be aware of it.
If you were close to surpassing 64K method count limit, you might suddenly step over it on production builds and find yourself in the MultiDex zone. And that means extra work to do and tests to run. All because Firebase Performance brings a formidable number of almost 5K method references (after applying ProGuard with optimizations).
You can also check out my article where I expand a bit more on this topic.
Firebase Performance has released a new version of perf-plugin (v1.3.0). This would enable disabling the Firebase Performance Monitoring Gradle plugin for a specific build variant (including buildTypes or productFlavors).
An example below:
android {
// ...
debug {
FirebasePerformance {
// Set this flag to 'false' to disable #AddTrace annotation processing and
// automatic HTTP/S network request monitoring
// for a specific build variant at compile time.
instrumentationEnabled false
}
}
}
Reference to release notes:
https://firebase.google.com/support/release-notes/android#2019-07-10
All comments in this thread are valid. I want to suggest a very simple way to disable that for debug builds:
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Release")) {
apply plugin: 'com.google.firebase.firebase-perf'
}
For newer versions of the Firebase perf plugin (1.3.0 and up) with Kotlin DSL you'll need to add the following:
android {
...
buildTypes {
...
all {
with((this as ExtensionAware).extensions["FirebasePerformance"] as FirebasePerfExtension) {
setInstrumentationEnabled(!isDebuggable)
}
}
...
}
}
For the Groovy version you can check out the Firebase documentation.
Just to give another option to disable transformClassesWithFirebasePerformancePluginForDebug, here's my recipe:
In main build.gradle folder:
if (!project.gradle.startParameter.taskNames.any { taskName ->
taskName.toLowerCase().contains('assemble') && taskName.toLowerCase().contains('debug') }) {
classpath("com.google.firebase:firebase-plugins:$firebasePluginVersion") {
exclude group: 'com.google.guava', module: 'guava-jdk5'
}
}
In the build.gradle app file:
if (!project.gradle.startParameter.taskNames.any { taskName ->
taskName.toLowerCase().contains('assemble') && taskName.toLowerCase().contains('debug') }) {
apply plugin: 'com.google.firebase.firebase-perf'
}
I ran into this problem as well. Originally we had been using a variant of the answer provided by R. Zagórski, but based on a similar thread from the Gradle forums it seems like conditionally applying a plugin to a project isn't the right way to go:
Plugins can’t be applied to only “part of your project”. They are either applied or not.
Conditionally applying plugins does seem to work if you can do it right, but it's not an officially supported feature. Further down the same thread, another point is made:
But the plugin should allow you to configure it at a finer grained level.
Sure enough, there actually is a property exposed by the Firebase plugin that lets you toggle instrumentation on or off (therefore toggling the increased build time). Using this property is tricky, though, since you have to apply it at exactly the right time during the building process, but once you've got that then you can essentially pivot it on whatever you want.
The following code snippet is how we're pivoting instrumentation based on Debug vs. Non-Debug build variants. It's written in Kotlin, but I imagine it would translate to Groovy just as well:
plugins {
...
id ("com.google.firebase.firebase-perf")
}
...
android {
...
applicationVariants.all {
val variant = this
val isFirebaseEnabled = !variant.javaCompiler.name.contains("Debug", true)
gradle.taskGraph.whenReady {
if (this.hasTask(variant.javaCompiler))
{
project.FirebasePerformance.isInstrumentationEnabled = isFirebaseEnabled
}
}
}
...
}
Note that with this in place, the transformClassesWithFirebasePerformancePluginFor* task will still always run for every build variant, but it will complete almost immediately for a variant that doesn't have instrumentation enabled.
I think the clearest way is this with kotlin DSL gradle 👇
in app level build.gradle
buildTypes {
getByName(BuildType.DEBUG) {
extensions.configure<com.google.firebase.perf.plugin.FirebasePerfExtension>{
setInstrumentationEnabled(false)
}
}
}
in dependendencies part:
dependencies {
releaseImplementation(Dependencies.FIREBASE_PERFORMANCE) //implementation for only release mode (you can vary for other variants)
}
I have simplified option 2 of this answer https://stackoverflow.com/a/53270530/1635488
Define a property in gradle.properties
useFirebasePerf=false
Disable perf plugin
if (useFirebasePerf.toBoolean()) {
apply plugin: 'com.google.firebase.firebase-perf'
}
Remove dependency
if (useFirebasePerf.toBoolean()) {
implementation 'com.google.firebase:firebase-perf:16.2.3'
}
Enable Performance Monitoring only for CI builds (i would recommend only for release builds)
gradlew assembleRelease -PuseFirebasePerf=true
A much cleaner way in kotlin DSL
buildTypes {
//My custom extension
forName("debug") {
roject.ext.set("firebasePerformanceInstrumentationEnabled", "false")
}
}
Implementation of forName
fun <T> NamedDomainObjectContainer<T>.forName(name: String, action: T.() -> Unit) {
this.maybeCreate(name)
this.getByName(name, object: Action<T>{
override fun execute(t: T) {
t.action()
}
})
}

How can I import one Gradle script into another?

I have a complex Gradle script that wraps up a load of functionality around building and deploying a number of NetBeans projects to a number of environments.
The script works very well, but in essence it is all configured through half a dozen maps holding project and environment information.
I want to abstract the tasks away into another file, so that I can simply define my maps in a simple build file, and import the tasks from the other file. In this way, I can use the same core tasks for a number of projects and configure those projects with a simple set of maps.
Can anyone tell me how I can import one Gradle file into another, in a similar manner to Ant's task? I've trawled Gradle's docs to no avail so far.
Additional Info
After Tom's response below, I thought I'd try and clarify exactly what I mean.
Basically I have a Gradle script which runs a number of subprojects. However, the subprojects are all NetBeans projects, and come with their own ant build scripts, so I have tasks in Gradle to call each of these.
My problem is that I have some configuration at the top of the file, such as:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
I then generate tasks such as:
projects.each({
task "checkout_$it.shortname" << {
// Code to for example check module out from cvs using config from 'it'.
}
})
I have many of these sort of task generation snippets, and all of them are generic - they entirely depend on the config in the projects list.
So what I want is a way to put this in a separate script and import it in the following sort of way:
projects = [
[name:"MySubproject1", shortname: "sub1", env:"mainEnv", cvs_module="mod1"],
[name:"MySubproject2", shortname: "sub2", env:"altEnv", cvs_module="mod2"]
]
import("tasks.gradle") // This will import and run the script so that all tasks are generated for the projects given above.
So, in this example, tasks.gradle will have all the generic task generation code in, and will get run for the projects defined in the main build.gradle file. In this way, tasks.gradle is a file that can be used by all large projects that consist of a number of sub-projects with NetBeans ant build files.
There is a new feature in 0.9. You can use apply from: 'other.gradle' command.
Read my question about same thing at: Is there a way to split/factor out common parts of Gradle build
The answer to the question turned out to be in the Plugins system, where you can add the desired functionality in a set of plugins which can be groovy files located in the directory buildSrc/src/main/groovy. Plugins can also be bundled as a Jar though I haven't tried this.
Details here: Custom Plugins
Well, it is hard to tell what serves you best without actually seeing your build file.
I could assume that stetting up your environment as multi-project build should provide you the abstraction you are looking for.
In your project root build.gradle you define all your domain specific stuff as well as the things that apply to all your subprojects:
repositories {
add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
name = 'destRepo'
addIvyPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/ivys/ivy(-[revision]).xml')
addArtifactPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/[type]s/[artifact](-[revision]).[ext]')
descriptor = 'optional'
checkmodified = true
}
...
}
...
subprojects {
sourceCompatibility = 1.5
targetCompatibility = 1.5
group = 'my.group'
version = '1.0'
uploadArchives {
uploadDescriptor = true
repositories {
add rootProject.repositories.destRepo
}
}
apply{ type my.group.gradle.api.plugins.MyPlugin }
...
}
dependsOnChildren()
The project root directory might also contain a gradle.properties file where you define properties used by your projects:
buildDirName=staging
repo.dest.dir=/var/repo
...
Then in an additional file from your project root named settings.gradle you actually point to your subprojects:
include 'my-first-component',
'my-second-component'
...
project(':my-first-component').projectDir = new File(rootDir, 'path/to/first/component')
project(':my-second-component').projectDir = new File(rootDir, 'path/to/second/component')
...
Each sub-project directory contains a build.gradle file containing the sub-project specific stuff only.
No matter if you invoke gradle from your project root or sub-project directory, gradle will automatically consider all your definitions done in the various files.
Also note that no compile task will be executed for your project root as long as you don't load any plugin beyond the default plugin at the root level.
This is an example for Kotlin DSL (build.gradle.kts).
apply(from = "scripts/my-script.gradle.kts")
scripts/my-script.gradle.kts:
println(
"""
I am defined at the top level of the script and
executed at the configuration phase of build process
"""
)
tasks.create("MyTask") {
println(
"""
I am defined in a task and
run at the configration phase of build process"""
)
doLast {
// ...
}
}
See this answer and this answer for how to import a function from another script in Kotlin DSL.
Based off this similar question/answer, the easiest solution I've found after searching for days is using buildscript.sourceFile. It correctly gives the file being run rather than the pwd/cwd/parent-file of said process. I feel like this would solve your issue.

Categories