1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import android.support.LibraryVersions
18import com.android.build.gradle.internal.coverage.JacocoPlugin
19import com.android.build.gradle.internal.coverage.JacocoReportTask
20import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
21import org.gradle.api.logging.configuration.ShowStacktrace
22import android.support.gmaven.GMavenVersionChecker
23
24def supportRoot = ext.supportRootFolder
25if (supportRoot == null) {
26    throw new RuntimeException("variable supportRootFolder is not set. you must set it before" +
27            " including this script")
28}
29def init = new Properties()
30ext.init = init
31ext.init.debugKeystore = file("${supportRoot}/development/keystore/debug.keystore")
32rootProject.ext.versionChecker = new GMavenVersionChecker(rootProject)
33ext.runningInBuildServer = System.env.DIST_DIR != null && System.env.OUT_DIR != null
34
35apply from: "${supportRoot}/buildSrc/dependencies.gradle"
36ext.docs = [:]
37ext.docs.offline = rootProject.getProperties().containsKey("offlineDocs")
38ext.docs.dac = [
39        libraryroot: "android/support",
40        dataname: "SUPPORT_DATA"
41]
42
43def enableDoclavaAndJDiff(p) {
44    p.configurations {
45        doclava
46        jdiff
47    }
48
49    p.dependencies {
50        doclava project(':doclava')
51        jdiff project(':jdiff')
52        jdiff libs.xml_parser_apis
53        jdiff libs.xerces_impl
54    }
55    apply from: "${ext.supportRootFolder}/buildSrc/diff_and_docs.gradle"
56}
57
58def getFullSdkPath() {
59    final String osName = System.getProperty("os.name").toLowerCase();
60    final boolean isMacOsX =
61            osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx");
62    final String platform = isMacOsX ? 'darwin' : 'linux'
63    return "${repos.prebuiltsRoot}/fullsdk-${platform}"
64}
65
66def setSdkInLocalPropertiesFile() {
67    ext.buildToolsVersion = '26.0.0'
68    final String fullSdkPath = getFullSdkPath();
69    if (file(fullSdkPath).exists()) {
70        gradle.ext.currentSdk = 26
71        project.ext.androidJar =
72                files("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android.jar")
73        project.ext.androidSrcJar =
74                file("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android-stubs-src.jar")
75        project.ext.androidApiTxt = null
76        File props = file("local.properties")
77        props.write "sdk.dir=${fullSdkPath}"
78        ext.usingFullSdk = true
79    } else {
80        gradle.ext.currentSdk = 'current'
81        project.ext.androidJar = files("${repos.prebuiltsRoot}/sdk/current/android.jar")
82        project.ext.androidSrcJar = null
83        project.ext.androidApiTxt = file("${repos.prebuiltsRoot}/sdk/api/26.txt")
84        System.setProperty('android.dir', "${supportRootFolder}/../../")
85        File props = file("local.properties")
86        props.write "android.dir=../../"
87        ext.usingFullSdk = false
88    }
89}
90
91def setupRepoOutAndBuildNumber() {
92    // common support repo folder which works well for prebuilts.
93    ext.supportRepoOut = ''
94    ext.buildNumber = "0"
95    /*
96     * With the build server you are given two env variables.
97     * The OUT_DIR is a temporary directory you can use to put things during the build.
98     * The DIST_DIR is where you want to save things from the build.
99     *
100     * The build server will copy the contents of DIST_DIR to somewhere and make it available.
101     */
102    if (ext.runningInBuildServer) {
103        buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build')
104                .getCanonicalFile()
105        project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
106
107        // the build server does not pass the build number so we infer it from the last folder of
108        // the dist path.
109        ext.buildNumber = project.ext.distDir.getName()
110
111        // the build server should always print out full stack traces for any failures.
112        gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
113    } else {
114        buildDir = file("${ext.supportRootFolder}/../../out/host/gradle/frameworks/support/build")
115        project.ext.distDir = new File("${ext.supportRootFolder}/../../out/dist")
116    }
117    subprojects {
118        // Change buildDir first so that all plugins pick up the new value.
119        project.buildDir = new File("$project.parent.buildDir/../$project.name/build")
120    }
121    ext.supportRepoOut = new File(buildDir, 'support_repo')
122    ext.testApkDistOut = ext.distDir
123    ext.testResultsDistDir = new File(distDir, "host-test-reports")
124    ext.docsDir = new File(buildDir, 'javadoc')
125}
126
127def configureBuildOnServer() {
128    def buildOnServerTask = rootProject.tasks.create("buildOnServer")
129    rootProject.tasks.whenTaskAdded { task ->
130        if ("createArchive".equals(task.name)) {
131            buildOnServerTask.dependsOn task
132        }
133    }
134
135    subprojects {
136        project.tasks.whenTaskAdded { task ->
137            if ("assembleErrorProne".equals(task.name) || "assembleAndroidTest".equals(task.name)) {
138                buildOnServerTask.dependsOn task
139            }
140        }
141    }
142}
143
144def configureSubProjects() {
145    // lint every library
146    def lintTask = project.tasks.create("lint")
147    subprojects {
148        repos.addMavenRepositories(repositories)
149
150        // Only modify Android projects.
151        if (project.name.equals('doclava')
152                || project.name.equals('jdiff')
153                || project.name.equals('noto-emoji-compat')
154                || project.name.equals('support-media-compat-test-lib')) {
155            // disable tests and return
156            project.tasks.whenTaskAdded { task ->
157                if (task instanceof org.gradle.api.tasks.testing.Test) {
158                    task.enabled = false
159                }
160            }
161            return
162        }
163
164        project.ext.currentSdk = gradle.ext.currentSdk
165        apply plugin: 'maven'
166
167        version = LibraryVersions.SUPPORT_LIBRARY.toString();
168        group = 'com.android.support'
169
170        project.plugins.whenPluginAdded { plugin ->
171            def isAndroidLibrary = "com.android.build.gradle.LibraryPlugin"
172                    .equals(plugin.class.name)
173            def isAndroidApp = "com.android.build.gradle.AppPlugin".equals(plugin.class.name)
174            def isJavaLibrary = "org.gradle.api.plugins.JavaPlugin".equals(plugin.class.name)
175
176            if (isAndroidLibrary || isAndroidApp) {
177                project.android.buildToolsVersion = rootProject.buildToolsVersion
178
179                // Enable code coverage for debug builds only if we are not running inside the IDE,
180                // since enabling coverage reports breaks the method parameter resolution in the IDE
181                // debugger.
182                project.android.buildTypes.debug.testCoverageEnabled =
183                        !project.hasProperty('android.injected.invoked.from.ide')
184
185                // Copy the class files in a jar to be later used to generate code coverage report
186                project.android.testVariants.all { v ->
187                    // check if the variant has any source files
188                    // and test coverage is enabled
189                    if (v.buildType.testCoverageEnabled
190                            && v.sourceSets.any { !it.java.sourceFiles.isEmpty() }) {
191                        def jarifyTask = project.tasks.create(
192                                name: "package${v.name.capitalize()}ClassFilesForCoverageReport",
193                                type: Jar) {
194                            from v.testedVariant.javaCompile.destinationDir
195                            exclude "**/R.class"
196                            exclude "**/R\$*.class"
197                            exclude "**/BuildConfig.class"
198                            destinationDir file(project.distDir)
199                            archiveName "${project.name}-${v.baseName}-allclasses.jar"
200                        }
201
202                        def collectJacocoAntPackages = project.tasks.create(
203                                name: "collectJacocoAntPackages",
204                                type: Jar) {
205                            inputs.files project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
206                            from {
207                                project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
208                                        .resolvedConfiguration
209                                        .resolvedArtifacts.collect{ zipTree(it.getFile()) }} {
210                                    // exclude all the signatures the jar might have
211                                    exclude "META-INF/*.SF"
212                                    exclude "META-INF/*.DSA"
213                                    exclude "META-INF/*.RSA"
214                                }
215                            destinationDir file(project.distDir)
216                            archiveName "jacocoant.jar"
217                        }
218
219                        jarifyTask.dependsOn v.getJavaCompiler()
220                        v.assemble.dependsOn jarifyTask , collectJacocoAntPackages
221                    }
222                }
223
224                // Enforce NewApi lint check as fatal.
225                project.android.lintOptions.fatal 'NewApi'
226                lintTask.dependsOn {project.lint}
227            }
228
229            if (isAndroidLibrary || isJavaLibrary) {
230                // Add library to the aggregate dependency report.
231                task allDeps(type: DependencyReportTask) {}
232
233                project.afterEvaluate {
234                    Upload uploadTask = (Upload) project.tasks.uploadArchives;
235                    uploadTask.repositories.mavenDeployer {
236                        repository(url: uri("$rootProject.ext.supportRepoOut"))
237                        setUniqueVersion(true)
238                    }
239
240                    // Before the upload, make sure the repo is ready.
241                    uploadTask.dependsOn rootProject.tasks.prepareRepo
242
243                    // Make the mainupload depend on this one.
244                    mainUpload.dependsOn uploadTask
245                }
246            }
247        }
248
249        // Copy instrumentation test APKs and app APKs into the dist dir
250        // For test apks, they are uploaded only if we have java test sources.
251        // For regular app apks, they are uploaded only if they have java sources.
252        project.tasks.whenTaskAdded { task ->
253            if (task.name.startsWith("packageDebug")) {
254                def testApk = task.name.contains("AndroidTest")
255                task.doLast {
256                    def source = testApk ? project.android.sourceSets.androidTest
257                            : project.android.sourceSets.main
258                    if (task.hasProperty("outputDirectory") && !source.java.sourceFiles.isEmpty()) {
259                        copy {
260                            from(task.outputDirectory)
261                            include '*.apk'
262                            into(rootProject.ext.testApkDistOut)
263                            rename { String fileName ->
264                                // Exclude media-compat-test-* modules from existing support library
265                                // presubmit tests.
266                                if (fileName.contains("media-compat-test")) {
267                                    fileName.replace("-debug-androidTest", "")
268                                } else {
269                                    // multiple modules may have the same name so prefix the name with
270                                    // the module's path to ensure it is unique.
271                                    // e.g. palette-v7-debug-androidTest.apk becomes
272                                    // support-palette-v7_palette-v7-debug-androidTest.apk
273                                    "${project.getPath().replace(':', '-').substring(1)}_${fileName}"
274                                }
275                            }
276                        }
277                    }
278                }
279            }
280        }
281
282        // copy host side test results to DIST
283        project.tasks.whenTaskAdded { task ->
284            if (task instanceof org.gradle.api.tasks.testing.Test) {
285                def junitReport = task.reports.junitXml
286                if (junitReport.enabled) {
287                    def zipTask = project.tasks.create(name : "zipResultsOf${task.name.capitalize()}", type : Zip) {
288                        destinationDir(testResultsDistDir)
289                        // first one is always :, drop it.
290                        archiveName("${project.getPath().split(":").join("_").substring(1)}.zip")
291                    }
292                    if (project.rootProject.ext.runningInBuildServer) {
293                        task.ignoreFailures = true
294                    }
295                    task.finalizedBy zipTask
296                    task.doFirst {
297                        zipTask.from(junitReport.destination)
298                    }
299                }
300            }
301        }
302
303        project.afterEvaluate { p ->
304            // remove dependency on the test so that we still get coverage even if some tests fail
305            p.tasks.findAll { it instanceof JacocoReportTask }.each { task ->
306                def toBeRemoved = new ArrayList()
307                def dependencyList = task.taskDependencies.values
308                dependencyList.each { dep ->
309                    if (dep instanceof String) {
310                        def t = tasks.findByName(dep)
311                        if (t instanceof DeviceProviderInstrumentTestTask) {
312                            toBeRemoved.add(dep)
313                            task.mustRunAfter(t)
314                        }
315                    }
316                }
317                toBeRemoved.each { dep ->
318                    dependencyList.remove(dep)
319                }
320            }
321        }
322    }
323}
324
325def setupRelease() {
326    apply from: "${ext.supportRootFolder}/buildSrc/release.gradle"
327}
328
329ext.init.enableDoclavaAndJDiff = this.&enableDoclavaAndJDiff
330ext.init.setSdkInLocalPropertiesFile = this.&setSdkInLocalPropertiesFile
331ext.init.setupRepoOutAndBuildNumber = this.&setupRepoOutAndBuildNumber
332ext.init.setupRelease = this.&setupRelease
333ext.init.configureSubProjects = this.&configureSubProjects
334ext.init.configureBuildOnServer = this.&configureBuildOnServer
335