How to stop a gradle sync if a certain condition is met - android

I have a doNecessaryJob task that depends on preProcessing task. If a certain condition in preProcessing is true, I want to stop the sync right away. How do I do that?
Part of my gradle is here:
task preProcessing() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find()) {
// do processing here
} else {
println "Error, stopping the sync."
// !STOP THE GRADLE SYNC HERE DUE TO ERROR!
}
}
task doNecessaryJob() {
dependsOn preProcessing
// do necessary processing here that depends on variables from preProcessing task
}
I checked other solutions here in SO but all seem to point to the command line solution such as How to stop Gradle task execution in Android Studio?.

I found it. I used GradleException like so:
task preProcessing() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find()) {
// do processing here
} else {
throw new GradleException("Error, stopping the sync.") // <--- THE FIX
}
}

Related

Gradle: Filter and execute tasks from a given task

I want to run ./gradlew verifyProjectDebug to run a subset of verification tasks.
verifyProjectDebug tries to extract a subset of the tasks in the project and execute them.
static def isValidTask(String name) {
def isLint = name.matches("lint.*Debug")
def isKtlint = name.matches("ktlint.*Debug.*Check")
def isUnitTest = name.matches("test((?!Prod|Staging).)*DebugUnitTest")
return (isLint || isKtlint || isUnitTest) && !name.contains("Prod")
}
task verifyProjectDebug() {
group = "verification"
description = "Runs lint, ktlint and tests for all debug non-production variants"
doLast {
getSubprojects()
.collect { it.tasks }
.flatten()
.findAll { isValidTask(it.name) }
.each { it.execute() }
}
}
Unfortunately, calling .execute() on a task does not invoke its dependencies so some of the tasks fails because its dependencies were not invoked.
Is there any way in gradle I can achieve this. Thanks a ton!
execute is a method of the Task class. You're trying to bypass Gradle build system. Executing tasks is not a simple matter or creating an instance and calling execute. Gradle handles dependency injection, caching, input & output processing, all kinds of stuff. So leverage Gradle.
1)
Create one lifecycle task that is the parent task for everything you want to execute.
final def verifyProject = tasks.register("verifyProject")
Lifecycle task is a task that doesn't do any work, it only depends on other tasks.
2)
You can only reference tasks that are already created. For example you can't reference lint task of the debug variant until the debug variant is created.
Process each variant when it's created, find all tasks you want executed and connect them to the master task.
android.applicationVariants.all {
final def cappedVariantName = name.capitalize()
// For every build variant that has build type "debug"...
if (variant.buildType == "debug") {
verifyProject.configure {
dependsOn("lint$cappedVariantName")
dependsOn("ktlint$cappedVariantName")
dependsOn("test${cappedVariantName}UnitTest")
}
}
}
Please verify the names of tasks you want executed.
Now ehen you run gradlew verifyProject all the tasks this task depends on will get executed. You're in charge of the dependencies.
If you want to use this in an Android library module replace android.applicationVariants with android.libraryVariants.
The code follows Task Conviguration Avoidance. This means the tasks you defined won't be configured unless you specifically invoke them. This should save resources (CPU & memory) when running a build.
3)
To do this automatically for all modules pick one or both of the following, and put to to your root project build.gradle.
subprojects { project ->
project.plugins.whenPluginAdded { plugin ->
// For all libraries and only libraries:
if (plugin instanceof com.android.build.gradle.LibraryPlugin) {
project.android.libraryVariants.all { variant ->
// See above.
}
}
// For all apps and only apps:
if (plugin instanceof com.android.build.gradle.AppPlugin) {
project.android.applicationVariants.all { variant ->
// See above.
}
}
}
}
Putting this in the project level gradle file did the trick.
task verifyDebugProjects() {
group = "verification"
description = "Runs lint, ktlint and tests for all debug non-production variants"
}
static def isValidVerifyDebugTask(String name) {
def isLint = name.matches("lint.*Debug")
def isKtlint = name.matches("ktlint.*Debug.*Check")
def isUnitTest = name.matches("test((?!Prod|Staging).)*DebugUnitTest")
return (isLint || isKtlint || isUnitTest) && !name.contains("Prod")
}
gradle.projectsEvaluated {
getSubprojects()
.collect { it.tasks }
.flatten()
.findAll { isValidVerifyDebugTask(it.name) }
.each { verifyDebugProjects.dependsOn it }
}

Android Studio: JavaExec: Could not find or load main class

As part of my build script, I would like to run some Java code before the APK is packaged.
When I try to build my project, I get the error:
Error: Could not find or load main class net.bgreco.myapp.build.BuildScript
BuildScript.java is located in app/src/main/java/net/bgreco/myapp/build/.
The BuildScript.class does get compiled successfully before I receive the error message above, and I can run it from the command line with no errors.
app/build.gradle :
task runBuildScript(type: JavaExec) {
main = 'net.bgreco.myapp.build.BuildScript'
}
tasks.whenTaskAdded { theTask ->
if (theTask.name.contains('package')) {
theTask.dependsOn 'runBuildScript'
}
}
With some help from this answer, I got it working by modifying the classpath as below. I also needed to reference some of the Android libraries so I included the path to android.jar.
app/build.gradle :
task runBuildScript(type: JavaExec) {
String flavor, buildType
(flavor, buildType) = getCurrentFlavorAndBuildType()
classpath files("build/intermediates/classes/" + flavor + "/" + buildType, "${android.getSdkDirectory().getAbsolutePath()}\\platforms\\${android.compileSdkVersion}\\android.jar")
main = 'net.bgreco.myapp.build.BuildScript'
args = [flavor] // Not needed unless you want to know the current flavor from within the BuildScript
}
tasks.whenTaskAdded { theTask ->
if (theTask.name.contains('package')) {
theTask.dependsOn 'runBuildScript'
}
}
def getCurrentFlavorAndBuildType() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find())
return [matcher.group(1).toLowerCase(), matcher.group(2).toLowerCase()]
else {
println "NO MATCH FOUND"
return ["", ""];
}
}

How to create flavor specific Android Lint baseline file

As per https://developer.android.com/studio/write/lint.html#snapshot we can create a Lint warning baseline file.
The problem is that I have multiple flavors, each having their own sourceSets. Some files are used in a single flavor.
When I generate the baseline file, it's always specific to a variant. Which means that it's invalid for the other variants, ie it will miss some existing issues.
I have tried putting the
lintOptions {
baseline file("lint-baseline.xml")
}
in the build and flavor blocks, but it won't generate multiple baselines.
Has anyone managed to generate flavor specific lint baseline file? And if so how?
Thanks!
I was trying the same thing and found a way of doing it.This create diff file for release and debug.You can put your custom logic in getfileName
lintOptions {
baseline file(getFileName())
checkAllWarnings true
warningsAsErrors true
abortOnError true
}
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().args.toString()
println tskReqStr
if (tskReqStr.contains("Debug")) {
return "debug"
} else {
return "release"
}
}
private String getFileName(String command) {
return getCurrentFlavor() + "-lint-baseline.xml"
}
I couldn't make the above answer exactly work as I got errors when trying to define the method in the build.gradle file.
Using himanshu saluja's answer this is what worked for me.
My root project's gradle file has:
ext.getLintFileName = {
Gradle gradle = getGradle()
String taskReq = gradle.getStartParameter().getTaskRequests().args.toString()
if(taskReq.contains("Debug")) {
return "debug-lint-baseline.xml"
} else {
return "release-lint-baseline.xml"
}
}
And the sub project's gradle file inside the android block uses the value like this:
lintOptions {
baseline file(rootProject.ext.getLintFileName)
checkDependencies true
abortOnError true
absolutePaths false
}
Given that the baseline feature is on LintOptions and this one is AFAIK not capable of being variant aware, this will not work out of the box.
You could file a feature request on https://b.android.com though.
according to my GitHub sample code:
1- add the following function to your app-level build.gradle file:
def getPath() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
String path
String fileName = "lint-baseline"
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else if (tskReqStr.contains("generate"))
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
else if (tskReqStr.contains("lint"))
pattern = Pattern.compile("lint(\\w+)(Release|Debug)")
if(pattern != null) {
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find()) {
path = matcher.group(1).toLowerCase() + matcher.group(2).toLowerCase()
return "lint-baselines/${path}-${fileName}.xml"
} else {
return "lint-baselines/${fileName}.xml"
}
}
return "lint-baselines/${fileName}.xml"
}
this function creates a specific path for each build variants. you can customize the file name by changing the "fileName" variable.
2- add the following line to lintOption scop of your app-level build.gradle file:
lintOptions {
...
// Use (or create) a baseline file for issues that should not be reported
baseline file("${getPath()}")
...
}
3- run linter for each of build varients.
for example type the following command in the terminal tab of Android studio in the root of project:
gradlew app:lintMyAppRelease
app = your module name
MyAppRelease = your build varient
4- Done

Check selected build on gradle

Is there a way we could get the selected build variant on the Gradle file when we compile?
I would like to use inside the dependencies task the following code:
buildVariant = awesome code to get the buildVariant selected flag
if (buildVariant == A)
compile project(':moduleA')
else
compile project(':moduleB')
Groovy supports java code so you can use something like this:
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( "assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
{
println "NO MATCH FOUND"
return "";
}
}
source: How to get current flavor in gradle
Click on Build Variant which is at the left bottom side of Android Studio
It has a list of all build variants available

Conditional build of dependant NDK with gradle

My project depends on a NDK. But my NDK build is different for my projectFlavors.
I like to build and pack my dependent NDK with -DFLAVOR1 compile option defined if app's flavor1 is selected for my app. -DFLAVOR2 when flavor2 is selected and etc.
My whole app will not work correctly if app is on flavor1 and incorrectly use a NDK built on -DFLAVOR2, so the correct selection is important.
Now how we can write our build.gradle to solve this special conditional build?
I finally found a hacky approach for my problem, then I though it is better to share it here for everyone and improvement.
Step 1:
You need to provide flag from app and depend it on preBuild. Here is a sample code for doing it.Thanks How to get current flavor in gradle for function. I just modified it a little.
import java.util.regex.Matcher
import java.util.regex.Pattern
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( ":app:assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else if( tskReqStr.contains( ":app:generate" ) )
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("incremental(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
{
println "NO MATCH FOUND"
return "";
}
}
task setFlavorFlag() {
def flavorName = getCurrentFlavor();
if (!flavorName.equals("")) {
printf("Setting flag from app...\n")
def f = file("../build/conf.tmp")
if (!f.exists()) f.createNewFile()
f.write("-D${flavorName.toUpperCase()}")
}
}
preBuild.dependsOn setFlavorFlag
dependencies {
compile project(path: ':mylibrary')
}
Step 2:
Access flag from library. I did it in ndk section:
ndk {
...
def f = file("../build/conf.tmp")
if (f.exists()) {
printf("Building library for Flavor:%s\n", f.text)
ndk.CFlags.add(f.text);
f.delete()
}
}
ok. Now your app Flavor is passed with a -D option to compile of library. Here was the approach I found after 2-3 days. I'm open for all improvements for this approach.

Categories

Resources