How to use different settings.gradle files for different environments - android

The problem
I have two projects, A (ui) and B (background service). Project A has a dependency on B. Project B gets published to a maven repository and included in project A like so in build.gradle
debugImplementation ('com.example:project-B:0.0.0-SNAPSHOT') { changing = true }
releaseImplementation ('com.example:project-B:1.6.2')
This works, but it's a pain to validate my service changes on the UI side. I need to publish project B to my nexus repo and resync project A.
I changed project A to the following:
build.gradle:
debugImplementation project(":project-b")
settings.gradle:
include ':project-a'
include 'project-b'
project(':project-b').projectDir = new File(settingsDir, "${project-b-path}")
I can have all my code in one IDE window and have A use local instance of B. But the problem is this will break on my build server since there is no local B project, only the one on nexus.
Is there a way to configure the settings.gradle for release vs debug? I can just commit my changes and overwrite the file on the build server, but I want to know if there are other ways?

You can use gradle command line to set which settings or build file should be used.
Settings File
-c, --settings-file
Specifies the settings file. For example: gradle --settings-file=somewhere/else/settings.gradle
Build File
-b, --build-file
Specifies the build file. For example: gradle --build-file=foo.gradle. The default is build.gradle, then build.gradle.kts, then myProjectName.gradle.
You can find more details here: Gradle docs: Environment options

Related

Gradle/Android: includeBuild sets build variant to null

Had no luck posting through the gradle community, so I thought I would reach out here.
In my root settings.gradle file, I use includeBuild to include the projects of interest. This works and I see that the builds have been included. Each one of these android libraries may contain a dependency on another module in a different project in the monorepo. All of these libraries produce artifacts that are published, so normally I would just target the recently released artifact. I want to use includeBuild with dependency substitution when developing locally. This way, if I make a change in a dependency I can make changes in all dependents immediately without having to release an artifact first.
Let me show you an example of one that I have working:
I have directory called base-implementation, this directory contains two gradle projects: base-api and base-ui-api. Each of these projects contains one singular android library module api and ui-api respectively. To further illustrate, one drilldown from a package structure would go base-implementation → base-api → api where api may be defined as an artifact dependency in other android libraries in this monorepo. For example ui-api defines a dependency on api.
Given this structure, in my base-ui-api project, I define an includeBuild on base-api and substitute out the dependency with the android library api. That looks like this:
def apiDependency = "com.myapp.example:api"
if(isIntegrationBuild.toBoolean()) {
includeBuild('../base-api') {
dependencySubstitution {
substitute module(apiDependency) using project(':api')
}
}
}
The isIntegrationBuild is just a gradle property I have set up as a development flag to use dependency substitution. I can run the gradle task provided by android called androidDependencies which will list all resolved dependencies for a given build. I can verify here that the dependency is indeed subbed out in ui-api by confirming this line “:api (variant: debug)”
It is important to note at this point, every single android library in this mono repo has only two build variants of debug and release there are no inconsistencies here with build variants.
The problem I am running into is this. I have a library that defines a dependency on another library which then defines a dependency on api. When going to build this library, a random nullPointerException is thrown without any message. What I have noticed however, is this; When I run androidDependencies task on the library that it is dependent on (the library that contains a dependency on api), the subbed out api dependency comes back with this “:api (variant: null)” and I cannot for the life of me figure out why. All includeBuilds and dependency substitution definitions are exactly the same. yet one resolves with variant debug and the other does not. They have next to identical build.gradle files at both the project and module level as well. There is nothing special happening to explicitly define a default variant implementation and all variants match across all libraries.
If what I explained above is unclear, let me drive home the issue with the project structure visual that doesn’t work. There is sensitive information in these library names so for sake of censoring, I will replace their names with A and B. Where A depends on B and B depends on api. A and B also share the same root dir but this root dir is just a container. Not a gradle project.
A depends on B so in the project level settings.gradle of A I define an includeBuild with a dependency substitution for B. This looks like the following:
def BDependency = "com.example.b:b"
if (isIntegrationBuild.toBoolean()) {
includeBuild('../project-b') {
dependencySubstitution {
substitute module(BDependency) using project(':b')
}
}
}
This block runs and works, I can see and confirm that the artifact is swapped out with the included build.
Now, B defines a dependency on api so its project level settings.gradle looks almost identical to the first case I stated where ui-api depends on api the only difference is relative pathing for includeBuild. This looks like the following:
def apiDependency = "com.myapp.example:api"
if(isIntegrationBuild.toBoolean()) {
includeBuild('../../base-api') {
dependencySubstitution {
substitute module(apiDependency) using project(':api')
}
}
}
Why is that when I build ui-api it pulls in api of variant debug, but when I build B it pulls in api of variant null? Is there a something I am missing about nested includedBuilds?

Minimum Necessary files for an android build

Trying to find the bare minimum source and build files needed to build an android project in Android Studio. I want to publish to github and avoid uploading generated build files or binaries.
I do have a Android.gitignore from but I still see some more files getting pushed into the repo which may not be necessary. I understand the few obvious ones but about others, do I need them and if so kindly explain the usage.
So the question, do I need the following and if so then a short description of why?
root
build.gradle
gradle.properties
gradlew
gradlew.bat
settings.gradle
/app
app/build.gradle
app/proguard-rules.pro
/gradle (tested, android can re-download/generate following it if not present)
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.properties
This question can have two different answers based on the meaning of the word needed.
First (the real one)
Assuming your project has currently those files, if your question is:
Should I commit these files on my Git repo?
The answer is yes, all of them, and I'm explaining why:
root
build.gradle -> defines the configuration for all the Gradle modules in your project (e.g. use the same remote repositories to download some Gradle plugins)
gradle.properties -> defines some optional flags used when building the app (e.g. enabling the incremental KAPT, enabling the AndroidX jetifier)
gradlew -> invokes the Gradle wrapper (which can be found under gradle/wrapper/gradle-wrapper.jar) to avoid to have Gradle installed when building your project on Darwin/Linux
gradlew.bat -> the same of gradlew but for Windows
settings.gradle -> defines the list of modules which are part of your project
app/
app/build.gradle -> defines the configuration only for your app module (e.g. its build types, its flavors, its version code and version name)
app/proguard-rules.pro -> defines the obfuscation rules when your app enables the minification
gradle/
gradle/wrapper/gradle-wrapper.jar -> provides the same version of the Gradle wrapper jar for all the users. This is very important because it forces the users to use the same version of the Gradle wrapper to compile your app
gradle/wrapper/gradle-wrapper.properties -> same as above, it defines which version of the Gradle wrapper you need
Second (the useless one)
Now, I'll give you the answer to the question:
Are these files strictly needed to compile an Android project?
To successfully compile an Android project with Gradle you just need the root build.gradle if you have Gradle installed on your machine or build.gradle + the wrapper files if you have not Gradle installed on your machine.
Theoretically you can:
put your application code in the root project and that avoids you one build.gradle and settings.gradle
disable the obfuscation and that avoids you proguard-rules.pro
remove gradle.properties and set the properties via command line
Obviously this solution won't happen on a real project scenario.

Gradle disable the build of dependency projects

I have a a multi-project gradle project (Android) with following structure:
root
|-projA
|-projB
|-projC
|-...
In my specific case, projA is an app which uses projB (module). When I run build on projA, projB also gets built. I need to perform an action only on the project that was originally built (the project where the original action was performed on).
For this purpose I need to somehow get the name/path of this project. I need to do this in the afterEvaluate step if this matters.
Example:
gradlew :projA:build // Builds A and B -> I want only "projA"
gradlew :projB:build // Builds B -> I want "projB"
gradlew :projC:build // Builds A, B and C -> I want only "projC"
I am not sure how I can achive this, I hope someone of you can help me.
You can check the official doc:
The standard project dependencies are supported and makes relevant projects configured. If project A has a compile dependency on project B then building A causes configuration of both projects.
However you can disable the build of dependency projects (but pay attention!)
If you don't want depended on projects to be built when doing a partial build. To disable the build of the depended on projects you can run Gradle with the -a option.
Example:
gradle -a :projA:build
From the doc:
-a, --no-rebuild
Do not rebuild project dependencies.

How to make a Android Gradle build dependent on a simple (non android library, non-java) task that provides a jar file?

I have a command line program that, depending on a set of source files, generates a jar file that I want to use in my Android gradle build (A). This command line program simply stores a jar file in a directory on the disk.
How can I create a new gradle project (B) that runs my command line program (inside of a gradle multi-project setup) that the Android Gradle project(A) will depend on: in other words, if the sources for B change, it should rebuild!
Note:
Project B is neither a Android library project nor is it a java project. It simply runs a program via command line that happens to store a jar file on the file system.
You could do this by creating your own custom task(s) in project B to execute your program which generates a JAR. Be sure to declare your inputs/outputs properly so Gradle knows how to handle dependencies for the task:
task buildProjB {
inputs.file file('my_source_file.name')
ext.dest = new File(buildDir, "libs")
outputs.dir ext.dest
doLast {
// Run your program and put output in "libs" subdir
}
}
tasks.whenTaskAdded { theTask ->
if (theTask.name.contains("assemble")) {
theTask.dependsOn "buildProjB"
}
}
Note that the buildProjB task gets "hooked" to any of the assemble tasks. You will likely need to do this or to one of the build tasks in order for it to be built properly as part of the normal Android app build.
In your project A settings.gradle, make project B part of the build:
include ':projectA', ':projectB'
In your project A build.gradle file, make project B a dependency:
dependencies {
...
compile project(':projectB')
}
This is assuming you have project B (module in Android Studio terms) beneath the top level Android Studio project, so at the same level as project A.

How to add a dependent project to an android gradle project that builds a debug or release jar approprately

I need a clear example of how to extend an Android Gradle project with an arbitrary project.
By arbitrary I mean that it can't just use the 'java' plugin since it doesn't support buildTypes to my knowledge. I am currently using an 'ant' task for this, which has two targets for debug and release, however I don't see how to tie it into an Android project.
Assume that your dependent project must build pure Java source in two ways:
debug build that produces a debug version in 'purejava.jar'
release build that produces a release version in 'purejava.jar'
The jar 'purejava.jar' is to be placed such that the Android project (could be a multi-project) is able to reference it at compile time, and it must therefore be the correct build to support both the debug and release configurations of the Android project.
How should this be tackled?
Since I am new to Android Studio and Gradle, I don't have a clear idea of how to manipulate extensions generated by the Android plugin, which are not available until after project evaluation.
How should the Android project be made dependent on this pure java project?
If it weren't for the fact that you need debug and release versions of your library, then your Android app could depend on a plain Java module just fine -- you could set up the library with the java plugin and put a compile project statement in the app's dependencies and it would work fine.
However, the Java plugin is never going to understand Android's notion of build types (unless GradleWare adds it at some point), so you can't propagate that to your Java modules. You could set up your plain Java project as an Android library and use the android-library plugin (you'll have to dummy out the manifest and other Android-specific stuff it expects to see in Android libraries), but you'll run into a different problem: https://code.google.com/p/android/issues/detail?id=52962 is a bug that reports that the build type is not propagated to library modules.
Until that bug is fixed, or if you're unwilling to make your plain Java library an android library, I think your only approach is to make two different versions of your library, compile them to different jar files, and selectively pull in dependencies.
This is my answer, with following project structure:
MyProject
-- MyAndroidLib
-- JarProject
This represents the gradle top project 'MyProject' which has a sub-project 'MyAndroidLib' which is dependent on a pure java project 'JarProject' which is built with different code for debug than for release builds.
I'll take advantage of Android's 'debugCompile' and
'releaseCompile' configurations. In the Android sub-project ('MyAndroidLib') that is dependent on the jars, add following lines to the dependencies:
//MyAndroidLib build.gradle
def jarProject = project(':MyProject:MyAndroidLib:JarProject')
def jarPath = pcfProject.projectDir.toString()
dependencies {
....
compile jarProject
debugCompile files(jarPath + '/' + jarProject.debugJarName)
releaseCompile files(jarPath + '/' + jarProject.releaseJarName)
}
The 'jarProject' def is defined to simplify accessing it from the MyAndroidLib project. (If you know a better way ...)
The main point of this is to define a separate debug and release jar path for the 'debugCompile' and 'releaseCompile' configurations. The 'debugJarName' and 'releaseJarName' are defined in a gradle.properties file for the JarProject as follows:
//JarProject gradle.properties
debugJarName=jarproject_d.jar
releaseJarName=jarproject_r.jar
In the gradle file for JarProject define a task that builds BOTH jar files named by this properties file. In my case, they are built right in the project folder by the 'compile' target of an ant build file located in that project.
//JarProject build.gradle
apply plugin: 'java'
project.ext.set("debugJar", file(projectDir.toString() + "/" + debugJarName))
project.ext.set("releaseJar",file(projectDir.toString() + "/" + releaseJarName))
task buildJars(type: Exec) {
description 'Build the debug and release jars for the JarProject'
outputs.files debugJar,releaseJar
commandLine 'ant', 'compile'
}
task compileJava.dependsOn('buildJars')
artifacts {
buildJars
}
clean.dependsOn('cleanBuildJars')
clean << {
exec {
commandLine 'ant', 'clean'
}
}
I took advantage of the 'java' plugin since it defines a 'compile' interface, and I haven't figured out how to build this from scratch, or even from the 'base' plugin. This project takes advantage of the automatic 'cleanBuildJars' task created because I defined the outputs in 'buildJars' task. This is necessary in order to have them built as needed. I probably need to define the 'inputs' too, since if they change ...
If anyone sees how my first stumblings in the gradle/Android world can be improved, pls. add comments as needed.

Categories

Resources