I'm trying to list all of mine Android Gradle project's actual dependencies in the BuildConfig.java (see How to determine during the build which exactly version of dependency is used). For that I'm using code similar to this:
def getProjectAndBuildscriptDependencies() {
project.allprojects.collectMany{ proj ->
def configurations = (proj.configurations + proj.buildscript.configurations)
configurations.collectMany { it.allDependencies }
}.findAll { it instanceof ExternalDependency }
}
private def resolveActualDependencyVersion(Dependency dependency) {
def version = dependency.version
boolean mightBeDynamicVersion = version != null && (version.endsWith('+') || version.endsWith(']') || version.endsWith(')') || version.startsWith('latest.'))
if (!mightBeDynamicVersion){
return version
}
def actualVersion = resolveWithAllRepositories{
project.configurations.detachedConfiguration(dependency).resolvedConfiguration.lenientConfiguration
.getFirstLevelModuleDependencies(org.gradle.api.specs.Specs.SATISFIES_ALL).find()?.moduleVersion ?: version
}
return actualVersion
}
This function is supposed to gather all the dependencies I have. If I call it from some task, it does what it has to:
task printCurrentDependencies << {
def current = getProjectAndBuildscriptDependencies()
def currentVersions = [:]
current.each { dependency ->
def actualVersion = resolveActualDependencyVersion(dependency)
currentVersions.put(keyOf(dependency), actualVersion)
}
}
So that currentVersions contain all the dependencies with their actual version.
However when I call it from defaultConfig of android section, it only gives me one dependency, which is the Gradle itself.
android {
defaultConfig {
buildConfigField "int", "depsCount", getProjectDependencies().size().toString()
}
}
This difference in behavior is confusing me. Why this happens, and how to fix it and achieve what I really want?
Related
I am having problems when trying to use maven-publish plugin from AS.
I tried that example with a project and it works without problem. But as soon as I move to kotlin dsl, I have this issue:
SoftwareComponentInternal with name 'release' not found.
This is my first time dealing with kotlin dsl. First, I do not know if you can have both, kotlin dsl and groovy, but I tried that the first time by just adding kotlin dsl to the root and the app:build.gradle. I have this error so I decided to migrate also the library to kotlin dsl: mylib:build.gradle. I ended having this code:
plugins {
id(BuildPlugins.androidLibrary)
id(BuildPlugins.kotlinAndroid)
id(BuildPlugins.kotlinAndroidExtensions)
id(BuildPlugins.mavenPublish)
}
afterEvaluate {
publishing {
publications {
// Creates a Maven publication called "release".
create<MavenPublication>("release") {
// Applies the component for the release build variant.
from(components["release"])
// You can then customize attributes of the publication as shown below.
groupId = "com.mylib"
artifactId = "alpha"
version = "0.1"
}
}
}
}
Any idea about this and how to solve it?
Please make sure that you're using AGP 3.6.0+ since this configuration was added starting 3.6.0. Documentation:
https://developer.android.com/studio/build/maven-publish-plugin
So far I could come with this solution when using kotlin dsl:
Keep in mind that in this case, the solution posted is in groovy. But you can do it in kotlin dsl without problem.
Why is this in groovy? Well, the problem I had started when I included kotlin dsl in the app module, even though I had groovy gradle files in the submodules (which I want to publish as libraries too).
In this example, I am publishing a debug and a release version.
afterEvaluate {
publishing {
publications {
def groupIdPublication = 'com.mypackage'
def artifactIdPublication = "util"
// Creates a Maven publication called "release".
release(MavenPublication) {
// Applies the component for the release build variant.
//from components.release - this is not working
// You can then customize attributes of the publication as shown below.
groupId = groupIdPublication
artifactId = artifactIdPublication
version = '0.1'
artifact("$buildDir/outputs/aar/${project.name}-release.aar") // this is the solution I came up with
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
applyDependenciesToPOM(dependenciesNode, configurations.api.allDependencies)
applyDependenciesToPOM(dependenciesNode, configurations.implementation.allDependencies)
}
}
// Creates a Maven publication called “debug”.
debug(MavenPublication) {
// Applies the component for the debug build variant.
//from components.debug - this is not working
groupId = groupIdPublication
artifactId = artifactIdPublication
version = 'debug-0.1'
artifact("$buildDir/outputs/aar/${project.name}-debug.aar") // this is the solution I came up with
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
applyDependenciesToPOM(dependenciesNode, configurations.api.allDependencies)
applyDependenciesToPOM(dependenciesNode, configurations.implementation.allDependencies)
}
}
}
}
}
static def applyDependenciesToPOM(Object dependenciesNode, DependencySet allDependencies) {
allDependencies.each {
if (it.group != null && (it.name != null && !it.name.equals("unspecified")) &&
(it.version != null && !it.version.equals("unspecified"))) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
Gradle changed it's behavior when I updated from 5.0 to 5.1.
Lets assume that we have an android project with single module named library.
On Gradle 5.0 (and previous versions) when I executed ./gradlew assembleRelease or ./gradlew assembleDebug generated output was library-release.aar or library-debug.aar respectively.
After I updated to Gradle 5.1 (I also tried 5.1.1) it generates only library.aar for any build type without any build-type classifier in output file name.
So my question is: how can I force Gradle 5.1 to set correct output file naming for different build types like it was before? Below is my library module's build.gradle.kts, but I doesn't think that there is something wrong with it:
import org.jetbrains.kotlin.config.KotlinCompilerVersion
plugins {
id("com.android.library")
id("kotlin-android")
id("maven-publish")
}
val libraryVersion = "1.5.0"
android {
compileSdkVersion(28)
defaultConfig {
minSdkVersion(16)
targetSdkVersion(28)
versionCode = 1
versionName = libraryVersion
}
lintOptions {
isAbortOnError = false
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
}
}
dependencies {
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
}
UPDATE:
If I add a following code to build.gradle.kts:
afterEvaluate {
android.libraryVariants.forEach { libraryVariant ->
libraryVariant.outputs.forEach { output ->
println(output.outputFile.name)
}
}
}
It will print:
library-debug.aar
library-release.aar
Now it starts looking like a bug in gradle 5.1
So as we can see in https://github.com/gradle/gradle/issues/8328, it is a bug which is fixed in Android Gradle Plugin 3.4.
libraryVariants.all { variant ->
variant.outputs.all { output ->
def filePrefix = "$buildDir/outputs/aar/$archivesBaseName"
outputFileName = "${archivesBaseName}-${version}.aar"
def fileSuffix = "aar"
def originalFile = file("$filePrefix-${variant.buildType.name}.$fileSuffix")
def renamedFile = "$filePrefix-${variant.buildType.name}-$version.$fileSuffix"
tasks.named("assemble").configure {
doLast {
originalFile.renameTo(renamedFile)
}
}
}
}
where archivesBaseName and version can defined in default block of libraries build.gradle
I have android project based on gradle and I want to change mapping.txt file name after it's generated for my build. How can it be done?
upd
How it can be done in build.gradle? Since I have access there to my flavors and other stiff, I would like to create mapping file name based on flavor/build variant version.
Simpler solution.
applicationVariants.all { variant ->
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
copy {
from variant.mappingFile
into "${rootDir}/proguardTools"
rename { String fileName ->
"mapping-${variant.name}.txt"
}
}
}
}
}
As of today (May 2020) former solution, which uses variant.mappingFile is not working anymore in new Android Gradle plugin (Android Studio) 3.6 and higher.
Instead variant.mappingFile returns null and following is displayed in the logs:
WARNING: API 'variant.getMappingFile()' is obsolete and has been replaced with 'variant.getMappingFileProvider()'.
I am sharing my working solution, which uses new api:
applicationVariants.all { variant ->
variant.assembleProvider.get().doLast {
def mappingFiles = variant.getMappingFileProvider().get().files
for (file in mappingFiles) {
if (file != null && file.exists()) {
def nameMatchingApkFile = "$archivesBaseName-$variant.baseName-$file.name"
def newMappingFile = new File(file.parent, nameMatchingApkFile)
newMappingFile.delete() //clean-up if exists already
file.renameTo(newMappingFile)
}
}
}
}
Note, that variant.getBuildType().isMinifyEnabled() is not used since we are using DexGuard.
The code above makes mapping file's name match apk's file name.
Just in case, if you need to change apk name - following could be used:
android {
defaultConfig {
//resulting apk will looks like: "archive base name" + -<flavour>-<buildType>.apk
archivesBaseName = "$applicationId-$versionName"
}
}
Use this command in your proguard-rules.pro file:
-printmapping path/to/your/file/file_name.txt
the file will be written in part {root}/path/to/your/file with file_name.txt name.
If you want to have different setting for different flavors you can define many proguard-rules for them
I found one more idea but I am not sure that it is right way.
You can define your path in flavors:
productFlavors {
test1 {
applicationId "com.android.application.test"
project.ext."${name}Path" = 'path/one/mapp.txt'
}
test2 {
project.ext."${name}Path" = 'path/two/mapp.txt'
}
}
And as next you can define new task before $asseble{variant.name.capitalize()} task as is shown below:
android.applicationVariants.all { variant ->
def envFlavor = variant.productFlavors.get(0).name
def modifyProguardPath = tasks.create(name: "modifyProguardFor${variant.name.capitalize()}", type: Exec) {
def pathToMap = project."${envFlavor}Test1"
doFirst {
println "==== Edit start: $pathToMap ===="
}
doLast {
println "==== Edit end: $pathToMap ===="
}
executable "${rootDir}/utils/test.bash"
args pathToMap
}
project.tasks["assemble${variant.name.capitalize()}"].dependsOn(modifyProguardPath);
}
and in script ${root}/utils/test.bash - you can modify proguard-rules.pro.
But I think that exist better solution.
Many thanx to Sergii Pechenizkyi who helped me to found this good solution.
To implement copying of proguard mapping files for each flavor we can create "root" task copyProguardMappingTask and number of dynamic tasks for each flavor
def copyProguardMappingTask = project.tasks.create("copyProguardMapping")
applicationVariants.all { variant ->
variant.outputs.each { output ->
...
if (variant.getBuildType().isMinifyEnabled()) {
def copyProguardMappingVariantTask = project.tasks.create("copyProguardMapping${variant.name.capitalize()}", Copy)
def fromPath = variant.mappingFile;
def intoPath = output.outputFile.parent;
copyProguardMappingVariantTask.from(fromPath)
copyProguardMappingVariantTask.into(intoPath)
copyProguardMappingVariantTask.rename('mapping.txt', "mapping-${variant.name}.txt")
copyProguardMappingVariantTask.mustRunAfter variant.assemble
copyProguardMappingTask.dependsOn copyProguardMappingVariantTask
}
}
}
afterwards we should run this task after assembling our project. I use jenkins and my tasks option looks like
gradle clean assembleProjectName copyProguardMapping
It works like a charm.
Since the last update variant.mappingFile is not longer available.
(I use ProGuard version 4.7, AndroidStudio 2.0)
This is (part of) my build.gradle file:
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( "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 "";
}
}
buildTypes {
release {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
minifyEnabled true
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(output.outputFile.parent, "${variant.name}_v${variant.versionName}.apk")
}
def mappingFile = "${rootDir}\\app\\build\\outputs\\mapping\\${getCurrentFlavor()}\\release\\mapping.txt"
println("mappingFile: ${mappingFile}")
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
copy {
from "${mappingFile}"
into "${rootDir}"
rename { String fileName ->
"mapping-${variant.name}.txt"
}
}
}
}
}
}
debug {
minifyEnabled false
useProguard false
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(output.outputFile.parent, "${variant.name}_v${variant.versionName}.apk")
}
}
}
}
variant.assemble is now deprecated, suggested solution incorporating previous modifications:
archivesBaseName = "MyCompany-MyAppName-$versionName"
...
applicationVariants.all { variant ->
variant.assembleProvider.get().doLast {
if (variant.mappingFile != null && variant.mappingFile.exists()) {
def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
(new File(variant.mappingFile.parent, mappingFilename)).delete()
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/" + mappingFilename)
}
}
}
If using app bundle (aab) instead of apk, add this to after the android section:
afterEvaluate {
bundleRelease.doLast {
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
tasks.create(name: "renameMappingFile") {
if (variant.mappingFile != null && variant.mappingFile.exists()) {
variant.mappingFile.renameTo(variant.mappingFile.parent + "/$variant.baseName-$versionName-${new Date().format('yyyy-MM-dd_HHmm')}-mapping.txt")
}
}
}
}
}
}
Swap bundleRelease for assembleRelease for apks in the last example too.
Update: However that last doesn't work if you try and build a normal debug directly to your phone then. Error:
Could not get unknown property 'bundleRelease' for project ':app' of type org.gradle.api.Project.
This is a variation of igorpst's answer but renames mapping.txt to match the apk's name exactly including the app version name. I've combined this with code to name the APK with a version number as described in this answer. I had to snoop through the gradle source code to find $variant.baseName
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "com.company.app"
minSdkVersion 13
targetSdkVersion 21
versionCode 14 // increment with every release
versionName '1.4.8' // change with every release
archivesBaseName = "MyCompany-MyAppName-$versionName"
}
applicationVariants.all { variant ->
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
(new File(variant.mappingFile.parent, "$archivesBaseName-$variant.baseName-mapping.txt")).delete();
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/$archivesBaseName-$variant.baseName-mapping.txt")
}
}
}
}
All these answers used copy to rename the file.
I didn't want to move the file however, I just wanted to change it's name, keeping in mind the build type and flavor.
I based myself on the code from the other posters here and changed it up a bit.
Since Minify can be false, while still using proguard, I just check if the file is present.
Following code accomplishes just that.
android {
applicationVariants.all { variant ->
variant.assemble.doLast {
def mappingFolderUrl = "${project.buildDir.path}/outputs/mapping/"
if (variant.buildType.name) {
mappingFolderUrl += variant.buildType.name + "/"
}
if (variant.flavorName) {
mappingFolderUrl += variant.flavorName + "/"
}
def mappingFileUrl = mappingFolderUrl + "mapping.txt"
logger.lifecycle("mappingFile path: ${mappingFileUrl}")
File mappingFile = file(mappingFileUrl)
if (mappingFile.exists()) {
def newFileName = mappingFolderUrl + "mapping-${variant.name}.txt"
mappingFile.renameTo(newFileName)
}
}
}
}
NOTE
You could probably use this code to move the file as well.
the .renameTo() method expects a full path, If you change the path, I would suppose you effectively move the File to another place.
A complete solution that worked for me
applicationVariants.all { variant ->
def variantType = variant.buildType.name
if (variantType == "release") {
variant.assemble.doLast {
def mappingFile = variant.mappingFile
mappingFile.renameTo(mappingFile.parent + "/mapping-${variant.name}.txt")
}
}
}
For Android Studio Gradle Plugin Version 4.1.0 and newer (since about May 2020)
This version fixes the following warning:
WARNING: API 'variant.getMappingFile()' is obsolete and has been replaced with 'variant.getMappingFileProvider()'.
applicationVariants.all { variant ->
variant.assembleProvider.get().doLast {
def mappingFileProvider = variant.getMappingFileProvider().get()
if (mappingFileProvider != null) {
try {
def mappingFiles = mappingFileProvider.getFiles()
for (mappingFile in mappingFiles) {
if (mappingFile != null && mappingFile.exists()) {
def newMappingFileName = "$archivesBaseName-$variant.baseName-$mappingFile.name"
project.logger.lifecycle("Renaming '${mappingFile.name}' to '${newMappingFileName}'")
def newMappingFile = new File(mappingFile.parent, newMappingFileName)
newMappingFile.delete()
mappingFile.renameTo(newMappingFile)
}
}
} catch (Exception ignored) {
project.logger.lifecycle("No mapping files found to rename")
}
}
}
}
For Android Studio Gradle Plugin Version 3.3.0 (January 2019) through about May 2020
This overcomes previous issues where Android 3.0/Android Gradle Plugin 3.0 deprecated BuildType.isMinifyEnabled() and the gradle plugin deprecated variant.getAssemble().
applicationVariants.all { variant ->
variant.assembleProvider.get().doLast {
if (variant.mappingFile != null && variant.mappingFile.exists()) {
def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
(new File(variant.mappingFile.parent, mappingFilename)).delete()
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/" + mappingFilename)
}
}
}
Pinhassi's solution above works great and it is conforms to the latest Gradle changes. There are a couple of things though that I had to change:
The module name is hardcoded ("app"), which is not ideal since in a lot of cases (including mine) that will not be true. It is better to dynamically detect the module name.
The mapping file also only conforms to the Windows file system by having backward escaped slashes ("\"). If you are on a *NIX system like Linux or Mac, you need to replace those with forward non escaped slashes ("/")
Changed a bit the renaming of the .apk file to include the project name and added a date/time stamp at the end.
Here is the finished code:
import java.util.regex.Matcher
import java.util.regex.Pattern
buildTypes {
release {
debuggable false
minifyEnabled true
proguardFiles 'proguard.cfg'
// Rename the apk file and copy the ProGuard mapping file to the root of the project
applicationVariants.all { variant ->
if (variant.getBuildType().name.equals("release")) {
def formattedDate = new Date().format('yyyyMMddHHmmss')
def projectName = ""
variant.outputs.each { output ->
def fullName = output.outputFile.name
projectName = fullName.substring(0, fullName.indexOf('-'))
// ${variant.name} has the value of "paidRelease"
output.outputFile = new File((String) output.outputFile.parent, (String) output.outputFile.name.replace(".apk", "-v${variant.versionName}-${formattedDate}.apk"))
}
def mappingFile = "${rootDir}/${projectName}/build/outputs/mapping/${getCurrentFlavor()}/release/mapping.txt"
println("mappingFile: ${mappingFile}")
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
copy {
from "${mappingFile}"
into "${rootDir}"
rename { String fileName ->
"mapping-${variant.name}.txt"
}
}
}
}
}
}
}
debug {
debuggable true
}
}
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 "";
}
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast{
copy {
from variant.mappingFile
into "${rootDir}/mapping"
rename { String fileName ->
"mapping-${variant.name}-${new Date().format('yyyy_MM_dd')}.txt"
}
}
}
}
}
}
Here is solution that helps me:
compileSdkVersion 30
JavaVersion.VERSION_1_8
kotlin_version = '1.5.31'
com.android.tools.build:gradle:7.0.2
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationVariants.all { variant ->
// Generating apk file for each flavour release build
variant.outputs.all {
outputFileName = "${variant.flavorName}-${variant.versionCode}.apk"
}
// Generating mapping file for each flavour release build
if (variant.getBuildType().isMinifyEnabled()) {
variant.assembleProvider.get().doLast {
def files = variant.getMappingFileProvider().get().getFiles()
for (file in files) {
if (file != null && file.exists()) {
def newName = "mapping-${variant.flavorName}-${variant.versionCode}.txt"
def newFile = new File(file.parent, newName)
newFile.delete()
file.renameTo(newName)
}
}
}
}
}
}
Is there a way to have compile dependencies by multiple flavors in Android Studio (build.gradle)?
I have 2 flavorGroups, and in each 2 variants. Out of the 4 possible combinations I would like to be able to depend on a lib only if I'm both in latest and in free flavor. latestCompile or freeCompile works, but latestFreeCompile doesn't. this is the relevant part of my build.gradle:
android {
defaultConfig {
minSdkVersion 7
targetSdkVersion 19
versionCode 15
versionName "1.9." + versionCode
}
flavorGroups 'sdk', 'cost'
productFlavors {
latest {
flavorGroup 'sdk'
minSdkVersion 8
}
sdk7 {
flavorGroup 'sdk'
minSdkVersion 7
versionName android.defaultConfig.versionName + ".sdk7"
}
free {
flavorGroup 'cost'
}
pro {
flavorGroup 'cost'
}
}
}
dependencies {
// this works:
freeCompile files('libs/StartAppInApp-2.2.1.jar')
// and I would like something like this:
latestFreeCompile 'com.google.android.gms:play-services:4.1.32' // minSdkVersion:8
}
If I would use:
latestCompile 'com.google.android.gms:play-services:4.1.32'
then it would be included in latestPro as well (not needed)
and if I'd use:
freeCompile 'com.google.android.gms:play-services:4.1.32'
then it would be included in sdk7Free as well (although it needs SDK 8)
As now described on the official Android Developers website, if you want to declare a dependency for a specific combination of product flavor AND build type you need to declare that configuration first.
For example for the free flavor and the debug type:
configurations {
freeDebugImplementation {}
}
dependencies {
freeDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
Advanced: multiple flavor dimensions
Note that if your app has multiple flavor dimensions, you must create the configuration corresponding to the full build variant (ALL flavors, then build type), like so:
flavorDimensions "money", "image"
productFlavors {
free { dimension "money" }
paid { dimension "money" }
picasso { dimension "image" }
glide { dimension "image" }
}
configurations {
freePicassoDebugImplementation {}
freeGlideDebugImplementation {}
}
dependencies {
freePicassoDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
freeGlideDebugImplementation 'com.google.firebase:firebase-ads:9.8.0'
}
I had the same problem. I resolved that with some gradle code in my build.gradle:
// global variables
ext {
buildType = ""
groupCost = ""
groupSdk = ""
}
def splitCamelCase(String word) {
def result = []
int nextStart = 0;
for (int i = 1; i < word.length(); i++) {
if(word.charAt(i).isUpperCase()) {
result.add(word.substring(nextStart, i));
nextStart = i;
}
}
result.add(word.substring(nextStart));
return result;
}
// start parameters
println "Start parametes: tasks = " + gradle.startParameter.getTaskNames()
gradle.startParameter.getTaskNames().each { task ->
// This line is needed to skip other projects' tasks
// You can safely remove it if you have only one project
if(!task.startsWith(':<your_application_name>:')) return;
def taskParts = splitCamelCase(task.split(":").last());
def groupCostPrefix = taskParts[taskParts.size() - 3];
def groupSdkPrefix = taskParts[taskParts.size() - 2];
def buildTypePrefix = taskParts[taskParts.size() - 1];
if("Debug".startsWith(buildTypePrefix)) {
buildType = 'debug';
}
else if("Release".startsWith(buildTypePrefix)) {
buildType = 'release';
}
else {
return; // do not process tasks that are not ending with proper build type.
}
if("Free".startsWith(groupCostPrefix)) {
groupCost = 'free';
}
else if("Pro".startsWith(groupCostPrefix)) {
groupCost = 'pro';
}
if("Sdk7".startsWith(groupSdkPrefix)) {
groupSdk = 'froyo';
}
else if("Latest".startsWith(groupSdkPrefix)) {
groupSdk = 'latest';
}
}
Then all you need is to add following code inside your 'dependencies' section:
if(groupSdk == 'latest' && groupCost == 'free') {
compile 'com.google.android.gms:play-services:4.1.32'
}
Same issue here, but Pawel's solution didn't work because gradle dependencies have other issues in which it starts building not only the selected flavors/build type, but all of them and it requires a more dynamic solution.
Still I found this issue tracker:
https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=52962
There is also a reference to fixing the rebuild-all bug I mentioned above, but I didn't try it yet.
And implemented this solution (as per issue tracker reply #60):
Map<String, Dependency> customDeps = new HashMap<String, Dependency>()
customDeps.put('flavor1GrpXflabor1GrpYDebugCompile', dependencies.project(path: ':lib', configuration: 'debug'))
customDeps.put('flavor1GrpXflavor1GrpYReleaseCompile', dependencies.project(path: ':lib', configuration: 'release'))
customDeps.put('flavor2GrpXflavor1GrpYDebugCompile', dependencies.project(path: ':other_lib', configuration: 'debug'))
customDeps.put('flavor2GrpXflavor1GrpYReleaseCompile', dependencies.project(path: ':other_lib', configuration: 'release'))
....
configurations.all() { config ->
Dependency d = customDeps.get(config.name)
if (d != null) {
config.dependencies.add(d)
}
}
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")
}