How to call buildConfigField() from another function in Gradle? - android

Here's an extremely simplified version of what I'm trying to do in Gradle for my Android app:
android {
buildTypes {
debug {
buildConfigFieldMyWay("keyName", "keyValue")
buildConfigField("String", "keyName", "keyValue") }
}
}
}
def buildConfigFieldMyWay(String keyName, String keyValue) {
buildConfigField("String", keyName, keyValue)
}
The real version is more complex, which is why it would be nice to organize what I'm doing into separate functions.
The problem is that when I do a Gradle sync, I get this error:
No signature of method: build_byqgds5lao5ipgp4gk5ftyud4.android() is applicable for argument types: (build_byqgds5lao5ipgp4gk5ftyud4$_run_closure2) values: [build_byqgds5lao5ipgp4gk5ftyud4$_run_closure2#536e468e]
I think this has to do with the DSL Gradle uses with Android; that when buildConfigField() is called inside of android { buildTypes { debug, that it is running buildConfigField() on a BuildType object. If I call it from a separate function, there is no BuildType for it to operate on.
So I either need to modify the DSL so that my function (buildConfigFieldMyWay()) can be called in android { buildTypes { debug and operate on the BuildType object, or I need to pass the BuildType to the function. There's likely some simple syntax I need to use here that I'm not finding. What is it?

You can pass the build type inside the function buildConfigFieldMyWay:
def buildConfigFieldMyWay(buildType, String keyName, String keyValue) {
buildType.buildConfigField("String", keyName, keyValue)
}
And then, you can get the build type using it:
android {
buildTypes {
debug {
buildConfigFieldMyWay(it, "keyName", "keyValue")
buildConfigField("String", "keyName", "keyValue")
}
}
}

Related

How to unit test the app behavior for different Gradle buildTypes in Android?

I am trying to test the behavior of different stages in an Android app. Considering the official docs, I use different Gradle BuildTypes in the module's build.gradle to define the settings needed for each stage.
Each buildType block contains a String with a similar key STAGE_NAME and a different value for each stage for example test_stage, and uat_stage.
The value of those is being accessed in the app using BuildConfig.STAGE_NAME to change some of the parameters like API endpoints accordingly.
But the problem is, there seems to be no official way to unit test the resulting behavior for each stage.
The Android docs, mention a way to change the test buildType which explicitly defines which buildType is being used for testing, but I was wondering if there is another approach to test the resulting behaviors.
The module's build.gradle file:
android {
buildTypes {
release {
...
}
debug {
applicationIdSuffix ".debug"
buildConfigField "String", "STAGE_NAME", "\"debugStage\""
...
}
testStage {
initWith(debug)
buildConfigField "String", "STAGE_NAME", "\"testStage\""
...
}
uatStage {
initWith(debug)
buildConfigField "String", "STAGE_NAME", "\"uatStage\""
...
}
}
}
Example Retrofit interface which is supposed to use different API URLs for each stage by accessing the STAGE_NAME and find out which of the URLs should be used as API_URL:
interface FakeService {
#GET("stuff")
suspend fun getStuff(): List<Stuff>?
...
companion object {
// Different urls which should be used for each stage:
private const val URL_DEBUG = "https://debug_api.example.com/"
private const val URL_TEST = "https://test_api.example.com/
private const val URL_UAT = "https://uat_api.example.com/"
private const val URL_PROD = "https://prod_api.example.com/"
// This is where we access the STAGE_NAME to figure out which stage url should be used
val API_URL_TO_USE = getUrl(BuildConfig.STAGE_NAME)
fun getUrl(stageName: String?): String {
return when (stageName) {
"debugStage" -> URL_DEBUG
"testStage" -> URL_TEST
"uatStage" -> URL_QA
else -> URL_PROD
}
}
}
}
For example in this retrofit interface, I would like to test the behavior of getUrl function to make sure it returns the right URL for each stage.
UPDATE:
After looking at this question, I was wondering maybe one way of testing this, could be mocking the BuildConfig. But since the STAGE_NAME is only available after a successful build, it can not be mocked normally.

Don't generate BuildConfig - Gradle Kotlin DSL

To disable generating BuildConfig with the Groovy DSL, one could do the following:
afterEvaluate {
generateReleaseBuildConfig.enabled = false
generateDebugBuildConfig.enabled = false
}
I am trying to find the equivalent when using Gradle's Kotlin DSL (build.gradle.kts). Anyone have any luck with this?
If you have Android Studio 4.1 or greater you can use the Build Features Field to disable it per module.
android {
...
buildFeatures {
buildConfig = false
resValues = false
}
}
I ended up finding this to work as well:
android { ... }
tasks.withType<GenerateBuildConfig> {
isEnabled = false
}
You can remove BuildConfig from all variants by adding the following:
For library
android {
...
// TODO replace with https://issuetracker.google.com/issues/72050365 once released.
libraryVariants.all {
generateBuildConfigProvider?.get()?.enabled = false
}
}
For application
android {
...
// TODO replace with https://issuetracker.google.com/issues/72050365 once released.
applicationVariants.all {
generateBuildConfigProvider?.get()?.enabled = false
}
}
If you want to specify a build type , then:
libraryVariants
.matching { it.buildType.name == "release"}
.all {
generateBuildConfigProvider?.get()?.enabled = false
}
}

How to get Current Build Type in Gradle

My Question is very direct and easy to understand.
Question
In Gradle, is there any way I can get the current build type at runtime. For example, when running an assembleDebug task, can tasks within the build.gradle file make decisions based on the fact that this task is related to the debug build variant?
Sample Code
apply plugin: 'com.android.library'
ext.buildInProgress = ""
buildscript {
repositories {
maven {
url = url_here
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
configurations {
//get current build in progress here e.g buildInProgress = this.getBuildType()
}
android {
//Android build settings here
}
buildTypes {
release {
//release type details here
}
debug {
//debug type details here
}
anotherBuildType{
//another build type details here
}
}
}
dependencies {
//dependency list here
}
repositories{
maven(url=url2_here)
}
task myTask{
if(buildInProgress=='release'){
//do something this way
}
else if(buildInProgress=='debug'){
//do something this way
}
else if(buildInProgress=='anotherBuildType'){
//do it another way
}
}
In Summary
Is there a way for me to get exactly the build type in progress within myTask{}?
You can get the exact build type by parsing your applicationVariants:
applicationVariants.all { variant ->
buildType = variant.buildType.name // sets the current build type
}
A implementation could look like the following:
def buildType // Your variable
android {
applicationVariants.all { variant ->
buildType = variant.buildType.name // Sets the current build type
}
}
task myTask{
// Compare buildType here
}
Also you can check this and this similar answers.
Update
This answer by this question helped the questioner to settle the problem.
This worked for me
applicationVariants.all { variant ->
def variantType = variant.buildType.name
println "Variant type: $variantType"
if (variantType == "debug") {
// do stuff
}
}
You should getBuildConfigFields().get("MY_BUILD_TYPE").getValue())
https://stackoverflow.com/a/59994937/5279996
GL
If you want to suffix the buildtype name to the versionname (like me) just add this line to the version name:
debug {
versionNameSuffix "-debug"
}
This way you can identify the build type in the build name. And it works without declaring anything else.
Correct way for getting the current buildType being used during build in Kotlin programming language for android platform (logic is the same for Java)
project.afterEvaluate {
this.android().variants().all {
this.assembleProvider.configure {
this.doLast{
val variant = this#all
variant.outputs
.map
.forEach{
//do something with current buildType, or build flavor or whatever
println(variant.flavorName)
println(variant.buildType)
}
}
}
}
}
I'm getting build type in this way:
BuildConfig.BUILD_TYPE
If you need to check what is the current build type, create an enum class in your utils package and use it in your if statement:
enum class Environment(val value: String) {
RELEASE("release"),
LOCAL("local"),
STAGING("staging"),
DEBUG("debug")
}
Your if/when statement:
if (BuildConfig.BUILD_TYPE == Environment.RELEASE.value) {
//TODO
} else if(...)
or through when:
when(BuildConfig.BUILD_TYPE) {
Environment.RELEASE.value -> { //TODO }
Environment.LOCAL.value -> { // TODO }
// etc.
}
I checked other answers, nothing works.
What's below will help.
In your build.gradle (:app):
tasks.all { Task task ->
if (task.name == "preDebugBuild") {
doFirst {
//for debug build
}
} else if (task.name == "preReleaseBuild") {
doFirst {
//for release build
}
}
}
dependencies {
...
}
Be aware, the code that you put inside will not be executed when you change the build variant, but when you build app.
Try like this in your gradle : It works fine for me
//get current build all params as string
def buildParams = getGradle().getStartParameter().toString().toLowerCase();
applicationVariants.all { variant ->
variant.outputs.all {
def typename = variant.buildType.name.toLowerCase();
//and check build state in all variants
if(buildParams.contains(typename)) {
// finally, you get what you want.
}
}
}
You can get the exact build type by parsing your applicationVariants:
applicationVariants.all { variant ->
buildType = variant.buildType.name // sets the current build type
}
Here's the approach I used to detect the runtime build type without declaring any variables at build time.
def isCurrentBuildType(buildType) {
return gradle.getStartParameter().taskNames.find({ it.endsWith(buildType) }) != null
}
print(isCurrentBuildType("Release")) //prints true if the current build type is release.
Note that the first letter should be capital (e.g. assembleRelease, bundleRelease)

Avoid code duplication - How to create functions (and call them) in gradle?

I've been looking for a few minutes over the internet on how to create functions and call them inside build.gradle without success. Since I found nothing I'm not sure if I'm searching for the right concept-keywords or if that's even possible.
I have two buildTypes:
release {
}
debug {
}
And I woud like to call this snippet() below inside both of them without duplicating it, or in other words, to create a function:
def propsFile = rootProject.file('properties')
def M_PROP = "mProp"
if (propsFile.exists()) {
//Math
}
Generating something like:
buildTypes {
release {
snippet()
}
}
debug {
snippet()
}
}
is that possible and how am I able to do this?
Perhaps you want
buildTypes {
[release, debug].each { buildType ->
if (foo) {
buildType.doStuff()
}
}
}
Or maybe
ext.snippet = { buildType ->
if (foo) {
buildType.doStuff()
}
}
buildTypes {
snippet(release)
snippet(debug)
}
Note: There's also the with { ... } method in groovy so
buildType.doStuff1()
buildType.doStuff2()
buildType.doStuff3()
can be written as
buildType.with {
doStuff1()
doStuff2()
doStuff3()
}

Set constant value with gradle

I just moved to the android gradle build system and it is not clear for me how to set constant values.
e.g. I have
API.java which contains
BASE_URL = "http://debug.server.com"
but when gradle builds release I need BASE_URL to be "http://release.server.com"
How to replace this value at the build time?
Gradle generates a class called BuildConfig which contains static members (such as the boolean DEBUG, which is set to true for debug variants).
You could either query this in java like so:
if (BuildConfig.DEBUG) {
BASE_URL = "http://debug.server.com"
} else {
BASE_URL = "http://release.server.com"
}
or the same thing as a one-liner:
BASE_URL = BuildConfig.DEBUG ? "http://debug.server.com" : "http://release.server.com"
OR
You could actually set the BASE_URL inside the the BuildConfig class using gradle like so:
android {
buildTypes {
debug {
buildConfigField "String", "BASE_URL", '"http://debug.server.com"'
}
release {
buildConfigField "String", "BASE_URL", '"http://release.server.com"'
}
}
}
Note the single and double quotations around the value in gradle, as others have mentioned in the comments. This way, the double quotes become part of the value.
As a result, the static reference BuildConfig.BASE_URL would point to the corresponding URL (i.e. "debug.server.com" for debug, "release.server.com" for release)
Answer of fifer-sheep is correct. Just wanted to leave a solution for more than two environments.
Two flavors for staging and live.
productFlavors {
staging {
...
}
production {
...
}
}
Whole app config relies on the current ENV. Using:
public static String ENV_DEVELOPMENT = "development";
public static String ENV_STAGING = "staging";
public static String ENV_PRODUCTION = "production";
ENV = BuildConfig.DEBUG ? ENV_DEVELOPMENT : BuildConfig.FLAVOR;
I can switch between all different ENVs while testing locally but force staging/live settings if released.

Categories

Resources