gradle : How to run externalNativeBuildDebug task after a custom task - gradle

I am working on Android project which uses build.gradle. I want to run externalNativeBuildDebug task after a custom task.
In build.gradle, I specify:
task mytask()
{
println 'hello'
}
tasks.getByName("externalNativeBuildDebug").mustRunAfter(mytask)
But gradle throws this error :
Task with name 'externalNativeBuildDebug' not found in project ':app'.
I have defined externalNativeBuild block in android{} block
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
version '3.18.1'
}
}
Is there a way to get externalNativeBuildDebug task and set its dependency?

Related

How can I get Gradle to build dependencies before running my task?

Background: I am trying to hook the compiler for my own domain-specific language into Gradle. The DSL is compiled to Java source code, so I have built a task that runs before the Java compiler. The compiler cannot currently handle multiple projects with dependencies, so I'm trying to add that.
My DSL has packages like Java that get mapped to identical Java packages. The same should be true for projects. In that case, for each project, the DSL sources get compiled to Java source code, as well as meta-data (a JSON file per compiled class, containing information from the DSL's type system that cannot be mapped to Java types). When project A depends on B, the DSL compilation process for A needs the meta-data files from B. That meta-data should be packaged as resources into the JAR file together with the generated and compiled Java code, as well as possibly hand-written and compiled Java code.
FoobarPlugin.groovy:
class FoobarPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
// create the compileFoobar task
CompileFoobarTask task = project.getTasks().create('compileFoobar', CompileFoobarTask.class);
task.group = 'build';
task.setDescription('Compiles Foobar to Java code.');
task.sourceDirectory = new File(project.projectDir, "src/main/foobar");
task.outputDirectory = new File(project.getBuildDir(), "foobar-java");
// compileFoobar must run before compiling Java code
project.tasks.compileJava.dependsOn(task);
// add the task's output folders as Java source folders
project.sourceSets.main.java.srcDirs += task.outputDirectory;
project.sourceSets.main.resources.srcDirs += task.outputDirectory;
project.sourceSets.test.java.srcDirs += task.outputDirectory;
project.sourceSets.test.resources.srcDirs += task.outputDirectory;
// Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
// phase because project dependencies are not fully known until then.
project.gradle.addBuildListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
project.configurations.compile.each {
task.dependencyOutputs += it
}
}
});
}
}
CompileFoobarTask.groovy:
class CompileFoobarTask extends DefaultTask {
#InputDirectory
File sourceDirectory;
#InputFiles
List<File> dependencyOutputs = new ArrayList<>();
#OutputDirectory
File outputDirectory;
#TaskAction
void run() {
FileUtils.write(new File(outputDirectory, "timestamp"), "" + System.currentTimeMillis(), StandardCharsets.UTF_8);
}
}
build.gradle from project A:
apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin
repositories {
mavenCentral()
}
dependencies {
compile project(':b')
}
build.gradle from project B:
apply plugin: 'java'
apply plugin: foobar.gradle.FoobarPlugin
repositories {
mavenCentral()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.0'
}
Test runs and output:
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :a:compileFoobar
running task ':a:compileFoobar'
BUILD SUCCESSFUL in 1s
4 actionable tasks: 2 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileFoobar
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 469ms
4 actionable tasks: 2 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean a:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :a:compileFoobar
running task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 487ms
7 actionable tasks: 5 executed, 2 up-to-date
martin#xyz:~/git-repos/gradle-test$ ./gradlew clean b:compileJava
adding dependency /home/martin/git-repos/gradle-test/b/build/libs/b.jar to task task ':a:compileFoobar'
> Task :b:compileFoobar
running task ':b:compileFoobar'
BUILD SUCCESSFUL in 471ms
4 actionable tasks: 3 executed, 1 up-to-date
As you can see, even though I add b.jar as a dependency to a:compileFoobar, Gradle won't build that JAR before running a:compileFoobar. The Java plugin seems to do something different because running a:compileJava WILL build b.jar first. What do I have to do to achieve the same for my task?
What you need to do is to explicitly create a Task dependency between consumer project's compileFoobar task and the producer project's jar task (in your example where project a depends on project b, you need to create task dependency a:compileFoobar -> b.jar)
You can achieve this in your custom plugin, by checking if the current project has dependencies of type ProjectDependency: if so you create the task dependency accordingly.
Code sample (in your plugin apply() method):
// Turn project dependencies into task dependencies. We have to delay this until the end of the configuration
// phase because project dependencies are not fully known until then.
project.gradle.addBuildListener(new BuildAdapter() {
#Override
void projectsEvaluated(Gradle gradle) {
project.configurations.each { config ->
config.dependencies.each { dep ->
if (dep instanceof ProjectDependency) {
def producerProject = ((ProjectDependency) dep).dependencyProject
def producerJarTask = producerProject.tasks.jar
println " **** Project $project.name depends on $producerProject.name"
println " => create dependency between $task to $producerJarTask"
task.dependsOn(producerJarTask)
}
}
}
}
})
Build execution:
$ ./gradlew clean a:compileFoobar
**** Project a depends on b
=> create dependency between task ':a:compileFoobar' to task ':b:jar'
> Task :a:clean
> Task :b:clean
> Task :b:compileFoobar
> Task :b:compileJava NO-SOURCE
> Task :b:processResources NO-SOURCE
> Task :b:classes UP-TO-DATE
> Task :b:jar
> Task :a:compileFoobar

findByName() method returns null for gradle plugin task

I am using maven-publish plugin. I have a publication called myMedia. When I try executing
tasks.findByName("publishMyMediaPublicationToSnapshotRepository"), it returns null. Why does it return null if there is a task by that name that maven-publish plugin created.
I'm not able to get around this issue. Any help is greatly appreciated.
EDIT 1 : START
I tried Francisco Mateo's suggestion to use the afterEvaluate method, but this too didn't seem to work for me.
However, while trying to figure out the actual cause I came across one interesting observation. I found that the when I call tasks.findByName("publishMyMediaPublicationToSnapshotRepository") from within a task, then it works perfectly fine and prints out the task, i.e. printTaskName runs as expected :
task printTaskName{
doLast{
println "task name found is :--- "+tasks.findByName("publishMyMediaPublicationToSnapshotRepository")
}
}
Now originally in my project's build.gradle I have a publishSnapshot task followed by ordering logic using mustRunAfter method:
task publishSnapshot (dependsOn: ['createMyMediaArchive','publishMyMediaPublicationToSnapshotRepository'])
tasks.findByName("publishMyMediaPublicationToSnapshotRepository").mustRunAfter tasks.findByName("createMyMediaArchive")
publishSnapshot.mustRunAfter tasks.findByName("publishMyMediaPublicationToSnapshotRepository")
executing the publishSnapshot task gives below error:
Cannot invoke method mustRunAfter() on null object
tasks.findByName method doesn't seem to work here as it returns null.
Am I missing out on some basics?
EDIT 1 : END
EDIT 2 : START
The below build.gradle is an edited version of what I actually have. I am not allowed to share the script. But the script is identical in structure to what I have. The rest of build script is as it is as far as the structure and syntax is concerned, just the names changed. Gradle version is 3.5.1
build.gradle
import java.time.LocalDate;
import java.time.format.DateTimeFormatter
import java.text.SimpleDateFormat
import Tasks.PublishArtifacts
import org.gradle.api.GradleException
buildscript {
repositories {
abcRelease()
}
dependencies {
classpath "org.codehaus.groovy.modules.http-builder:http-builder:0.7"
classpath "commons-collections:commons-collections:3.2.1"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.9.10"
}
}
repositories {
mavenCentral()
abcSnapshot()
xyzRelease()
}
apply plugin: 'maven-publish'
apply plugin: 'base'
apply plugin:'java'
apply from: "$rootDir/repositories.gradle"
def getMediaVersion(){
project.mediaVersion
}
configurations{
releaseMedia
}
publishing{
publications {
myMedia(MavenPublication) {
artifactId project.artifactId
artifact "$project.artifactId"+"-"+getMediaVersion()+".tgz"
version getMediaVersion()
}
}
repositories project.artifactoryRepositories
}
task publishSnapshot (dependsOn: ['createMyMediaArchive','publishMyMediaPublicationToSnapshotRepository'])
tasks.findByName("publishMyMediaPublicationToSnapshotRepository").mustRunAfter tasks.findByName("createMyMediaArchive")
publishSnapshot.mustRunAfter tasks.findByName("publishMyMediaPublicationToSnapshotRepository")
afterEvaluate { evaluated ->
evaluated.tasks.findByName("publishMyMediaPublicationToSnapshotRepository").configure {
println "My Media name --------- "+it.name
}
}
task printTaskName{
doLast{
println "task name found is :--- "+tasks.findByName("publishMyMediaPublicationToSnapshotRepository") // This returns task name as exected
}
}
task createMyMediaArchive{
doLast{
//code for creating media
}
}
EDIT 2 : END
EDIT 3: START
If I run the tasks individually, i.e. first running task createMyMediaArchive and then running task publishSnapshot ,where the taskpublishSnapshot depends on
task publishHelmMediaPublicationToSnapshotRepository, ie.
task publishSnapshot (dependsOn: ['publishHelmMediaPublicationToSnapshotRepository'])
then both the tasks run successfully. This proves that gradle does have the task publishHelmMediaPublicationToSnapshotRepository pre-configured.
Also gradlew tasks --all prints out this task.
This behavior is making things look more confusing to me.
EDIT 3 : END
A lot of the configuration performed by the maven-publish plugin is done lazily.
Wrapping in afterEvaluate should work.
afterEvaluate { evaluated ->
evaluated.tasks.findByName("publishMyMediaPublicationToSnapshotRepository").configure {
println it.name
}
}

Could not find method finalizedBy()

This is my code inside build.gradle
task eatBreakfast {
finalizedBy "brushYourTeeth"
doLast{
println "Om nom nom breakfast!"
}
}
task brushYourTeeth {
doLast {
println "Brushie Brushie Brushie."
}
}
Whenever I run, this is what I get
Where: Build file '/home/android-14/gradleUdacity/build.gradle' line: 3
What went wrong:
A problem occurred evaluating root project 'gradleUdacity'.
Could not find method finalizedBy() for arguments [brushYourTeeth] on root project 'gradleUdacity'.
Can anyone tell me what is the problem?
task.finalizedby(...) was added in gradle 1.7. Which version of gradle are you using?

Is it possible to throw an error if no arguments are provided to Gradle?

My gradle setup accepts arguments during runtime which are checked in a shell script that Gradle Exec task calls. However, in order to reach that point, Gradle deals with dependencies and spends a good amount of time before the end script is executed, which would then throw an error if no arguments are passed.
Build task in gradle looks like below:
task buildAll(type: Exec) {
environment['PROJECT_ROOT'] = "${projectDir}"
workingDir rootProject.projectDir.path
executable rootProject.projectDir.path + "/script.ksh"
if (project.hasProperty('ARGS')) {
args(ARGS.split(','))
}
}
Gradle is called as follows:
./gradlew build -PARGS="-arg1,value1,-arg2,value2,-arg3,-arg4,value4"
I intend to check for -arg2 and if it is not provided, I would like the gradle build to fail with some sort of usage displayed. Is this possible?
You can add an if block at the very beginning of the script:
if (!project.hasProperty('lol')) {
throw new GradleException("'lol' property is required!")
}
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.guava:guava:18.0'
}
}
however it will not prevent gradle from resolving the dependencies of the script itself - buildscript block will be evaluated first.

is there an anchor task that contains bundleDebug and bundleRelease in Android Gradle plugin?

In Android Gradle plugin, task "assemble" is an anchor task that contains assembleDebug and assembleRelease. Is there a similar anchor task that contains bundleDebug and bundleRelease. I currently have following build script where a task depends on bundleDebug:
android.libraryVariants.all {
variant -> variant.javaCompile.classpath += configurations.provided
}
task removeCameraApiJar(dependsOn: 'bundleDebug') << {
FileCollection outputs = tasks['bundleDebug'].getOutputs().getFiles()
outputs.each {
File file ->
println file.name
}
println 'removeCameraApiJar'
}
task assemble.dependsOn(removeCameraApiJar)
However if I replace bundleDebug with just "bundle", the script would fail with following message:
What went wrong: Could not determine the dependencies of task ':camerasupport:removeCameraApiJar'.
Task with path 'bundle' not found in project ':camerasupport'.
It doesn't seem as though the Android plugin creates such a task. You could however do something like
task removeCameraApiJar(dependsOn: tasks.matching { it.name.startsWith('bundle') })
In the current version of the gradle android plugin (1.5.0), the bundle task can be found as a property ("packageLibrary") of the variant output :
android.libraryVariants.all {
variant ->
variant.outputs.each { output ->
FileCollection outputs =
output.packageLibrary.getOutputs().getFiles()
}
}
prinln output.packageLibrary.name will yield "bundle"+buildVariant.

Resources