Revision control

Copy as Markdown

import org.mozilla.gradle.tasks.VerifyWebViewCount
import org.mozilla.android.tasks.*
buildscript {
repositories {
maven {
}
dependencies {
classpath "org.mozilla.telemetry:glean-gradle-plugin:31.6.0"
}
}
}
plugins {
id "com.jetbrains.python.envs" version "0.0.26"
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'findbugs'
apply plugin: 'jacoco'
apply plugin: 'pmd'
apply plugin: 'checkstyle'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'org.mozilla.android'
apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply from: "$project.rootDir/tools/gradle/versionCode.gradle"
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "org.mozilla.connect"
minSdkVersion 22
targetSdkVersion 22
versionCode 11 // This versionCode is "frozen" for local builds. For "release" builds we
// override this with a generated versionCode at build time.
versionName "1.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
multiDexEnabled true
}
dexOptions {
preDexLibraries true
}
lintOptions {
lintConfig file("lint.xml")
baseline file("lint-baseline.xml")
// We want to fail the build if there is a lint error, otherwise we'll never fix them.
// This is the most efficient way to make Lint fail: the lint tool will add additional
// checks in the future so this ensures they will fail our build as soon as they are added,
// unlike a whitelist.
warningsAsErrors true
}
// We have a three dimensional build configuration:
// BUILD TYPE (debug, release) X PRODUCT FLAVOR (focus) X ENGINE FLAVOR (webview, gecko)
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
// The device has an extended screen timeout for our release app ID but not for our
// debug app ID. it's difficult to debug with a 30s screen timeout so we do not
// change the debug app ID here, making it the same as release.
}
}
testOptions {
unitTests.includeAndroidResources true // for robolectric.
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
flavorDimensions "engine"
productFlavors {
// We can build with two engines: amazonWebview or gecko. #399 to restore native Webview.
amazonWebview {
dimension "engine"
applicationIdSuffix ".firefox"
}
gecko {
dimension "engine"
applicationIdSuffix ".gecko"
}
}
variantFilter { variant ->
def flavors = variant.flavors*.name
// We only need a gecko debug build for now.
if (flavors.contains("gecko") && variant.buildType.name != "debug") {
setIgnore(true)
}
}
sourceSets {
test {
resources {
// Make the default asset folder available as test resource folder. This is only
// necessary for SearchEngineParserTest.getBasePath, which access the test resources
// before RuntimeEnvironment.application is available.
srcDir "${projectDir}/src/main/assets/"
}
}
}
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions.allWarningsAsErrors = true
}
repositories {
flatDir {
dirs 'libs'
}
maven {
}
mavenCentral()
}
// *** DO NOT USE THIS OUTSIDE OF THIS PROJECT *** - Reach out to the
// Glean Team if you think you need to override this.
ext.gleanNamespace = "mozilla.telemetry.glean"
// Optionally, set any parameters to send to the plugin.
ext.gleanGenerateMarkdownDocs = true
apply plugin: "org.mozilla.telemetry.glean-gradle-plugin"
// Define library names and version constants.
String GLEAN_VERSION = "31.6.0"
String GLEAN_LIBRARY = "org.mozilla.telemetry:glean:$GLEAN_VERSION"
String GLEAN_LIBRARY_FORUNITTESTS = "org.mozilla.telemetry:glean-forUnitTests:$GLEAN_VERSION"
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
implementation "androidx.appcompat:appcompat:$androidx_version"
implementation "androidx.cardview:cardview:$androidx_version"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.browser:browser:$androidx_version"
implementation "com.google.android.material:material:$androidx_version"
implementation "androidx.preference:preference:$androidx_version"
implementation "androidx.recyclerview:recyclerview:$androidx_version"
implementation 'io.sentry:sentry-android:1.7.14'
implementation ("com.google.code.findbugs:annotations:3.0.1") {
// We really only need the SuppressFBWarnings annotation, everything else can be ignored.
// Without this we get weird failures due to dependencies.
transitive = false
}
implementation GLEAN_LIBRARY
implementation "org.mozilla.components:browser-domains:$moz_components_version"
implementation "org.mozilla.components:browser-toolbar:$moz_components_version"
implementation "org.mozilla.components:service-telemetry:$moz_components_version"
implementation "org.mozilla.components:support-utils:$moz_components_version"
implementation "org.mozilla.components:ui-autocomplete:$moz_components_version"
implementation "org.mozilla.components:ui-colors:$moz_components_version"
implementation "org.mozilla.components:ui-fonts:$moz_components_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
geckoImplementation(name: 'geckoview-latest', ext: 'aar')
implementation "com.google.android.gms:play-services-oss-licenses:$oss_licenses_version"
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:3.8"
testImplementation 'org.mockito:mockito-core:2.23.0'
testImplementation "androidx.arch.core:core-testing:$architecture_components_version"
// There is an AndroidX equivelent for this dependency (androidx.test.uiautomator:uiautomator),
// but it conflicts with our version of Fastlane. This seems to work on the old version of
// UI Automator, so for now we're sticking with that. Also see the comment in the FFTV
// Fastlane dependency for more information on how that problem is handled.
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'
testImplementation GLEAN_LIBRARY_FORUNITTESTS
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-web:$espresso_version"
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
androidTestImplementation "tools.fastlane:screengrab:1.1.0"
androidTestImplementation 'androidx.test:rules:1.1.1'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestUtil 'androidx.test:orchestrator:1.1.1'
}
// This task runs automatically during release builds and is likely to break. If people are unfamiliar with gradle, they
// may not know we define this task. As such, we namespace it to make them more likely to read the gradle source.
task validateMozillaAndroidAppReleaseConfiguration(type: ValidateAndroidAppReleaseConfiguration)
task verifyWebViewCount(type: VerifyWebViewCount)
// -------------------------------------------------------------------------------------------------
// Dynamically set versionCode (See tools/build/versionCode.gradle
// -------------------------------------------------------------------------------------------------
android.applicationVariants.all { variant ->
def buildType = variant.buildType.name
if (buildType == "release") {
variant.outputs.all { output ->
setVersionCodeOverride(generatedVersionCode)
}
}
println("Build type: " + buildType + " (versionCode = " + variant.mergedFlavor.versionCode + ")")
}
// -------------------------------------------------------------------------------------------------
// Generate blocklists
// -------------------------------------------------------------------------------------------------
def blockListOutputDir = 'src/amazonWebview/res/raw'
task buildBlocklists(type:Copy) {
from('../shavar-prod-lists') {
include '*.json'
}
into blockListOutputDir
// Android can't handle dashes in the filename, so we need to rename:
rename 'disconnect-blacklist.json', 'blocklist.json'
rename 'disconnect-entitylist.json', 'entitylist.json'
// google_mapping.json already has an expected name
}
clean.doLast {
file(blockListOutputDir).deleteDir()
}
tasks.whenTaskAdded { task ->
def name = task.name
if (name.contains("generate") && name.contains("Config") && name.contains("Webview")) {
task.dependsOn buildBlocklists
}
}
// -------------------------------------------------------------------------------------------------
// Secrets: add API secrets from config files into the BuildConfig instance.
// We use files because they're easier to manage than environment variables.
// -------------------------------------------------------------------------------------------------
def addSecretToBuildConfig(variant, fieldName, fileBaseName) {
def variantName = variant.getName()
print("${fieldName} (" + variantName + "): ")
// We separate the debug files from the release files so that we don't
// accidentally ship a release using the debug key or vice versa.
def fileSuffix
if (variantName.contains("Debug")) {
fileSuffix = "debug"
} else if (variantName.contains("Release")) {
fileSuffix = "release"
} else {
throw new IllegalStateException("Unhandled variant $variantName")
}
def filePath = "${rootDir}/${fileBaseName}_${fileSuffix}"
def file = new File(filePath)
if (file.exists()) {
def token = file.text.trim()
variant.buildConfigField 'String', fieldName, '"' + token + '"'
println "Added from $filePath"
} else {
variant.buildConfigField 'String', fieldName, 'null'
println("X_X")
}
}
android.applicationVariants.all { variant ->
addSecretToBuildConfig(variant, 'SENTRY_DSN', ".sentry_dsn")
}
// -------------------------------------------------------------------------------------------------
// L10N: Generate list of locales
// Focus provides its own (Android independent) locale switcher. That switcher requires a list
// of locale codes. We generate that list here to avoid having to manually maintain a list of locales:
// -------------------------------------------------------------------------------------------------
def getEnabledLocales() {
def resDir = file('src/main/res')
def potentialLanguageDirs = resDir.listFiles(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
return name.startsWith("values-");
}
})
def langs = potentialLanguageDirs.findAll {
// Only select locales where strings.xml exists
// Some locales might only contain e.g. sumo URLS in urls.xml, and should be skipped (see es vs es-ES/es-MX/etc)
return file(new File(it, "strings.xml")).exists()
} .collect {
// And reduce down to actual values-* names
return it.name
} .collect {
return it.substring("values-".length())
} .collect {
if (it.length() > 3 && it.contains("-r")) {
// Android resource dirs add an "r" prefix to the region - we need to strip that for java usage
// Add 1 to have the index of the r, without the dash
def regionPrefixPosition = it.indexOf("-r") + 1
return it.substring(0, regionPrefixPosition) + it.substring(regionPrefixPosition + 1)
} else {
return it
}
}.collect {
return '"' + it + '"'
}
// en-US is the default language (in "values") and therefore needs to be added separately
langs << "\"en-US\""
return langs.sort { it }
}
def generatedLocaleListDir = 'src/main/java/org/mozilla/focus/generated'
def generatedLocaleListFilename = 'LocaleList.java'
task generateLocaleList {
doLast {
def dir = file(generatedLocaleListDir)
dir.mkdir()
def localeList = file(new File(dir, generatedLocaleListFilename))
localeList.delete()
localeList.createNewFile()
localeList << "package org.mozilla.focus.generated;" << "\n" << "\n"
localeList << "import java.util.Arrays;" << "\n"
localeList << "import java.util.Collections;" << "\n"
localeList << "import java.util.List;" << "\n"
localeList << "\n"
localeList << "public class LocaleList {" << "\n"
// findbugs doesn't like "public static final String[]", see http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_ARRAY
localeList << " public static final List<String> BUNDLED_LOCALES = Collections.unmodifiableList(Arrays.asList(new String[] { "
localeList << getEnabledLocales().join(", ") + " }));" << "\n"
localeList << "}" << "\n"
}
}
tasks.whenTaskAdded { task ->
if (name.contains("compile")) {
task.dependsOn generateLocaleList
}
}
clean.doLast {
file(generatedLocaleListDir).deleteDir()
}
// -------------------------------------------------------------------------------------------------
// Static Analysis: findbugs and pmd
// -------------------------------------------------------------------------------------------------
findbugs {
ignoreFailures = false
effort = "max"
// This selects what level of bugs to report: low means low priority issues will be reported
// (in addition to medium+high), which corresponds to warning about everything.
// TODO: boost this to low once low priority issues are fixed.
reportLevel = "medium"
excludeFilter = new File("${project.rootDir}/quality/findbugs-exclude.xml")
}
task findbugs(type: FindBugs, dependsOn: "assemble", group: 'verification') {
classes = files("$projectDir.absolutePath/build/intermediates/classes")
source = fileTree('src/main/java')
classpath = files()
// Only one report format is supported. Html is easier to read, so let's use that
// (xml is the one that's enabled by default).
reports {
xml.enabled = false
html.enabled = true
}
}
pmd {
toolVersion = '5.5.2'
ignoreFailures = true
ruleSetFiles = files("${project.rootDir}/quality/pmd-rules.xml")
ruleSets = []
}
task pmd(type: Pmd, group: 'verification') {
source 'src'
include '**/*.java'
exclude('**/gen/**',
'**/debug/**',
'**/androidTest/**',
'**/test**/**')
reports {
xml.enabled = false
html.enabled = true
html {
destination file("$projectDir.absolutePath/build/reports/pmd/pmd.html")
}
}
}
task checkstyle(type: Checkstyle) {
configFile file("${project.rootDir}/quality/checkstyle.xml")
source 'src'
include '**/*.java'
exclude '**/gen/**'
classpath = files()
}
// Set up jacoco for code coverage. We originally used the jacoco-android plugin
// but it doesn't support kotlin so we're configuring it manually.
if (project.hasProperty("coverage")) {
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
android.applicationVariants.all { variant ->
// We dynamically generate our jacoco tasks to avoid creating too many build variants,
// which takes up space in AS despite not being run by most users and it can accidentally
// be run during CI with commands like `./gradlew test`, taking up more build time.
task "jacoco${variant.name.capitalize()}TestReport"(type: JacocoReport,
dependsOn: ["test${variant.name.capitalize()}UnitTest"]) {
reports {
html.enabled true
xml.enabled true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
'**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
def kotlinTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/${variant.name}", excludes: fileFilter)
def javaTree = fileTree(dir: "$project.buildDir/intermediates/classes/${variant.flavorName}/${variant.buildType.name}",
excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([kotlinTree, javaTree])
executionData = fileTree(dir: project.buildDir, includes: [
"jacoco/test${variant.name.capitalize()}UnitTest.exec", 'outputs/code-coverage/connected/*coverage.ec'
])
}
}
android {
buildTypes {
debug {
testCoverageEnabled true
applicationIdSuffix ".coverage"
}
}
}
}
afterEvaluate {
check.dependsOn 'findbugs', 'pmd', 'checkstyle'
}