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.
Related
While converting the build.gradle to build.gradle.kts is a manual process, I'm struggling in conversion in the below piece of code.
I tried with invalidating the cache and restarting the studio many times. However, android.varinatFilter is not recognized.
android.variantFilter { variant ->
if (variant.buildType.name == 'release'
&& variant.getFlavors().get(0).name == 'development') {
variant.setIgnore(true)
}
if (variant.buildType.name == 'debug'
&& variant.getFlavors().get(0).name == 'production') {
variant.setIgnore(true)
}
}
Properties class of Java.util.Properties dependency doesn't get resolved in .kts file also the FileInputStream class of Java.io is not recognized.
def getProps(path) {
Properties props = new Properties()
props.load(new FileInputStream(file(path)))
return props
}
Also while applying the kotlin annotation processor
kapt 'androidx.lifecycle:lifecycle-common-java8:2.1.0' To
kapt {'androidx.lifecycle:lifecycle-common-java8:2.1.0'}
doesn't work and returns compile-time error.
Any help would be appreciated.
UPDATE
Properties class of Java.util.Properties dependency doesn't get resolved in .kts file also the FileInputStream class of Java.io is not recognized.
This will get resolved with Invalidate cache and Restart.(Start refactoring project level gradle then settings.gradle and then app.gradle file in sequence)
For kapt {'androidx.lifecycle:lifecycle-common-java8:2.1.0'} - please use double quotes, e.g. kapt {"androidx.lifecycle:lifecycle-common-java8:2.1.0"}, please check details here.
Please also use the following syntax for method:
import java.io.FileInputStream
import java.util.Properties
/***/
fun getProps(path: String): Properties {
val props = Properties()
props.load(FileInputStream(file(path)))
return props
}
Changes:
You need imports with java packages at the beginning of the file.
Use fun instead of def
Parameter types are required for methods, use ':' for this - path: String
new keyword is not needed
Variable declaration can started with val, e.g. if compiler is able to understand the type, you don't need to enter it manually.
Return type is mandatory if your result is not Unit.
For filter - I didn't worked with this. However please consider:
Replacing quotes ' to "
Replacing variant.getFlavors().get(0).name to variant.flavors[0].name
Replacing variant.setIgnore(true) to variant.ignore=true
That would be
android.variantFilter {
if (buildType.name == "release" && flavors[0].name == "development") {
ignore = true
}
if (buildType.name == "debug" && flavors[0].name == "production") {
ignore = true
}
}
although I think that a more correct approach for
flavors[0].name = "xyz"
should be
flavors.map { it.name }.contains("xyz")
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 ["", ""];
}
}
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
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
I would like to remove all my logs commands from my java (android) project on Release build using Gradle.
I'm using regex (which is correct):
'/Logger.log[^,]+[^L]+Logger.DEBUG[^;];/g'
and the following gradle script:
task dropJavaDebugLogs << {
FileTree javaFiles = fileTree("src/main/java") {
include "**/*.java"
}
String regex = "Logger.log[^,]+[^L]+Logger.DEBUG[^;];";
javaFiles.each { File javaFile ->
content = content.removeAll(regex);
}
}
Can you tell me what i doing wrong?