Gradle multi-project conditional dependencry - android

How would one create an Android Studio (Gradle) multi-project configuration such that projB depends on project(':projA') if projA is defined, but uses a file in libs/ otherwise?
Since it may be asked, in this case projA is an SDK; projB is a test application designed to demonstrate the SDK. If the SDK team gets a bug report, it often includes reproduction steps using projB.
When projB team does work, they do so on RC builds of projA, whereas the SDK team uses projB, with a dependency on project(':projA') so that a debug session can be run.
projB has no specific definition of its dependency on projA; that team takes the projA output from the build server and drops it in the libs/ folder, and has a wildcard dependency.
EDIT
I finally went with this code in the dependencies closure, and it works like a charm:
def sdkRef
project.getRootProject().allprojects.each { proj ->
if (proj.name.equals("Sdk")) {
sdkRef = proj;
return true;
}
}
if (sdkRef) {
println "SDK present in project; using project reference as dependency"
compile sdkRef
} else {
println "SDK is not present in project; using libs/"
}

I wonder if that's something you can do with flavors and build variants.
Through code you might try in your build file :
dependencies {
if (project.getRootProject().findProject(":projectA")) {
compile project(":projectA")
} else {
compile files("libs/projectA.jar")
}
}
One thing you have to consider is that your settings.gradle defines what modules are included in your project. So your two teams might end up with different files anyway for the project.

You can achieve that with productFlavors.
You just have to define:
2 product flavors in projB/build.gradle
a specific dependency for each flavor
android {
productFlavors {
demo{}
sdkdev{}
}
...
}
dependencies{
demoCompile files("libs/projectA.jar")
sdkdevCompile project(":projectA")
...
}
The build will produce 2 apks.
In Android studio, someone from the demo team can run the demo flavor by selecting the "demoDebug" (or "demoRelease") variant (in Build Variant tab) and someone from sdk team will select the "sdkdevDebug" variant.
The gradle.settings must contains references for projA and projB, but a user from demo team will never have to compile projA because the demo flavor have no dependencies on it.

Related

importing an existing JAR or AAR as new project module

how to import JAR or AAR package as new project module in A new Android Studio Arctic Fox | 2020.3.1 Canary 9 ?
please let me know.
This works on Android Studio Arctic Fox Beta 02
Step 1 : Navigate to, File -> Project Structure. You can also press Ctrl+Alt+Shift+S
You will see a window just like below.
Step 2 : Click On app module as shown in image
Step 3 : Click on + icon as marked in image
Step 4 : You will see option to select jar/aar dependency. Click on it
You will see another window just like above asking you to specify path. Specify the path in which you kept the aar/jar file and hit Ok.
That should work
You can directly implement using JAR/ARR file path.
implementation files('/File Path/file.aar')
For Android Studio Bumblebee, original answer given here
I have followed steps suggested by the Android developer site:
Copy .aar file into the libs folder of the app
File -> Project Structure... -> Dependencies
Click on "+" icon and select JR/AAR Dependency and select app module
Add .aar file path in step 1.
Check your app’s build.gradle file to confirm a declaration.
Step 1: Put your aar file in the libs folder. And let’s take the file name is supernover.aar as an example.
Step 2: Put the following code in your Project level
build.gradle file,
allprojects {
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
}
and in the app level module write the below code,
dependencies {
Implementation(name:'supernover', ext:'aar')
}
Step 3: Then Click sync project with Gradle files.
If everything is working fine, then you will see library entry is made in build ->intermediates -> exploded-aar.
In my opinion, the best way to do this is to deploy the jar/aar to a local maven repository. if you install maven, you can use the mavenLocal() repository in gradle and read from there as with any other repo, regardless of the IDE you are using. All versions of Android Studio will work, all version of IntelliJ will work, VSCode will work, the command line will work, etc. Another advantage is, you'll be able to swap versions of the library as you do with all the others, just change the version in gradle (after deploying the new one), and will work for all your projects. Putting jars/aars manually into a project is just a bad practice, and reaaally outdated to top.
Once you installed maven, type this in your terminal:
mvn install:install-file -Dfile=d:\mylibrary-{version}.aar -DgroupId=com.example -DartifactId=mylibrary -Dversion={version} -Dpackaging=aar
Where you swap aar and jar depending on the type. The package name, group ID and library name are up to you, anything will work. I would use the library's package and name, and version 1.0 if you don`t have a version.
Here's an example link. Is old, but the process is the same. mvn install, then consume from mavenLocal().
For anyone in search of a solution still.
Create a new android Application project.
Convert new project into a standalone Library module.
Add maven-publish plugin to the module-level build.gradle
Connect your project to your Github repository (or create a new one).
In the module-level build.gradle, implement the Github Packages authentication flow. I'm using 'zuko' as an example - replace every instance of that name with your Github login.
android {
...
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/zuko/[git-repository]")
credentials {
username = 'zuko'
password = 'token' // this is a Git Personal Access Token
}
}
}
publications {
release(MavenPublication) {
groupId 'com.zuko.libraries'
artifactId 'choose-a-name'
version '1.0.0'
artifact("$buildDir/ogury-mediation-mopub-5.2.0.aar")
// you can actually put the artifact anywhere you want.
// This is the location of where you place your .aar file
}
}
}
...
}
If everything is connected properly, save your work, and run the the task: ./gradlew publish. The error logs are straightforward so just defer to the instructions and Google for more assistance.
To install a successfully published package into your desired project, use the same auth procedure for publishing.repositories, you don't need the second half, publishing.publications.
example: implementation 'com.zuko.libraries:choose-a-name:1.0.0'
You could configure a repository in you buildscript that looks for dependencies in a local directory
Use this to register a local directory as repository in your app module's build.gradle where libs is a directory under app module (<project>/app/libs/)
buildscript {
repositories {
flatDir { dirs 'libs' }
}
}
then declare your dependencies from the local file tree you registered earlier
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}
This will include all jar/aar artifacts present under libs directory to be included in your module's dependencies.
PS: Local jar/aar artifacts will expect any transitive dependencies to be on the classpath unless they are fat-jars (package all transitive dependencies within the artifact), so these need to be added explicitly as dependencies.

Gradle: interchangeably use AAR or project dependency

I have an AAR library that I want to distribute to partners along with a sample project that uses it. I want to also use the same sample project for manual testing while developing the AAR library. Thus, I would like to be able to use the library project as a dependency when developing, and the AAR file as a dependency when distributing.
I tried to define two flavors in the sample application:
productFlavors {
aar {}
project {}
}
...and then define dependencies like this:
dependencies {
//other dependencies
projectCompile project(':myLibrary')
aarCompile 'com.example.mylibrary:myLibrary-release#aar'
}
The aar flavor builds if I comment out the dependency for the project flavor, but if I leave both uncommented, Gradle sync fails if the myLibrary directory is not present - despite the fact that the project build flavor is not part of the current build variant.
What is the correct way to do this? Or do I have to choose between creating a whole separate project for distribution or always referencing the AAR even when debugging/testing?

Android Library Project Dependencies and NoClassDefFound

EDIT: This project demos the behavior:
https://github.com/NathanielWaggoner/AndroidExamples/tree/master/packing
There is a read me that explains the first time you build it.
I have a set of projects built using Gradle and AndroidStudio. We'll call them Lib1, SDK and APP. Lib1 and SDK are deployed to a private Sonotype repo that I maintain.
App depends on SDK - App is a normal android project
SDK depends on Lib1 - SDK is an Android Library Project repackaged as a Jar
Lib1 depends on some Android Stuff. - Lib1 is a normal android library project, packaged as an aar.
When I run gradle dependencies In each project i see some things i don't expect.
In Lib1 I see all appropriate dependencies, just as I would expect (including dependencies of dependencies)
In SDK I see all relevant dependencies and their trees except for that of Lib1. In the case of lib1 the only thing I see is lib1, not any of its dependencies.
In APP i see something very simlar to the SDK dependencies - I see all dependencies are their trees, except for SDK. In the case of SDK I only see the SDK. I don't see Lib1 listed as a dependency (or any of the other dependencies of SDK).
Everything builds fine - that is i can compile and deploy Lib1, and compile and deploy SDK. I can compile the APP - however when it uses SDK code which references Lib1 I get noClassDefFound on the Lib1 classes.
Checking the output jars none of the library classes are included in the Jars created during the build phases of SDK or Lib1, and the poms created don't reference any dependencies (from installArchives/uploadArchives tasks).
How do I work around this? I don't want consumers of the SDK to have to directly compile in the Lib1 in order for those classes to be found.
This topic:
http://forums.gradle.org/gradle/topics/using_the_maven_publish_plugin_no_dependencies_in_pom_xml
shows some extremely similar behavior to what it turns out was happening to me - my poms were being generated without their dependency information being included.
For now i've got this work around in my installArchives.`
task installArchives(type: Upload) {
repositories.mavenInstaller {
configuration = configurations.archives
//configuration = configurations.default
pom.version = "0.0.1-SNAPSHOT"
pom.artifactId = "lib2"
pom.groupId = "waggoner.android.examples"
pom.withXml {
def node = asNode().appendNode('dependencies').appendNode('dependency')
node.appendNode('groupId','waggoner.android.examples')
node.appendNode('artifactId','lib1')
node.appendNode('version','0.0.1-SNAPSHOT')
}
}
}

Android Gradle main project and submodule flavors

I have a main Android project, which depends on a submodule.
The main project has flavors defined in Gradle.
Also the submodule has a few flavors defined. This should be logical - to be able to have flavors for both projects.
For example in the submodule:
productFlavors {
flavorName {
}
}
But this does not work - The build crashes with a message saying that submodule resources are not found in the main project.
But when I delete flavors from the submodule, everything works fine.
It seems to mix the build order when flavors are defined for the subproject, could this be true?
What am I missing? Is it even possible for both main and sub projects to have flavors?
When you reference your sub-modules as dependencies in your build.gradle file, be sure to specify which flavor of the sub-module you are referring to:
dependencies {
compile project(path: ':module', configuration:'yourflavorDebug')
}
then be sure in your build variants, you are building the flavor your main module depends on, and everything should work out.
Also be sure the libraries you are referencing have this in their build.gradle as well:
publishNonDefault true
Without it android studio doesn't seem to be able to depend on flavors of that module. More information here.

Conditional dependencies with Gradle, is it possible?

I have an Android project (already ported to Android Studio and using Gradle) that is made up of different modules.
The project is actually used to create two different apps, where the code is pretty much the same, except for some resources.
Thus the resources have been split into two different modules.
The original author of this project used to work in Eclipse and switch the resource modules included in the dependencies based on which app he wanted to build. And he also used to change by hand the package name in AndroidManifest.xml
I would like to automate all of this and still have a single code base, but have two build targets with specific modules for each target. Is that doable with Gradle?
Update:
To make things even harder, my project has a hierarchy that is pretty much the following:
--+--MainProject
+--LibData
+--LibBase
+--LibResA
+--LibResB
Where:
MainProject depends on LibBase and LibData.
LibData depends on LibBase
LibBase either depends on LibResA or LibResB based on the final APK that I need to build.
As suggested, I've tried implementing this with flavors by adding in the MainProject build.gradle the following:
productFlavors {
producta {
}
productb {
}
}
And then in LibBase I've added the following to its build.gradle:
dependencies {
productaCompile project(':LibResA')
productbCompile project(':LibResB')
}
But then, when I build the project, LibData can't find the classes and resources inherited from LibBase. So now I'm stuck with this error. To me it looks like LibBase isn't being copied to the intermediates of LibData. That way LibData can't resolve the classes in LibBase, but it's just my assumption.
Update 2:
I kept investigating this issue and now I've changed my build.gradle files to look like this:
Main Project build.gradle:
defaultPublishConfig "productaRelease"
publishNonDefault true
productFlavors {
producta {
applicationId "com.producta"
}
productb {
applicationId "com.productb"
}
}
dependencies {
compile project(':LibData')
}
LibData build.gradle (has no product flavors, just the dependencies):
dependencies {
compile project(':LibBase')
}
LibBase build.gradle:
defaultPublishConfig "productaRelease"
publishNonDefault true
productFlavors {
producta {
}
productb {
}
}
dependencies {
productaCompile project(path: ':LibResA')
productbCompile project(path: ':LibResB')
}
This way I get no errors when doing the usual gradle clean build but I can see that the resources included are always those of LibResA just like the defaultPublishConfig is the only one used at all times.
If I open this project in Android Studio (0.8.1 atm) the result is that if I try to switch the build variant of the LibBase module and set it to productbRelease, the following error is being shown: Error:Module 'LibBase' has variant 'productbRelease' selected, but the module ''LibData'' depends on variant 'productaRelease'.
I'm running out of ideas.
Since you already have the product flavors:
productFlavors {
producta {
}
productb {
}
}
Define your dependencies prefixed with flavor name.
Example:
dependencies {
productaImplementation 'com.google.android.gms:play-services:11.0.2'
productbImplementation 'com.google.android.gms:play-services:12.0.1'
}
Common dependencies will be defined normally.
Now build apk for individual flavors.
Not the best way to do it, but if productFlavors is not enough to specify conditional dependencies you can rely on an inline if and evaluate it based on some value that can be injected via external properties.
For example here is how I toggle LeakCanary (no-op is just the empty implementation of the other one):
build.gradle
dependencies {
compile "com.squareup.leakcanary:leakcanary-android"+(project.ext.has("leakCanary")?"":"-no-op")+":1.3.1"
}
To build with com.squareup.leakcanary:leakcanary-android:1.3.1:
$ ./gradlew :app:assembleDebug -PleakCanary
By default it builds with the empty implementation com.squareup.leakcanary:leakcanary-android-no-op:1.3.1:
$ ./gradlew :app:assembleDebug
This provides a quick and more flexible way to toggle things using build command, but too much of it and things will get messy real quick.
Yes, it is. New Android build system based on Gradle supports your use case with its concept of product flavors. http://tools.android.com/tech-docs/new-build-system/user-guide
Note that you will likely want to switch from Eclipse to Android Studio when you do migration to Gradle build.

Categories

Resources