Home | History | Annotate | Download | only in buildSrc
      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 
     17 
     18 import androidx.build.DiffAndDocs
     19 import androidx.build.PublishDocsRulesKt
     20 import androidx.build.gmaven.GMavenVersionChecker
     21 import androidx.build.license.CheckExternalDependencyLicensesTask
     22 import com.android.build.gradle.internal.coverage.JacocoReportTask
     23 import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
     24 import org.gradle.api.logging.configuration.ShowStacktrace
     25 
     26 import javax.tools.ToolProvider
     27 
     28 def supportRoot = ext.supportRootFolder
     29 if (supportRoot == null) {
     30     throw new RuntimeException("variable supportRootFolder is not set. you must set it before" +
     31             " including this script")
     32 }
     33 def init = new Properties()
     34 ext.init = init
     35 rootProject.ext.versionChecker = new GMavenVersionChecker(rootProject.logger)
     36 ext.runningInBuildServer = System.env.DIST_DIR != null && System.env.OUT_DIR != null
     37 
     38 apply from: "${supportRoot}/buildSrc/dependencies.gradle"
     39 apply from: "${supportRoot}/buildSrc/build_dependencies.gradle"
     40 apply from: "${supportRoot}/buildSrc/unbundled_check.gradle"
     41 
     42 
     43 def enableDoclavaAndJDiff(p, dacOptions, rules = []) {
     44     p.configurations {
     45         doclava
     46         jdiff
     47     }
     48 
     49     p.dependencies {
     50         doclava build_libs.doclava
     51         // tools.jar required for com.sun.javadoc
     52         doclava files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs())
     53         jdiff build_libs.jdiff
     54         jdiff build_libs.xml_parser_apis
     55         jdiff build_libs.xerces_impl
     56     }
     57 
     58     return DiffAndDocs.configureDiffAndDocs(rootProject, supportRootFolder,
     59             dacOptions, rules)
     60 }
     61 
     62 def getFullSdkPath() {
     63     if (isUnbundledBuild(ext.supportRootFolder)) {
     64         Properties properties = new Properties()
     65         File propertiesFile = new File('local.properties')
     66         if (propertiesFile.exists()) {
     67             propertiesFile.withInputStream {
     68                 properties.load(it)
     69             }
     70         }
     71         File location = findSdkLocation(properties, supportRootFolder)
     72         return location.getAbsolutePath()
     73     } else {
     74         final String osName = System.getProperty("os.name").toLowerCase();
     75         final boolean isMacOsX =
     76                 osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx");
     77         final String platform = isMacOsX ? 'darwin' : 'linux'
     78         return "${repos.prebuiltsRoot}/fullsdk-${platform}"
     79     }
     80 }
     81 
     82 /**
     83  * Adapted from com.android.build.gradle.internal.SdkHandler
     84  */
     85 public static File findSdkLocation(Properties properties, File rootDir) {
     86     String sdkDirProp = properties.getProperty("sdk.dir");
     87     if (sdkDirProp != null) {
     88         File sdk = new File(sdkDirProp);
     89         if (!sdk.isAbsolute()) {
     90             sdk = new File(rootDir, sdkDirProp);
     91         }
     92         return sdk
     93     }
     94 
     95     sdkDirProp = properties.getProperty("android.dir");
     96     if (sdkDirProp != null) {
     97         return new File(rootDir, sdkDirProp);
     98     }
     99 
    100     String envVar = System.getenv("ANDROID_HOME");
    101     if (envVar != null) {
    102         return new File(envVar);
    103     }
    104 
    105     String property = System.getProperty("android.home");
    106     if (property != null) {
    107         return new File(property);
    108     }
    109     return null;
    110 }
    111 
    112 def setSdkInLocalPropertiesFile() {
    113     final File fullSdkPath = file(getFullSdkPath())
    114     if (fullSdkPath.exists()) {
    115         project.ext.fullSdkPath = fullSdkPath
    116         File props = file("local.properties")
    117         props.write "sdk.dir=${fullSdkPath.getAbsolutePath()}"
    118         ext.usingFullSdk = true
    119     } else {
    120         throw Exception("You are using non ub-supportlib-* checkout. You need to check out "
    121                 + "ub-supportlib-* to work on support library. See go/supportlib for details.")
    122     }
    123 }
    124 
    125 def setupRepoOutAndBuildNumber() {
    126     // common support repo folder which works well for prebuilts.
    127     ext.supportRepoOut = ''
    128     ext.buildNumber = "0"
    129     /*
    130      * With the build server you are given two env variables.
    131      * The OUT_DIR is a temporary directory you can use to put things during the build.
    132      * The DIST_DIR is where you want to save things from the build.
    133      *
    134      * The build server will copy the contents of DIST_DIR to somewhere and make it available.
    135      */
    136     if (ext.runningInBuildServer) {
    137         buildDir = new File(System.env.OUT_DIR + '/gradle/frameworks/support/build')
    138                 .getCanonicalFile()
    139         project.ext.distDir = new File(System.env.DIST_DIR).getCanonicalFile()
    140 
    141         // the build server does not pass the build number so we infer it from the last folder of
    142         // the dist path.
    143         ext.buildNumber = project.ext.distDir.getName()
    144 
    145         // the build server should always print out full stack traces for any failures.
    146         gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
    147     } else {
    148         buildDir = file("${ext.supportRootFolder}/../../out/host/gradle/frameworks/support/build")
    149         project.ext.distDir = new File("${ext.supportRootFolder}/../../out/dist")
    150     }
    151     subprojects {
    152         // Change buildDir first so that all plugins pick up the new value.
    153         project.buildDir = new File("$project.parent.buildDir/../$project.name/build")
    154     }
    155     ext.supportRepoOut = new File(buildDir, 'support_repo')
    156     ext.testApkDistOut = ext.distDir
    157     ext.testResultsDistDir = new File(distDir, "host-test-reports")
    158     ext.docsDir = new File(buildDir, 'javadoc')
    159 }
    160 
    161 def configureBuildOnServer() {
    162     def buildOnServerTask = rootProject.tasks.create("buildOnServer")
    163     rootProject.tasks.whenTaskAdded { task ->
    164         if ("createArchive".equals(task.name)
    165                 || "createDiffArchive".equals(task.name)
    166                 || "distDocs".equals(task.name)
    167                 || "dejetifyArchive".equals(task.name)
    168                 || CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)) {
    169             buildOnServerTask.dependsOn task
    170         }
    171     }
    172     def docsProject = rootProject.findProject(":docs-fake")
    173     subprojects {
    174         if (docsProject == project) {
    175             return
    176         }
    177         project.tasks.whenTaskAdded { task ->
    178             if ("assembleErrorProne".equals(task.name)
    179                     || "assembleAndroidTest".equals(task.name)
    180                     || "assembleDebug".equals(task.name)) {
    181                 buildOnServerTask.dependsOn task
    182             }
    183         }
    184     }
    185     buildOnServerTask.dependsOn createJacocoAntUberJarTask()
    186     return buildOnServerTask
    187 }
    188 
    189 def createJacocoAntUberJarTask() {
    190     def myJacoco = project.configurations.create('myJacoco')
    191     project.dependencies.add('myJacoco', build_libs.jacoco_ant)
    192 
    193     return project.tasks.create(
    194             name: "JacocoAntUberJar",
    195             type: Jar) {
    196         inputs.files myJacoco
    197         from {
    198             myJacoco
    199                     .resolvedConfiguration
    200                     .resolvedArtifacts.collect{ zipTree(it.getFile()) }} {
    201             // exclude all the signatures the jar might have
    202             exclude "META-INF/*.SF"
    203             exclude "META-INF/*.DSA"
    204             exclude "META-INF/*.RSA"
    205         }
    206         destinationDir file(project.distDir)
    207         archiveName "jacocoant.jar"
    208     }
    209 }
    210 
    211 def configureSubProjects() {
    212     subprojects {
    213         repos.addMavenRepositories(repositories)
    214 
    215         // Only modify Android projects.
    216         if (project.name.equals('noto-emoji-compat')) {
    217             // disable tests and return
    218             project.tasks.whenTaskAdded { task ->
    219                 if (task instanceof org.gradle.api.tasks.testing.Test) {
    220                     task.enabled = false
    221                 }
    222             }
    223             return
    224         }
    225 
    226         project.plugins.whenPluginAdded { plugin ->
    227             def isAndroidLibrary = "com.android.build.gradle.LibraryPlugin"
    228                     .equals(plugin.class.name)
    229             def isAndroidApp = "com.android.build.gradle.AppPlugin".equals(plugin.class.name)
    230 
    231             if (isAndroidLibrary || isAndroidApp) {
    232                 // Enable code coverage for debug builds only if we are not running inside the IDE,
    233                 // since enabling coverage reports breaks the method parameter resolution in the IDE
    234                 // debugger.
    235                 project.android.buildTypes.debug.testCoverageEnabled =
    236                         !project.hasProperty('android.injected.invoked.from.ide')
    237 
    238                 // Copy the class files in a jar to be later used to generate code coverage report
    239                 project.android.testVariants.all { v ->
    240                     // check if the variant has any source files
    241                     // and test coverage is enabled
    242                     if (v.buildType.testCoverageEnabled
    243                             && v.sourceSets.any { !it.java.sourceFiles.isEmpty() }) {
    244                         def jarifyTask = project.tasks.create(
    245                                 name: "package${v.name.capitalize()}ClassFilesForCoverageReport",
    246                                 type: Jar) {
    247                             from v.testedVariant.javaCompile.destinationDir
    248                             exclude "**/R.class"
    249                             exclude "**/R\$*.class"
    250                             exclude "**/BuildConfig.class"
    251                             destinationDir file(project.distDir)
    252                             archiveName "${project.name}-${v.baseName}-allclasses.jar"
    253                         }
    254 
    255                         jarifyTask.dependsOn v.getJavaCompiler()
    256                         v.assemble.dependsOn jarifyTask
    257                     }
    258                 }
    259             }
    260         }
    261 
    262         // Copy instrumentation test APKs and app APKs into the dist dir
    263         // For test apks, they are uploaded only if we have java test sources.
    264         // For regular app apks, they are uploaded only if they have java sources.
    265         project.tasks.whenTaskAdded { task ->
    266             if (task.name.startsWith("packageDebug")) {
    267                 def testApk = task.name.contains("AndroidTest")
    268                 task.doLast {
    269                     def source = testApk ? project.android.sourceSets.androidTest
    270                             : project.android.sourceSets.main
    271                     def hasKotlinSources = false
    272                     if (source.hasProperty('kotlin')) {
    273                         if (!source.kotlin.files.isEmpty()) {
    274                             hasKotlinSources = true
    275                         } else {
    276                             // kotlin files does not show in java sources due to the *.java filter
    277                             // so we need to check them manually
    278                             hasKotlinSources = source.java.sourceDirectoryTrees.any {
    279                                 !fileTree(dir: it.dir, include:'**/*.kt').files.isEmpty()
    280                             }
    281                         }
    282                     }
    283                     def hasSourceCode = !source.java.sourceFiles.isEmpty() || hasKotlinSources
    284                     if (task.hasProperty("outputDirectory") && (hasSourceCode || !testApk)) {
    285                         copy {
    286                             from(task.outputDirectory)
    287                             include '*.apk'
    288                             into(rootProject.ext.testApkDistOut)
    289                             rename { String fileName ->
    290                                 // Exclude media-compat-test-* modules from existing support library
    291                                 // presubmit tests.
    292                                 if (fileName.contains("media-compat-test")) {
    293                                     fileName.replace("-debug-androidTest", "")
    294                                 } else {
    295                                     // multiple modules may have the same name so prefix the name with
    296                                     // the module's path to ensure it is unique.
    297                                     // e.g. palette-v7-debug-androidTest.apk becomes
    298                                     // support-palette-v7_palette-v7-debug-androidTest.apk
    299                                     "${project.getPath().replace(':', '-').substring(1)}_${fileName}"
    300                                 }
    301                             }
    302                         }
    303                     }
    304                 }
    305             }
    306         }
    307 
    308         // copy host side test results to DIST
    309         project.tasks.whenTaskAdded { task ->
    310             if (task instanceof org.gradle.api.tasks.testing.Test) {
    311                 def junitReport = task.reports.junitXml
    312                 if (junitReport.enabled) {
    313                     def zipTask = project.tasks.create(name : "zipResultsOf${task.name.capitalize()}", type : Zip) {
    314                         destinationDir(testResultsDistDir)
    315                         // first one is always :, drop it.
    316                         archiveName("${project.getPath().split(":").join("_").substring(1)}.zip")
    317                     }
    318                     if (project.rootProject.ext.runningInBuildServer) {
    319                         task.ignoreFailures = true
    320                     }
    321                     task.finalizedBy zipTask
    322                     task.doFirst {
    323                         zipTask.from(junitReport.destination)
    324                     }
    325                 }
    326             }
    327         }
    328 
    329         project.afterEvaluate { p ->
    330             // remove dependency on the test so that we still get coverage even if some tests fail
    331             p.tasks.findAll { it instanceof JacocoReportTask }.each { task ->
    332                 def toBeRemoved = new ArrayList()
    333                 def dependencyList = task.taskDependencies.values
    334                 dependencyList.each { dep ->
    335                     if (dep instanceof String) {
    336                         def t = tasks.findByName(dep)
    337                         if (t instanceof DeviceProviderInstrumentTestTask) {
    338                             toBeRemoved.add(dep)
    339                             task.mustRunAfter(t)
    340                         }
    341                     }
    342                 }
    343                 toBeRemoved.each { dep ->
    344                     dependencyList.remove(dep)
    345                 }
    346             }
    347         }
    348     }
    349 }
    350 
    351 def setupRelease() {
    352     apply from: "${ext.supportRootFolder}/buildSrc/release.gradle"
    353 }
    354 
    355 ext.init.enableDoclavaAndJDiff = this.&enableDoclavaAndJDiff
    356 ext.init.setSdkInLocalPropertiesFile = this.&setSdkInLocalPropertiesFile
    357 ext.init.setupRepoOutAndBuildNumber = this.&setupRepoOutAndBuildNumber
    358 ext.init.setupRelease = this.&setupRelease
    359 ext.init.configureSubProjects = this.&configureSubProjects
    360 ext.init.configureBuildOnServer = this.&configureBuildOnServer
    361