Playing with Gradle 4: Getting Real

Author’s highlight

Mathias thinks you, as Android developer, can be interested by the following article : Rx Android everywhere… why, but why ?

Playing with Gradle series:

Chapter 1: Basics

Chapter 2: Android Tasks Basics

Chapter 3: Flavors, BuildTypes and Variants

Chapter 3 1/4: Flavors And Manifest

Chapter 3 and some few: Understanding the life cycle.

Chapter 3 and some few more: Code Coverage on Android with Jacoco

Chapter 4: Getting real

Image for post
Image for post

(Download the ebook: https://android2ee.com/Tutoriaux/Playing-with-Gradle.html for free and without any registration or what ever, it’s a gift to the community and the pdf is well code formatted, this article is not).

The goal of this chapter is to drive you through a real example of implementation and a step by step explanation.

So you have read all the previous chapters, you have assimilate the notions, now let’s start use them.

We presume in this chapter that you have already define your build.gradle and your project compile. We are beyond that point in this chapter; we are optimizing and enhancing it. We use a project with two dimensions of flavor (because it happens). We want to release automatically 2 applications with their reports, their tests ran, the upload to the maven central (private nexus in our case) and deployment.

Table of Content:

1 Organize your Gradle files as your code. 2

2 Extract your build variables and constants. 2

3 Load your external properties files. 4

4 Extract your hooks. 6

5 Building, testing and Analyzing Script. 7

5.1 BuildAndCheckProject task. 8

5.2 fullBuild task. 8

5.3 runReporters task. 9

5.3.1 moveTheReportToReleaseFolder task. 10

5.4 runReporters:FindBug task. 10

5.4.1 The findbugs-filter.xml file. 12

5.5 runReporters:Jacoco task. 12

5.5.1 Jacoco with several flavors and product. 15

5.6 runReporters: JavaDoc task. 17

5.7 runReporters: PMD task. 18

6 The release scripts. 20

6.1 One entry page for your report. 23

7 The upload part. 25

7.1 Upload: Maven basics principles. 25

7.2 Upload : In a Nexus Repository. 29

8 Taking care of libraries. 33

9 Conclusion. 34

1 Organize your Gradle files as your code

In you project, start by creating a “gradle_scripts” folder (or a “gradle” folder as you want, you are the one responsible for the naming, nobody cares except you and your team).

And get used to create your Gradle scripts in this folder, with sub folders hierarchy. Depending on your project, you will we quickly see the number of Gradle files increase.

Image for post
Image for post

So organize your files, use sub-folders and split by responsibilities. You should only let at the app sub-level the files: gradle.properties and build.gradle.

Once you have done that, let’s see what to put in our gradle files.

2 Extract your build variables and constants

As usual in your build.gradle you define your “project to build the project” receipt. You should start by defining and extracting in a specific file (mine is called var_definition.gradle) all the constants and variables that you want to use in your project.

So you first line should be to define if you build a library or an application. And the second line should be to define your variables:

apply plugin: ‘com.android.application’
/**
* Our variables definition for the build script
*/
apply from: ‘gradle/var_definition.gradle’

Where you are applying the gradle file that defines your variable like this:

The gradle/var_definition.gradle file is the following:

`***********************************************************
* Gradle definition file of our dynamic variables
**********************************************************/
println ‘in the var_defintion’

/***********************************************************
* Then define you attibutes/variables
* You need to define that way for others file to know them
* Others gradle file can not call the method (or I didn’t find yet)
**********************************************************/
project.ext{

versionName = “1.0.0”
compileSdk = 26
minSdk = 14
targetSdk = 26
//Can not decrease
versionCode = 1
//Gradle groupd name (for your own tasks)
myGradleGroup=”Multiplication Basile tasks”


useSupportLibVectorDrawable = true

//build var
def_time_format=getDateTime()
def_branche_name=branch()
def_commit_number=commitNumber()
def_current_apk_name=apkCurrentSuffix()
def_apk_name_wrelease=apkWReleaseSuffix()

//natif tools
buildToolsVersion = “26.0.1”
supportLibVersion = “26.1.0”
supportAnnotationVersion = “23.1.1”

//natif
androidTestVersion = “1.0.0”
junitVersion = “4.12”
googlePlayServices = “11.0.1”
androidGoogleMapUtil = “0.5”
constraintLayout = “1.0.2”
supprtMultidex = “1.0.1”

//natural libs
sugarVersion = “1.4”
eventbusVersion = “3.0.0”
//debug tools
leakcanaryVersion = “1.5.1”
crashlyticsVersion = “2.6.8@aar”

//facebook analyse tools
fbStetho = “1.5.0”
//tests
mokitoVersion = “2.8.47”
espressoVersion = “3.0.0”
jsonVersion = “20160810”

//android architecture components
archiComponentVersion = “1.0.0-beta2”
} ```

Mainly we define in this file:

· The version of the SDK to use

· Variables for signing configurations

· Versions of the library we use

· Some variables used to create the file name when releasing the application.

We start our gradle.build by this definition because those variables are used in all the rest of the scripts. So if you don’t define at the beginning, they are not know until you declare them. It’s a good practice to define your variables at the beginning and to define them all, at the same place at the same moment.

Then we use those variables in our main gradle build script (or anywhere else):

android {
compileSdkVersion project.compileSdk
buildToolsVersion project.buildToolsVersion
defaultConfig {
applicationId “com.android2ee.basile.multiplication”
minSdkVersion project.minSdk
targetSdkVersion project.targetSdk
versionCode project.versionCode
versionName project.versionName
testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”
vectorDrawables.useSupportLibrary = project.useSupportLibVectorDrawable
}

3 Load your external properties files

Sometimes some critical information have to be removed from the project and you extract them in a specific file (out of git watch). Then you want to load the value from this file into your gradle.

The best way is to read your properties file and doing this, to create your variables.

For the example we define our JavaKeyStore and the key to use for signing the release and the debug apk. We use the same key (not the goal to show how to sign apk):

This is the properties file used, called accounts_properties.properties:

#Signing configurations
JKSFile=keystore_sample.jks
JKSPassword
=android
JKSKeyAlias
=android
JKSKeyPassword
=android

Then we want to load those properties and to use them to define variables that will be available in our Gradle file (or our code, as we want):

Then I create a file that will read this properties file and create the associated variables in the gradle’s project object (project.myvar ou project.ext.myvar).

Extract of the file load_accounts.gradle:

def Properties props = new Properties()
//find the file defining those properties
def propFile = project.file(‘./gradle/properties/accounts_properties.properties’)
//You can also use rootProject
//def propFileNotUsed = rootProject.file(‘./app/gradle_others.properties’)
//do a log (in case), it will be displayed every time you avluate the gradle task graph
println “The propreties file should be ${propFile}

if
(propFile.canRead()){
//load the properties in your Properties object
props.load(new FileInputStream(propFile))

//You can define specific variables of the build directly
if (props!=null
&& props.containsKey(‘JKSFile’)) {
//Now the properties are loaded in your props’s set of variables, enjoy an reuse them
//here defining the debug signing config
//https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
//To set an already existing value (but useless)
project.ext.set(“JKSFile” , file(props[‘JKSFile’]))

Nothing complex here, we define a properties object, locate our properties file, load it in the properties object and then use the properties object to define our own gradle’s variables.

To define gradle variables, it’s just easy and you have several syntaxes. You can use the way to code you like.

//To create or set a value
project.ext.set(“JKSFile” , file(props[‘JKSFile’]))
//This syntax works
project.ext.set(“JKSPassword” , props[‘JKSPassword’])
//This syntax works too
project.ext.JKSKeyAlias= props[‘JKSKeyAlias’]
//This syntax works also
project.ext.JKSKeyPassword=props[‘JKSKeyPassword’]

They all work.

To use those elements, it’s also straight forward:

println “You have = project.ext.JKSPassword=${project.ext.JKSPassword}
println “You have = project.JKSPassword=${project.JKSPassword}

And in reality, you use them in your signing block of your main gradle build file for the module:

Extract of your build.gradle file:

/***********************************************************
* Defining and Loading Properties
**********************************************************/
apply from: ‘gradle/load_accounts.gradle’
/***********************************************************
* Signing
**********************************************************/
signingConfigs {
debug {
storeFile project.ext.JKSFile
storePassword project.ext.JKSPassword
keyAlias project.ext.JKSKeyAlias
keyPassword project.ext.JKSKeyPassword
}
release {
storeFile project.ext.JKSFile
storePassword project.ext.JKSPassword
keyAlias project.ext.JKSKeyAlias
keyPassword project.ext.JKSKeyPassword
}
}

release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}

4 Extract your hooks

From the same point of view, extract your hooks. A hook is when during the build of the gradle graph, you make an action, like ignoring specific build variants:

android.variantFilter { variant ->
if (variant.getFlavors().get(0).name.equals(‘basile’)
&& variant.getFlavors().get(1).name.equals(‘addition’)) {
variant.setIgnore(true);
}

Mainly in our hook, we:

· Ignore specific build variants

· Rename the output apk’s name

· Add tests options or ignoreFailure flags

Like this:

/***********************************************************
* Apply the build hooks here
**********************************************************/
/**
* Rename all the apk to obtain the following format:
* app-mycms_0.0.1_201704100_0101.apk
*/
android.variantFilter { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(“.apk”, def_current_apk_name + “.apk”))

}
}

/**
* Adadpt the variants existence depending on the build you are doing ‘aar or apk)
* You don’t need the same variants
*/
//Remove the BasileAddition and LilaMultiplication flavor
android.variantFilter { variant ->
if (variant.getFlavors().get(0).name.equals(‘basile’)
&& variant.getFlavors().get(1).name.equals(‘addition’)) {
variant.setIgnore(true);
} else {
println “Var of the project”
println variant.getFlavors().get(0).name
println file(STORE_FILE)
println uploadRepo
println “ — — — — — — — — — “
}
if (variant.getFlavors().get(0).name.equals(‘lila’)
&& variant.getFlavors().get(1).name.equals(‘multiplication’)) {
variant.setIgnore(true);
}
}

// Continue gradle tasks even if test fails
tasks.withType(VerificationTask) { task ->
task.ignoreFailures = true
}

//Enable the coverage report for unit test
android.testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}

android.testOptions {
unitTests.returnDefaultValues = true
}

To call this script, you just have to apply it from your main build.gradle file. But this time, as the hooks concern the end of the process. You need to wait your flavors are defined (the objects you work on), so you need to call it after this declaration:

/***********************************************************
* Managing flavors
**********************************************************/

//Give a name to your dimension
flavorDimensions “enfants”, “operator”

//define your flavors (as one flavor has a dimension they must all have one)
productFlavors {//blabla flavors description
}

/***********************************************************
* Apply the build hooks here
**********************************************************/
apply from: ‘gradle/builds_hooks.gradle’

5 Building, testing and Analyzing Script

To create a release script you will need to aggregate several tasks:

· Build the project

· Run the tests (Unit and Android)

· Run the analyzer and gather the reports

· Move the deliverables to the delivery folder

· Upload the application (or archive) to your Nexus

· Deploy the application on GooglePlay (but not explain in this article)

Let’s create those elements steps by steps. We want first a task that build and check the result of the build.

5.1 BuildAndCheckProject task

This task is simple, it’s an entry point and you just want it to run the task build and check. It looks like something like this:

/**
* Run the build, the tests and the analyzers tools reports
*/
task buildAndCheckProject(dependsOn: [
‘fullBuild’,//ok
‘runReportersTools’
]) {
group = project.ext.myGradleGroup
description =”Run full build and then the full check.”
doFirst {
println ‘Starting the build’
}
doLast {
println ‘Full complete check of MyCms application is done’
}
}

Yep, we just say what we depend on two tasks, fullBuild and runReportersTools. You need to synchronize those two tasks, because you need to run your checking AFTER the build is done and the tests are passed so at the end of the file you just explain that to your script:

runReportersTools.mustRunAfter ‘:app:fullBuild’

Those tasks are not defined yet, this is the goal of the next chapters.

5.2 fullBuild task

The task fullBuild is dedicated to create the applications and run the tests on it. In the example project there is no smart tests yet, perhaps one day they will come.

So full build, in a way does nothing, it just depends on 3 tasks:

· ‘:app:build’,

· ‘:app:connectedAndroidTest’,

· ‘:app:test’

Those tasks are native tasks of your project.

So all you have to do is:

/**
* Build then Run AndroidTests and UnitTests.
*/
task fullBuild(dependsOn: [‘:app:build’,’:app:connectedAndroidTest’,’:app:test’]) {
group = project.ext.myGradleGroup
description =”Build then Run AndroidTests and UnitTests.”
doLast {
println ‘Done’
}
}

5.3 runReporters task

This task is launching several tasks; one task for a specific tool. The several tools we want to use:

· FindBugs

· Jacoco

· PMD

· Lint

· Unit tests results

· Android tests results

· Javadocs

In fact tests results are already created by the FullBuild, you just want to move them.

moveTheReportToReleaseFolder
println ‘check done’

}
}

In fact we are going to inject libraries in our gradle script. All those libraries scripts will be related with reporters. It’s the moment to create a folder:

Image for post
Image for post

In the toolsReporter folder we gather all those scripts.

5.3.1 moveTheReportToReleaseFolder task

It’s just a task that copy paste content from one folder to another one. This type of task is current in gradle scripts.

/***********************************************************
* Move your report to your release folders
**********************************************************/

//Move the generated files to the delivery
task moveTheReportToReleaseFolder(type: Copy,
dependsOn: [‘fullBuild’, ‘cleanReleaseReportDirectory’]) {
group = project.ext.myGradleGroup
description =”Move the build/report folder into the weekly_release.”
println ‘Task moveTheReportsToReleaseFolder’
from ‘build/reports/’
into ‘weekly_release/’ + def_branche_name + ‘/reports/’
include(‘**/*’)
exclude(‘**/*-release-*’)
doLast {
println ‘moveTheFileToReleaseFolder over’
}
}
//Remove directory where release test reports will be copied into
task cleanReleaseReportDirectory(type: Delete) {
group = project.ext.myGradleGroup
description =”Clean the directory of the report in the weekly_release.”
doLast {
delete ‘weekly_release/’ + def_branche_name + ‘/reports/’
}
}

5.4 runReporters:FindBug task

We start by include FindBugs.

First, we need to retrieve the gradle plugin itself. You need to add those repository to your artefact repository list, in your main build.gradle (the one of the project, not the one of your module):

buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
mavenCentral()
}
}

Second, you need to create your gradle script file to run the library, let’s call it findbugs.gradle under toolsReporter folder:

apply plugin: ‘findbugs’
/**
* Define your directory
*/
def reportsDir = ${project.buildDir}/reports”

task findbugs(type: FindBugs, dependsOn: [
‘:app:assembleBasileMultiplicationDebug’,
‘:app:assembleLilaAdditionDebug’,
‘:app:cleanPreviousFindbugsReports’,//ok
]) {
group = project.ext.myGradleGroup
description =”Generate the findbugs report.”
ignoreFailures = true
reportLevel = “low”
effort = “max”
excludeFilter = new File(“gradle/toolsReporter/findbugs/findbugs-filter.xml”)
//Define exactly where are the class in your Build folder to analyze
classes = files(${buildDir}/intermediates/classes/basileMultiplication”,
${buildDir}/intermediates/classes/lilaAddition”)

//Where are your sources:
source ‘src/main/java’, ‘src/basile/java’, ‘src/lila/java’, ‘src/addition/java’, ‘src/multiplication/java’
include ‘**/*.java’
exclude ‘**/gen/**’

reports {
xml.enabled = false
html.enabled = true
xml {
destination new File($reportsDir/findbugs/findbugs.xml”)
}
html {
destination new File($reportsDir/findbugs/findbugs.html”)
}
}

classpath = files()
}

First notice we start by applying the plugin findbugs.

Then we define a task called findBugs:

task findbugs(type: FindBugs, dependsOn: [
‘:app:assembleBasileMultiplicationDebug’,
‘:app:assembleLilaAdditionDebug’,
‘:app:cleanPreviousFindbugsReports’,//ok
]) {
This task is a FindBugs type task (the one defined in the plugin finbugs we imported) and depends on assemble.

The FindBugs task needs several parameters to work fine:

· Which file to exclude from the analysis

· Where is the classes (your bytecode to analyze, in your build folder)

· Where is your source class (your code in fact)

· What is the pattern of the file to include, the one to exclude

· What type of report you want and where

· Which is the min level you want to use to detect bugs

· Which is the effort to make

· If you fail when findbugs detects errors

And the file above is just the syntax to use to provide those parameters to the task.

5.4.1 The findbugs-filter.xml file

This is a configuration file for findBugs, (documentation can be found here: http://findbugs.sourceforge.net/manual/filter.html). It is localized under toolsReporter/findbugs.

<?xml version=”1.0" encoding=”UTF-8"?>
<FindBugsFilter>
<! — http://stackoverflow.com/questions/7568579/eclipsefindbugs-exclude-filter-files-doesnt-work
<Match>
<Class name=”~.*\.R\$.*” />
</Match>
<Match>
<Class name=”~.*\.Manifest\$.*” />
</Match>
<! — All bugs in test classes, except for JUnit-specific bugs →
<Match>
<Class name=”~.*\.*Test” />
<Not>
<Bug code=”IJU” />
</Not>
</Match>


</FindBugsFilter>

5.5 runReporters:Jacoco task

Jacoco is a test coverage tool.

First, we need to retrieve the gradle plugin itself. You need to add those repository to your artefact repository list, in your main build.gradle (the one of the project, not the one of your module):

buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath ‘com.android.tools.build:gradle:3.0.0-beta2’
//Gradle check tasks : Jacoco
classpath ‘org.jacoco:org.jacoco.core:0.7.7.201606060606’
}
}

Second step, disable your default test coverage for your build gradle

Extract from build.gradle

buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
shrinkResources false
useProguard false
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
//add tests coverage using Jacoco
testCoverageEnabled false
}
debug {
signingConfig signingConfigs.debug
applicationIdSuffix ‘.debug’
versionNameSuffix ‘.debug’
buildConfigField(“boolean”, “isallowed”, “true”)
resValue “string”, “hidden_string”, “I love debug”
//add tests coverage using Jacoco
testCoverageEnabled false
}
}

Third, you need to create your gradle script file to run the library, let’s call it jacoco.gradle under toolsReporter folder:

Image for post
Image for post

The task itself follows the same principles as the ones for findBugs, you apply the plugin, your task is of a specific type and you give the parameters to the “parent” task.

It looks like something like that:

apply plugin: ‘jacoco’

/***********************************************************
* Create Jacoco Report for each application you release
**********************************************************/
/**
* Generate the JaCoco report for Basile Apk
*/
task jacocoTestReportBasile(type: JacocoReport, dependsOn: [
//you depend on nothing because it’s called by gWR…but
//if you want to just generate your reports from scratch uncomment those lines
// ‘:app:assembleBasileMultiplicationDebug’,
]) {

reports {
xml.enabled = true
html.enabled = true
xml {
destination new File($reportsDir/jacocoBasile/jacocoReportBasile.xml”)
}
html {
destination new File($reportsDir/jacocoBasile/jacocoReportBasile.html”)
}
}

//Define exactly where are the class in your Build folder to analyze
def debugTreeBasile = fileTree(dir: ${buildDir}/intermediates/classes/basileMultiplication/release”,
excludes: fileFilter)
def mainSrc = files([“src/main/java”,
“src/basile/java”,
“src/addition/java”,
“src/multiplication/java”])


//Where are your sources:
sourceDirectories = files([mainSrc])
classDirectories = files([debugTreeBasile])
//As you want to gather all your tests reports, add the ec and exec you want to be took into
//account when generating the report
executionData = fileTree(dir: $buildDir, includes: [
“jacoco/testBasileMultiplicationDebugUnitTest.exec”,
“jacoco/testBasileMultiplicationReleaseUnitTest.exec”,
“outputs/code-coverage/connected/flavors/**/*coverage.ec”
])
}

So mainly, for a JacocoReport task you need to provide the following parameters:

· Which and where you want your reports

· Where is the bytecode you want to analyze with the file name patterns you want to exclude from your analysis

· Where is your source code

· And only tricky part, where are your execution data

So the only point is the execution data, right ?

executionData = fileTree(dir: $buildDir, includes: [
“jacoco/testBasileMultiplicationDebugUnitTest.exec”,
“jacoco/testBasileMultiplicationReleaseUnitTest.exec”,
“outputs/code-coverage/connected/flavors/**/*coverage.ec”
])
So, you have to know that when we are running our test and analyze them, jacoco generates files that are how tests have been ran on your code. When you parametrize a Jacoco task, you need to provide the file you want to analyze. Mainly, for Android Tests, they are generated under jacoco folder (in your build folder) with the exec extension. For Unit Tests, you have them under outputs/code-coverage/connected/flavors with the extension ec. Find the file you are interested with and use them for your analysis.

This is the way to gather several type of tests into the same code coverage report using Jacoco. Here we use the tests from AndroidTest and unit tests to create the final code coverage report including all those tests. This is an important point.

5.5.1 Jacoco with several flavors and product

If you deliver several applications or archives with one project, because you play with flavors, you will want to have a speciifc report for each apk (or aar). To do that you need to create a specific task by apk and gather them like this:

/***********************************************************
* Main task
**********************************************************/
task jacocoTestReport( dependsOn: [
‘:app:jacocoTestReportBasile’
,
‘:app:jacocoTestReportLila’,
])
/***********************************************************
* Create Jacoco Report for each application you release
**********************************************************/
/**
* Generate the JaCoco report for Basile Apk
*/
task jacocoTestReportBasile(type: JacocoReport, dependsOn: [
//you depend on nothing because it’s called by gWR…but
//if you want to just generate your reports from scratch uncomment those lines
// ‘:app:assembleBasileMultiplicationDebug’,
]) {

reports {
xml.enabled = true
html.enabled = true
xml {
destination new File($reportsDir/jacocoBasile/jacocoReportBasile.xml”)
}
html {
destination new File($reportsDir/jacocoBasile/jacocoReportBasile.html”)
}
}

//Define exactly where are the class in your Build folder to analyze
def debugTreeBasile = fileTree(dir: ${buildDir}/intermediates/classes/basileMultiplication/release”,
excludes: fileFilter)
def mainSrc = files([“src/main/java”,
“src/basile/java”,
“src/addition/java”,
“src/multiplication/java”])


//Where are your sources:
sourceDirectories = files([mainSrc])
classDirectories = files([debugTreeBasile])
//As you want to gather all your tests reports, add the ec and exec you want to be took into
//account when generating the report
executionData = fileTree(dir: $buildDir, includes: [
“jacoco/testBasileMultiplicationDebugUnitTest.exec”,
“jacoco/testBasileMultiplicationReleaseUnitTest.exec”,
“outputs/code-coverage/connected/flavors/**/*coverage.ec”
])
}

/**
* Generate the JaCoco report for Lila Apk
*/
task jacocoTestReportLila(type: JacocoReport, dependsOn: [
//you depend on nothing because it’s called by gWR…but
//if you want to just generate your reports from scratch uncomment those lines
// ‘:app:assembleLilaAdditionDebug’,
]) {

reports {
xml.enabled = true
html.enabled = true
xml {
destination new File($reportsDir/jacocoLila/jacocoReportLila.xml”)
}
html {
destination new File($reportsDir/jacocoLila/jacocoReportLila.html”)
}
}

//Define exactly where are the class in your Build folder to analyze
def debugTreeLila = fileTree(dir: ${buildDir}/intermediates/classes/lilaAddition/release”,
excludes: fileFilter)
def mainSrc = files([“src/main/java”,
“src/lila/java”,
“src/addition/java”,
“src/multiplication/java”])


//Where are your sources:
sourceDirectories = files([mainSrc])
classDirectories = files([debugTreeLila])
//As you want to gather all your tests reports, add the ec and exec you want to be took into
//account when generating the report
executionData = fileTree(dir: $buildDir, includes: [
“jacoco/testLilaAdditionDebugUnitTest.exec”,
“jacoco/testLilaAdditionEleaseUnitTest.exec”,
“outputs/code-coverage/connected/flavors/**/*coverage.ec”
])
}

You have your entry task that launch, for each application, the associated Jacoco task, with its specific parameters.

5.6 runReporters: JavaDoc task

As usual for those tasks, in your tools_reporter.gradle file, you apply your gradle file containing your Javadoc task, like this

apply from: ‘gradle/toolsReporter/javadoc.gradle’

/***********************************************************
* Your Tasks for building and checking
**********************************************************/
/**
* Generate the Analysers tools reports on your projects
*/
task runReportersTools(dependsOn: [
‘findbugs’,//ok
‘jacocoTestReport’,//ok
‘generateProjectJavadocs’//ok even if the tasks fails, javadoc are generated
//but if not declared no javadoc generated

I spend some times trying to make this task running without problem, but I failed. All I can do is generating the javadocs but the task failed with errors. Most of the time it doesn’t have the reference to outside classes. By the way, so it failed, but it does enough.

So then the file Javadoc.gradle, is like usual, in a way:

/***********************************************************
* Creating the Javadocs
*
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.javadoc.Javadoc.html
**********************************************************/
task generateProjectJavadocs(type: Javadoc) {
group = project.ext.myGradleGroup
description =”Generate the JavaDoc of the project.”
//because it will fail but generate your JavaDoc (probability of failure 80%, javadoc generation 100% sure)
failOnError false
title = “Javadoc of the Project Multiplication”
source ‘src/main/java’, ‘src/basile/java’, ‘src/lila/java’, ‘src/addition/java’, ‘src/multiplication/java’
classpath = files(project.android.getBootClasspath())
destinationDir = file(${buildDir}/reports/javadoc/”)

exclude ‘**/BuildConfig.java’
exclude ‘**/R.java’
doFirst(){
println(“Generating the Javadocs is starting”)
}
doLast(){
println(“Generating the Javadocs is done”)
}
}

As for the others tasks of that type, you need to provide information. Here the task needs to know:

· Where is the source

· Where is the destination

· Which file to exclude

We also add the flag failOnError to false to keep going with our gradle build even if Javadoc find a problem (like where is android.View class).

5.7 runReporters: PMD task

Really the same pattern we apply:

In your tools_reporter.gradle file you add PMD and call it in the runReportersTools task:

apply from: ‘gradle/toolsReporter/pmd.gradle’
//apply from: “gradle/check/codequality-infer.gradle”
//apply from: “gradle/check/codequality-checkstyle.gradle”

/***********************************************************
* Your Tasks for building and checking
**********************************************************/

/**
* Build then Run AndroidTests and UnitTests.
*/
task fullBuild(dependsOn: [‘:app:build’,’:app:connectedAndroidTest’,’:app:test’]) {
group = project.ext.myGradleGroup
description =”Build then Run AndroidTests and UnitTests.”
doLast {
println ‘Done’
}
}

/**
* Generate the Analysers tools reports on your projects
*/
task runReportersTools(dependsOn: [
‘findbugs’,//ok
‘jacocoTestReport’,//ok
‘pmd’,//ok
‘generateProjectJavadocs’//ok even if the tasks fails, javadoc are generated

Now you create your file script,

And you create your PMD task in it:

apply plugin: ‘pmd’

/***********************************************************
* PMD file
**********************************************************/
def reportsDir = ${project.buildDir}/reports”

// Add checkstyle, findbugs, pmd and lint to the check task.
check.dependsOn ‘pmd’

/**
* Launch PMD on the project
*/
task pmd(type: Pmd, dependsOn: [
‘:app:assembleDebug’,
‘:app:cleanPreviousPmdReports’,//ok
]) {
group = “MyCMS_tasks”
description =”Generate the pmd report, but I am not sure we want it.”
ignoreFailures = true
ruleSetFiles = files(“gradle/toolsReporter/pmd/pmd-ruleset.xml”)
ruleSets = []

source ‘src/main/java’, ‘src/basile/java’, ‘src/lila/java’, ‘src/addition/java’, ‘src/multiplication/java’
include ‘**/*.java’
exclude ‘**/gen/**’

reports {
xml.enabled = false
html.enabled = true
xml {
destination $reportsDir/pmd/pmd.xml”
}
html {
destination $reportsDir/pmd/pmd.html”
}
}
}
//Remove directory where release test reports will be copied into
task cleanPreviousPmdReports(type: Delete) {
doLast {
delete ‘${project.buildDir}/reports/pmd’
}
}

Really as usual. You apply the plugin you want to use, here pmd, and you give it the parameters, source, report types, reports destination.

The only main difference is the rule set. I create the file under ./pmd/ and its content is the default one:

<?xml version=”1.0"?>
<ruleset xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance" name=”Android Application Rules”
xmlns=”http://pmd.sf.net/ruleset/1.0.0"
xsi:noNamespaceSchemaLocation=”http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:schemaLocation=”http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
>

<description>Custom ruleset for Android application</description>

<exclude-pattern>.*/R.java</exclude-pattern>
<exclude-pattern>.*/gen/.*</exclude-pattern>

<rule ref=”rulesets/java/android.xml” />
<rule ref=”rulesets/java/clone.xml” />
<rule ref=”rulesets/java/finalizers.xml” />
<rule ref=”rulesets/java/imports.xml”>
<! — Espresso is designed this way ! →
<exclude name=”TooManyStaticImports” />
</rule>
<rule ref=”rulesets/java/logging-java.xml” />
<rule ref=”rulesets/java/braces.xml” />
<rule ref=”rulesets/java/strings.xml” />
<rule ref=”rulesets/java/basic.xml” />
<rule ref=”rulesets/java/naming.xml”>
<exclude name=”AbstractNaming” />
<exclude name=”LongVariable” />
<exclude name=”ShortMethodName” />
<exclude name=”ShortVariable” />
<exclude name=”VariableNamingConventions” />
</rule>
</ruleset>

http://pmd.sourceforge.net/pmd-5.1.1/howtomakearuleset.html

https://pmd.github.io/pmd-5.8.1/pmd-java/rules/index.html

6 The release scripts

We are ready to create the release. The goal, press a button and have every think generated for you, like this:

Image for post
Image for post

In your release folder, you want:

· All your apk

· your lint, androidTests,findbugs,jacoco,pmd,tests reports

· your Javadoc

So what we want to do is simple:

//This is the root task
task generateRelease(dependsOn: [‘clean’,
‘cleanReleaseReportDirectory’,
‘packageRelease’,
‘releaseBuild’]) {
group = project.ext.myGradleGroup
description =”Generate the Release.”
println ‘Task generateRelease processing…’
//I do nothing, In am an entry point
//I use task dependency to launch the build
doLast{
println ‘upload is next step…’ }
}

The only not yet explained task is packageRelease. In fact it moves around the files from somewhere to your release folder. We have defined those tasks in the release/to_release_folder.gradle file.

This file has 5 gradle tasks:

· packageRelease

· moveTheApkToReleaseFolder

· moveTheReportToReleaseFolder

· moveTheReportIndexToReleaseFolder

· cleanReleaseReportDirectory

This file is:

/***********************************************************
* Task to move report/apk/aar to the release folder
**********************************************************/
def releaseRootFolder=’release/’ + def_branche_name+’_v’+project.ext.versionCode
/**printing content of the release directory
//http://stackoverflow.com/questions/18569983/androidgradle-list-directories-into-a-file
//The task name is normally packageRelease
//But for this file, the following name is better*/
task packageRelease(dependsOn: [‘moveTheReportToReleaseFolder’,
‘moveTheReportIndexToReleaseFolder’,
‘moveTheApkToReleaseFolder’]) {
group = project.ext.myGradleGroup
description =”List the file that belongs to the release folder. Depends on the fullBuild and move tasks.”
doFirst {
println ‘Task packageRelease’
}
doLast {
//then list them
println ‘starting the work with the branch’ + def_apk_name_wrelease
println “Listing files contained in:${releaseRootFolder} == “
//define the variables for files location
def releaseRootF =new File(releaseRootFolder)
releaseRootF.mkdirs()
releaseRootF.setWritable(true)
def listFile = new File(releaseRootF, ‘FolderContent.txt’)
listFile.setWritable(true)
//define the content to write
def contents = “”
//get the files tree
def tree = fileTree(dir: releaseRootF, excludes: [‘**/*.js’,
‘**/*.css’,
‘**/*.html’,
‘**/*.xml’])
//browse all those files
tree.visit { fileDetails ->
if (!fileDetails.isDirectory()) {
contents += ${fileDetails.relativePath}+ “\n”
}
}
//write the content
listFile.write contents
println ‘listReleaseFiles over’
}
}

/**
* Move the generated files to the delivery
*/
task moveTheApkToReleaseFolder(type: Copy,
dependsOn: ‘fullBuild’) {
group = project.ext.myGradleGroup
description =”Move generated apk into the release_folder inside the good folder (depend on the branch). It depends on the “
//you need to define the xact folder, else it reproduces the folder hierarchy
from ‘build/outputs/apk/basileMultiplication/debug’,
‘build/outputs/apk/lilaAddition/debug’,
‘build/outputs/apk/basileMultiplication/release’,
‘build/outputs/apk/lilaAddition/release’
into releaseRootFolder
include(‘**/*.apk’)
//I want all the apk, but if you want only debug, you have them below:
// exclude(‘**/*-release-*’)
rename { String fileName ->
fileName.replace(“.apk”, def_apk_name_wrelease + “.apk”)
}
doFirst {
println ‘Task moveTheFileToReleaseFolder’
}
doLast {
println ‘moveTheFileToReleaseFolder over’
}
}

//Move the generated files to the delivery
task moveTheReportToReleaseFolder(type: Copy,
dependsOn: [‘runReportersTools’,
‘cleanReleaseReportDirectory’]) {
group = project.ext.myGradleGroup
description =”Move the build/report folder into the weekly_release.”
from ‘build/reports/’
into releaseRootFolder + ‘/reports/’
include(‘**/*’)
exclude(‘**/*-release-*’)
doFirst {
println ‘Task moveTheReportsToReleaseFolder’
}
doLast {
println ‘moveTheFileToReleaseFolder over’
}
}

//Move the report index to the delivery
task moveTheReportIndexToReleaseFolder(type: Copy) {
group = project.ext.myGradleGroup
description =”Move the build/report index into the weekly_release.”
from ‘gradle/release/report’
into releaseRootFolder
include(‘**/*’)
exclude(‘**/*-release-*’)
filter { String line ->
if(line.contains(‘${DateOfTheDay}’)){
line.replace(‘${DateOfTheDay}’,project.ext.def_readableSchoolDate)
}else line
}
doFirst {
println ‘Task moveTheReportsIndexToReleaseFolder’
}
doLast {
println ‘moveTheFileToReleaseFolder over’
}
}

//Remove directory where release test reports will be copied into
task cleanReleaseReportDirectory(type: Delete) {
group = project.ext.myGradleGroup
description =”Clean the directory of the report in the weekly_release.”
delete releaseRootFolder + ‘/reports/’,
releaseRootFolder + ‘/report_index.html’
}
runReportersTools.mustRunAfter ‘cleanReleaseReportDirectory’
moveTheReportIndexToReleaseFolder.mustRunAfter ‘cleanReleaseReportDirectory’

6.1 One entry page for your report

A good practice is to have one html entry point to browse your tests and reports. I reuse the one of lint to obtain this one:

Image for post
Image for post

It’s pretty easy and will save you a lot of time if you want to use your reports to analyze your project.

I just created a folder with the root page:

Image for post
Image for post

And we create a task to copy paste this file into the release folder. We add the date of the generation of the report and the version of the application in the html file.

Image for post
Image for post

The task is easy:

task moveTheReportIndexToReleaseFolder(type: Copy) {
group = project.ext.myGradleGroup
description =”Move the build/report index into the weekly_release.”
from ‘gradle/release/report’
into releaseRootFolder
include(‘**/*’)
exclude(‘**/*-release-*’)
filter { String line ->
if(line.contains(‘${DateOfTheDay}’)){
line.replace(‘${DateOfTheDay}’,project.ext.def_readableSchoolDate)
}else line
}
doFirst {
println ‘Task moveTheReportsIndexToReleaseFolder’
}
doLast {
println ‘moveTheFileToReleaseFolder over’
}
}
The tricky part is the filter, which is in fact call each time a line is found, you can manipulate the string. And it’s related to this html extract (in the file report_index):

<header class=”mdl-layout__header”>
<div class=”mdl-layout__header-row”>
<span class=”mdl-layout-title”>
Tools analysis reports for the project MultiplicationBasile</span>
<div class=”mdl-layout-spacer”></div>
<nav class=”mdl-navigation mdl-layout — large-screen-only”>
Automaticly generated the ${DateOfTheDay}
<br>
BasileMultiplication exact version name: ${VersionNumber}
</nav>
</div>
</header>

You just have to provide a good html file to present your report. In this html file you just have to use relative path (you are in your release folder).Like this:

<h2 class=”mdl-card__title-text”>
<a href=”./reports/tests/testLilaAdditionDebugUnitTest/index.html”>
Unit Tests Lila Debug</a>
</h2>
Is the tests associated with the Lila apk (debug config).
<br><br>

7 The upload part

What’s left to do according to the release? Two elements; upload on a repository (maven repository) and deliver the application.

I use private Nexus Repository and Balto for that. So useless for us in a generic way.

For automatically deliver to GooglePlay, I have found this plugin that seems to be really easy to use:

https://github.com/Triple-T/gradle-play-publisher

For the upload to mavenCentral, JForg or Nexus, you always will have the same information to provide.

7.1 Upload: Maven basics principles

When uploading your project to a MavenCentral repo, you go back to Maven concepts. Gradle has been built upon Maven (which has been created upon Ant…) and it really helps us. A while ago (7 years), I wrote a book explaining how to use Maven and Jenkins for CI on Android. Well almost a prehistoric book now in 2017, but at the time, it was hell just to make Eclipse, Maven and the Android project build worked together. Today, I can tell you, each morning you can send a “I love you” to the Gradle team and the Gradle community, those silent actors that have enhanced our daily life so concretely. Hey dudes, thank.

So I want to share with you the basics on Maven, I copy paste what I was explaining 7 years ago. It’s really important to have those concepts in mind when trying to talk with a Maven repository.

[Extract from “Android A Complete Course” M. Seguy android2ee@2010]

Let’s stop thinking « Android » for a while and start thinking « Java » again.

Maven’s goal is to rationalize a project’s life cycle by means of tools and norms. This means managing compilation, setting unitary tests, managing packaging, deployment, project reports (java doc, test reports, PMD), linking to a source versioning tool, linking to a continuous integration tool…

That is why Maven provides tools and norms.

The tools will write certain files for you (MakeFile, Ant). The normalization allows any developer to go from one project to another without losing sight of the project. The POM file (Project Object Model) is a normalized file that describes the project and enables Maven to follow its life cycle.

Let’s start by the beginning and build a Maven project:

1.1.1.1 First Pom

Create a project called MyProject

Create a pom.xml file in MyProject and write in this content:

<project>

<modelVersion>4.0.0</modelVersion>

<groupId>com.mycompany.myproject</groupId>

<artifactId>MyProject</artifactId>

<version>SNAPSHOT</version>

</project>

Create the folder src/main/java/com/mycompany/myproject in MyProject

Write the source src/main/java/com/mycompany/myproject/Person.java in

package com.mycompany.myproject;

public class Person {

public String name;

public String telephone;

public String email;

public String toString() {

return new StringBuilder()

.append(name).append(“,”)

.append(telephone).append(“,”)

.append(email).toString();

}

}

Compilator et packager: Do “mvn package” in the MyProject folder

[INFO] Building jar: /MyProject/target/MyProject-SNAPSHOT.jar

[INFO] — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

[INFO] BUILD SUCCESSFUL

[INFO] — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

The projects’ sources are compiled in target/classes.

The project is packaged in target/MyProject-SNAPSHOT.jar

1.1.1.2 What just happened?

1.1.1.2.1 Project coordinates

Maven provides a coordinates system for all projects. These coordinates have three variables (just like with latitude, longitude, altitude) that are:

groupId: This is the group identifier (for an organization, team, company or any other kind of development group). Conventionally, start with the domain name of the organization (in the same way as with the project’s root java package) which can be com, net, org… Example net.fr.tlse.sti.android

artifactId: Identifier of the project in the group of projects that belong to groupId. Do not use any « . » in the names of the artifactId

version: Identifier of the version to build. There are two different types: the first type is a normal version number, the other type is a Snapshot. By convention for a normal version number, the identifier should be:

<major version>.<minor version>.<incremental version>-<qualifier>

This, for example, would give us 2.1.0-alpha. The qualifier is not mandatory, the other elements are highly recommended. The Snapshot version type is in fact a kind of syntax that allows you to build versions (typically during the over-night build) that are named in function of the date on which the build took place. This syntax is to be used in a project under development that has not yet reached a version but is performing builds on a regular basis (either with continuous integration, or by opening up the source for other modules or projects).

There is also a fourth identifier:

Classifier: Allows you to add to coordinate (so to a same project with a same version i.e. the same code) a piece of additional information. This might be the type of JDK used by the build, the destination platform… It is a way to add information to a code that is the same but not packaged in the same way. In other words, a project that is compiled with the JDK 1.3 and the JDK 1.6 without any modifications in the code will use classifier to distinguish between the two builds.

So my first POM defines the project (its unique and exact name).

1.1.1.2.2 Project structure convention

By convention, Maven assumes that a project is structured in the following way:

my-app

| — pom.xml

` — src

| — main

| | — java

| | ` — com

| | ` — mycompany

| | ` — app

| | ` — App.java

| ` — resources

| | — com

| ` — mycompany

| ` — app

| ` — App.properties

` — test

| — java

| ` — com

| ` — mycompany

| ` — app

| ` — AppTest.java

` — resources

` — com

` — mycompany

` — app

` — AppTest.properties

This way, Maven does not need any additional information to compile, package or test a project. By respecting this structure you allow Maven to perform actions on the project. It is for this reason that the class has been put in the directory src/main/java/com/mycompany/myproject.

When working with Android, it is impossible to respect this structure because it clashes with the imposed structure of an Android project. It’s up to Maven to adapt, not Android.

[Note from 2017: At the time, it was really a hell, not possible to be compatible with the maven default structure. It was tough, I swear, it was really tough]

1.1.1.2.3 A build project’s life cycle

For Maven, projects are always built step by step. Each phase being necessary to perform the one following it. Each stage can be composed of several sub-tasks, goal. The list of tasks for a build is:

Validate: Validates that the project is correct and that all the necessary information is present.

Compile: Compiles the source code of the project.

Test: Tests the compiled source code by using the available test framework. These tests do not require that the code be deployed or packaged.

Package: Packages the compiled code in its targeted distribution format (Jar, War, Apk).

Integration-test : Generates and deploys the package in an integration space when necessary and performs integration tests.

Verify: Launches the verifications that makes sure that the package is valid and that it meets quality criteria.

Install: Installs the package in the local repository so it can be used as a dependency for local projects that reference it.

Deploy: This is done in an integration or production space, it copies the final package in the remote repository which allows it to be shared with other developers and other projects.

For Maven to perform a task, simply tell it to do so. Hence, to launch:

· compilation: « mvn: package » is sufficient,

· tests, « mvn:verify » is sufficient,

· sharing with other projects, « mvn:install » must be called (the package is deployed in the local repository).

Each phase has a certain number of goals. Each phase is linked to a principal goal, which is called during mvn:***. This mapping has been partially reproduced here:

Phases

Goals

process-resources

resources:resources

compile

compiler:compile

process-test-resources

resources:testResources

test-compile

compiler:testCompile

test

surefire:test

package

jar:jar

install

install:install

deploy

deploy:deploy

[End of Extract from “Android A Complete Course” M. Seguy android2ee@2010]

7.2 Upload : In a Nexus Repository

We use the Maven plugin to do that. So at the top of your gradle.build, apply it:

/**
* Used to deploy on Nexus our apk
*/
apply plugin: ‘maven’

More resources on the plugin: https://docs.gradle.org/current/userguide/maven_plugin.html

To upload in a Nexus, you will need two elements. The script in one file, the parameters in an other one. The first one is shared with the team, the second one is confidential. Like you did for your Signing configurations. This way you insure the internal ship cannot make the biggest mistake of his stage.

So create your files in your gradle/upload/nexus or gradle/upload folder.

Image for post
Image for post

Let’s start by the properties you need. The file nexus_apk_var_def.gradle define them like this:

You will need all the value below:

//Define the coordinates of your project
// — — — — — — — — — — — — — — — — — — —
project.ext {
//Describe the coordinates of your project (optional)
POM_NAME = “BasileMultiplication Application deployed on Nexus”
//groupId: This is the group identifier (for an organization, team, company or any other kind of
//development group). Conventionally, start with the domain name of the organization (in the same
//way as with the project’s root java package) which can be com, net, org… Example
POM_GROUP_ID = “com.android2ee.basile.multiplication”
//artifactId: Identifier of the project in the group of projects that belong to groupId. Do not use any
//« . » in the names of the artifactId
//Exemple: acms-android-mycms or acms-android-androidauto
POM_ARTIFACT_ID = “basile-multiplication”
//version: Identifier of the version to build. There are two different types: the first type is a normal
//version number, the other type is a Snapshot. By convention for a normal version number, the
//identifier should be:
//<major version>.<minor version>.<incremental version>-<qualifier>
//This, for example, would give us 2.1.0-alpha. The qualifier is not mandatory, the other elements are
//highly recommended. The Snapshot version type is in fact a kind of syntax that allows you to build
//versions (typically during the over-night build) that are named in function of the date on which the
//build took place. This syntax is to be used in a project under development that has not yet reached a
//version but is performing builds on a regular basis (either with continuous integration, or by opening
//up the source for other modules or projects).
VERSION_NAME = project.def_apk_name_wrelease
//Tell if it is a release or not
//Release version needs to be sign using a gpg (bottom of the file)
//It can be TRUE or FALSE
RELEASE_VERSION = “FALSE”
//The packaging of the build
//Exemple aar or apk or jar”
POM_PACKAGING = “apk”

//Define How we can retrieve the source of the project
// — — — — — — — — — — — — — — — — — — —
//Have a look here to understand this section
//https://maven.apache.org/scm/git.html

//Give us a description of the project (optional)
POM_DESCRIPTION = “This is the apk of the Basile Multiplication”
//The Url where your project lie (only for information)
POM_URL = “https://github.com/MathiasSeguy-Android2EE/MultiplicationBasile"
//The url to connect to push/pull source
POM_SCM_URL = “https://github.com/MathiasSeguy-Android2EE/MultiplicationBasile"
//The connection ot the source
POM_SCM_CONNECTION = “scm: git:ssh://github.com/MathiasSeguy-Android2EE/MultiplicationBasile”
//The connection ot the source as a developper
POM_SCM_DEV_CONNECTION = “scm: git:ssh://github.com/MathiasSeguy-Android2EE/MultiplicationBasile”
//Year of the birth of the project
POM_INCEPTION_YEAR = “2017”

//Define your licenec type
// — — — — — — — — — — — — — — — — — — —
POM_LICENCE_NAME =”Apache2"
POM_LICENCE_URL =”https://www.apache.org/licenses/LICENSE-2.0"
POM_LICENCE_DIST =

//Define your name
// — — — — — — — — — — — — — — — — — — —
POM_DEVELOPER_ID = “MSE_A2EE”
POM_DEVELOPER_NAME = “Seguy Mathias”
POM_DEVELOPER_MAIL = “mathias.seguy@android2ee.com”
POM_DEVELOPER_ROLE = “Android Expert”

//Define where to deploy
// — — — — — — — — — — — — — — — — — — —
NEXUS_USERNAME = “Name”
NEXUS_PASSWORD =’Password’
NEXUS_URL = “http://nexus.somewhere.com:8081"

SNAPSHOT_REPOSITORY_URL = “http://nexus.somewhere.com:8081/repository/basile_multiplication_apk_snap"
RELEASE_REPOSITORY_URL = “http://nexus.somewhere.com:8081/repository/basile_multiplication_apk"

//Define your GPG for signing your archives
// — — — — — — — — — — — — — — — — — — — — -
//signing.keyId=
//signing.password=
//signing.secretKeyRingFile=

//Reference:
//http://zserge.com/blog/gradle-maven-publish.html
//https://github.com/A-CMS/alliance-platform-parent/blob/master/pom.xml
//https://maven.apache.org/scm/git.html
}

We will use them in our script to upload our archives. We will override the uploadArchives task to upload the default apk on Nexus.

You have 3 main steps:

· Define in the mavenDeployer section, the coordinates of the project

· and its pom project description

· You can after that add tasks (source, Javadoc…) using the artifact pattern

We use and reuse the variables we have defined in nexus_apk_var_def.gradle file.

uploadArchives {
group = project.ext.myGradleGroup
description =”Upload the achive in Nexus repository. The selected archive is defined in build.gradle” +
“in the android.”
repositories {
mavenDeployer {
//Define the Coordinates of the project
// — — — — — — — — — — — — — — — — — —
pom.artifactId = project.POM_ARTIFACT_ID
pom.groupId = project.POM_GROUP_ID
//If you are on master, push on release
if (project.def_branche_name.contains(‘master’)
||project.def_branche_name.contains(‘dev’)){
println ‘ReleaseVersion’
pom.version = project.VERSION_NAME
repository(url: project.RELEASE_REPOSITORY_URL) {
authentication(userName: project.NEXUS_USERNAME, password: project.NEXUS_PASSWORD)
}
} else {
println ‘Snapshot’
pom.version = project.VERSION_NAME+’-SNAPSHOT’
repository(url: project.SNAPSHOT_REPOSITORY_URL) {
authentication(userName: project.NEXUS_USERNAME, password: project.NEXUS_PASSWORD)
}
}

//Describe your POM and its content
pom.project {
name project.POM_NAME
packaging project.POM_PACKAGING
description project.POM_DESCRIPTION+’for the apk ‘+project.def_apk_name_wrelease
url project.POM_URL

scm {
url project.POM_SCM_URL
connection project.POM_SCM_CONNECTION
developerConnection project.POM_SCM_DEV_CONNECTION
}

licenses {
license {
name project.POM_LICENCE_NAME
url project.POM_LICENCE_URL
distribution project.POM_LICENCE_DIST
}
}

developers {
developer {
id project.POM_DEVELOPER_ID
name project.POM_DEVELOPER_NAME
}
}
}
}

}

//Add the Javadocs, the source jar tasks and some more if you want
task androidJavadocsJar(type: Jar, dependsOn: generateProjectJavadocs) {
classifier = ‘javadoc’
from generateProjectJavadocs.destinationDir
}

task androidSourcesJar(type: Jar) {
classifier = ‘sources’
from android.sourceSets.main.java.sourceFiles
}
//Add those tasks as artifacts to have them took into account
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}

//You could have had signing and javadocs also.
// signing {
// required { isReleaseBuild() && gradle.taskGraph.hasTask(“uploadArchives”) }
// sign configurations.archives
// }

// task androidJavadocs(type: Javadoc) {
// failOnError false // add this line
//// source = ‘src/main/java’
// source = android.sourceSets.main.java.getSrcDirs()
// }
}

And it should works.

8 Taking care of libraries

As a developer, you should respect the open source libraries much more than your managers. And a good way to do that is to display the reward associated with the libraries you use. And by the way, you can be suit if you don’t respect the OSP’s licensing of the OSP you use.

For that I use the plugin Android License Tools Plugin (https://github.com/cookpad/license-tools-plugin) which does that really well. Have a look at its ReadMe and five minutes later, it’s implemented on the project.

In your application build.gradle, link to the plugin

buildscript {
repositories {
mavenCentral()
jcenter()
maven { url ‘https://maven.fabric.io/public' }
google()
}
dependencies {
//…

//Gradle License Plugin
classpath ‘com.cookpad.android.licensetools:license-tools-plugin:0.23.0’

}
}

Then in the gradle.build of your module (of your application), apply the plugin:

/**
* This plugin provides a task to generate a HTML license report based on the configuration
* You can find the plugin project here :https://github.com/cookpad/license-tools-plugin
*/
apply plugin: ‘com.cookpad.android.licensetools’

Now you just have to call the task that generates your licences html file (or json) you can use to display it in a webview in your application. The best is to do it when making a release build. So just make your releaseBuild task depends on it. As simple as that:

//Make a full build :clean/build/InstrumenetationTests
task releaseBuild(dependsOn: [
‘generateLicensePage’,
// ‘enableCrashlytics’,
‘buildAndCheckProject’
]) {

It’s just perfect.

9 Conclusion

Having this tooling on any Android project is bless. I hope you will add them, I hope those articles helped you. Thanks for reading and have a good life.

La bise.

Written by

Android2ee alias Mathias Seguy. Android expert gives you some news on the Android world. mathias.seguy@android2ee.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store