When building an Android Library Project with Gradle, what's the correct way to exclude BuildConfig.class and R.class from the resulting .jar?
Probably the most elegant solution is to add at the end of the library's build.gradle:
afterEvaluate {
generateReleaseBuildConfig.enabled = false
}
To excude R and BuildConfig use this
afterEvaluate {
generateReleaseBuildConfig.enabled = false
generateDebugBuildConfig.enabled = false
generateReleaseResValues.enabled = false
generateDebugResValues.enabled = false
}
Don't forget to clean the project before building
Add a custom task:
task androidReleaseJar(type: Jar, dependsOn: assembleRelease) {
from "$buildDir/intermediates/classes/release/"
exclude '**/BuildConfig.class'
exclude '**/R.class'
exclude '**/R$*.class'
}
Reference:
1.https://github.com/facebook/rebound/blob/master/build.gradle
2.https://github.com/keyboardsurfer/Crouton/blob/master/library/build.gradle
3.https://github.com/SnowdreamFramework/android-log/commit/4297a0244c972e3fcb9042b5e12181b21c33b524
you should get it right after the CompileReleaseSources step.
this worked for me:
task removeBuildConfig(dependsOn: "compileReleaseSources") {
doFirst {
file("$buildDir/intermediates/classes/release/pathToFile/BuildConfig.class").delete()
}
}
This works for me
afterEvaluate {
bundleDebug.dependsOn "removeBuildConfigDebug"
bundleRelease.dependsOn "removeBuildConfigRelease"
}
task removeBuildConfigDebug(dependsOn: "generateDebugBuildConfig") {
doFirst {
file("$buildDir/intermediates/classes/debug/com/amazon/geo/routing/BuildConfig.class").delete()
file("$buildDir/generated/source/buildConfig/debug/com/amazon/geo/routing/BuildConfig.java").delete()
}
}
task removeBuildConfigRelease(dependsOn: "generateReleaseBuildConfig") {
doFirst {
file("$buildDir/intermediates/classes/release/com/amazon/geo/routing/BuildConfig.class").delete()
file("$buildDir/generated/source/buildConfig/release/com/amazon/geo/routing/BuildConfig.java").delete()
}
}
Here is my code, when bundle task added, we can hook it here.
tasks.whenTaskAdded {
if (it.name == 'bundleRelease' || it.name == 'bundleDebug') {
// bundle task is Zip
it.exclude '**/BuildConfig.class', '**/R.class', '**/R$*.class'
}
}
Related
I'm trying to use the Open API 3 generator plugin for gradle.
It is enough to add the lines
plugins {
id "org.openapi.generator" version "5.0.1"
}
in the project's build.gradle to get the error:
com.google.common.base.Suppliers$NonSerializableMemoizingSupplier
cannot be cast to java.util.function.Supplier
I'm using gradle 6.4
This is how I have implemented it now. Please notice the line:
classpath("com.google.guava:guava:30.1-jre")
that is the fix of the issue.
In the project build.gradle add:
buildscript {
dependecies {
classpath("com.google.guava:guava:30.1-jre")
}
plugins {
id "org.openapi.generator" version "5.0.1"
}
task generateCode(type: org.openapitools.generator.gradle.plugin.tasks.GenerateTask) {
generatorName = "kotlin"
inputSpec = sampleApiSpec
outputDir = "$projectDir/generatedapi"
groupId = "$project.group"
id = "$project.name"
version = "$project.version"
apiPackage = "com.package.name.generatedapi.apis"
invokerPackage = "com.package.name.generatedapi.apis.invoker"
modelPackage = "com.package.name.generatedapi.apis.model"
enablePostProcessFile = true
skipOverwrite = false
modelNamePrefix = "Raw"
configOptions = [
java8 : "true",
dateLibrary : "java8",
serializationLibrary: "moshi",
library : "resttemplate",
useBeanValidation : "true",
enableBuilderSupport: "true",
]
}
android.sourceSets.main.java.srcDirs += ['generatedapi']
Then you can run:
./gradlew generateCode
or you can set generateCode as a dependency for your build task
Please check:
https://github.com/GoogleCloudPlatform/artifact-registry-maven-tools/issues/27
EDIT
Even better:
buildscript {
dependecies {
classpath('org.openapitools:openapi-generator-gradle-plugin:5.0.1') {
exclude group: 'com.google.guava'
}
}
configurations {
compile.exclude module: 'guava-jdk5'
}
So no need to overwrite the guava lib just exclude the one imported by the generator
https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator-gradle-plugin
I am using Gradle 4.1 with Gradle-Android plugin 3.0.1 on Android Studio 3.2
I have 2 flavors 'production' and 'staging' and I am unable to build my project as a library with different build variants.
app build.gradle:
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
...
productFlavors {
production {
}
staging {
}
}
defaultPublishConfig "productionRelease"
publishNonDefault true
}
if( android.productFlavors.size() > 0 ) {
android.libraryVariants.all { variant ->
if( android.publishNonDefault && variant.name == android.defaultPublishConfig ) {
def bundleTask = tasks["bundle${name.capitalize()}"]
artifacts {
archives(bundleTask.archivePath) {
classifier name.replace('-' + variant.name, '')
builtBy bundleTask
name name.replace('-' + variant.name, '')
}
}
}
...
Then I run: ./gradlew clean install, errors I got is:
Execution failed for task ‘:app:install’.
Could not publish configuration ‘archives’
A POM cannot have multiple artifacts with the same type and classifier. Already have MavenArtifact app:aar:aar:null, trying to add MavenArtifact app:aar:aar:null.
And to get this code to compile, I need to swap android.publishNonDefault with true, otherwise I will get an error of: Cannot get the value of write-only property 'publishNonDefault'
Any suggestions or hint would be really helpful, the aim is to build the library module on jitpack, where we can import it in project with build variants. thanks!
After digging into this for 2 days, and emailing Jitpack support, the issue is because the lib has updated and publishNonDefault is deprecated. you just need to change your app build.gradle to:
apply plugin: 'com.github.dcendents.android-maven'
dependencies {...}
group = 'com.github.your-group'
if (android.productFlavors.size() > 0) {
android.libraryVariants.all { variant ->
if (variant.name.toLowerCase().contains("debug")) {
return
}
def bundleTask = tasks["bundle${variant.name.capitalize()}"]
artifacts {
archives(bundleTask.archivePath) {
classifier variant.flavorName
builtBy bundleTask
name = project.name
}
}
}
}
problem was to create multiple flavors using jitpack
so what I do is, create a variable which stores the flavor name, after that loop through the available variant, pass the flavorBuild to artifact so when u push the code to GitHub and create artifact via jitpack then jitpack create the required implementation based on your build flavor and then u can use it. You need to just change build flavor
publishing {
publications {
def flavorBuild ='production'
android.libraryVariants.all { variant ->
"maven${variant.name.capitalize()}Aar"(MavenPublication) {
from components.findByName("android${variant.name.capitalize()}")
groupId = 'com.examplesdk'
artifactId = 'examplesdk'
version "1.0.0-${variant.name}"
artifact "$buildDir/outputs/aar/examplesdk-${flavorBuild}-release.aar"
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.api.allDependencies.each { dependency ->
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependency.group)
dependencyNode.appendNode('artifactId', dependency.name)
dependencyNode.appendNode('version', dependency.version)
}
}
}
}
}
repositories{
maven {
url "$buildDir/repo"
}
}
}
On previous versions of android plugin for Gradle I could, with my own task, take the path of the jar dependencies using this:
android.libraryVariants.all { variant ->
task "copyDependencies${variant.name.capitalize()}"(type: Copy) {
configurations.compile.files().each { dependency ->
from dependency.path
}
into project.projectDir.path + "/build/libs/${variant.name}"
}
}
But in the latest version of this plugin, compile pass to deprecated and they introduced api and implementation configurations, so when I try to use the previous code, gradle said that:
Resolving configuration 'api' directly is not allowed
Any suggestion for this new change introduced?
UPDATE
I have got a dependencies list and filter for configurations doing this:
android.libraryVariants.all { variant ->
task "copyDependencies${variant.name.capitalize()}"(type: Copy) {
from {
variant.getCompileConfiguration().files().each { dependency ->
configurations.api.getDependencies().each { configDep ->
if (dependency.name.contains(configDep.name)) {
from dependency.path
}
}
}
}
into project.projectDir.path + "/build/libs/${variant.name}"
}
}
But this solution still has problems, in addiction when project B is dependent on project A. Both with this task defined, Gradle doesn't build.
Finally, I found a solution for copy dependencies from a configuration:
android.libraryVariants.all { variant ->
task "copyDependencies${variant.name.capitalize()}"() {
outputs.upToDateWhen { false }
doLast {
println "Executing from in copyDependencies${variant.name.capitalize()}"
variant.getCompileConfiguration().getAllDependencies().each { dependency ->
configurations.api.getDependencies().each { configDep ->
if (dependency.name.contains(configDep.name)) {
println "Dependency detected: " + dependency.name
variant.getCompileClasspath().each { fileDependency ->
if (fileDependency.absolutePath.contains(dependency.name)) {
println fileDependency.absolutePath
copy {
from fileDependency.absolutePath
into project.projectDir.path + "/build/intermediates/bundles/${variant.name}/libs"
exclude '**/classes.jar'
}
}
}
}
}
}
}
}
}
Please see chapter 48.4 of the userguide to find all available configurations and which are to be consumed and / or resolved.
I'm in the early stages of migrating an android project to gradle, using the experimental plugin 0.4.0
As part of the build process I have a number of scripts that should run prior to compiling my code / building the apk. The arguments or tasks themselves will be different for a debug or release build.
I'm struggling to find a straight forward way to achieve this.
I've stripped everything back to a simple hello world project while I figure this out.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.4.0'
}
}
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
}
android.buildTypes {
debug {
//
}
release {
//runProguard, sign etc.
}
}
android.productFlavors {
create("free")
{
applicationId = "com.example.app.free"
versionName = "1.0-free"
}
create("full")
{
applicationId = "com.example.app.full"
versionName = "1.0-full"
}
}
}
repositories {
jcenter()
}
dependencies {
compile 'joda-time:joda-time:2.7'
}
task runScript(type: Exec) {
executable "sh"
args "-c", "echo SomeScriptHere"
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn runScript
}
task wrapper(type: Wrapper) {
gradleVersion = '2.5'
}
Ideally I want to differentiate by buildType ( not productFlavor ) as the tasks required as based on the type of build not the variety of app produced.
Is it possible to specify that a task should only run on a release or debug build?
Alternatively is it possible to define different arguments to be used for my runScript task based on being a release or debug build?
Apologies if I'm missing something obvious, I'm pretty new to using gradle.
using onlyIf() would be one option gradle docs here but having to define properties seemed awkward especially as a project gets larger and more complicated.
ZXStudio has a good blog post / example here where instead of using properties or rules will iterate over the existing tasks and create a new task based on the buildType / flavor.
So for my original question the answer mean removing the runScript task above and replacing tasks.withType(JavaCompile) as follows;
Could also be extended to match build flavors and create tasks appropriately.
tasks.withType(JavaCompile) {
def newTaskName = "runScript_" + name;
def isDebug = false;
if( name.contains("Debug") )
{
isDebug = true;
}
//Create a new task
tasks.create( newTaskName, Exec ) {
if( isDebug )
{
executable "sh"
args "-c", "echo this is a DEBUG task"
}
else
{
executable "sh"
args "-c", "echo this is a RELEASE task"
}
}
dependsOn newTaskName
}
I'm wondering how to add dependency to specific productFlavor and buildType in gradle.
For example I have productFlavor free and build type release, how can I add a dependency on the assembleFreeRelease task?
I've tried many variants but neither works.
For example I tried:
task('release', dependsOn: assembleProductionRelease) {
}
// error: Could not find property 'assembleProductionRelease' on root project 'app'.
Or:
task('release', dependsOn: 'assembleProductionRelease') {
}
Here there is no error but the task is executed for every flavor and build type, very confusing.
There is built-in support for flavor and buildType dependencies.
dependencies {
flavor1Compile "..."
freeReleaseCompile "..."
}
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Sourcesets-and-Dependencies
These task are generated dynamically based on your Android plugin configuration. At the time of configuration they are not available to you yet. You can defer the creation of your task in two ways:
Wait until the project is evaluated.
afterEvaluate {
task yourTask(dependsOn: assembleFreeRelease) {
println "Your task"
}
}
Lazily declaring the task dependency as String.
task yourTask(dependsOn: 'assembleFreeRelease') {
println "Your task"
}
Looks like now in version 1 of the android gradle plugin and gradle 2.2+, you can do this with a configuration:
configurations {
freeReleaseCompile
}
android {
...
productFlavors {
free {
...
}
}
}
dependencies {
freeReleaseCompile('myLib#aar')
}
Reference: https://groups.google.com/forum/#!msg/adt-dev/E2H_rNyO6-o/h-zFJso-ncoJ
Muschko's answer didn't work for me, so this is my solution, written and posted by me here
Define the task that should only be executed on a specific buildType/variant/flavor
task doSomethingOnWhenBuildProductionRelease << {
//code
}
It's important to use the "<<" syntax or else the task will automatically be called every time.
Dynamically set the dependency when the tasks are added by gradle
tasks.whenTaskAdded { task ->
if (task.name == 'assembleProductionRelease') {
task.dependsOn doSomethingOnWhenBuildProductionRelease
}
}
android {
ext.addDependency = {
task, flavor, dependency ->
def taskName = task.name.toLowerCase(Locale.US)
if (taskName.indexOf(flavor.toLowerCase(Locale.US)) >= 0) {
task.dependsOn dependency
}
}
productFlavors {
production {
}
other
}
task theProductionTask << {
println('only in production')
}
tasks.withType(JavaCompile) {
compileTask -> addDependency compileTask, "production", theProductionTask
}
}
To be frank, I don't which locale is used to generate names for compile taks so toLowerCase(Locale.US) may be counterproductive.
Here is another way that I used:
tasks.withType(JavaCompile) {
compileTask ->
def dependedTaskName = "dependedTask_";
if(compileTask.name.contains('Release') {
dependedTaskName += "Release";
}
createTask(dependedTaskName, Exec) {
........
}
compileTask.dependsOn ndkBuildTaskName
}
Another way:
tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildTypeBuildConfig') {
task.dependsOn doSomethingForReleaseBuild
}
}
The 1st method is dynamic while the second one is simpler.
Update: This solution no longer works as of 12/8/2022
I now get the error message: No such property: productFlavors for class: org.gradle.api.internal.provider.DefaultProperty
All the above answers helped me, but I ended up needing to run a script before the compilation of each flavor's debug + release build. The first argument of the script takes the flavor name.
In order to have this trigger early on, but after the flavor specific task has been created, the key is binding the dependsOn to generateMyflavorDebugBuildConfig.
Here's the script I ended up with, which can be placed at the bottom of app/build.gradle
// When a task is active, loop through all app flavors, and see if the
// task.name matches the earliest task we can set up the dependsOn
// loop through all flavors, and create a task that does the work we want to do
// before anything else
android.productFlavors.all { flavor ->
task("${flavor.name}RunCommandBefore", type: Exec) {
workingDir "$projectDir/../.."
commandLine 'sh', 'scripts/run_thing.sh', "${flavor.name}"
}
}
// when tasks created, loop through and as early as we can, bind the
// RunCommandBefore task we created above
tasks.whenTaskAdded { task ->
def taskName = task.name
// loop through all flavors
android.productFlavors.all { flavor ->
def flavorName = flavor.name
// loop through release types so that this happens for debug and release
['debug', 'release'].each { releaseType ->
// generateMyflavorDebugBuildConfig is the earliest task we're able
// to set up the dependsOn to make sure that our script runs
if (taskName.toLowerCase() == "generate${flavorName.toLowerCase()}${releaseType}buildconfig") {
// now myflavorRunCommandBefore will run before
// generateMyflavorDebugBuildConfig
tasks."$taskName".dependsOn "${flavorName}RunCommandBefore"
}
}
}
}
Hope this helps someone! Amazing how difficult Android makes running a simple script first thing..