I have a flavor dimensions in module build.gradle file, and gradle build process runs through all build variants whatever actual build variant is. Here is module build.gradle:
flavorDimensions 'type', 'jnitype'
productFlavors {
demo {
dimension 'type'
versionNameSuffix '.demo'
}
production {
dimension 'type'
versionNameSuffix '.production'
}
usejni {
dimension 'jnitype'
versionNameSuffix '.usejni'
copy {
from('../jnilib/data') {
include 'sdk_data.gpu'
....
}
into 'src/main/assets/data'
}
}
nojni {
dimension 'jnitype'
versionNameSuffix '.nojni'
delete('src/main/assets/data/*.*')
packagingOptions {
exclude 'lib/arm64-v8a/sdk.so'
...
}
}
}
So no matter what build variant selected, demoUsejni or demoNojni, gradle runs through 'usejni' and then 'nojni' variants - it copies files and libraries, and then deletes them. I used gradle debug to confirm this.
How can I tell gradle to use just a selected build flavor?
AS 3.5.2, gradle plugin 5.4.1, android build tools 3.5.2.
Finally, the solution is found: use gradle task name to check for required flavor. For an action at compile type gradle task name will be ":module_name:assembleFlavor1Flavor2...FlavornBuildtype". So i will check for "assemble" and "Nojni"/"Usejni".
String taskName = "";
if (gradle.startParameter.taskNames.size > 0)
taskName = gradle.startParameter.taskNames.get(0)
usejni {
dimension 'jnitype'
versionNameSuffix '.usejni'
if (taskName.contains("Usejni") && taskName.contains("assemble")) {
copy {
from('../jnilib/data') {
include 'sdk_data.gpu'
....
}
into 'src/main/assets/data'
}
}
}
nojni {
dimension 'jnitype'
versionNameSuffix '.nojni'
if (taskName.contains("Nojni") && taskName.contains("assemble")) {
delete('src/main/assets/data/*.*')
packagingOptions {
exclude 'lib/arm64-v8a/sdk.so'
...
}
}
}
Like a charm!
Hint for solution found in this answer: How to get current buildType in Android Gradle configuration
Related
I'm trying to set androidResources.noCompress for only one product flavor
When I try this
flavorDimensions "ring"
productFlavors {
innerRing {
dimension "ring"
}
outerRing {
dimension "ring"
androidResources {
noCompress 'so'
}
}
}
Both innerRing and outerRing end up with .so files uncompressed. I believe this is due to gradle configuring all the product flavors statically. (see here)
But when I try to change noCompress afterEvaluate
afterEvaluate {
android.applicationVariants.all { variant ->
def ring = variant.getProductFlavors().get(0).name
if (ring == "outerRing") {
println("Don't compress .so files for outer ring build")
android.androidResources.noCompress = ['so']
}
}
}
I get this error
com.android.build.gradle.internal.dsl.AgpDslLockedException:
It is too late to modify internalNoCompressList
It has already been read to configure this project.
Consider either moving this call to be during evaluation,
or using the variant API.
How can I use the variant API to fix this?
Any help is appreciated!
If you are on AGP 7.2
android {
...
androidComponents {
onVariants(selector().all()) { variant ->
if(variant.name.startsWith("outerRing")) {
variant.androidResources.noCompress.add('so')
}
}
}
...
}
If AGP is below 7.2 then from the documentation theoretically the following code should work, but I've had no success with it
android {
...
androidComponents {
onVariants(selector().all()) { variant ->
if(variant.name.startsWith("outerRing")) {
def aaptParams = variant.androidResources.aaptAdditionalParameters
aaptParams.add("-0")
aaptParams.add("so")
}
}
}
...
}
I'm writing am Android Studio Gradle plugin and one of the things I need to do is iterate over the projects and detect if they are of type Android Application or library (or a non android project like general java libs).
I tried to use the following code but it doesn't work properly (detects the application but not libraries):
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.LibraryPlugin
...
public void getVariants(){
...
def hasApp = p.plugins.withType(AppPlugin)
def hasLib = p.plugins.withType(LibraryPlugin)
...
}
Are there any other proper ways in Groovy script to detect of a project is of type library ?
project.afterEvaluate {
plugins.withId('com.android.application') {
println("==> module application")
}
plugins.withId('com.android.library') {
println("==> module library")
}
plugins.withId('java') {
println("==> module pure java")
}
}
I have a flavor.gradle defined which is getting imported into application & library modules. I need to add applicationSuffixId only for application flavor. So I done it like this.
def isApplication = plugins.hasPlugin("com.android.application")
android {
flavorDimensions "version"
productFlavors {
dev {
dimension "version"
if (isApplication) {
applicationIdSuffix ".dev"
}
}
qa {
dimension "version"
if (isApplication) {
applicationIdSuffix ".qa"
}
}
beta {
dimension "version"
if (isApplication) {
applicationIdSuffix ".beta"
}
}
prod {
dimension "version"
}
}
}
I have this gradle:
android {
...
buildTypes {
debug {
externalNativeBuild {
cmake {
cppFlags "-DGENDEV"
}
}
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
...
}
This if condition works fine in my CMakeList file:
if("${CMAKE_CXX_FLAGS}" MATCHES "GENDEV$")
// true
endif()
I recently added flavors in my build like so:
flavorDimensions "version"
productFlavors {
free {
dimension "version"
externalNativeBuild.cmake {
cppFlags "-DFLAVOR_FREE"
}
}
full {
dimension "version"
externalNativeBuild.cmake {
cppFlags "-DFLAVOR_FULL"
}
}
}
Now, I have these checks in my CMakeList file:
if("${CMAKE_CXX_FLAGS}" MATCHES "FLAVOR_FULL$")
// full version
else()
// free version
endif()
if("${CMAKE_CXX_FLAGS}" MATCHES "GENDEV$")
// true
endif()
The first check is always false, thus every build is a FREE version! What am I doing wrong?
Your code is alright, but the Gradle plugin was too old to handle it correctly.
For me, same code worked with
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
}
}
This plugin requires Gradle 5.5.
Note that an easier approach to CMake integration could be to specify arguments:
flavorDimensions "version"
productFlavors {
free {
dimension "version"
externalNativeBuild.cmake {
arguments "-DFLAVOR=FREE"
}
}
full {
dimension "version"
externalNativeBuild.cmake {
arguments "-DFLAVOR=FULL"
}
}
}
This way, your CMakeLists.txt will be more readable:
if(${FLAVOR} STREQUAL "FULL")
// full version
else()
// free version
endif()
Or, you can choose to use arguments "-DFLAVOR_FULL" and if(FLAVOR_FULL)
I want to also point out that MATCHES is matching a regex expression. Thus, changing the following:
if("${CMAKE_CXX_FLAGS}" MATCHES "FLAVOR_FULL$")
to
if("${CMAKE_CXX_FLAGS}" MATCHES "FLAVOR_FULL") // without the $
helped solved the problem. Basically, the original condition means to match the "FLAVOR_FULL" text if it is at the end of the string such as "-DGENDEV -DFLAVOR_FULL". This won't match if the string is "-DFLAVOR_FULL -DGENDEV".
I have the two default build types: debug / release and a couple of flavors: prod / dev.
Now I want to exclude the build variant dev-release, but keep all other possible combinations. Is there a way to achieve this?
Variant filter
Use the variantFilter of the gradle android plugin to mark certain combinations as ignored. Here is an example from the official documentation that works with flavor dimensions and shows how it can be used:
android {
...
buildTypes {...}
flavorDimensions "api", "mode"
productFlavors {
demo {...}
full {...}
minApi24 {...}
minApi23 {...}
minApi21 {...}
}
variantFilter { variant ->
def names = variant.flavors*.name
// To check for a certain build type, use variant.buildType.name == "<buildType>"
if (names.contains("minApi21") && names.contains("demo")) {
// Gradle ignores any variants that satisfy the conditions above.
setIgnore(true)
}
}
}
As the comment says, you can also check the buildType like so:
android {
variantFilter { variant ->
def names = variant.flavors*.name
if(variant.buildType.name == 'release' && names.contains("myforbiddenflavor")) {
setIgnore(true)
}
}
}
Using variant filters like others I found it was easiest to do this by comparing the variant name against a list of variants that I want to keep.
So in my app/build.gradle file I have something like:
android {
variantFilter { variant ->
def needed = variant.name in [
'stagingQuickDebug', // for development
'stagingFullDebug', // for debugging all configurations
'stagingFullCandidate', // for local builds before beta release
'stagingFullRelease', // for beta releases
'productionFullCandidate', // for local builds before going public
'productionFullRelease' // for public releases
]
variant.setIgnore(!needed)
}
buildTypes {
debug {
}
release {
}
candidate.initWith(release)
}
flavorDimensions "server", "build"
productFlavors {
staging {
dimension "server"
buildConfigField "String", "API_URL", '"https://example-preprod.com/"'
}
production {
dimension "server"
buildConfigField "String", "API_URL", '"https://example.com/"'
}
quick {
dimension "build"
minSdkVersion 21
resConfigs("en", "xxhdpi")
}
full {
dimension "build"
}
}
}
When working with flavor dimensions try this one
variantFilter { variant ->
def dim = variant.flavors.collectEntries {
[(it.productFlavor.dimension): it.productFlavor.name]
}
if (dim.dimensionOne == 'paid' && dim.dimensionSecond == 'someVal') {
variant.setIgnore(true);
}
}
If you use flavor dimensions do this:
flavorDimensions "device", "server"
productFlavors {
emulator {
dimension = "device"
}
phone {
dimension = "device"
}
staging {
dimension = "server"
}
production {
dimension = "server"
}
}
android.variantFilter { variant ->
def device = variant.getFlavors().get(0).name
def server = variant.getFlavors().get(1).name
def isRelease = variant.buildType.name.equals('release')
def isDebug = variant.buildType.name.equals('debug')
// Disable emulatorProductionRelease build variant
if (device.equals('emulator') && server.equals('production') && isRelease) {
variant.setIgnore(true)
}
}
It's easy to read and you can target specific build variants.
The solutions here didn't work for me - I run into this post and added this to build.gradle in my app and it solved the issue for me
gradle.taskGraph.whenReady { graph ->
graph.allTasks.findAll { it.name ==~ /.*MyVariant.*/ }*.enabled = false
}
This is what it does - waits for gradle to assemble the complete list of tasks to execute and then it marks all the tasks that match the name pattern as disabled
NOTE
The match is exact - the expression above lets you match any task that has "MyVariant" somewhere in it's name and it is case sensitive
One more simpler way
android.variantFilter { variant ->
if (variant.name == "qaDebug" || variant.name == "devRelease") {
setIgnore(true)
}
}
Or if you place this code inside android {} closure, android. can be omitted
android {
// Please always specify the reason for such filtering
variantFilter { variant ->
if (variant.name == "qaDebug" || variant.name == "devRelease") {
setIgnore(true)
}
}
}
Please always put a meaningful comment for things like this.
UPD: For Kotlin Gradle DSL there is another way:
android {
variantFilter {
ignore = listOf("qaDebug", "devRelease").contains(name)
}
}
The answer of #ade.se didn't work for me. But I've struggled a little, and written this, that works great:
android {
compileSdkVersion 22
buildToolsVersion '20.0.0'
variantFilter { variant ->
if (variant.buildType.name.equals('debug') || variant.buildType.name.equals('release')) {
variant.setIgnore(true);
}
}
defaultConfig {
applicationId "com.fewlaps.quitnow"
minSdkVersion 15
targetSdkVersion 22
versionCode 35
versionName "1.35"
}
The code you have to add is the variantFilter one, but I've pasted a little of the context to make it easy to understand.
See Variant filter answer above.
Old Answer:
It's not possible at the moment, but it's something we want to add. Probably soon.
In the meantime you could disable the assemble task I think. Something like this:
android.applicationVariants.all { variant ->
if ("devRelease".equals(variant.name)) {
variant.assembleTask.enabled = false
}
}
In Gradle's Kotlin DSL (i.e. build.gradle.kts), that would be:
variantFilter {
ignore = buildType.name == "release" &&
flavors.map { it.name }.contains("dev")
}
I have a Gradle Android Project with this product Groups and Flavors configuration:
/*
* Define different flavor groups
*/
flavorGroups 'market', 'version'
/*
* Defile product flavors
*/
productFlavors {
amazon {
flavorGroup 'market'
}
google {
flavorGroup 'market'
}
flav1 {
flavorGroup 'version'
packageName 'com.company.flav1'
}
flav2 {
flavorGroup 'version'
packageName 'com.company.flav2'
}
flav3 {
flavorGroup 'version'
packageName 'com.company.flav3'
}
}
// .. Other stuff
It works great. All sources and resources are merged correctly.
But for specific reasons I need the package suffix to be .amz for the amazon product flavor. How can I achieve that?
I tried this way:
amazon {
flavorGroup 'market'
packageNameSuffix '.amz'
}
but gradle throws an exception.
This is not possible at the moment.
packageNameSuffix is strictly a property of Build Type.
I'd like to offer a way to customize the ProductFlavor's values for a given variant (ie a given combination of all flavor dimensions and of build type) but it's not possible at the moment.
Instead of a new dimension of flavor, you could do a "amzRelease" buildtype that extends the existing release buildtype and add a suffix.
If your current "amazon" flavor does more (configure versionCode/Name/etc...), it won't work though. You could then use both your amazon flavor and a amzRelease build type. It'll create a lot more variants than you need but it'd work until we have some better.
You could use a variant of the solution I've written about here: https://stackoverflow.com/a/26585241/4177090
In short, you can find the combined variants using variantFilter and then update the appliciationId from there:
android.variantFilter { variant ->
def flavorString = ""
def flavors = variant.getFlavors()
for (int i = 0; i < flavors.size(); i++) {
flavorString += flavors[i].name;
}
if(flavorString.contains("amazon")) {
variant.getDefaultConfig().applicationId variant.getDefaultConfig().applicationId + ".amz"
}
}
Working solution for gradle 1.0.0+:
android.applicationVariants.all { variant ->
def flavorName = variant.getVariantData().getVariantConfiguration().getFlavorName()
def mergedFlavour = variant.getVariantData().getVariantConfiguration().getMergedFlavor();
if (flavorName.toLowerCase().contains("amazon")) {
mergedFlavour.setApplicationId(mergedFlavour.getApplicationId() + ".amz")
}
}