build.gradle 15.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Copyright (C) 2016 CZ.NIC, z.s.p.o. (http://www.nic.cz/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

18 19 20 21 22
final ASSETS_CHECKSUM_FIELD_TYPE = "String";
final ASSETS_CHECKSUM_FIELD_NAME = "ASSETS_CHECKSUMS";
final BUILD_CONFIG_DIR           = "${buildDir}/generated/source/buildConfig/"
final BUILD_CONFIG_ENCODING      = 'UTF-8'

23
android {
24
    buildToolsVersion '24.0.3'
25
    compileSdkVersion 21
26

27 28 29 30 31
    packagingOptions {
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
    }

32 33 34
    lintOptions {
        abortOnError false
    }
35

36
    defaultConfig {
37
        targetSdkVersion 21
38 39
        versionName tablexiaVersionName
        versionCode tablexiaVersionCode
40 41
        applicationId rootProject.applicationBaseId
        testApplicationId rootProject.applicationBaseId + ".test"
42 43 44
	    jackOptions {
            enabled true
	    }
45
    }
46 47 48

    buildTypes {
        debug {
49
            applicationIdSuffix rootProject.applicationIdBranch + rootProject.applicationIdDebugSuffix
50
            buildConfigField ASSETS_CHECKSUM_FIELD_TYPE, ASSETS_CHECKSUM_FIELD_NAME, rootProject.ext.assetsChecksumPattern
51
            resValue "string", "app_name", "${tablexiaAppName}"
52
            resValue "string", "sentry_dsn", project.sentryDSN
53 54 55
        }
        release {
            debuggable false
56
            applicationIdSuffix rootProject.applicationIdBranch
57
            buildConfigField ASSETS_CHECKSUM_FIELD_TYPE, ASSETS_CHECKSUM_FIELD_NAME, rootProject.ext.assetsChecksumPattern
58
            resValue "string", "app_name", "${tablexiaAppName}"
59
            resValue "string", "sentry_dsn", project.sentryDSN
60
        }
61 62 63 64 65 66
        iTest.initWith(buildTypes.debug)
        iTest {
            applicationIdSuffix rootProject.applicationIdITestSuffix
            buildConfigField ASSETS_CHECKSUM_FIELD_TYPE, ASSETS_CHECKSUM_FIELD_NAME, rootProject.ext.assetsChecksumPattern
            resValue "string", "app_name", "${tablexiaAppName}"
        }
67 68 69
        devel.initWith(buildTypes.debug)
        devel {
            applicationIdSuffix rootProject.applicationIdDevelSuffix
70
            buildConfigField ASSETS_CHECKSUM_FIELD_TYPE, ASSETS_CHECKSUM_FIELD_NAME, rootProject.ext.assetsChecksumPattern
71
            resValue "string", "app_name", "${tablexiaAppName}"
72
            resValue "string", "sentry_dsn", project.sentryDSNFallbackValue
73 74 75
        }
    }

76 77
    sourceSets {
        main {
78
            manifest.srcFile 'src/main/AndroidManifest.xml'
79
            res.srcDirs = ['res/main']
80
            assets.srcDirs = ['assets']
81
            java.srcDirs = ['src/main/java']
82
            jniLibs.srcDirs = ['libs']
83 84 85 86 87
        }
        test {
            java.srcDir file('src/test/java')
        }

88 89 90 91 92 93
        release {
            res.srcDirs = ['res/release']
        }
        debug {
            res.srcDirs = ['res/debug']
        }
94 95
        iTest {
            manifest.srcFile 'src/iTest/AndroidManifest.xml'
96
            java.srcDirs = ['src/main/java', 'src/iTest/java', '../itest/src/']
97 98
            res.srcDirs = ['res/iTest']
        }
99 100 101
        devel {
            res.srcDirs = ['res/devel']
        }
102 103
    }

104
    assemble.dependsOn = ['assembleRelease', 'assembleDebug', 'assembleITest']
105 106

    applicationVariants.all { variant ->
107
        if (!variant.buildType.name.equals("devel")) {
108 109
            variant.outputs.each { output ->
                def file = output.outputFile
110
                output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + tablexiaVersionName + ".apk").replace("android", tablexiaAppName))
111
            }
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
        }
    }

    if (project.hasProperty('TABLEXIA_RELEASE_KEYSTORE') && project.hasProperty('TABLEXIA_RELEASE_KEYSTORE_PASSWORD')
            && project.hasProperty('TABLEXIA_RELEASE_KEY_ALIAS') && project.hasProperty('TABLEXIA_RELEASE_KEY_PASSWORD')) {
        signingConfigs {
            release {
                storeFile file(TABLEXIA_RELEASE_KEYSTORE)
                storePassword TABLEXIA_RELEASE_KEYSTORE_PASSWORD
                keyAlias TABLEXIA_RELEASE_KEY_ALIAS
                keyPassword TABLEXIA_RELEASE_KEY_PASSWORD
            }
        }

        buildTypes {
            release {
                signingConfig signingConfigs.release
            }
        }
    }
132
    compileOptions {
133 134
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
135
    }
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

    command {
        // list manufacturer and model for all attached devices
        task('uninstallAllDevices') << {
            devices().each {
                applicationVariants.all { variant ->
                    if (variant.buildType.name.equals("debug")) {
                        variant.outputs.each { output ->
                            print "Uninstalling " + variant.name + "(" + variant.applicationId + ") from device " + it.id + ": .. "
                            [adb, '-s', it.id, 'shell', 'pm', 'uninstall', variant.applicationId].execute()
                            println "done"
                        }
                    }
                }
                // Uninstall test apk
                print "Uninstalling test(" + defaultConfig.testApplicationId + ") from device " + it.id + ": .. "
                [adb, '-s', it.id, 'shell', 'pm', 'uninstall', defaultConfig.testApplicationId].execute()
                println "done"
            }
        }
    }
157
}
158

159 160 161 162 163 164 165 166 167 168 169 170 171
// hack to add assets checksum to BuildConfig -> build config fields are generated at script startup
// -> calculated values from script runtime have to be added to file manually
tasks.whenTaskAdded { compileTask ->
    // select all "compile{BUILD_TYPE}Java" android tasks
    def matcher = (compileTask.name =~ 'compile(.+)Java')
    if (matcher.matches()) {
        String buildName = matcher[0][1]
        // without test tasks
        if (!buildName.contains("Test")) {

            // for all selected tasks create dependency task "checksum{BUILD_TYPE}Assets"
            task("checksum${buildName}Assets") {
                // calculated checksum is dependency
172
                dependsOn(':util:checksum:runChecksum')
173 174 175 176 177 178 179 180 181 182 183 184
                // wait for generating android sources
                dependsOn("generate${buildName}Sources")

                // modify BuildConfig.java -> write checksum in
                doLast{
                    ConfigurableFileTree cft = getProject().fileTree(BUILD_CONFIG_DIR + buildName.toLowerCase())
                    cft.include("**/BuildConfig.java")
                    Iterator<File> iter = cft.iterator()
                    while (iter.hasNext()){
                        File buildConfigFile = iter.next()
                        String buildConfigContent = buildConfigFile.getText(BUILD_CONFIG_ENCODING)

185
                        buildConfigContent = buildConfigContent.replace(rootProject.ext.assetsChecksumPattern, '\"' + getMapConvertedToString(rootProject.ext.assetsChecksum) + '\"')
186 187 188 189 190 191 192 193 194 195 196
                        buildConfigFile.write(buildConfigContent, BUILD_CONFIG_ENCODING)
                    }
                }
            }

            // set as dependency for "compile{BUILD_TYPE}Java" task
            compileTask.dependsOn("checksum${buildName}Assets")
        }
    }
}

197 198 199
// called every time gradle gets executed, takes the native dependencies of
// the natives configuration, and extracts them to the proper libs/ folders
// so they get packed with the APK.
200
task copyAndroidNatives() {
201 202 203 204 205 206
    file("libs/armeabi/").mkdirs();
    file("libs/armeabi-v7a/").mkdirs();
    file("libs/x86/").mkdirs();

    configurations.natives.files.each { jar ->
        def outputDir = null
207 208 209 210
        if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
        if (jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
        if (jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
        if (outputDir != null) {
211 212 213 214 215 216 217 218
            copy {
                from zipTree(jar)
                into outputDir
                include "*.so"
            }
        }
    }
}
219

220
task run(type: Exec) {
221 222 223 224 225
    def adb = doAndroidSettings()
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'cz.nic.tablexia.android/cz.nic.tablexia.android.AndroidLauncher'
}

def doAndroidSettings() {
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

243
    return path + "/platform-tools/adb"
244
}
245

246 247 248 249
// sets up the Android Idea project, using the old Ant based build.
idea {
    module {
        sourceDirs += file("src");
250
        scopes = [COMPILE: [plus: [project.configurations.compile]]]
251 252 253 254 255 256 257 258 259

        iml {
            withXml {
                def node = it.asNode()
                def builder = NodeBuilder.newInstance();
                builder.current = node;
                builder.component(name: "FacetManager") {
                    facet(type: "android", name: "Android") {
                        configuration {
260
                            option(name: "UPDATE_PROPERTY_FILES", value: "true")
261 262 263 264 265 266 267
                        }
                    }
                }
            }
        }
    }
}
268

269 270
def runTest(String iTestName, String device) {
    println "[iTest-" + device + "] STARTING TEST: " + iTestName
271 272
    def adb = doAndroidSettings()
    exec {
273
        commandLine "$adb", '-s', device, 'shell', 'am', 'start', '-n', 'cz.nic.tablexia.itest/cz.nic.tablexia.android.AndroidITestLauncher', '-e', 'testClassName',  iTestName
274 275 276 277
    }


    def adbStdOut = new ByteArrayOutputStream()
278 279
    def timeout = 0;
    while(timeout<1200) { //timeout is set to 20 minuts
280 281
        if(adbStdOut.toString().trim().contains("PROCESSING")){
            def adbPSOut = new ByteArrayOutputStream()
282
            exec {
283
                commandLine "$adb", '-s', device, 'shell', "ps", "|", "grep", "cz.nic.tablexia.itest"
284
                standardOutput = adbPSOut
285
            }
286 287
            if(!adbPSOut.toString().contains("cz.nic.tablexia.itest")) return //stop if test is not running anymore
        }
288 289 290
        if(adbStdOut.toString().trim().contains("OK")) return
        if(adbStdOut.toString().trim().contains("FAIL")) return
        exec {
291
            commandLine "$adb", '-s', device, 'shell', "ls", "/sdcard/iTest_results"
292 293
            standardOutput = adbStdOut
        }
294 295
        sleep(1000)
        timeout++;
296
    }
297
    println "[iTest-" + device + "] Test " + iTestName + " timed out."
298 299
}

300
String androidITestBundle(String device){
301 302
        int testsCount = 0;
        int successfulTestsCount = 0;
303

304 305 306 307
        def tablexiaApk = project.tablexiaAppName + "-iTest-" + "$tablexiaVersionName" + ".apk"
        def apkFolder = "${project(':android').projectDir}/build/outputs/apk/"
        def sdCardResults = "/sdcard/iTest_results"

308
        println "[iTest-" + device + "] INSTALLING " + tablexiaApk + " into connected Android device"
309 310 311
        if(!new File(apkFolder + tablexiaApk).exists()){
            throw new GradleException("Could not install " + tablexiaApk + " - file does not exists!")
            return
312
        }
313

314 315
        def adb = doAndroidSettings()
        exec {
316
            commandLine "$adb", '-s', device, 'install', '-r', apkFolder + tablexiaApk
317
        }
318
        println "[iTest-" + device + "] INSTALLING Tablexia COMPLETED"
319

320
        println "[iTest-" + device + "] CREATING NEW iTest OUTPUT DIRECTORY"
321 322
        def iTestOutputFolder = "iTest_results-" + device
        def iTestOutputFile = new File(apkFolder + iTestOutputFolder)
323
        if (iTestOutputFile.exists()) {
324
            println "[iTest-" + device + "] REMOVING OLD iTest OUTPUT DIRECTORY"
325
            iTestOutputFile.deleteDir()
326
        }
327
        project.file(apkFolder + iTestOutputFolder).mkdirs()
328 329

        new File("${project(':itest').projectDir}/iTestBundle.txt").eachLine { iTestName ->
330
            exec {
331
                commandLine "$adb", '-s', device, 'shell', 'rm', '-r', sdCardResults
332
            }
333
            println "[iTest-" + device + "] OLD FILES on SDCARD REMOVED"
334
            testsCount++
335 336
            runTest(iTestName, device)
            println "[iTest-" + device + "] TEST: " + iTestName + " FINISHED"
337
            exec {
338
                commandLine "$adb", '-s', device, 'pull', sdCardResults, apkFolder + iTestOutputFolder+ "/"
339
            }
340
            println "[iTest-" + device + "] LOG FILES WERE MOVED INTO " + iTestOutputFile.toString()
341 342
        }

343
        file("$iTestOutputFile" + "/iTest_results").eachFile  { logFile ->
344 345
            if(logFile.toString().contains("OK")) successfulTestsCount++;
        }
346

347
        if (testsCount != successfulTestsCount){
348
            println "[iTest-" + device + "]  ONE OR MORE OF TESTS ON THIS DEVICE FINISHED WITH STATUS FAIL!"
349 350 351 352 353 354
            return "[iTest-" + device + "] Failed to complete all iTests! Check iTest log files in: " + iTestOutputFile.toString() + "/iTest_results/\n"
        }
        else{
            println "[iTest-" + device + "] ALL TESTS ON THIS DEVICE FINISHED WITH STATUS OK!"
            return ""
        }
355 356 357 358 359 360 361 362 363 364 365
}

task runAndroidITestBundle (dependsOn:'assembleITest'){
    doLast {
        def adbOut = new ByteArrayOutputStream()
        def adb = doAndroidSettings()
        exec {
            commandLine "$adb", 'devices'
            standardOutput = adbOut
        }
        def connectedDevices = 0
366
        def exceptions = ""
367
        def threads = []
368
        adbOut.toString().eachLine { line ->
369 370 371 372 373 374 375 376 377 378 379 380 381 382
            def th = new Thread({ //run devices parallel
                if (!line.contains("List") && line.contains("device")) {
                    connectedDevices++
                    def device = line.split('\t')[0]
                    println "Connected device: " + device
                    def adbLsOut = new ByteArrayOutputStream()
                    exec {
                        commandLine "$adb", '-s', device, 'shell', 'ls'
                        standardOutput = adbLsOut
                    }
                    if (adbLsOut.toString().contains("error")) {
                        exceptions += "[iTest-" + device + "] Device does not meet requirements for running tests. Try to check out if device has SD card and SD card is accessible from computer."
                        println "[iTest-" + device + "] THIS DEVICE DOES NOT MEET REQUIREMENTS FOR RUNNING TESTS."
                    } else exceptions += androidITestBundle(device)
383
                }
384 385
            })
            threads << th
386
        }
387 388
        threads.each { it.start() }
        threads.each { it.join() }
389
        if(connectedDevices<1) throw new GradleException("NO CONNECTED DEVICE WAS FOUND!")
390
        if(exceptions!="") throw new GradleException(exceptions)
391
    }
392
}